Help us shape the future of Go! Take the 2026 Go Developer Survey

user

package
v2.17.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Oct 25, 2026 License: BSD-3-Clause Imports: 30 Imported by: 0

Documentation

Overview

Package user owns user persistence, password hashing, authorization guards, and the user HTTP surface.

Index

Constants

This section is empty.

Variables

View Source
var (
	ErrCannotRemoveLastAdmin = errors.New("cannot remove the last admin user")
	ErrUsernameRequired      = errors.New("username is required")
	ErrUsernameTaken         = errors.New("username already in use")
	ErrEmailTaken            = common.Classify(common.ErrConflict, errors.New("email already in use"))

	// ErrInsufficientPrivilege is returned when a caller attempts to modify a
	// target whose effective privilege is equal to or higher than the caller's
	// (e.g. a delegated users:update holder trying to edit a global admin).
	ErrInsufficientPrivilege = errors.New("insufficient privilege to modify this user")
)

Functions

func NormalizeOptionalEmail

func NormalizeOptionalEmail(email *string) (*string, error)

func RegisterUsers

func RegisterUsers(api huma.API, userService *UserService, invalidateUserTokenCache func(string), settingsService *settings.SettingsService)

RegisterUsers registers all user management endpoints.

Types

type Argon2Params

type Argon2Params struct {
	// contains filtered or unexported fields
}

func DefaultArgon2Params

func DefaultArgon2Params() *Argon2Params

type CreateUserInput

type CreateUserInput struct {
	Body user.CreateUser
}

type DeleteUserInput

type DeleteUserInput struct {
	UserID string `path:"userId" doc:"User ID"`
}

type GetUserAvatarInput

type GetUserAvatarInput struct {
	UserID string `path:"userId" doc:"User ID"`
}

type GetUserAvatarOutput

type GetUserAvatarOutput struct {
	ContentType         string `header:"Content-Type"`
	CacheControl        string `header:"Cache-Control"`
	XContentTypeOptions string `header:"X-Content-Type-Options"`
	Body                []byte
}

type GetUserInput

type GetUserInput struct {
	UserID string `path:"userId" doc:"User ID"`
}

type ListUsersInput

type ListUsersInput struct {
	Search string `query:"search" doc:"Search query"`
	Sort   string `query:"sort" doc:"Column to sort by"`
	Order  string `query:"order" default:"asc" doc:"Sort direction"`
	Start  int    `query:"start" default:"0" doc:"Start index"`
	Limit  int    `query:"limit" default:"20" doc:"Items per page"`
}

type Module

type Module struct {
	// contains filtered or unexported fields
}

func New

func New(service *UserService, invalidateUserTokenCache func(string), settingsService *settings.SettingsService) *Module

func (*Module) RegisterRoutes

func (m *Module) RegisterRoutes(api huma.API)

func (*Module) Service

func (m *Module) Service() *UserService

type UpdateUserInput

type UpdateUserInput struct {
	UserID string `path:"userId" doc:"User ID"`
	Body   user.UpdateUser
}

type User added in v2.15.0

type User struct {
	database.BaseModel

	Username               string           `json:"username" sortable:"true"`
	PasswordHash           string           `json:"-" gorm:"column:password_hash"`
	DisplayName            *string          `json:"displayName,omitempty" gorm:"column:display_name" sortable:"true" unorm:"nfc" trim:"true"`
	Email                  *string          `json:"email,omitempty" sortable:"true"`
	OidcSubjectId          *string          `json:"oidcSubjectId,omitempty" gorm:"column:oidc_subject_id"`
	LastLogin              *time.Time       `json:"lastLogin,omitempty" gorm:"column:last_login" sortable:"true"`
	Locale                 *string          `json:"locale,omitempty" gorm:"column:locale"`
	TimeFormat             user.TimeFormat  `json:"timeFormat" gorm:"column:time_format;not null;default:auto"`
	FontSize               *int             `json:"fontSize,omitempty" gorm:"column:font_size"`
	Preferences            user.Preferences `json:"preferences" gorm:"column:preferences;serializer:json"`
	RequiresPasswordChange bool             `json:"requiresPasswordChange" gorm:"column:requires_password_change"`
	IsServiceAccount       bool             `json:"isServiceAccount" gorm:"column:is_service_account;not null;default:false"`
	PasskeyMFAEnabled      bool             `json:"passkeyMfaEnabled" gorm:"column:passkey_mfa_enabled;not null;default:false"`

	// Avatar metadata
	HasAvatar bool `json:"hasAvatar" gorm:"column:has_avatar;not null;default:false"`

	// OIDC provider tokens
	OidcAccessToken          *string    `json:"-" gorm:"type:text"`
	OidcRefreshToken         *string    `json:"-" gorm:"type:text"`
	OidcAccessTokenExpiresAt *time.Time `json:"-"`
}

