Documentation ¶
Overview ¶
Package user specifies:
- user-specific domain types;
- a service interface and corresponding implementation, for injection into inbound methods;
- an outbound interface describing a store of user data.
nolint:gosec
Index ¶
- Constants
- func NewDuplicateEmailError(email EmailAddress) error
- func NewDuplicateUsernameError(username Username) error
- func NewEmailAddressFormatError(candidate string) error
- func NewInvalidURLError() error
- func NewNotFoundByEmailError(email EmailAddress) error
- func NewNotFoundByIDError(id uuid.UUID) error
- func NewPasswordTooLongError() error
- func NewPasswordTooShortError() error
- func NewUsernameFormatError() error
- func NewUsernameTooLongError() error
- func NewUsernameTooShortError() error
- func RandomEmailAddressCandidate() string
- func RandomOption[T any](t *testing.T) option.Option[T]
- func RandomOptionFromInstance[T any](instance T) option.Option[T]
- func RandomPasswordCandidate() string
- func RandomURLCandidate() string
- func RandomUsernameCandidate() string
- type AuthError
- type AuthRequest
- type Bio
- type ConcurrentModificationError
- type EmailAddress
- type FieldType
- type NotFoundError
- type PasswordHash
- type RegistrationRequest
- func NewRegistrationRequest(username Username, email EmailAddress, passwordHash PasswordHash) *RegistrationRequest
- func ParseRegistrationRequest(usernameCandidate string, emailCandidate string, passwordCandidate string) (*RegistrationRequest, error)
- func RandomRegistrationRequest(t *testing.T) *RegistrationRequest
- func (r *RegistrationRequest) Email() EmailAddress
- func (r *RegistrationRequest) Equal(other *RegistrationRequest, password string) bool
- func (r RegistrationRequest) GoString() string
- func (r *RegistrationRequest) PasswordHash() PasswordHash
- func (r RegistrationRequest) String() string
- func (r *RegistrationRequest) Username() Username
- type Repository
- type Service
- type URL
- type UpdateRequest
- func (ur *UpdateRequest) Bio() option.Option[Bio]
- func (ur *UpdateRequest) ETag() etag.ETag
- func (ur *UpdateRequest) Email() option.Option[EmailAddress]
- func (ur *UpdateRequest) Equal(other *UpdateRequest, password option.Option[string]) bool
- func (ur UpdateRequest) GoString() string
- func (ur *UpdateRequest) ImageURL() option.Option[URL]
- func (ur *UpdateRequest) PasswordHash() option.Option[PasswordHash]
- func (ur UpdateRequest) String() string
- func (ur *UpdateRequest) UserID() uuid.UUID
- type User
- func (u *User) Bio() option.Option[Bio]
- func (u *User) ETag() etag.ETag
- func (u *User) Email() EmailAddress
- func (u User) GoString() string
- func (u *User) ID() uuid.UUID
- func (u *User) ImageURL() option.Option[URL]
- func (u *User) PasswordHash() PasswordHash
- func (u User) String() string
- func (u *User) Username() Username
- type Username
- type ValidationError
- type ValidationErrors
Constants ¶
const ( // UsernameMinLen is the minimum length of a username in bytes. UsernameMinLen = 3 // UsernameMaxLen is the maximum length of a username in bytes. UsernameMaxLen = 16 UsernamePatternTemplate = "^[a-zA-Z0-9_]{%d,%d}$" )
const ( PasswordMinLen = 8 PasswordMaxLen = 72 )
Variables ¶
This section is empty.
Functions ¶
func NewDuplicateEmailError ¶
func NewDuplicateEmailError(email EmailAddress) error
func NewInvalidURLError ¶
func NewInvalidURLError() error
func NewNotFoundByEmailError ¶
func NewNotFoundByEmailError(email EmailAddress) error
func NewNotFoundByIDError ¶
func NewPasswordTooLongError ¶
func NewPasswordTooLongError() error
func NewPasswordTooShortError ¶
func NewPasswordTooShortError() error
func NewUsernameFormatError ¶
func NewUsernameFormatError() error
func NewUsernameTooLongError ¶
func NewUsernameTooLongError() error
func NewUsernameTooShortError ¶
func NewUsernameTooShortError() error
func RandomEmailAddressCandidate ¶
func RandomEmailAddressCandidate() string
func RandomPasswordCandidate ¶
func RandomPasswordCandidate() string
func RandomURLCandidate ¶
func RandomURLCandidate() string
func RandomUsernameCandidate ¶
func RandomUsernameCandidate() string
Types ¶
type AuthError ¶
type AuthError struct {
Cause error
}
AuthError is a wrapper for an authentication error result, which may include errors that would be considered validation errors by other endpoints. This reinforces the security convention that an end user should not receive the specifics of why an authentication request failed.
type AuthRequest ¶
type AuthRequest struct {
// contains filtered or unexported fields
}
AuthRequest describes the data required to authenticate a user.
func NewAuthRequest ¶
func NewAuthRequest(email EmailAddress, passwordCandidate string) *AuthRequest
func ParseAuthRequest ¶
func ParseAuthRequest(emailCandidate string, passwordCandidate string) (*AuthRequest, error)
ParseAuthRequest returns a new AuthRequest from raw inputs.
Errors ¶
- ValidationErrors, if `emailCandidate` is invalid.
func RandomAuthRequest ¶
func RandomAuthRequest(t *testing.T) *AuthRequest
func (*AuthRequest) Email ¶
func (ar *AuthRequest) Email() EmailAddress
func (AuthRequest) GoString ¶
func (ar AuthRequest) GoString() string
GoString ensures that `passwordCandidate` is obfuscated when the request is printed with the %#v verb. Unexported fields are otherwise printed reflectively, which would expose the hash.
func (*AuthRequest) PasswordCandidate ¶
func (ar *AuthRequest) PasswordCandidate() string
func (AuthRequest) String ¶
func (ar AuthRequest) String() string
GoString ensures that `passwordCandidate` is obfuscated when the request is printed with the %s or %v verbs. Unexported fields are otherwise printed reflectively, which would expose the hash.
type Bio ¶
type Bio string
Bio represents a user's bio.
type ConcurrentModificationError ¶
ConcurrentModificationError indicates that a resource was modified prior to processing the current request, making the current request stale.
func (*ConcurrentModificationError) Error ¶
func (e *ConcurrentModificationError) Error() string
type EmailAddress ¶
type EmailAddress struct {
// contains filtered or unexported fields
}
EmailAddress is a dedicated usernameCandidate type for valid email addresses. New instances are validated for RFC5332 compliance.
func ParseEmailAddress ¶
func ParseEmailAddress(candidate string) (EmailAddress, error)
ParseEmailAddress returns a new email address from `candidate`, validating that the email address conforms to RFC5332 standards (with the minor divergences introduce by the Go standard library, documented in net/mail).
The formats that constitute a valid email address may surprise you. For example, single-value domain names like `angus@com` are valid.
func RandomEmailAddress ¶
func RandomEmailAddress(t *testing.T) EmailAddress
func (EmailAddress) String ¶
func (ea EmailAddress) String() string
String returns the raw email address.
type NotFoundError ¶
NotFoundError should be returned by a Repository when the specified user does not exist.
func (*NotFoundError) Error ¶
func (e *NotFoundError) Error() string
func (*NotFoundError) Is ¶
func (e *NotFoundError) Is(target error) bool
type PasswordHash ¶
type PasswordHash struct {
// contains filtered or unexported fields
}
PasswordHash represents a validated and hashed password.
The hash is obfuscated when printed with the %s, %v and %#v verbs.
CAUTION: The fmt package uses reflection to print unexported fields without invoking their String or GoString methods. Printing structs containing unexported PasswordHashes will result in the hash bytes being exposed.
func NewPasswordHashFromTrustedSource ¶
func NewPasswordHashFromTrustedSource(raw []byte) PasswordHash
NewPasswordHashFromTrustedSource wraps a hashed password in a PasswordHash.
func ParsePassword ¶
func ParsePassword(candidate string) (PasswordHash, error)
func RandomPasswordHash ¶
func RandomPasswordHash(t *testing.T) PasswordHash
func (PasswordHash) Bytes ¶
func (ph PasswordHash) Bytes() []byte
func (PasswordHash) GoString ¶
func (ph PasswordHash) GoString() string
GoString obfuscates the hash bytes when the hash is printed with the %#v verb.
func (PasswordHash) String ¶
func (ph PasswordHash) String() string
String obfuscates the hash bytes when the hash is printed with the %s and %v verbs.
type RegistrationRequest ¶
type RegistrationRequest struct {
// contains filtered or unexported fields
}
RegistrationRequest carries validated data required to register a new user.
func NewRegistrationRequest ¶
func NewRegistrationRequest( username Username, email EmailAddress, passwordHash PasswordHash, ) *RegistrationRequest
func ParseRegistrationRequest ¶
func ParseRegistrationRequest( usernameCandidate string, emailCandidate string, passwordCandidate string, ) (*RegistrationRequest, error)
ParseRegistrationRequest returns a new RegistrationRequest from raw inputs.
Errors ¶
- ValidationErrors, if one or more inputs are invalid.
- Unexpected internal response.
func RandomRegistrationRequest ¶
func RandomRegistrationRequest(t *testing.T) *RegistrationRequest
func (*RegistrationRequest) Email ¶
func (r *RegistrationRequest) Email() EmailAddress
func (*RegistrationRequest) Equal ¶
func (r *RegistrationRequest) Equal(other *RegistrationRequest, password string) bool
Equal returns true if `r.passwordHash` can be obtained from `password`, and the two requests are equal in all other fields.
Direct comparison of password hashes is impossible by design.
func (RegistrationRequest) GoString ¶
func (r RegistrationRequest) GoString() string
GoString ensures that the PasswordHash's GoString method is invoked when the request is printed with the %#v verb. Unexported fields are otherwise printed reflectively, which would expose the hash.
func (*RegistrationRequest) PasswordHash ¶
func (r *RegistrationRequest) PasswordHash() PasswordHash
func (RegistrationRequest) String ¶
func (r RegistrationRequest) String() string
String ensures that the PasswordHash's String method is invoked when the request is printed with the %s or %v verbs. Unexported fields are otherwise printed reflectively, which would expose the hash.
func (*RegistrationRequest) Username ¶
func (r *RegistrationRequest) Username() Username
type Repository ¶
type Repository interface { // GetUserByID retrieves the [User] with `id`. // // # Errors // - [NotFoundError] if no such User exists. GetUserByID(ctx context.Context, id uuid.UUID) (*User, error) // GetUserByEmail returns a user by email. // // # Errors // - [NotFoundError] if no such User exists. GetUserByEmail(ctx context.Context, email EmailAddress) (*User, error) // CreateUser persists a new user. // // # Errors // - [ValidationError] if email or username is already taken. CreateUser(ctx context.Context, req *RegistrationRequest) (*User, error) // UpdateUser updates an existing user. // // # Errors // - [NotFoundError] if no such User exists. // - [ValidationError] if email is already taken. // - [ConcurrentModificationError] if the user has been modified since the last // read. UpdateUser(ctx context.Context, req *UpdateRequest) (*User, error) }
Repository is a store of user data.
type Service ¶
type Service interface { // Register a new user. // // # Errors // - [ValidationError] if email or username is already taken. Register(ctx context.Context, req *RegistrationRequest) (*User, error) // Authenticate a user, returning the authenticated [User] if successful. // // # Errors // - [AuthError]. Authenticate(ctx context.Context, req *AuthRequest) (*User, error) // GetUser a user by ID. // // # Errors // - [NotFoundError] if no such User exists. GetUser(ctx context.Context, id uuid.UUID) (*User, error) // UpdateUser updates an existing user. // // # Errors // - [NotFoundError] if no such User exists. // - [ValidationError] if email is already taken. // - [ConcurrentModificationError] if the user has been modified since the last // read. UpdateUser(ctx context.Context, req *UpdateRequest) (*User, error) }
Service describes the business API accessible to inbound methods.
func NewService ¶
func NewService(repo Repository) Service
type URL ¶
type URL struct {
// contains filtered or unexported fields
}
URL represents a valid, immutable URL.
type UpdateRequest ¶
type UpdateRequest struct {
// contains filtered or unexported fields
}
UpdateRequest describes the data required to update a user. All fields but userID are optional. To unset a FieldType, pass [Option] containing the zero value of the FieldType's type.
func NewUpdateRequest ¶
func ParseUpdateRequest ¶
func ParseUpdateRequest( userID uuid.UUID, eTag etag.ETag, emailCandidate option.Option[string], passwordCandidate option.Option[string], rawBio option.Option[string], imageURLCandidate option.Option[string], ) (*UpdateRequest, error)
ParseUpdateRequest returns a new UpdateRequest from raw inputs.
Errors ¶
- ValidationErrors, if one or more inputs are invalid.
- Unexpected internal response.
func RandomUpdateRequest ¶
func RandomUpdateRequest(t *testing.T) *UpdateRequest
func (*UpdateRequest) ETag ¶
func (ur *UpdateRequest) ETag() etag.ETag
func (*UpdateRequest) Email ¶
func (ur *UpdateRequest) Email() option.Option[EmailAddress]
func (*UpdateRequest) Equal ¶
func (ur *UpdateRequest) Equal(other *UpdateRequest, password option.Option[string]) bool
Equal returns true if `ur.passwordHash` can be obtained from `password`, and the two requests are equal in all other fields.
Direct comparison of password hashes is impossible by design.
func (UpdateRequest) GoString ¶
func (ur UpdateRequest) GoString() string
GoString ensures that the PasswordHash option.Option's GoString method is invoked when the request is printed with the %#v verb, which in turn calls GoString on the PasswordHash, if present. Unexported fields are otherwise printed reflectively, which would expose the hash.
func (*UpdateRequest) PasswordHash ¶
func (ur *UpdateRequest) PasswordHash() option.Option[PasswordHash]
func (UpdateRequest) String ¶
func (ur UpdateRequest) String() string
GoString ensures that the PasswordHash option.Option's GoString method is invoked when the request is printed with the %s or %v verbs, which in turn calls GoString on the PasswordHash, if present. Unexported fields are otherwise printed reflectively, which would expose the hash.
func (*UpdateRequest) UserID ¶
func (ur *UpdateRequest) UserID() uuid.UUID
type User ¶
type User struct {
// contains filtered or unexported fields
}
User is the central domain type for this package.
func RandomUser ¶
func (*User) Email ¶
func (u *User) Email() EmailAddress
func (User) GoString ¶
GoString ensures that the PasswordHash's GoString method is invoked when the User is printed with the %#v verb. Unexported fields are otherwise printed reflectively, which would expose the hash.
func (*User) PasswordHash ¶
func (u *User) PasswordHash() PasswordHash
func (User) String ¶
GoString ensures that the PasswordHash's GoString method is invoked when the User is printed with the %s or %v verbs. Unexported fields are otherwise printed reflectively, which would expose the hash.
type Username ¶
type Username struct {
// contains filtered or unexported fields
}
Username represents a valid Username.
func ParseUsername ¶
ParseUsername returns either a valid Username or an error indicating why the raw username was invalid.
func RandomUsername ¶
type ValidationError ¶
ValidationError represents a single error encountered when validation a field of the given FieldType.
func (*ValidationError) Error ¶
func (e *ValidationError) Error() string
func (*ValidationError) Is ¶
func (e *ValidationError) Is(target error) bool
type ValidationErrors ¶
type ValidationErrors []*ValidationError
ValidationErrors is a collection of ValidationError, allowing us to report on the validation of several inputs rather than returning the first validation error encountered.
func (ValidationErrors) Any ¶
func (e ValidationErrors) Any() bool
Any returns true if the collection contains any response, and false otherwise.
func (ValidationErrors) Error ¶
func (e ValidationErrors) Error() string
func (*ValidationErrors) PushValidationError ¶
func (e *ValidationErrors) PushValidationError(err error) error
PushValidationError adds a ValidationError to the collection. If the error is not a ValidationError (including nil response), it is returned as-is. The supports the following pattern for successive validations:
var validationErrs ValidationErrors field1, err := parseField1() if err := validationErrs.PushValidationError(err); err != nil { return nil, err // not a ValidationError } field2, err := parseField2() ...