mirror of
https://github.com/absmach/magistrala.git
synced 2026-06-22 20:00:22 +00:00
61d0427898
Signed-off-by: dusan <borovcanindusan1@gmail.com>
289 lines
11 KiB
Go
289 lines
11 KiB
Go
// Copyright (c) Abstract Machines
|
|
// SPDX-License-Identifier: Apache-2.0
|
|
|
|
package users
|
|
|
|
import (
|
|
"context"
|
|
"net/mail"
|
|
"strings"
|
|
"time"
|
|
|
|
grpcTokenV1 "github.com/absmach/magistrala/api/grpc/token/v1"
|
|
"github.com/absmach/magistrala/pkg/authn"
|
|
"github.com/absmach/magistrala/pkg/errors"
|
|
"github.com/absmach/magistrala/pkg/postgres"
|
|
)
|
|
|
|
type User struct {
|
|
ID string `json:"id"`
|
|
FirstName string `json:"first_name,omitempty"`
|
|
LastName string `json:"last_name,omitempty"`
|
|
Tags []string `json:"tags,omitempty"`
|
|
Metadata Metadata `json:"metadata,omitempty"`
|
|
PrivateMetadata Metadata `json:"private_metadata,omitempty"`
|
|
Status Status `json:"status"` // 0 for enabled, 1 for disabled
|
|
Role Role `json:"role"` // 0 for normal user, 1 for admin
|
|
ProfilePicture string `json:"profile_picture,omitempty"` // profile picture URL
|
|
Credentials Credentials `json:"credentials,omitempty"`
|
|
Permissions []string `json:"permissions,omitempty"`
|
|
Email string `json:"email,omitempty"`
|
|
CreatedAt time.Time `json:"created_at,omitempty"`
|
|
UpdatedAt time.Time `json:"updated_at,omitempty"`
|
|
UpdatedBy string `json:"updated_by,omitempty"`
|
|
VerifiedAt time.Time `json:"verified_at,omitempty"`
|
|
AuthProvider string `json:"auth_provider,omitempty"`
|
|
}
|
|
|
|
type Credentials struct {
|
|
Username string `json:"username,omitempty"` // username or profile name
|
|
Secret string `json:"secret,omitempty"` // password or token
|
|
}
|
|
|
|
type UsersPage struct {
|
|
Page
|
|
Users []User
|
|
}
|
|
|
|
// Metadata represents arbitrary JSON.
|
|
type Metadata map[string]any
|
|
|
|
type UserReq struct {
|
|
FirstName *string `json:"first_name,omitempty"`
|
|
LastName *string `json:"last_name,omitempty"`
|
|
Metadata *Metadata `json:"metadata,omitempty"`
|
|
PrivateMetadata *Metadata `json:"private_metadata,omitempty"`
|
|
Tags *[]string `json:"tags,omitempty"`
|
|
ProfilePicture *string `json:"profile_picture,omitempty"`
|
|
UpdatedBy *string `json:"updated_by,omitempty"`
|
|
UpdatedAt *time.Time `json:"updated_at,omitempty"`
|
|
}
|
|
|
|
// MembersPage contains page related metadata as well as list of members that
|
|
// belong to this page.
|
|
type MembersPage struct {
|
|
Page
|
|
Members []User
|
|
}
|
|
|
|
// UserRepository struct implements the Repository interface.
|
|
type UserRepository struct {
|
|
DB postgres.Database
|
|
}
|
|
|
|
type Repository interface {
|
|
// RetrieveByID retrieves user by their unique ID.
|
|
RetrieveByID(ctx context.Context, id string) (User, error)
|
|
|
|
// RetrieveAll retrieves all users.
|
|
RetrieveAll(ctx context.Context, pm Page) (UsersPage, error)
|
|
|
|
// RetrieveByEmail retrieves user by its unique credentials.
|
|
RetrieveByEmail(ctx context.Context, email string) (User, error)
|
|
|
|
// RetrieveByUsername retrieves user by its unique credentials.
|
|
RetrieveByUsername(ctx context.Context, username string) (User, error)
|
|
|
|
// Update updates the user name and metadata.
|
|
Update(ctx context.Context, id string, user UserReq) (User, error)
|
|
|
|
// UpdateUsername updates the User's names.
|
|
UpdateUsername(ctx context.Context, user User) (User, error)
|
|
|
|
// UpdateSecret updates secret for user with given email.
|
|
UpdateSecret(ctx context.Context, user User) (User, error)
|
|
|
|
// UpdateEmail updates email for user with given id.
|
|
UpdateEmail(ctx context.Context, user User) (User, error)
|
|
|
|
// UpdateRole updates role for user with given id.
|
|
UpdateRole(ctx context.Context, user User) (User, error)
|
|
|
|
// UpdateVerifiedAt updates the verified time for user with given id.
|
|
UpdateVerifiedAt(ctx context.Context, user User) (User, error)
|
|
|
|
// ChangeStatus changes user status to enabled or disabled
|
|
ChangeStatus(ctx context.Context, user User) (User, error)
|
|
|
|
// Delete deletes user with given id
|
|
Delete(ctx context.Context, id string) error
|
|
|
|
// Searchusers retrieves users based on search criteria.
|
|
SearchUsers(ctx context.Context, pm Page) (UsersPage, error)
|
|
|
|
// RetrieveAllByIDs retrieves for given user IDs .
|
|
RetrieveAllByIDs(ctx context.Context, pm Page) (UsersPage, error)
|
|
|
|
CheckSuperAdmin(ctx context.Context, adminID string) error
|
|
|
|
// Save persists the user account. A non-nil error is returned to indicate
|
|
// operation failure.
|
|
Save(ctx context.Context, user User) (User, error)
|
|
|
|
// AddUserVerification adds new verification for given user id and email
|
|
AddUserVerification(ctx context.Context, uv UserVerification) error
|
|
|
|
// RetrieveVerificationToken retrieves verification token of given user id and email.
|
|
RetrieveUserVerification(ctx context.Context, userID, email string) (UserVerification, error)
|
|
|
|
// UpdateUserVerificationDetails update verification details for the given user id and email.
|
|
UpdateUserVerification(ctx context.Context, uv UserVerification) error
|
|
}
|
|
|
|
// Validate returns an error if user representation is invalid.
|
|
func (u User) Validate() error {
|
|
if !isEmail(u.Email) {
|
|
return errors.ErrMalformedEntity
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func isEmail(email string) bool {
|
|
_, err := mail.ParseAddress(email)
|
|
return err == nil
|
|
}
|
|
|
|
type Operator uint8
|
|
|
|
const (
|
|
OrOp Operator = iota
|
|
AndOp
|
|
)
|
|
|
|
type TagsQuery struct {
|
|
Elements []string
|
|
Operator Operator
|
|
}
|
|
|
|
func ToTagsQuery(s string) TagsQuery {
|
|
switch {
|
|
case strings.Contains(s, "+"):
|
|
elements := strings.Split(s, "+")
|
|
for i := range elements {
|
|
elements[i] = strings.TrimSpace(elements[i])
|
|
}
|
|
return TagsQuery{Elements: elements, Operator: AndOp}
|
|
case strings.Contains(s, ","):
|
|
elements := strings.Split(s, ",")
|
|
for i := range elements {
|
|
elements[i] = strings.TrimSpace(elements[i])
|
|
}
|
|
return TagsQuery{Elements: elements, Operator: OrOp}
|
|
default:
|
|
return TagsQuery{Elements: []string{s}, Operator: OrOp}
|
|
}
|
|
}
|
|
|
|
// Page contains page metadata that helps navigation.
|
|
type Page struct {
|
|
Total uint64 `json:"total"`
|
|
Offset uint64 `json:"offset"`
|
|
Limit uint64 `json:"limit"`
|
|
OnlyTotal bool `json:"only_total"`
|
|
Id string `json:"id,omitempty"`
|
|
Order string `json:"order,omitempty"`
|
|
Dir string `json:"dir,omitempty"`
|
|
Metadata Metadata `json:"metadata,omitempty"`
|
|
Domain string `json:"domain,omitempty"`
|
|
Tags TagsQuery `json:"tag,omitempty"`
|
|
Permission string `json:"permission,omitempty"`
|
|
Status Status `json:"status,omitempty"`
|
|
IDs []string `json:"ids,omitempty"`
|
|
Role Role `json:"-"`
|
|
ListPerms bool `json:"-"`
|
|
Username string `json:"username,omitempty"`
|
|
FirstName string `json:"first_name,omitempty"`
|
|
LastName string `json:"last_name,omitempty"`
|
|
Email string `json:"email,omitempty"`
|
|
Verified bool `json:"verified,omitempty"`
|
|
CreatedFrom time.Time `json:"created_from,omitempty"`
|
|
CreatedTo time.Time `json:"created_to,omitempty"`
|
|
}
|
|
|
|
// Service specifies an API that must be fullfiled by the domain service
|
|
// implementation, and all of its decorators (e.g. logging & metrics).
|
|
type Service interface {
|
|
// Register creates new user. In case of the failed registration, a
|
|
// non-nil error value is returned.
|
|
Register(ctx context.Context, session authn.Session, user User, selfRegister bool) (User, error)
|
|
|
|
// SendVerification sends a verification email to the user.
|
|
SendVerification(ctx context.Context, session authn.Session) error
|
|
|
|
// VerifyEmail verifies user's email using the verification token.
|
|
VerifyEmail(ctx context.Context, verificationToken string) (User, error)
|
|
|
|
// View retrieves user info for a given user ID and an authorized token.
|
|
View(ctx context.Context, session authn.Session, id string) (User, error)
|
|
|
|
// ViewProfile retrieves user info for a given token.
|
|
ViewProfile(ctx context.Context, session authn.Session) (User, error)
|
|
|
|
// ListUsers retrieves users list for a valid auth token.
|
|
ListUsers(ctx context.Context, session authn.Session, pm Page) (UsersPage, error)
|
|
|
|
// SearchUsers searches for users with provided filters for a valid auth token.
|
|
SearchUsers(ctx context.Context, pm Page) (UsersPage, error)
|
|
|
|
// Update updates the user's name and metadata.
|
|
Update(ctx context.Context, session authn.Session, id string, user UserReq) (User, error)
|
|
|
|
// UpdateTags updates the user's tags.
|
|
UpdateTags(ctx context.Context, session authn.Session, id string, user UserReq) (User, error)
|
|
|
|
// UpdateEmail updates the user's email.
|
|
UpdateEmail(ctx context.Context, session authn.Session, id, email string) (User, error)
|
|
|
|
// UpdateUsername updates the user's username.
|
|
UpdateUsername(ctx context.Context, session authn.Session, id, username string) (User, error)
|
|
|
|
// UpdateProfilePicture updates the user's profile picture.
|
|
UpdateProfilePicture(ctx context.Context, session authn.Session, id string, usr UserReq) (User, error)
|
|
|
|
// SendPasswordReset generates reset password link and sends it to the user via email.
|
|
SendPasswordReset(ctx context.Context, email string) error
|
|
|
|
// UpdateSecret updates the user's secret.
|
|
UpdateSecret(ctx context.Context, session authn.Session, oldSecret, newSecret string) (User, error)
|
|
|
|
// ResetSecret change users secret in reset flow.
|
|
// token can be authentication token or secret reset token.
|
|
ResetSecret(ctx context.Context, session authn.Session, secret string) error
|
|
|
|
// UpdateRole updates the user's Role.
|
|
UpdateRole(ctx context.Context, session authn.Session, user User) (User, error)
|
|
|
|
// Enable logically enables the user identified with the provided ID.
|
|
Enable(ctx context.Context, session authn.Session, id string) (User, error)
|
|
|
|
// Disable logically disables the user identified with the provided ID.
|
|
Disable(ctx context.Context, session authn.Session, id string) (User, error)
|
|
|
|
// Delete deletes user with given ID.
|
|
Delete(ctx context.Context, session authn.Session, id string) error
|
|
|
|
// Identify returns the user id from the given token.
|
|
Identify(ctx context.Context, session authn.Session) (string, error)
|
|
|
|
// IssueToken issues a new access and refresh token when provided with either a username or email.
|
|
IssueToken(ctx context.Context, identity, secret, description string) (*grpcTokenV1.Token, error)
|
|
|
|
// RefreshToken refreshes expired access tokens.
|
|
// After an access token expires, the refresh token is used to get
|
|
// a new pair of access and refresh tokens.
|
|
RefreshToken(ctx context.Context, session authn.Session, refreshToken string) (*grpcTokenV1.Token, error)
|
|
|
|
// RevokeRefreshToken revokes a refresh token by its ID.
|
|
RevokeRefreshToken(ctx context.Context, session authn.Session, tokenID string) error
|
|
|
|
// ListActiveRefreshTokens lists all active refresh tokens for the authenticated user.
|
|
ListActiveRefreshTokens(ctx context.Context, session authn.Session) (*grpcTokenV1.ListUserRefreshTokensRes, error)
|
|
|
|
// OAuthCallback handles the callback from any supported OAuth provider.
|
|
// It processes the OAuth tokens and either signs in or signs up the user based on the provided state.
|
|
OAuthCallback(ctx context.Context, user User) (User, error)
|
|
|
|
// OAuthAddUserPolicy adds a policy to the user for an OAuth request.
|
|
OAuthAddUserPolicy(ctx context.Context, user User) error
|
|
}
|