func CurrentUserFromContext added in v2.15.0

func CurrentUserFromContext(ctx context.Context) (*User, bool)

CurrentUserFromContext retrieves the authenticated persistence model.

func (*User) Actor added in v2.15.0

func (u *User) Actor() *user.Actor

Actor returns the identity and preferences used by requests and background work.

func (User) TableName added in v2.15.0

func (User) TableName() string

type UserAvatar added in v2.8.1

type UserAvatar struct {
	UserID   string `json:"userId" gorm:"column:user_id;primaryKey"`
	Data     []byte `json:"-" gorm:"column:data;type:blob;not null"`
	MimeType string `json:"mimeType" gorm:"column:mime_type;not null"`
}

UserAvatar represents the raw profile picture data for a user. Stored separately from the main User struct to prevent loading up to 2MB of binary data on every user query.

func (UserAvatar) TableName added in v2.8.1

func (UserAvatar) TableName() string

type UserHandler

type UserHandler struct {
	// contains filtered or unexported fields
}

UserHandler handles user management endpoints.

func (*UserHandler) CreateUser

func (h *UserHandler) CreateUser(ctx context.Context, input *CreateUserInput) (*handlerutil.Out[user.User], error)

CreateUser creates a new usertypes.

func (*UserHandler) DeleteUser

DeleteUser deletes a usertypes.

func (*UserHandler) GetUser

func (h *UserHandler) GetUser(ctx context.Context, input *GetUserInput) (*handlerutil.Out[user.User], error)

GetUser returns a user by ID.

func (*UserHandler) GetUserAvatar

func (h *UserHandler) GetUserAvatar(ctx context.Context, input *GetUserAvatarInput) (*GetUserAvatarOutput, error)

GetUserAvatar returns the custom profile picture for a usertypes.

func (*UserHandler) ListUsers

func (h *UserHandler) ListUsers(ctx context.Context, input *ListUsersInput) (*handlerutil.Page[user.User], error)

ListUsers returns a paginated list of users.

func (*UserHandler) UpdateUser

func (h *UserHandler) UpdateUser(ctx context.Context, input *UpdateUserInput) (*handlerutil.Out[user.User], error)

UpdateUser updates a usertypes.

type UserService

type UserService struct {
	// contains filtered or unexported fields
}

func NewUserService

func NewUserService(db *database.DB, roleService *role.RoleService, revokeSessionsExcept func(context.Context, *gorm.DB, string, string) error) *UserService

func (*UserService) AttachOidcSubjectTransactional

func (s *UserService) AttachOidcSubjectTransactional(ctx context.Context, userID, subject string, updateFn func(u *User)) (*User, error)

AttachOidcSubjectTransactional safely links an OIDC subject to the given user inside a DB transaction. It uses a row lock (FOR UPDATE) to prevent concurrent merges from racing and validates that the user isn't already linked to a different subject. The provided updateFn can mutate the user (e.g., roles, display name, tokens, last login) before persisting.

Note: The clause.Locking{Strength: "UPDATE"} statement is used to acquire a row-level lock. This MUST be done inside a transaction to ensure the lock is held until the update is committed.

func (*UserService) CreateDefaultAdmin

func (s *UserService) CreateDefaultAdmin(ctx context.Context) error

