package types import ( "context" "encoding/json" "time" "github.com/SigNoz/signoz/pkg/errors" "github.com/SigNoz/signoz/pkg/valuer" "github.com/uptrace/bun" ) var ( ErrCodeUserNotFound = errors.MustNewCode("user_not_found") ErrCodeAmbiguousUser = errors.MustNewCode("ambiguous_user") ErrUserAlreadyExists = errors.MustNewCode("user_already_exists") ErrPasswordAlreadyExists = errors.MustNewCode("password_already_exists") ErrResetPasswordTokenAlreadyExists = errors.MustNewCode("reset_password_token_already_exists") ErrPasswordNotFound = errors.MustNewCode("password_not_found") ErrResetPasswordTokenNotFound = errors.MustNewCode("reset_password_token_not_found") ErrAPIKeyAlreadyExists = errors.MustNewCode("api_key_already_exists") ErrAPIKeyNotFound = errors.MustNewCode("api_key_not_found") ErrCodeRootUserOperationUnsupported = errors.MustNewCode("root_user_operation_unsupported") ) type GettableUser = User type User struct { bun.BaseModel `bun:"table:users"` Identifiable DisplayName string `bun:"display_name" json:"displayName"` Email valuer.Email `bun:"email" json:"email"` Role Role `bun:"role" json:"role"` OrgID valuer.UUID `bun:"org_id" json:"orgId"` IsRoot bool `bun:"is_root" json:"isRoot"` TimeAuditable } type PostableRegisterOrgAndAdmin struct { Name string `json:"name"` Email valuer.Email `json:"email"` Password string `json:"password"` OrgDisplayName string `json:"orgDisplayName"` OrgName string `json:"orgName"` } func NewUser(displayName string, email valuer.Email, role Role, orgID valuer.UUID) (*User, error) { if email.IsZero() { 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.IsZero() { return nil, errors.New(errors.TypeInvalidInput, errors.CodeInvalidInput, "orgID is required") } return &User{ Identifiable: Identifiable{ ID: valuer.GenerateUUID(), }, DisplayName: displayName, Email: email, Role: role, OrgID: orgID, IsRoot: false, TimeAuditable: TimeAuditable{ CreatedAt: time.Now(), UpdatedAt: time.Now(), }, }, nil } func NewRootUser(displayName string, email valuer.Email, orgID valuer.UUID) (*User, error) { if email.IsZero() { return nil, errors.New(errors.TypeInvalidInput, errors.CodeInvalidInput, "email is required") } if orgID.IsZero() { return nil, errors.New(errors.TypeInvalidInput, errors.CodeInvalidInput, "orgID is required") } return &User{ Identifiable: Identifiable{ ID: valuer.GenerateUUID(), }, DisplayName: displayName, Email: email, Role: RoleAdmin, OrgID: orgID, IsRoot: true, TimeAuditable: TimeAuditable{ CreatedAt: time.Now(), UpdatedAt: time.Now(), }, }, nil } // Update applies mutable fields from the input to the user. Immutable fields // (email, is_root, org_id, id) are preserved. Only non-zero input fields are applied. func (u *User) Update(displayName string, role Role) { if displayName != "" { u.DisplayName = displayName } if role != "" { u.Role = role } u.UpdatedAt = time.Now() } // PromoteToRoot promotes the user to a root user with admin role. func (u *User) PromoteToRoot() { u.IsRoot = true u.Role = RoleAdmin u.UpdatedAt = time.Now() } // UpdateEmail updates the email of the user. func (u *User) UpdateEmail(email valuer.Email) { u.Email = email u.UpdatedAt = time.Now() } // ErrIfRoot returns an error if the user is a root user. The caller should // enrich the error with the specific operation using errors.WithAdditionalf. func (u *User) ErrIfRoot() error { if u.IsRoot { return errors.New(errors.TypeUnsupported, ErrCodeRootUserOperationUnsupported, "this operation is not supported for the root user") } return nil } func NewTraitsFromUser(user *User) map[string]any { return map[string]any{ "name": user.DisplayName, "role": user.Role, "email": user.Email.String(), "display_name": user.DisplayName, "created_at": user.CreatedAt, } } func (request *PostableRegisterOrgAndAdmin) UnmarshalJSON(data []byte) error { type Alias PostableRegisterOrgAndAdmin var temp Alias if err := json.Unmarshal(data, &temp); err != nil { return err } if !IsPasswordValid(temp.Password) { return ErrInvalidPassword } *request = PostableRegisterOrgAndAdmin(temp) return nil } 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 // Get invite by token. GetInviteByToken(ctx context.Context, token string) (*Invite, error) // Get invite by email and org. GetInviteByEmailAndOrgID(ctx context.Context, email valuer.Email, orgID valuer.UUID) (*Invite, error) // Creates a user. CreateUser(ctx context.Context, user *User) error // Get user by id. GetUser(context.Context, valuer.UUID) (*User, error) // Get user by orgID and id. GetByOrgIDAndID(ctx context.Context, orgID valuer.UUID, id valuer.UUID) (*User, error) // Get user by email and orgID. GetUserByEmailAndOrgID(ctx context.Context, email valuer.Email, orgID valuer.UUID) (*User, error) // Get users by email. GetUsersByEmail(ctx context.Context, email valuer.Email) ([]*User, error) // Get users by role and org. GetUsersByRoleAndOrgID(ctx context.Context, role Role, orgID valuer.UUID) ([]*User, error) // List users by org. ListUsersByOrgID(ctx context.Context, orgID valuer.UUID) ([]*User, error) // List users by email and org ids. ListUsersByEmailAndOrgIDs(ctx context.Context, email valuer.Email, orgIDs []valuer.UUID) ([]*User, error) UpdateUser(ctx context.Context, orgID valuer.UUID, user *User) error DeleteUser(ctx context.Context, orgID string, id string) error // Creates a password. CreatePassword(ctx context.Context, password *FactorPassword) error CreateResetPasswordToken(ctx context.Context, resetPasswordRequest *ResetPasswordToken) error GetPassword(ctx context.Context, id valuer.UUID) (*FactorPassword, error) GetPasswordByUserID(ctx context.Context, userID valuer.UUID) (*FactorPassword, error) GetResetPasswordToken(ctx context.Context, token string) (*ResetPasswordToken, error) GetResetPasswordTokenByPasswordID(ctx context.Context, passwordID valuer.UUID) (*ResetPasswordToken, error) DeleteResetPasswordTokenByPasswordID(ctx context.Context, passwordID valuer.UUID) error UpdatePassword(ctx context.Context, password *FactorPassword) error // API KEY CreateAPIKey(ctx context.Context, apiKey *StorableAPIKey) error UpdateAPIKey(ctx context.Context, id valuer.UUID, apiKey *StorableAPIKey, updaterID valuer.UUID) error ListAPIKeys(ctx context.Context, orgID valuer.UUID) ([]*StorableAPIKeyUser, error) RevokeAPIKey(ctx context.Context, id valuer.UUID, revokedByUserID valuer.UUID) error GetAPIKey(ctx context.Context, orgID, id valuer.UUID) (*StorableAPIKeyUser, error) CountAPIKeyByOrgID(ctx context.Context, orgID valuer.UUID) (int64, error) CountByOrgID(ctx context.Context, orgID valuer.UUID) (int64, error) // Get root user by org. GetRootUserByOrgID(ctx context.Context, orgID valuer.UUID) (*User, error) // Transaction RunInTx(ctx context.Context, cb func(ctx context.Context) error) error }