mirror of
https://git.mirrors.martin98.com/https://github.com/SigNoz/signoz
synced 2025-06-04 11:25:52 +08:00

* chore: update auth * chore: password changes * chore: make changes in oss code * chore: login * chore: get to a running state * fix: migration inital commit * fix: signoz cloud intgtn tests * fix: minor fixes * chore: sso code fixed with org domain * fix: tests * fix: ee auth api's * fix: changes in name * fix: return user in login api * fix: address comments * fix: validate password * fix: handle get domain by email properly * fix: move authomain to usermodule * fix: use displayname instead of hname * fix: rename back endpoints * fix: update telemetry * fix: correct errors * fix: test and fix the invite endpoints * fix: delete all things related to user in store * fix: address issues * fix: ee delete invite * fix: rename func * fix: update user and update role * fix: update role * fix: login and invite changes * fix: return org name in users response * fix: update user role * fix: nil check * fix: getinvite and update role * fix: sso * fix: getinvite use sso ctx * fix: use correct sourceurl * fix: getsourceurl from req payload * fix: update created_at * fix: fix reset password * fix: sso signup and token password change * fix: don't delete last admin * fix: reset password and migration * fix: migration * fix: reset password for sso users * fix: clean up invite * fix: migration * fix: update claims and store code * fix: use correct error * fix: proper nil checks * fix: make migration multitenant * fix: address comments * fix: minor fixes * fix: test * fix: rename reset password --------- Co-authored-by: Vikrant Gupta <vikrant@signoz.io>
241 lines
7.7 KiB
Go
241 lines
7.7 KiB
Go
package types
|
|
|
|
import (
|
|
"context"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/SigNoz/signoz/pkg/errors"
|
|
"github.com/SigNoz/signoz/pkg/valuer"
|
|
"github.com/uptrace/bun"
|
|
"golang.org/x/crypto/bcrypt"
|
|
)
|
|
|
|
const (
|
|
SSOAvailable = "sso_available"
|
|
)
|
|
|
|
var (
|
|
ErrUserAlreadyExists = errors.MustNewCode("user_already_exists")
|
|
ErrPasswordAlreadyExists = errors.MustNewCode("password_already_exists")
|
|
ErrUserNotFound = errors.MustNewCode("user_not_found")
|
|
ErrResetPasswordTokenAlreadyExists = errors.MustNewCode("reset_password_token_already_exists")
|
|
ErrPasswordNotFound = errors.MustNewCode("password_not_found")
|
|
ErrResetPasswordTokenNotFound = errors.MustNewCode("reset_password_token_not_found")
|
|
)
|
|
|
|
type UserStore interface {
|
|
// invite
|
|
CreateBulkInvite(ctx context.Context, invites []*Invite) error
|
|
ListInvite(ctx context.Context, orgID string) ([]*Invite, error)
|
|
DeleteInvite(ctx context.Context, orgID string, id valuer.UUID) error
|
|
GetInviteByToken(ctx context.Context, token string) (*GettableInvite, error)
|
|
GetInviteByEmailInOrg(ctx context.Context, orgID string, email string) (*Invite, error)
|
|
|
|
// user
|
|
CreateUserWithPassword(ctx context.Context, user *User, password *FactorPassword) (*User, error)
|
|
CreateUser(ctx context.Context, user *User) error
|
|
GetUserByID(ctx context.Context, orgID string, id string) (*GettableUser, error)
|
|
GetUserByEmailInOrg(ctx context.Context, orgID string, email string) (*GettableUser, error)
|
|
GetUsersByEmail(ctx context.Context, email string) ([]*GettableUser, error)
|
|
GetUsersByRoleInOrg(ctx context.Context, orgID string, role Role) ([]*GettableUser, error)
|
|
ListUsers(ctx context.Context, orgID string) ([]*GettableUser, error)
|
|
UpdateUser(ctx context.Context, orgID string, id string, user *User) (*User, error)
|
|
DeleteUser(ctx context.Context, orgID string, id string) error
|
|
|
|
// password
|
|
CreatePassword(ctx context.Context, password *FactorPassword) (*FactorPassword, error)
|
|
CreateResetPasswordToken(ctx context.Context, resetPasswordRequest *ResetPasswordRequest) error
|
|
GetPasswordByID(ctx context.Context, id string) (*FactorPassword, error)
|
|
GetPasswordByUserID(ctx context.Context, id string) (*FactorPassword, error)
|
|
GetResetPassword(ctx context.Context, token string) (*ResetPasswordRequest, error)
|
|
GetResetPasswordByPasswordID(ctx context.Context, passwordID string) (*ResetPasswordRequest, error)
|
|
UpdatePassword(ctx context.Context, userID string, password string) error
|
|
UpdatePasswordAndDeleteResetPasswordEntry(ctx context.Context, userID string, password string) error
|
|
|
|
// Auth Domain
|
|
GetDomainByName(ctx context.Context, name string) (*StorableOrgDomain, error)
|
|
|
|
// Temporary func for SSO
|
|
GetDefaultOrgID(ctx context.Context) (string, error)
|
|
}
|
|
|
|
type GettableUser struct {
|
|
User
|
|
Organization string `json:"organization"`
|
|
}
|
|
|
|
type User struct {
|
|
bun.BaseModel `bun:"table:users"`
|
|
|
|
Identifiable
|
|
TimeAuditable
|
|
DisplayName string `bun:"display_name,type:text,notnull" json:"displayName"`
|
|
Email string `bun:"email,type:text,notnull,unique:org_email" json:"email"`
|
|
Role string `bun:"role,type:text,notnull" json:"role"`
|
|
OrgID string `bun:"org_id,type:text,notnull,unique:org_email,references:org(id),on_delete:CASCADE" json:"orgId"`
|
|
}
|
|
|
|
func NewUser(displayName string, email string, role string, orgID string) (*User, error) {
|
|
if email == "" {
|
|
return nil, errors.New(errors.TypeInvalidInput, errors.CodeInvalidInput, "email is required")
|
|
}
|
|
|
|
if role == "" {
|
|
return nil, errors.New(errors.TypeInvalidInput, errors.CodeInvalidInput, "role is required")
|
|
}
|
|
|
|
if orgID == "" {
|
|
return nil, errors.New(errors.TypeInvalidInput, errors.CodeInvalidInput, "orgID is required")
|
|
}
|
|
|
|
return &User{
|
|
Identifiable: Identifiable{
|
|
ID: valuer.GenerateUUID(),
|
|
},
|
|
TimeAuditable: TimeAuditable{
|
|
CreatedAt: time.Now(),
|
|
},
|
|
DisplayName: displayName,
|
|
Email: email,
|
|
Role: role,
|
|
OrgID: orgID,
|
|
}, nil
|
|
}
|
|
|
|
type PostableRegisterOrgAndAdmin struct {
|
|
PostableAcceptInvite
|
|
Name string `json:"name"`
|
|
OrgID string `json:"orgId"`
|
|
OrgDisplayName string `json:"orgDisplayName"`
|
|
OrgName string `json:"orgName"`
|
|
Email string `json:"email"`
|
|
}
|
|
|
|
type PostableAcceptInvite struct {
|
|
DisplayName string `json:"displayName"`
|
|
InviteToken string `json:"token"`
|
|
Password string `json:"password"`
|
|
|
|
// reference URL to track where the register request is coming from
|
|
SourceURL string `json:"sourceUrl"`
|
|
}
|
|
|
|
func (p *PostableAcceptInvite) Validate() error {
|
|
if p.InviteToken == "" {
|
|
return errors.New(errors.TypeInvalidInput, errors.CodeInvalidInput, "invite token is required")
|
|
}
|
|
|
|
if p.Password == "" || len(p.Password) < 8 {
|
|
return errors.New(errors.TypeInvalidInput, errors.CodeInvalidInput, "password must be at least 8 characters long")
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
type FactorPassword struct {
|
|
bun.BaseModel `bun:"table:factor_password"`
|
|
|
|
Identifiable
|
|
TimeAuditable
|
|
Password string `bun:"password,type:text,notnull" json:"password"`
|
|
Temporary bool `bun:"temporary,type:boolean,notnull" json:"temporary"`
|
|
UserID string `bun:"user_id,type:text,notnull,unique,references:user(id)" json:"userId"`
|
|
}
|
|
|
|
func NewFactorPassword(password string) (*FactorPassword, error) {
|
|
|
|
if password == "" && len(password) < 8 {
|
|
return nil, errors.New(errors.TypeInvalidInput, errors.CodeInvalidInput, "password must be at least 8 characters long")
|
|
}
|
|
|
|
password = strings.TrimSpace(password)
|
|
|
|
hashedPassword, err := HashPassword(password)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return &FactorPassword{
|
|
Identifiable: Identifiable{
|
|
ID: valuer.GenerateUUID(),
|
|
},
|
|
TimeAuditable: TimeAuditable{
|
|
CreatedAt: time.Now(),
|
|
},
|
|
Password: hashedPassword,
|
|
Temporary: false,
|
|
}, nil
|
|
}
|
|
|
|
func HashPassword(password string) (string, error) {
|
|
// bcrypt automatically handles salting and uses a secure work factor
|
|
hashedPassword, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
return string(hashedPassword), nil
|
|
}
|
|
|
|
func ComparePassword(hashedPassword, password string) bool {
|
|
return bcrypt.CompareHashAndPassword([]byte(hashedPassword), []byte(password)) == nil
|
|
}
|
|
|
|
type ResetPasswordRequest struct {
|
|
bun.BaseModel `bun:"table:reset_password_token"`
|
|
|
|
Identifiable
|
|
Token string `bun:"token,type:text,notnull" json:"token"`
|
|
PasswordID string `bun:"password_id,type:text,notnull,unique,references:factor_password(id)" json:"passwordId"`
|
|
}
|
|
|
|
func NewResetPasswordRequest(passwordID string) (*ResetPasswordRequest, error) {
|
|
return &ResetPasswordRequest{
|
|
Identifiable: Identifiable{
|
|
ID: valuer.GenerateUUID(),
|
|
},
|
|
Token: valuer.GenerateUUID().String(),
|
|
PasswordID: passwordID,
|
|
}, nil
|
|
}
|
|
|
|
type PostableResetPassword struct {
|
|
Password string `json:"password"`
|
|
Token string `json:"token"`
|
|
}
|
|
|
|
type ChangePasswordRequest struct {
|
|
UserId string `json:"userId"`
|
|
OldPassword string `json:"oldPassword"`
|
|
NewPassword string `json:"newPassword"`
|
|
}
|
|
|
|
type PostableLoginRequest struct {
|
|
OrgID string `json:"orgId"`
|
|
Email string `json:"email"`
|
|
Password string `json:"password"`
|
|
RefreshToken string `json:"refreshToken"`
|
|
}
|
|
|
|
type GettableUserJwt struct {
|
|
AccessJwt string `json:"accessJwt"`
|
|
AccessJwtExpiry int64 `json:"accessJwtExpiry"`
|
|
RefreshJwt string `json:"refreshJwt"`
|
|
RefreshJwtExpiry int64 `json:"refreshJwtExpiry"`
|
|
}
|
|
|
|
type GettableLoginResponse struct {
|
|
GettableUserJwt
|
|
UserID string `json:"userId"`
|
|
}
|
|
|
|
type GettableLoginPrecheck struct {
|
|
SSO bool `json:"sso"`
|
|
SSOUrl string `json:"ssoUrl"`
|
|
CanSelfRegister bool `json:"canSelfRegister"`
|
|
IsUser bool `json:"isUser"`
|
|
SSOError string `json:"ssoError"`
|
|
SelectOrg bool `json:"selectOrg"`
|
|
Orgs []string `json:"orgs"`
|
|
}
|