func (*UserService) CreateUser

func (s *UserService) CreateUser(ctx context.Context, localUser *User) (*User, error)

func (*UserService) DeleteAvatar

func (s *UserService) DeleteAvatar(ctx context.Context, userID string) error

DeleteAvatar removes the avatar for a user.

func (*UserService) DeleteUser

func (s *UserService) DeleteUser(ctx context.Context, id string, actorPerms *authz.PermissionSet) error

DeleteUser removes the user with the given id. actorPerms identifies the caller performing the deletion; authenticated global-admin callers must also be present in ctx. Pass nil for internal service-to-service calls. A non-admin caller may not delete a global admin target.

func (*UserService) GetAvatar

func (s *UserService) GetAvatar(ctx context.Context, userID string) ([]byte, string, error)

GetAvatar returns the raw avatar data and MIME type for a user. Returns (nil, "", nil) when the user has no custom avatar.

func (*UserService) GetUser

func (s *UserService) GetUser(ctx context.Context, userID string) (*User, error)

func (*UserService) GetUserByEmail

func (s *UserService) GetUserByEmail(ctx context.Context, email string) (*User, error)

GetUserByEmail returns the user with the given email. The schema does not enforce email uniqueness, so a lookup matching more than one account returns common.ErrAmbiguousUserEmail instead of arbitrarily selecting one.

func (*UserService) GetUserByID

func (s *UserService) GetUserByID(ctx context.Context, id string) (*User, error)

func (*UserService) GetUserByOidcSubjectId

func (s *UserService) GetUserByOidcSubjectId(ctx context.Context, subjectId string) (*User, error)

func (*UserService) GetUserByUsername

func (s *UserService) GetUserByUsername(ctx context.Context, username string) (*User, error)

func (*UserService) HashPassword

func (s *UserService) HashPassword(ctx context.Context, password string) (string, error)

func (*UserService) ListUsersPaginated

func (s *UserService) ListUsersPaginated(ctx context.Context, params pagination.QueryParams) ([]user.User, pagination.Response, error)

func (*UserService) ResolveUserPermissions

func (s *UserService) ResolveUserPermissions(ctx context.Context, userID string) (*authz.PermissionSet, error)

ResolveUserPermissions returns the effective PermissionSet for the given user ID, or nil when no role.RoleService is wired (RBAC disabled paths).

func (*UserService) SetPassword

func (s *UserService) SetPassword(ctx context.Context, localUser *User, password string) (*User, error)

SetPassword hashes and persists a new password for the given user and clears the first-login password-change requirement for trusted internal callers.

func (*UserService) SetPasswordAndRevokeSessionsExcept

func (s *UserService) SetPasswordAndRevokeSessionsExcept(ctx context.Context, localUser *User, password, exceptSessionID string, actorPerms *authz.PermissionSet) (*User, error)

SetPasswordAndRevokeSessionsExcept atomically sets a new password, clears the first-login password-change requirement, and revokes every session for the user except exceptSessionID. Pass an empty exceptSessionID to revoke all sessions. This is for trusted internal callers.

func (*UserService) ToUserResponseDto

func (s *UserService) ToUserResponseDto(ctx context.Context, u User) (user.User, error)

func (*UserService) TouchLastLogin added in v2.16.0

func (s *UserService) TouchLastLogin(ctx context.Context, userID string, at time.Time) error

TouchLastLogin updates only the user's last login time.

func (*UserService) UpdateUser

func (s *UserService) UpdateUser(ctx context.Context, localUser *User, actorPerms *authz.PermissionSet) (*User, error)

UpdateUser persists the given user. actorPerms identifies the caller performing the change; authenticated global-admin callers must also be present in ctx. Pass nil for internal service-to-service calls.

func (*UserService) UploadAvatar

func (s *UserService) UploadAvatar(ctx context.Context, userID string, data []byte, mimeType string) error

UploadAvatar stores avatar bytes for a user, replacing any existing avatar.

func (*UserService) ValidatePassword

func (s *UserService) ValidatePassword(ctx context.Context, encodedHash, password string) error

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL