mirror of
https://github.com/mainflux/mainflux.git
synced 2025-04-28 13:48:49 +08:00
MF1621 - Logical user removal (#1620)
* Initial commit Signed-off-by: 0x6f736f646f <blackd0t@protonmail.com> * change active to string Signed-off-by: 0x6f736f646f <blackd0t@protonmail.com> * Set default Signed-off-by: 0x6f736f646f <blackd0t@protonmail.com> * Fix query all users Signed-off-by: GitHub <noreply@github.com> * Set user active on service Signed-off-by: GitHub <noreply@github.com> * Rename active to state Signed-off-by: GitHub <noreply@github.com> * check user active on service Signed-off-by: 0x6f736f646f <blackd0t@protonmail.com> * format Signed-off-by: 0x6f736f646f <blackd0t@protonmail.com> * format Signed-off-by: 0x6f736f646f <blackd0t@protonmail.com> * fix test Signed-off-by: GitHub <noreply@github.com> * Add deactivate user tests Signed-off-by: 0x6f736f646f <blackd0t@protonmail.com> * Rename deactivate to change user status Signed-off-by: 0x6f736f646f <blackd0t@protonmail.com> * Revert to sorting users Signed-off-by: GitHub <noreply@github.com> * change user state Signed-off-by: 0x6f736f646f <blackd0t@protonmail.com> * Change user status to enable and disable Signed-off-by: 0x6f736f646f <blackd0t@protonmail.com> * change user state to status Signed-off-by: 0x6f736f646f <blackd0t@protonmail.com> * from enable to activate Signed-off-by: 0x6f736f646f <blackd0t@protonmail.com> * from activate to enable Signed-off-by: GitHub <noreply@github.com> * not found error by retrievebyID Signed-off-by: 0x6f736f646f <blackd0t@protonmail.com> * Combine enable and disable user Signed-off-by: 0x6f736f646f <blackd0t@protonmail.com> * Add api docs Signed-off-by: b1ackd0t <blackd0t@protonmail.com> * verify docs Signed-off-by: b1ackd0t <blackd0t@protonmail.com> * change to camel Signed-off-by: b1ackd0t <blackd0t@protonmail.com> * Reword Signed-off-by: b1ackd0t <blackd0t@protonmail.com> * fix default state Signed-off-by: 0x6f736f646f <blackd0t@protonmail.com> * change from VARCHAR to ENUM Signed-off-by: 0x6f736f646f <blackd0t@protonmail.com> * invalid user status test Signed-off-by: 0x6f736f646f <blackd0t@protonmail.com> Signed-off-by: 0x6f736f646f <blackd0t@protonmail.com> Signed-off-by: GitHub <noreply@github.com> Signed-off-by: b1ackd0t <blackd0t@protonmail.com> Co-authored-by: Dušan Borovčanin <dusan.borovcanin@mainflux.com>
This commit is contained in:
parent
f89ea226e5
commit
721ee545f9
@ -41,6 +41,7 @@ paths:
|
|||||||
- $ref: "#/components/parameters/Limit"
|
- $ref: "#/components/parameters/Limit"
|
||||||
- $ref: "#/components/parameters/Offset"
|
- $ref: "#/components/parameters/Offset"
|
||||||
- $ref: "#/components/parameters/Metadata"
|
- $ref: "#/components/parameters/Metadata"
|
||||||
|
- $ref: "#/components/parameters/Status"
|
||||||
responses:
|
responses:
|
||||||
'200':
|
'200':
|
||||||
$ref: "#/components/responses/UsersPageRes"
|
$ref: "#/components/responses/UsersPageRes"
|
||||||
@ -215,6 +216,46 @@ paths:
|
|||||||
description: Missing or invalid content type.
|
description: Missing or invalid content type.
|
||||||
'500':
|
'500':
|
||||||
$ref: "#/components/responses/ServiceError"
|
$ref: "#/components/responses/ServiceError"
|
||||||
|
/users/{userId}/enable:
|
||||||
|
post:
|
||||||
|
summary: Enables a user account
|
||||||
|
description: |
|
||||||
|
Enables a disabled user account for a given user ID.
|
||||||
|
tags:
|
||||||
|
- users
|
||||||
|
parameters:
|
||||||
|
- $ref: "#/components/parameters/UserId"
|
||||||
|
responses:
|
||||||
|
'200':
|
||||||
|
description: User enabled.
|
||||||
|
'400':
|
||||||
|
description: Failed due to malformed JSON.
|
||||||
|
'404':
|
||||||
|
description: Failed due to non existing user.
|
||||||
|
'401':
|
||||||
|
description: Missing or invalid access token provided.
|
||||||
|
'500':
|
||||||
|
$ref: "#/components/responses/ServiceError"
|
||||||
|
/users/{userId}/disable:
|
||||||
|
post:
|
||||||
|
summary: Disables a user account
|
||||||
|
description: |
|
||||||
|
Disables a user account for a given user ID.
|
||||||
|
tags:
|
||||||
|
- users
|
||||||
|
parameters:
|
||||||
|
- $ref: "#/components/parameters/UserId"
|
||||||
|
responses:
|
||||||
|
'200':
|
||||||
|
description: User disabled.
|
||||||
|
'400':
|
||||||
|
description: Failed due to malformed JSON.
|
||||||
|
'404':
|
||||||
|
description: Failed due to non existing user.
|
||||||
|
'401':
|
||||||
|
description: Missing or invalid access token provided.
|
||||||
|
'500':
|
||||||
|
$ref: "#/components/responses/ServiceError"
|
||||||
/health:
|
/health:
|
||||||
get:
|
get:
|
||||||
summary: Retrieves service health check info.
|
summary: Retrieves service health check info.
|
||||||
@ -318,7 +359,7 @@ components:
|
|||||||
type: string
|
type: string
|
||||||
minimum: 0
|
minimum: 0
|
||||||
required: false
|
required: false
|
||||||
UserID:
|
UserId:
|
||||||
name: userId
|
name: userId
|
||||||
description: Unique user identifier.
|
description: Unique user identifier.
|
||||||
in: path
|
in: path
|
||||||
@ -353,7 +394,14 @@ components:
|
|||||||
default: 0
|
default: 0
|
||||||
minimum: 0
|
minimum: 0
|
||||||
required: false
|
required: false
|
||||||
|
Status:
|
||||||
|
name: status
|
||||||
|
description: User account status.
|
||||||
|
in: query
|
||||||
|
schema:
|
||||||
|
type: string
|
||||||
|
default: enabled
|
||||||
|
required: false
|
||||||
requestBodies:
|
requestBodies:
|
||||||
UserCreateReq:
|
UserCreateReq:
|
||||||
description: JSON-formatted document describing the new user to be registered
|
description: JSON-formatted document describing the new user to be registered
|
||||||
|
39
cli/users.go
39
cli/users.go
@ -58,6 +58,7 @@ var cmdUsers = []cobra.Command{
|
|||||||
Offset: uint64(Offset),
|
Offset: uint64(Offset),
|
||||||
Limit: uint64(Limit),
|
Limit: uint64(Limit),
|
||||||
Metadata: metadata,
|
Metadata: metadata,
|
||||||
|
Status: Status,
|
||||||
}
|
}
|
||||||
if args[0] == "all" {
|
if args[0] == "all" {
|
||||||
l, err := sdk.Users(args[1], pageMetadata)
|
l, err := sdk.Users(args[1], pageMetadata)
|
||||||
@ -140,6 +141,42 @@ var cmdUsers = []cobra.Command{
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
logOK()
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Use: "enable <user_id> <user_auth_token>",
|
||||||
|
Short: "Change user status to enabled",
|
||||||
|
Long: `Change user status to enabled`,
|
||||||
|
Run: func(cmd *cobra.Command, args []string) {
|
||||||
|
if len(args) != 2 {
|
||||||
|
logUsage(cmd.Use)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := sdk.EnableUser(args[0], args[1]); err != nil {
|
||||||
|
logError(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
logOK()
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Use: "disable <user_id> <user_auth_token>",
|
||||||
|
Short: "Change user status to disabled",
|
||||||
|
Long: `Change user status to disabled`,
|
||||||
|
Run: func(cmd *cobra.Command, args []string) {
|
||||||
|
if len(args) != 2 {
|
||||||
|
logUsage(cmd.Use)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := sdk.DisableUser(args[0], args[1]); err != nil {
|
||||||
|
logError(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
logOK()
|
logOK()
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -148,7 +185,7 @@ var cmdUsers = []cobra.Command{
|
|||||||
// NewUsersCmd returns users command.
|
// NewUsersCmd returns users command.
|
||||||
func NewUsersCmd() *cobra.Command {
|
func NewUsersCmd() *cobra.Command {
|
||||||
cmd := cobra.Command{
|
cmd := cobra.Command{
|
||||||
Use: "users [create | get | update | token | password]",
|
Use: "users [create | get | update | token | password | enable | disable]",
|
||||||
Short: "Users management",
|
Short: "Users management",
|
||||||
Long: `Users management: create accounts and tokens"`,
|
Long: `Users management: create accounts and tokens"`,
|
||||||
}
|
}
|
||||||
|
@ -22,6 +22,8 @@ var (
|
|||||||
Email string = ""
|
Email string = ""
|
||||||
// Metadata query parameter
|
// Metadata query parameter
|
||||||
Metadata string = ""
|
Metadata string = ""
|
||||||
|
// Status query parameter
|
||||||
|
Status string = ""
|
||||||
// ConfigPath config path parameter
|
// ConfigPath config path parameter
|
||||||
ConfigPath string = ""
|
ConfigPath string = ""
|
||||||
// RawOutput raw output mode
|
// RawOutput raw output mode
|
||||||
|
@ -186,6 +186,14 @@ func main() {
|
|||||||
"Metadata query parameter",
|
"Metadata query parameter",
|
||||||
)
|
)
|
||||||
|
|
||||||
|
rootCmd.PersistentFlags().StringVarP(
|
||||||
|
&cli.Status,
|
||||||
|
"status",
|
||||||
|
"S",
|
||||||
|
"",
|
||||||
|
"Status query parameter",
|
||||||
|
)
|
||||||
|
|
||||||
if err := rootCmd.Execute(); err != nil {
|
if err := rootCmd.Execute(); err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
|
@ -30,6 +30,9 @@ var (
|
|||||||
// ErrEmailSize indicates that email size exceeds the max.
|
// ErrEmailSize indicates that email size exceeds the max.
|
||||||
ErrEmailSize = errors.New("invalid email size")
|
ErrEmailSize = errors.New("invalid email size")
|
||||||
|
|
||||||
|
// ErrInvalidStatus indicates an invalid user account status.
|
||||||
|
ErrInvalidStatus = errors.New("invalid user account status")
|
||||||
|
|
||||||
// ErrLimitSize indicates that an invalid limit.
|
// ErrLimitSize indicates that an invalid limit.
|
||||||
ErrLimitSize = errors.New("invalid limit size")
|
ErrLimitSize = errors.New("invalid limit size")
|
||||||
|
|
||||||
|
@ -98,6 +98,7 @@ type PageMetadata struct {
|
|||||||
Name string `json:"name,omitempty"`
|
Name string `json:"name,omitempty"`
|
||||||
Type string `json:"type,omitempty"`
|
Type string `json:"type,omitempty"`
|
||||||
Metadata map[string]interface{} `json:"metadata,omitempty"`
|
Metadata map[string]interface{} `json:"metadata,omitempty"`
|
||||||
|
Status string `json:"status,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// Group represents mainflux users group.
|
// Group represents mainflux users group.
|
||||||
@ -153,6 +154,12 @@ type SDK interface {
|
|||||||
// UpdatePassword updates user password.
|
// UpdatePassword updates user password.
|
||||||
UpdatePassword(oldPass, newPass, token string) error
|
UpdatePassword(oldPass, newPass, token string) error
|
||||||
|
|
||||||
|
// EnableUser changes the status of the user to enabled.
|
||||||
|
EnableUser(id, token string) error
|
||||||
|
|
||||||
|
// DisableUser changes the status of the user to disabled.
|
||||||
|
DisableUser(id, token string) error
|
||||||
|
|
||||||
// CreateThing registers new thing and returns its id.
|
// CreateThing registers new thing and returns its id.
|
||||||
CreateThing(thing Thing, token string) (string, error)
|
CreateThing(thing Thing, token string) (string, error)
|
||||||
|
|
||||||
@ -389,6 +396,9 @@ func (pm PageMetadata) query() (string, error) {
|
|||||||
if pm.Type != "" {
|
if pm.Type != "" {
|
||||||
q.Add("type", pm.Type)
|
q.Add("type", pm.Type)
|
||||||
}
|
}
|
||||||
|
if pm.Status != "" {
|
||||||
|
q.Add("status", pm.Status)
|
||||||
|
}
|
||||||
if pm.Metadata != nil {
|
if pm.Metadata != nil {
|
||||||
md, err := json.Marshal(pm.Metadata)
|
md, err := json.Marshal(pm.Metadata)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -189,3 +189,43 @@ func (sdk mfSDK) UpdatePassword(oldPass, newPass, token string) error {
|
|||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (sdk mfSDK) EnableUser(id, token string) error {
|
||||||
|
url := fmt.Sprintf("%s/%s/%s/enable", sdk.usersURL, usersEndpoint, id)
|
||||||
|
|
||||||
|
req, err := http.NewRequest(http.MethodPost, url, nil)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
resp, err := sdk.sendRequest(req, token, string(CTJSON))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if resp.StatusCode != http.StatusNoContent {
|
||||||
|
return errors.Wrap(ErrFailedRemoval, errors.New(resp.Status))
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (sdk mfSDK) DisableUser(id, token string) error {
|
||||||
|
url := fmt.Sprintf("%s/%s/%s/disable", sdk.usersURL, usersEndpoint, id)
|
||||||
|
|
||||||
|
req, err := http.NewRequest(http.MethodPost, url, nil)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
resp, err := sdk.sendRequest(req, token, string(CTJSON))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if resp.StatusCode != http.StatusNoContent {
|
||||||
|
return errors.Wrap(ErrFailedRemoval, errors.New(resp.Status))
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
@ -176,7 +176,7 @@ func TestUser(t *testing.T) {
|
|||||||
desc: "get non-existent user",
|
desc: "get non-existent user",
|
||||||
userID: "43",
|
userID: "43",
|
||||||
token: usertoken,
|
token: usertoken,
|
||||||
err: createError(sdk.ErrFailedFetch, http.StatusUnauthorized),
|
err: createError(sdk.ErrFailedFetch, http.StatusNotFound),
|
||||||
response: sdk.User{},
|
response: sdk.User{},
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -81,7 +81,7 @@ func viewUserEndpoint(svc users.Service) endpoint.Endpoint {
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
u, err := svc.ViewUser(ctx, req.token, req.userID)
|
u, err := svc.ViewUser(ctx, req.token, req.id)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -118,7 +118,14 @@ func listUsersEndpoint(svc users.Service) endpoint.Endpoint {
|
|||||||
if err := req.validate(); err != nil {
|
if err := req.validate(); err != nil {
|
||||||
return users.UserPage{}, err
|
return users.UserPage{}, err
|
||||||
}
|
}
|
||||||
up, err := svc.ListUsers(ctx, req.token, req.offset, req.limit, req.email, req.metadata)
|
pm := users.PageMetadata{
|
||||||
|
Offset: req.offset,
|
||||||
|
Limit: req.limit,
|
||||||
|
Email: req.email,
|
||||||
|
Status: req.status,
|
||||||
|
Metadata: req.metadata,
|
||||||
|
}
|
||||||
|
up, err := svc.ListUsers(ctx, req.token, pm)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return users.UserPage{}, err
|
return users.UserPage{}, err
|
||||||
}
|
}
|
||||||
@ -179,7 +186,13 @@ func listMembersEndpoint(svc users.Service) endpoint.Endpoint {
|
|||||||
return userPageRes{}, errors.Wrap(errors.ErrMalformedEntity, err)
|
return userPageRes{}, errors.Wrap(errors.ErrMalformedEntity, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
page, err := svc.ListMembers(ctx, req.token, req.groupID, req.offset, req.limit, req.metadata)
|
pm := users.PageMetadata{
|
||||||
|
Offset: req.offset,
|
||||||
|
Limit: req.limit,
|
||||||
|
Status: req.status,
|
||||||
|
Metadata: req.metadata,
|
||||||
|
}
|
||||||
|
page, err := svc.ListMembers(ctx, req.token, req.id, pm)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return userPageRes{}, err
|
return userPageRes{}, err
|
||||||
}
|
}
|
||||||
@ -188,6 +201,32 @@ func listMembersEndpoint(svc users.Service) endpoint.Endpoint {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func enableUserEndpoint(svc users.Service) endpoint.Endpoint {
|
||||||
|
return func(ctx context.Context, request interface{}) (interface{}, error) {
|
||||||
|
req := request.(changeUserStatusReq)
|
||||||
|
if err := req.validate(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if err := svc.EnableUser(ctx, req.token, req.id); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return deleteRes{}, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func disableUserEndpoint(svc users.Service) endpoint.Endpoint {
|
||||||
|
return func(ctx context.Context, request interface{}) (interface{}, error) {
|
||||||
|
req := request.(changeUserStatusReq)
|
||||||
|
if err := req.validate(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if err := svc.DisableUser(ctx, req.token, req.id); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return deleteRes{}, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func buildUsersResponse(up users.UserPage) userPageRes {
|
func buildUsersResponse(up users.UserPage) userPageRes {
|
||||||
res := userPageRes{
|
res := userPageRes{
|
||||||
pageRes: pageRes{
|
pageRes: pageRes{
|
||||||
|
@ -79,7 +79,7 @@ func (lm *loggingMiddleware) ViewProfile(ctx context.Context, token string) (u u
|
|||||||
return lm.svc.ViewProfile(ctx, token)
|
return lm.svc.ViewProfile(ctx, token)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (lm *loggingMiddleware) ListUsers(ctx context.Context, token string, offset, limit uint64, email string, um users.Metadata) (e users.UserPage, err error) {
|
func (lm *loggingMiddleware) ListUsers(ctx context.Context, token string, pm users.PageMetadata) (e users.UserPage, err error) {
|
||||||
defer func(begin time.Time) {
|
defer func(begin time.Time) {
|
||||||
message := fmt.Sprintf("Method list_users for token %s took %s to complete", token, time.Since(begin))
|
message := fmt.Sprintf("Method list_users for token %s took %s to complete", token, time.Since(begin))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -89,7 +89,7 @@ func (lm *loggingMiddleware) ListUsers(ctx context.Context, token string, offset
|
|||||||
lm.logger.Info(fmt.Sprintf("%s without errors.", message))
|
lm.logger.Info(fmt.Sprintf("%s without errors.", message))
|
||||||
}(time.Now())
|
}(time.Now())
|
||||||
|
|
||||||
return lm.svc.ListUsers(ctx, token, offset, limit, email, um)
|
return lm.svc.ListUsers(ctx, token, pm)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (lm *loggingMiddleware) UpdateUser(ctx context.Context, token string, u users.User) (err error) {
|
func (lm *loggingMiddleware) UpdateUser(ctx context.Context, token string, u users.User) (err error) {
|
||||||
@ -157,7 +157,7 @@ func (lm *loggingMiddleware) SendPasswordReset(ctx context.Context, host, email,
|
|||||||
return lm.svc.SendPasswordReset(ctx, host, email, token)
|
return lm.svc.SendPasswordReset(ctx, host, email, token)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (lm *loggingMiddleware) ListMembers(ctx context.Context, token, groupID string, offset, limit uint64, m users.Metadata) (mp users.UserPage, err error) {
|
func (lm *loggingMiddleware) ListMembers(ctx context.Context, token, groupID string, pm users.PageMetadata) (mp users.UserPage, err error) {
|
||||||
defer func(begin time.Time) {
|
defer func(begin time.Time) {
|
||||||
message := fmt.Sprintf("Method list_members for group %s took %s to complete", groupID, time.Since(begin))
|
message := fmt.Sprintf("Method list_members for group %s took %s to complete", groupID, time.Since(begin))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -167,5 +167,31 @@ func (lm *loggingMiddleware) ListMembers(ctx context.Context, token, groupID str
|
|||||||
lm.logger.Info(fmt.Sprintf("%s without errors.", message))
|
lm.logger.Info(fmt.Sprintf("%s without errors.", message))
|
||||||
}(time.Now())
|
}(time.Now())
|
||||||
|
|
||||||
return lm.svc.ListMembers(ctx, token, groupID, offset, limit, m)
|
return lm.svc.ListMembers(ctx, token, groupID, pm)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (lm *loggingMiddleware) EnableUser(ctx context.Context, token string, id string) (err error) {
|
||||||
|
defer func(begin time.Time) {
|
||||||
|
message := fmt.Sprintf("Method enable_user for user %s took %s to complete", id, time.Since(begin))
|
||||||
|
if err != nil {
|
||||||
|
lm.logger.Warn(fmt.Sprintf("%s with error: %s.", message, err))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
lm.logger.Info(fmt.Sprintf("%s without errors.", message))
|
||||||
|
}(time.Now())
|
||||||
|
|
||||||
|
return lm.svc.EnableUser(ctx, token, id)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (lm *loggingMiddleware) DisableUser(ctx context.Context, token string, id string) (err error) {
|
||||||
|
defer func(begin time.Time) {
|
||||||
|
message := fmt.Sprintf("Method disable_user for user %s took %s to complete", id, time.Since(begin))
|
||||||
|
if err != nil {
|
||||||
|
lm.logger.Warn(fmt.Sprintf("%s with error: %s.", message, err))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
lm.logger.Info(fmt.Sprintf("%s without errors.", message))
|
||||||
|
}(time.Now())
|
||||||
|
|
||||||
|
return lm.svc.DisableUser(ctx, token, id)
|
||||||
}
|
}
|
||||||
|
@ -66,13 +66,13 @@ func (ms *metricsMiddleware) ViewProfile(ctx context.Context, token string) (use
|
|||||||
return ms.svc.ViewProfile(ctx, token)
|
return ms.svc.ViewProfile(ctx, token)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ms *metricsMiddleware) ListUsers(ctx context.Context, token string, offset, limit uint64, email string, um users.Metadata) (users.UserPage, error) {
|
func (ms *metricsMiddleware) ListUsers(ctx context.Context, token string, pm users.PageMetadata) (users.UserPage, error) {
|
||||||
defer func(begin time.Time) {
|
defer func(begin time.Time) {
|
||||||
ms.counter.With("method", "list_users").Add(1)
|
ms.counter.With("method", "list_users").Add(1)
|
||||||
ms.latency.With("method", "list_users").Observe(time.Since(begin).Seconds())
|
ms.latency.With("method", "list_users").Observe(time.Since(begin).Seconds())
|
||||||
}(time.Now())
|
}(time.Now())
|
||||||
|
|
||||||
return ms.svc.ListUsers(ctx, token, offset, limit, email, um)
|
return ms.svc.ListUsers(ctx, token, pm)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ms *metricsMiddleware) UpdateUser(ctx context.Context, token string, u users.User) (err error) {
|
func (ms *metricsMiddleware) UpdateUser(ctx context.Context, token string, u users.User) (err error) {
|
||||||
@ -120,11 +120,29 @@ func (ms *metricsMiddleware) SendPasswordReset(ctx context.Context, host, email,
|
|||||||
return ms.svc.SendPasswordReset(ctx, host, email, token)
|
return ms.svc.SendPasswordReset(ctx, host, email, token)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ms *metricsMiddleware) ListMembers(ctx context.Context, token, groupID string, offset, limit uint64, gm users.Metadata) (users.UserPage, error) {
|
func (ms *metricsMiddleware) ListMembers(ctx context.Context, token, groupID string, pm users.PageMetadata) (users.UserPage, error) {
|
||||||
defer func(begin time.Time) {
|
defer func(begin time.Time) {
|
||||||
ms.counter.With("method", "list_members").Add(1)
|
ms.counter.With("method", "list_members").Add(1)
|
||||||
ms.latency.With("method", "list_members").Observe(time.Since(begin).Seconds())
|
ms.latency.With("method", "list_members").Observe(time.Since(begin).Seconds())
|
||||||
}(time.Now())
|
}(time.Now())
|
||||||
|
|
||||||
return ms.svc.ListMembers(ctx, token, groupID, offset, limit, gm)
|
return ms.svc.ListMembers(ctx, token, groupID, pm)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ms *metricsMiddleware) EnableUser(ctx context.Context, token string, id string) (err error) {
|
||||||
|
defer func(begin time.Time) {
|
||||||
|
ms.counter.With("method", "enable_user").Add(1)
|
||||||
|
ms.latency.With("method", "enable_user").Observe(time.Since(begin).Seconds())
|
||||||
|
}(time.Now())
|
||||||
|
|
||||||
|
return ms.svc.EnableUser(ctx, token, id)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ms *metricsMiddleware) DisableUser(ctx context.Context, token string, id string) (err error) {
|
||||||
|
defer func(begin time.Time) {
|
||||||
|
ms.counter.With("method", "disable_user").Add(1)
|
||||||
|
ms.latency.With("method", "disable_user").Observe(time.Since(begin).Seconds())
|
||||||
|
}(time.Now())
|
||||||
|
|
||||||
|
return ms.svc.DisableUser(ctx, token, id)
|
||||||
}
|
}
|
||||||
|
@ -31,8 +31,8 @@ func (req createUserReq) validate() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type viewUserReq struct {
|
type viewUserReq struct {
|
||||||
token string
|
token string
|
||||||
userID string
|
id string
|
||||||
}
|
}
|
||||||
|
|
||||||
func (req viewUserReq) validate() error {
|
func (req viewUserReq) validate() error {
|
||||||
@ -44,6 +44,7 @@ func (req viewUserReq) validate() error {
|
|||||||
|
|
||||||
type listUsersReq struct {
|
type listUsersReq struct {
|
||||||
token string
|
token string
|
||||||
|
status string
|
||||||
offset uint64
|
offset uint64
|
||||||
limit uint64
|
limit uint64
|
||||||
email string
|
email string
|
||||||
@ -62,6 +63,11 @@ func (req listUsersReq) validate() error {
|
|||||||
if len(req.email) > maxEmailSize {
|
if len(req.email) > maxEmailSize {
|
||||||
return apiutil.ErrEmailSize
|
return apiutil.ErrEmailSize
|
||||||
}
|
}
|
||||||
|
if req.status != users.AllStatusKey &&
|
||||||
|
req.status != users.EnabledStatusKey &&
|
||||||
|
req.status != users.DisabledStatusKey {
|
||||||
|
return apiutil.ErrInvalidStatus
|
||||||
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@ -139,10 +145,11 @@ func (req passwChangeReq) validate() error {
|
|||||||
|
|
||||||
type listMemberGroupReq struct {
|
type listMemberGroupReq struct {
|
||||||
token string
|
token string
|
||||||
|
status string
|
||||||
offset uint64
|
offset uint64
|
||||||
limit uint64
|
limit uint64
|
||||||
metadata users.Metadata
|
metadata users.Metadata
|
||||||
groupID string
|
id string
|
||||||
}
|
}
|
||||||
|
|
||||||
func (req listMemberGroupReq) validate() error {
|
func (req listMemberGroupReq) validate() error {
|
||||||
@ -150,9 +157,28 @@ func (req listMemberGroupReq) validate() error {
|
|||||||
return apiutil.ErrBearerToken
|
return apiutil.ErrBearerToken
|
||||||
}
|
}
|
||||||
|
|
||||||
if req.groupID == "" {
|
if req.id == "" {
|
||||||
|
return apiutil.ErrMissingID
|
||||||
|
}
|
||||||
|
if req.status != users.AllStatusKey &&
|
||||||
|
req.status != users.EnabledStatusKey &&
|
||||||
|
req.status != users.DisabledStatusKey {
|
||||||
|
return apiutil.ErrInvalidStatus
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type changeUserStatusReq struct {
|
||||||
|
token string
|
||||||
|
id string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (req changeUserStatusReq) validate() error {
|
||||||
|
if req.token == "" {
|
||||||
|
return apiutil.ErrBearerToken
|
||||||
|
}
|
||||||
|
if req.id == "" {
|
||||||
return apiutil.ErrMissingID
|
return apiutil.ErrMissingID
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -28,6 +28,7 @@ const (
|
|||||||
limitKey = "limit"
|
limitKey = "limit"
|
||||||
emailKey = "email"
|
emailKey = "email"
|
||||||
metadataKey = "metadata"
|
metadataKey = "metadata"
|
||||||
|
statusKey = "status"
|
||||||
defOffset = 0
|
defOffset = 0
|
||||||
defLimit = 10
|
defLimit = 10
|
||||||
)
|
)
|
||||||
@ -54,7 +55,7 @@ func MakeHandler(svc users.Service, tracer opentracing.Tracer, logger logger.Log
|
|||||||
opts...,
|
opts...,
|
||||||
))
|
))
|
||||||
|
|
||||||
mux.Get("/users/:userID", kithttp.NewServer(
|
mux.Get("/users/:id", kithttp.NewServer(
|
||||||
kitot.TraceServer(tracer, "view_user")(viewUserEndpoint(svc)),
|
kitot.TraceServer(tracer, "view_user")(viewUserEndpoint(svc)),
|
||||||
decodeViewUser,
|
decodeViewUser,
|
||||||
encodeResponse,
|
encodeResponse,
|
||||||
@ -96,7 +97,7 @@ func MakeHandler(svc users.Service, tracer opentracing.Tracer, logger logger.Log
|
|||||||
opts...,
|
opts...,
|
||||||
))
|
))
|
||||||
|
|
||||||
mux.Get("/groups/:groupId", kithttp.NewServer(
|
mux.Get("/groups/:id", kithttp.NewServer(
|
||||||
kitot.TraceServer(tracer, "list_members")(listMembersEndpoint(svc)),
|
kitot.TraceServer(tracer, "list_members")(listMembersEndpoint(svc)),
|
||||||
decodeListMembersRequest,
|
decodeListMembersRequest,
|
||||||
encodeResponse,
|
encodeResponse,
|
||||||
@ -110,6 +111,20 @@ func MakeHandler(svc users.Service, tracer opentracing.Tracer, logger logger.Log
|
|||||||
opts...,
|
opts...,
|
||||||
))
|
))
|
||||||
|
|
||||||
|
mux.Post("/users/:id/enable", kithttp.NewServer(
|
||||||
|
kitot.TraceServer(tracer, "enable_user")(enableUserEndpoint(svc)),
|
||||||
|
decodeChangeUserStatus,
|
||||||
|
encodeResponse,
|
||||||
|
opts...,
|
||||||
|
))
|
||||||
|
|
||||||
|
mux.Post("/users/:id/disable", kithttp.NewServer(
|
||||||
|
kitot.TraceServer(tracer, "disable_user")(disableUserEndpoint(svc)),
|
||||||
|
decodeChangeUserStatus,
|
||||||
|
encodeResponse,
|
||||||
|
opts...,
|
||||||
|
))
|
||||||
|
|
||||||
mux.GetFunc("/health", mainflux.Health("users"))
|
mux.GetFunc("/health", mainflux.Health("users"))
|
||||||
mux.Handle("/metrics", promhttp.Handler())
|
mux.Handle("/metrics", promhttp.Handler())
|
||||||
|
|
||||||
@ -118,8 +133,8 @@ func MakeHandler(svc users.Service, tracer opentracing.Tracer, logger logger.Log
|
|||||||
|
|
||||||
func decodeViewUser(_ context.Context, r *http.Request) (interface{}, error) {
|
func decodeViewUser(_ context.Context, r *http.Request) (interface{}, error) {
|
||||||
req := viewUserReq{
|
req := viewUserReq{
|
||||||
token: apiutil.ExtractBearerToken(r),
|
token: apiutil.ExtractBearerToken(r),
|
||||||
userID: bone.GetValue(r, "userID"),
|
id: bone.GetValue(r, "id"),
|
||||||
}
|
}
|
||||||
|
|
||||||
return req, nil
|
return req, nil
|
||||||
@ -152,8 +167,13 @@ func decodeListUsers(_ context.Context, r *http.Request) (interface{}, error) {
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
s, err := apiutil.ReadStringQuery(r, statusKey, users.EnabledStatusKey)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
req := listUsersReq{
|
req := listUsersReq{
|
||||||
token: apiutil.ExtractBearerToken(r),
|
token: apiutil.ExtractBearerToken(r),
|
||||||
|
status: s,
|
||||||
offset: o,
|
offset: o,
|
||||||
limit: l,
|
limit: l,
|
||||||
email: e,
|
email: e,
|
||||||
@ -258,10 +278,15 @@ func decodeListMembersRequest(_ context.Context, r *http.Request) (interface{},
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
s, err := apiutil.ReadStringQuery(r, statusKey, users.EnabledStatusKey)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
req := listMemberGroupReq{
|
req := listMemberGroupReq{
|
||||||
token: apiutil.ExtractBearerToken(r),
|
token: apiutil.ExtractBearerToken(r),
|
||||||
groupID: bone.GetValue(r, "groupId"),
|
status: s,
|
||||||
|
id: bone.GetValue(r, "id"),
|
||||||
offset: o,
|
offset: o,
|
||||||
limit: l,
|
limit: l,
|
||||||
metadata: m,
|
metadata: m,
|
||||||
@ -269,6 +294,15 @@ func decodeListMembersRequest(_ context.Context, r *http.Request) (interface{},
|
|||||||
return req, nil
|
return req, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func decodeChangeUserStatus(_ context.Context, r *http.Request) (interface{}, error) {
|
||||||
|
req := changeUserStatusReq{
|
||||||
|
token: apiutil.ExtractBearerToken(r),
|
||||||
|
id: bone.GetValue(r, "id"),
|
||||||
|
}
|
||||||
|
|
||||||
|
return req, nil
|
||||||
|
}
|
||||||
|
|
||||||
func encodeResponse(_ context.Context, w http.ResponseWriter, response interface{}) error {
|
func encodeResponse(_ context.Context, w http.ResponseWriter, response interface{}) error {
|
||||||
if ar, ok := response.(mainflux.Response); ok {
|
if ar, ok := response.(mainflux.Response); ok {
|
||||||
for k, v := range ar.Headers() {
|
for k, v := range ar.Headers() {
|
||||||
|
@ -91,7 +91,7 @@ func (urm *userRepositoryMock) RetrieveByID(ctx context.Context, id string) (use
|
|||||||
return val, nil
|
return val, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (urm *userRepositoryMock) RetrieveAll(ctx context.Context, offset, limit uint64, ids []string, email string, um users.Metadata) (users.UserPage, error) {
|
func (urm *userRepositoryMock) RetrieveAll(ctx context.Context, status string, offset, limit uint64, ids []string, email string, um users.Metadata) (users.UserPage, error) {
|
||||||
urm.mu.Lock()
|
urm.mu.Lock()
|
||||||
defer urm.mu.Unlock()
|
defer urm.mu.Unlock()
|
||||||
|
|
||||||
@ -110,6 +110,20 @@ func (urm *userRepositoryMock) RetrieveAll(ctx context.Context, offset, limit ui
|
|||||||
return up, nil
|
return up, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if status == users.EnabledStatusKey || status == users.DisabledStatusKey {
|
||||||
|
for _, u := range sortUsers(urm.users) {
|
||||||
|
if i >= offset && i < (limit+offset) {
|
||||||
|
if status == u.Status {
|
||||||
|
up.Users = append(up.Users, u)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
up.Offset = offset
|
||||||
|
up.Limit = limit
|
||||||
|
up.Total = uint64(i)
|
||||||
|
return up, nil
|
||||||
|
}
|
||||||
for _, u := range sortUsers(urm.users) {
|
for _, u := range sortUsers(urm.users) {
|
||||||
if i >= offset && i < (limit+offset) {
|
if i >= offset && i < (limit+offset) {
|
||||||
up.Users = append(up.Users, u)
|
up.Users = append(up.Users, u)
|
||||||
@ -134,6 +148,19 @@ func (urm *userRepositoryMock) UpdatePassword(_ context.Context, token, password
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (urm *userRepositoryMock) ChangeStatus(ctx context.Context, id, status string) error {
|
||||||
|
urm.mu.Lock()
|
||||||
|
defer urm.mu.Unlock()
|
||||||
|
|
||||||
|
user, ok := urm.usersByID[id]
|
||||||
|
if !ok {
|
||||||
|
return errors.ErrNotFound
|
||||||
|
}
|
||||||
|
user.Status = status
|
||||||
|
urm.usersByID[id] = user
|
||||||
|
urm.users[user.Email] = user
|
||||||
|
return nil
|
||||||
|
}
|
||||||
func sortUsers(us map[string]users.User) []users.User {
|
func sortUsers(us map[string]users.User) []users.User {
|
||||||
users := []users.User{}
|
users := []users.User{}
|
||||||
ids := make([]string, 0, len(us))
|
ids := make([]string, 0, len(us))
|
||||||
|
@ -78,6 +78,14 @@ func migrateDB(db *sqlx.DB) error {
|
|||||||
`ALTER TABLE IF EXISTS users ADD PRIMARY KEY (id)`,
|
`ALTER TABLE IF EXISTS users ADD PRIMARY KEY (id)`,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
Id: "users_5",
|
||||||
|
Up: []string{
|
||||||
|
`CREATE TYPE USER_STATUS AS ENUM ('enabled', 'disabled');`,
|
||||||
|
`ALTER TABLE IF EXISTS users ADD COLUMN IF NOT EXISTS
|
||||||
|
status USER_STATUS NOT NULL DEFAULT 'enabled'`,
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -38,7 +38,7 @@ func NewUserRepo(db Database) users.UserRepository {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (ur userRepository) Save(ctx context.Context, user users.User) (string, error) {
|
func (ur userRepository) Save(ctx context.Context, user users.User) (string, error) {
|
||||||
q := `INSERT INTO users (email, password, id, metadata) VALUES (:email, :password, :id, :metadata) RETURNING id`
|
q := `INSERT INTO users (email, password, id, metadata, status) VALUES (:email, :password, :id, :metadata, :status) RETURNING id`
|
||||||
if user.ID == "" || user.Email == "" {
|
if user.ID == "" || user.Email == "" {
|
||||||
return "", errors.ErrMalformedEntity
|
return "", errors.ErrMalformedEntity
|
||||||
}
|
}
|
||||||
@ -72,7 +72,7 @@ func (ur userRepository) Save(ctx context.Context, user users.User) (string, err
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (ur userRepository) Update(ctx context.Context, user users.User) error {
|
func (ur userRepository) Update(ctx context.Context, user users.User) error {
|
||||||
q := `UPDATE users SET(email, password, metadata) VALUES (:email, :password, :metadata) WHERE email = :email`
|
q := `UPDATE users SET(email, password, metadata, status) VALUES (:email, :password, :metadata, :status) WHERE email = :email;`
|
||||||
|
|
||||||
dbu, err := toDBUser(user)
|
dbu, err := toDBUser(user)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -87,7 +87,7 @@ func (ur userRepository) Update(ctx context.Context, user users.User) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (ur userRepository) UpdateUser(ctx context.Context, user users.User) error {
|
func (ur userRepository) UpdateUser(ctx context.Context, user users.User) error {
|
||||||
q := `UPDATE users SET metadata = :metadata WHERE email = :email`
|
q := `UPDATE users SET metadata = :metadata WHERE email = :email AND status = 'enabled'`
|
||||||
|
|
||||||
dbu, err := toDBUser(user)
|
dbu, err := toDBUser(user)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -102,7 +102,7 @@ func (ur userRepository) UpdateUser(ctx context.Context, user users.User) error
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (ur userRepository) RetrieveByEmail(ctx context.Context, email string) (users.User, error) {
|
func (ur userRepository) RetrieveByEmail(ctx context.Context, email string) (users.User, error) {
|
||||||
q := `SELECT id, password, metadata FROM users WHERE email = $1`
|
q := `SELECT id, password, metadata FROM users WHERE email = $1 AND status = 'enabled'`
|
||||||
|
|
||||||
dbu := dbUser{
|
dbu := dbUser{
|
||||||
Email: email,
|
Email: email,
|
||||||
@ -137,7 +137,7 @@ func (ur userRepository) RetrieveByID(ctx context.Context, id string) (users.Use
|
|||||||
return toUser(dbu)
|
return toUser(dbu)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ur userRepository) RetrieveAll(ctx context.Context, offset, limit uint64, userIDs []string, email string, um users.Metadata) (users.UserPage, error) {
|
func (ur userRepository) RetrieveAll(ctx context.Context, status string, offset, limit uint64, userIDs []string, email string, um users.Metadata) (users.UserPage, error) {
|
||||||
eq, ep, err := createEmailQuery("", email)
|
eq, ep, err := createEmailQuery("", email)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return users.UserPage{}, errors.Wrap(errors.ErrViewEntity, err)
|
return users.UserPage{}, errors.Wrap(errors.ErrViewEntity, err)
|
||||||
@ -147,6 +147,10 @@ func (ur userRepository) RetrieveAll(ctx context.Context, offset, limit uint64,
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return users.UserPage{}, errors.Wrap(errors.ErrViewEntity, err)
|
return users.UserPage{}, errors.Wrap(errors.ErrViewEntity, err)
|
||||||
}
|
}
|
||||||
|
aq := fmt.Sprintf("status = '%s'", status)
|
||||||
|
if status == users.AllStatusKey {
|
||||||
|
aq = ""
|
||||||
|
}
|
||||||
|
|
||||||
var query []string
|
var query []string
|
||||||
var emq string
|
var emq string
|
||||||
@ -156,6 +160,9 @@ func (ur userRepository) RetrieveAll(ctx context.Context, offset, limit uint64,
|
|||||||
if mq != "" {
|
if mq != "" {
|
||||||
query = append(query, mq)
|
query = append(query, mq)
|
||||||
}
|
}
|
||||||
|
if aq != "" {
|
||||||
|
query = append(query, aq)
|
||||||
|
}
|
||||||
|
|
||||||
if len(userIDs) > 0 {
|
if len(userIDs) > 0 {
|
||||||
query = append(query, fmt.Sprintf("id IN ('%s')", strings.Join(userIDs, "','")))
|
query = append(query, fmt.Sprintf("id IN ('%s')", strings.Join(userIDs, "','")))
|
||||||
@ -213,7 +220,7 @@ func (ur userRepository) RetrieveAll(ctx context.Context, offset, limit uint64,
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (ur userRepository) UpdatePassword(ctx context.Context, email, password string) error {
|
func (ur userRepository) UpdatePassword(ctx context.Context, email, password string) error {
|
||||||
q := `UPDATE users SET password = :password WHERE email = :email`
|
q := `UPDATE users SET password = :password WHERE status = 'enabled' AND email = :email`
|
||||||
|
|
||||||
db := dbUser{
|
db := dbUser{
|
||||||
Email: email,
|
Email: email,
|
||||||
@ -227,12 +234,27 @@ func (ur userRepository) UpdatePassword(ctx context.Context, email, password str
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (ur userRepository) ChangeStatus(ctx context.Context, id, status string) error {
|
||||||
|
q := fmt.Sprintf(`UPDATE users SET status = '%s' WHERE id = :id`, status)
|
||||||
|
|
||||||
|
dbu := dbUser{
|
||||||
|
ID: id,
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err := ur.db.NamedExecContext(ctx, q, dbu); err != nil {
|
||||||
|
return errors.Wrap(errors.ErrUpdateEntity, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
type dbUser struct {
|
type dbUser struct {
|
||||||
ID string `db:"id"`
|
ID string `db:"id"`
|
||||||
Email string `db:"email"`
|
Email string `db:"email"`
|
||||||
Password string `db:"password"`
|
Password string `db:"password"`
|
||||||
Metadata []byte `db:"metadata"`
|
Metadata []byte `db:"metadata"`
|
||||||
Groups []auth.Group `db:"groups"`
|
Groups []auth.Group `db:"groups"`
|
||||||
|
Status string `db:"status"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func toDBUser(u users.User) (dbUser, error) {
|
func toDBUser(u users.User) (dbUser, error) {
|
||||||
@ -250,6 +272,7 @@ func toDBUser(u users.User) (dbUser, error) {
|
|||||||
Email: u.Email,
|
Email: u.Email,
|
||||||
Password: u.Password,
|
Password: u.Password,
|
||||||
Metadata: data,
|
Metadata: data,
|
||||||
|
Status: u.Status,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -281,6 +304,7 @@ func toUser(dbu dbUser) (users.User, error) {
|
|||||||
Email: dbu.Email,
|
Email: dbu.Email,
|
||||||
Password: dbu.Password,
|
Password: dbu.Password,
|
||||||
Metadata: metadata,
|
Metadata: metadata,
|
||||||
|
Status: dbu.Status,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -35,6 +35,7 @@ func TestUserSave(t *testing.T) {
|
|||||||
ID: uid,
|
ID: uid,
|
||||||
Email: email,
|
Email: email,
|
||||||
Password: "pass",
|
Password: "pass",
|
||||||
|
Status: users.EnabledStatusKey,
|
||||||
},
|
},
|
||||||
err: nil,
|
err: nil,
|
||||||
},
|
},
|
||||||
@ -44,9 +45,20 @@ func TestUserSave(t *testing.T) {
|
|||||||
ID: uid,
|
ID: uid,
|
||||||
Email: email,
|
Email: email,
|
||||||
Password: "pass",
|
Password: "pass",
|
||||||
|
Status: users.EnabledStatusKey,
|
||||||
},
|
},
|
||||||
err: errors.ErrConflict,
|
err: errors.ErrConflict,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
desc: "invalid user status",
|
||||||
|
user: users.User{
|
||||||
|
ID: uid,
|
||||||
|
Email: email,
|
||||||
|
Password: "pass",
|
||||||
|
Status: "invalid",
|
||||||
|
},
|
||||||
|
err: errors.ErrMalformedEntity,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
dbMiddleware := postgres.NewDatabase(db)
|
dbMiddleware := postgres.NewDatabase(db)
|
||||||
@ -71,6 +83,7 @@ func TestSingleUserRetrieval(t *testing.T) {
|
|||||||
ID: uid,
|
ID: uid,
|
||||||
Email: email,
|
Email: email,
|
||||||
Password: "pass",
|
Password: "pass",
|
||||||
|
Status: users.EnabledStatusKey,
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err = repo.Save(context.Background(), user)
|
_, err = repo.Save(context.Background(), user)
|
||||||
@ -113,6 +126,7 @@ func TestRetrieveAll(t *testing.T) {
|
|||||||
ID: uid,
|
ID: uid,
|
||||||
Email: email,
|
Email: email,
|
||||||
Password: "pass",
|
Password: "pass",
|
||||||
|
Status: users.EnabledStatusKey,
|
||||||
}
|
}
|
||||||
if i < metaNum {
|
if i < metaNum {
|
||||||
user.Metadata = meta
|
user.Metadata = meta
|
||||||
@ -218,7 +232,7 @@ func TestRetrieveAll(t *testing.T) {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
for desc, tc := range cases {
|
for desc, tc := range cases {
|
||||||
page, err := userRepo.RetrieveAll(context.Background(), tc.offset, tc.limit, tc.ids, tc.email, tc.metadata)
|
page, err := userRepo.RetrieveAll(context.Background(), users.EnabledStatusKey, tc.offset, tc.limit, tc.ids, tc.email, tc.metadata)
|
||||||
size := uint64(len(page.Users))
|
size := uint64(len(page.Users))
|
||||||
assert.Equal(t, tc.size, size, fmt.Sprintf("%s: expected size %d got %d\n", desc, tc.size, size))
|
assert.Equal(t, tc.size, size, fmt.Sprintf("%s: expected size %d got %d\n", desc, tc.size, size))
|
||||||
assert.Nil(t, err, fmt.Sprintf("%s: expected no error got %d\n", desc, err))
|
assert.Nil(t, err, fmt.Sprintf("%s: expected no error got %d\n", desc, err))
|
||||||
|
@ -9,6 +9,7 @@ import (
|
|||||||
|
|
||||||
"github.com/mainflux/mainflux"
|
"github.com/mainflux/mainflux"
|
||||||
"github.com/mainflux/mainflux/auth"
|
"github.com/mainflux/mainflux/auth"
|
||||||
|
"github.com/mainflux/mainflux/internal/apiutil"
|
||||||
"github.com/mainflux/mainflux/pkg/errors"
|
"github.com/mainflux/mainflux/pkg/errors"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -16,6 +17,9 @@ const (
|
|||||||
memberRelationKey = "member"
|
memberRelationKey = "member"
|
||||||
authoritiesObjKey = "authorities"
|
authoritiesObjKey = "authorities"
|
||||||
usersObjKey = "users"
|
usersObjKey = "users"
|
||||||
|
EnabledStatusKey = "enabled"
|
||||||
|
DisabledStatusKey = "disabled"
|
||||||
|
AllStatusKey = "all"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@ -31,6 +35,12 @@ var (
|
|||||||
|
|
||||||
// ErrPasswordFormat indicates weak password.
|
// ErrPasswordFormat indicates weak password.
|
||||||
ErrPasswordFormat = errors.New("password does not meet the requirements")
|
ErrPasswordFormat = errors.New("password does not meet the requirements")
|
||||||
|
|
||||||
|
// ErrAlreadyEnabledUser indicates the user is already enabled.
|
||||||
|
ErrAlreadyEnabledUser = errors.New("the user is already enabled")
|
||||||
|
|
||||||
|
// ErrAlreadyDisabledUser indicates the user is already disabled.
|
||||||
|
ErrAlreadyDisabledUser = errors.New("the user is already disabled")
|
||||||
)
|
)
|
||||||
|
|
||||||
// Service specifies an API that must be fullfiled by the domain service
|
// Service specifies an API that must be fullfiled by the domain service
|
||||||
@ -53,7 +63,7 @@ type Service interface {
|
|||||||
ViewProfile(ctx context.Context, token string) (User, error)
|
ViewProfile(ctx context.Context, token string) (User, error)
|
||||||
|
|
||||||
// ListUsers retrieves users list for a valid admin token.
|
// ListUsers retrieves users list for a valid admin token.
|
||||||
ListUsers(ctx context.Context, token string, offset, limit uint64, email string, meta Metadata) (UserPage, error)
|
ListUsers(ctx context.Context, token string, pm PageMetadata) (UserPage, error)
|
||||||
|
|
||||||
// UpdateUser updates the user metadata.
|
// UpdateUser updates the user metadata.
|
||||||
UpdateUser(ctx context.Context, token string, user User) error
|
UpdateUser(ctx context.Context, token string, user User) error
|
||||||
@ -73,15 +83,23 @@ type Service interface {
|
|||||||
SendPasswordReset(ctx context.Context, host, email, token string) error
|
SendPasswordReset(ctx context.Context, host, email, token string) error
|
||||||
|
|
||||||
// ListMembers retrieves everything that is assigned to a group identified by groupID.
|
// ListMembers retrieves everything that is assigned to a group identified by groupID.
|
||||||
ListMembers(ctx context.Context, token, groupID string, offset, limit uint64, meta Metadata) (UserPage, error)
|
ListMembers(ctx context.Context, token, groupID string, pm PageMetadata) (UserPage, error)
|
||||||
|
|
||||||
|
// EnableUser logically enableds the user identified with the provided ID
|
||||||
|
EnableUser(ctx context.Context, token, id string) error
|
||||||
|
|
||||||
|
// DisableUser logically disables the user identified with the provided ID
|
||||||
|
DisableUser(ctx context.Context, token, id string) error
|
||||||
}
|
}
|
||||||
|
|
||||||
// PageMetadata contains page metadata that helps navigation.
|
// PageMetadata contains page metadata that helps navigation.
|
||||||
type PageMetadata struct {
|
type PageMetadata struct {
|
||||||
Total uint64
|
Total uint64
|
||||||
Offset uint64
|
Offset uint64
|
||||||
Limit uint64
|
Limit uint64
|
||||||
Email string
|
Email string
|
||||||
|
Status string
|
||||||
|
Metadata Metadata
|
||||||
}
|
}
|
||||||
|
|
||||||
// GroupPage contains a page of groups.
|
// GroupPage contains a page of groups.
|
||||||
@ -146,6 +164,16 @@ func (svc usersService) Register(ctx context.Context, token string, user User) (
|
|||||||
return "", errors.Wrap(errors.ErrMalformedEntity, err)
|
return "", errors.Wrap(errors.ErrMalformedEntity, err)
|
||||||
}
|
}
|
||||||
user.Password = hash
|
user.Password = hash
|
||||||
|
if user.Status == "" {
|
||||||
|
user.Status = EnabledStatusKey
|
||||||
|
}
|
||||||
|
|
||||||
|
if user.Status != AllStatusKey &&
|
||||||
|
user.Status != EnabledStatusKey &&
|
||||||
|
user.Status != DisabledStatusKey {
|
||||||
|
return "", apiutil.ErrInvalidStatus
|
||||||
|
}
|
||||||
|
|
||||||
uid, err = svc.users.Save(ctx, user)
|
uid, err = svc.users.Save(ctx, user)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
@ -181,14 +209,13 @@ func (svc usersService) Login(ctx context.Context, user User) (string, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (svc usersService) ViewUser(ctx context.Context, token, id string) (User, error) {
|
func (svc usersService) ViewUser(ctx context.Context, token, id string) (User, error) {
|
||||||
_, err := svc.identify(ctx, token)
|
if _, err := svc.identify(ctx, token); err != nil {
|
||||||
if err != nil {
|
|
||||||
return User{}, err
|
return User{}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
dbUser, err := svc.users.RetrieveByID(ctx, id)
|
dbUser, err := svc.users.RetrieveByID(ctx, id)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return User{}, errors.Wrap(errors.ErrAuthentication, err)
|
return User{}, errors.Wrap(errors.ErrNotFound, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return User{
|
return User{
|
||||||
@ -196,6 +223,7 @@ func (svc usersService) ViewUser(ctx context.Context, token, id string) (User, e
|
|||||||
Email: dbUser.Email,
|
Email: dbUser.Email,
|
||||||
Password: "",
|
Password: "",
|
||||||
Metadata: dbUser.Metadata,
|
Metadata: dbUser.Metadata,
|
||||||
|
Status: dbUser.Status,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -217,7 +245,7 @@ func (svc usersService) ViewProfile(ctx context.Context, token string) (User, er
|
|||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (svc usersService) ListUsers(ctx context.Context, token string, offset, limit uint64, email string, m Metadata) (UserPage, error) {
|
func (svc usersService) ListUsers(ctx context.Context, token string, pm PageMetadata) (UserPage, error) {
|
||||||
id, err := svc.identify(ctx, token)
|
id, err := svc.identify(ctx, token)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return UserPage{}, err
|
return UserPage{}, err
|
||||||
@ -226,7 +254,7 @@ func (svc usersService) ListUsers(ctx context.Context, token string, offset, lim
|
|||||||
if err := svc.authorize(ctx, id.id, "authorities", "member"); err != nil {
|
if err := svc.authorize(ctx, id.id, "authorities", "member"); err != nil {
|
||||||
return UserPage{}, errors.Wrap(errors.ErrAuthentication, err)
|
return UserPage{}, errors.Wrap(errors.ErrAuthentication, err)
|
||||||
}
|
}
|
||||||
return svc.users.RetrieveAll(ctx, offset, limit, nil, email, m)
|
return svc.users.RetrieveAll(ctx, pm.Status, pm.Offset, pm.Limit, nil, pm.Email, pm.Metadata)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (svc usersService) UpdateUser(ctx context.Context, token string, u User) error {
|
func (svc usersService) UpdateUser(ctx context.Context, token string, u User) error {
|
||||||
@ -307,12 +335,12 @@ func (svc usersService) SendPasswordReset(_ context.Context, host, email, token
|
|||||||
return svc.email.SendPasswordReset(to, host, token)
|
return svc.email.SendPasswordReset(to, host, token)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (svc usersService) ListMembers(ctx context.Context, token, groupID string, offset, limit uint64, m Metadata) (UserPage, error) {
|
func (svc usersService) ListMembers(ctx context.Context, token, groupID string, pm PageMetadata) (UserPage, error) {
|
||||||
if _, err := svc.identify(ctx, token); err != nil {
|
if _, err := svc.identify(ctx, token); err != nil {
|
||||||
return UserPage{}, err
|
return UserPage{}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
userIDs, err := svc.members(ctx, token, groupID, offset, limit)
|
userIDs, err := svc.members(ctx, token, groupID, pm.Offset, pm.Limit)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return UserPage{}, err
|
return UserPage{}, err
|
||||||
}
|
}
|
||||||
@ -322,13 +350,46 @@ func (svc usersService) ListMembers(ctx context.Context, token, groupID string,
|
|||||||
Users: []User{},
|
Users: []User{},
|
||||||
PageMetadata: PageMetadata{
|
PageMetadata: PageMetadata{
|
||||||
Total: 0,
|
Total: 0,
|
||||||
Offset: offset,
|
Offset: pm.Offset,
|
||||||
Limit: limit,
|
Limit: pm.Limit,
|
||||||
},
|
},
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
return svc.users.RetrieveAll(ctx, offset, limit, userIDs, "", m)
|
return svc.users.RetrieveAll(ctx, pm.Status, pm.Offset, pm.Limit, userIDs, pm.Email, pm.Metadata)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (svc usersService) EnableUser(ctx context.Context, token, id string) error {
|
||||||
|
if err := svc.changeStatus(ctx, token, id, EnabledStatusKey); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (svc usersService) DisableUser(ctx context.Context, token, id string) error {
|
||||||
|
if err := svc.changeStatus(ctx, token, id, DisabledStatusKey); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (svc usersService) changeStatus(ctx context.Context, token, id, status string) error {
|
||||||
|
if _, err := svc.identify(ctx, token); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
dbUser, err := svc.users.RetrieveByID(ctx, id)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrap(errors.ErrNotFound, err)
|
||||||
|
}
|
||||||
|
if dbUser.Status == status {
|
||||||
|
if status == DisabledStatusKey {
|
||||||
|
return ErrAlreadyDisabledUser
|
||||||
|
}
|
||||||
|
return ErrAlreadyEnabledUser
|
||||||
|
}
|
||||||
|
|
||||||
|
return svc.users.ChangeStatus(ctx, id, status)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Auth helpers
|
// Auth helpers
|
||||||
|
@ -168,7 +168,7 @@ func TestViewUser(t *testing.T) {
|
|||||||
user: users.User{},
|
user: users.User{},
|
||||||
token: token,
|
token: token,
|
||||||
userID: "",
|
userID: "",
|
||||||
err: errors.ErrAuthentication,
|
err: errors.ErrNotFound,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -260,7 +260,13 @@ func TestListUsers(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for desc, tc := range cases {
|
for desc, tc := range cases {
|
||||||
page, err := svc.ListUsers(context.Background(), tc.token, tc.offset, tc.limit, tc.email, nil)
|
pm := users.PageMetadata{
|
||||||
|
Offset: tc.offset,
|
||||||
|
Limit: tc.limit,
|
||||||
|
Email: tc.email,
|
||||||
|
Status: "all",
|
||||||
|
}
|
||||||
|
page, err := svc.ListUsers(context.Background(), tc.token, pm)
|
||||||
size := uint64(len(page.Users))
|
size := uint64(len(page.Users))
|
||||||
assert.Equal(t, tc.size, size, fmt.Sprintf("%s: expected size %d got %d\n", desc, tc.size, size))
|
assert.Equal(t, tc.size, size, fmt.Sprintf("%s: expected size %d got %d\n", desc, tc.size, size))
|
||||||
assert.True(t, errors.Contains(err, tc.err), fmt.Sprintf("%s: expected %s got %s\n", desc, tc.err, err))
|
assert.True(t, errors.Contains(err, tc.err), fmt.Sprintf("%s: expected %s got %s\n", desc, tc.err, err))
|
||||||
@ -390,3 +396,108 @@ func TestSendPasswordReset(t *testing.T) {
|
|||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestDisableUser(t *testing.T) {
|
||||||
|
enabledUser1 := users.User{Email: "user1@example.com", Password: "password"}
|
||||||
|
enabledUser2 := users.User{Email: "user2@example.com", Password: "password", Status: "enabled"}
|
||||||
|
disabledUser1 := users.User{Email: "user3@example.com", Password: "password", Status: "disabled"}
|
||||||
|
|
||||||
|
svc := newService()
|
||||||
|
|
||||||
|
id, err := svc.Register(context.Background(), user.Email, user)
|
||||||
|
require.Nil(t, err, fmt.Sprintf("unexpected error: %s", err))
|
||||||
|
user.ID = id
|
||||||
|
user.Status = "enabled"
|
||||||
|
token, err := svc.Login(context.Background(), user)
|
||||||
|
require.Nil(t, err, fmt.Sprintf("unexpected error: %s", err))
|
||||||
|
|
||||||
|
id, err = svc.Register(context.Background(), token, enabledUser1)
|
||||||
|
require.Nil(t, err, fmt.Sprintf("register enabledUser1 error: %s", err))
|
||||||
|
enabledUser1.ID = id
|
||||||
|
enabledUser1.Status = "enabled"
|
||||||
|
|
||||||
|
id, err = svc.Register(context.Background(), token, enabledUser2)
|
||||||
|
require.Nil(t, err, fmt.Sprintf("register enabledUser2 error: %s", err))
|
||||||
|
enabledUser2.ID = id
|
||||||
|
enabledUser2.Status = "disabled"
|
||||||
|
|
||||||
|
id, err = svc.Register(context.Background(), token, disabledUser1)
|
||||||
|
require.Nil(t, err, fmt.Sprintf("register disabledUser1 error: %s", err))
|
||||||
|
disabledUser1.ID = id
|
||||||
|
disabledUser1.Status = "disabled"
|
||||||
|
|
||||||
|
cases := []struct {
|
||||||
|
desc string
|
||||||
|
id string
|
||||||
|
token string
|
||||||
|
err error
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
desc: "disable user with wrong credentials",
|
||||||
|
id: enabledUser2.ID,
|
||||||
|
token: "",
|
||||||
|
err: errors.ErrAuthentication,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "disable existing user",
|
||||||
|
id: enabledUser2.ID,
|
||||||
|
token: token,
|
||||||
|
err: nil,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "disable disabled user",
|
||||||
|
id: enabledUser2.ID,
|
||||||
|
token: token,
|
||||||
|
err: users.ErrAlreadyDisabledUser,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "disable non-existing user",
|
||||||
|
id: "",
|
||||||
|
token: token,
|
||||||
|
err: errors.ErrNotFound,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tc := range cases {
|
||||||
|
err := svc.DisableUser(context.Background(), tc.token, tc.id)
|
||||||
|
assert.True(t, errors.Contains(err, tc.err), fmt.Sprintf("%s: expected %s got %s\n", tc.desc, tc.err, err))
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = svc.Login(context.Background(), enabledUser2)
|
||||||
|
assert.True(t, errors.Contains(err, errors.ErrNotFound), fmt.Sprintf("Login disabled user: expected %s got %s\n", errors.ErrNotFound, err))
|
||||||
|
|
||||||
|
cases2 := map[string]struct {
|
||||||
|
status string
|
||||||
|
size uint64
|
||||||
|
response []users.User
|
||||||
|
}{
|
||||||
|
"list enabled users": {
|
||||||
|
status: "enabled",
|
||||||
|
size: 2,
|
||||||
|
response: []users.User{enabledUser1, user},
|
||||||
|
},
|
||||||
|
"list disabled users": {
|
||||||
|
status: "disabled",
|
||||||
|
size: 2,
|
||||||
|
response: []users.User{enabledUser2, disabledUser1},
|
||||||
|
},
|
||||||
|
"list enabled and disabled users": {
|
||||||
|
status: "all",
|
||||||
|
size: 4,
|
||||||
|
response: []users.User{enabledUser1, enabledUser2, disabledUser1, user},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for desc, tc := range cases2 {
|
||||||
|
pm := users.PageMetadata{
|
||||||
|
Offset: 0,
|
||||||
|
Limit: 100,
|
||||||
|
Status: tc.status,
|
||||||
|
}
|
||||||
|
page, err := svc.ListUsers(context.Background(), token, pm)
|
||||||
|
require.Nil(t, err, fmt.Sprintf("unexpected error: %s", err))
|
||||||
|
size := uint64(len(page.Users))
|
||||||
|
assert.Equal(t, tc.size, size, fmt.Sprintf("%s: expected size %d got %d\n", desc, tc.size, size))
|
||||||
|
assert.ElementsMatch(t, tc.response, page.Users, fmt.Sprintf("%s: expected %s got %s\n", desc, tc.response, page.Users))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -75,12 +75,20 @@ func (urm userRepositoryMiddleware) UpdatePassword(ctx context.Context, email, p
|
|||||||
return urm.repo.UpdatePassword(ctx, email, password)
|
return urm.repo.UpdatePassword(ctx, email, password)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (urm userRepositoryMiddleware) RetrieveAll(ctx context.Context, offset, limit uint64, ids []string, email string, um users.Metadata) (users.UserPage, error) {
|
func (urm userRepositoryMiddleware) RetrieveAll(ctx context.Context, status string, offset, limit uint64, ids []string, email string, um users.Metadata) (users.UserPage, error) {
|
||||||
span := createSpan(ctx, urm.tracer, members)
|
span := createSpan(ctx, urm.tracer, members)
|
||||||
defer span.Finish()
|
defer span.Finish()
|
||||||
ctx = opentracing.ContextWithSpan(ctx, span)
|
ctx = opentracing.ContextWithSpan(ctx, span)
|
||||||
|
|
||||||
return urm.repo.RetrieveAll(ctx, offset, limit, ids, email, um)
|
return urm.repo.RetrieveAll(ctx, status, offset, limit, ids, email, um)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (urm userRepositoryMiddleware) ChangeStatus(ctx context.Context, id, status string) error {
|
||||||
|
span := createSpan(ctx, urm.tracer, members)
|
||||||
|
defer span.Finish()
|
||||||
|
ctx = opentracing.ContextWithSpan(ctx, span)
|
||||||
|
|
||||||
|
return urm.repo.ChangeStatus(ctx, id, status)
|
||||||
}
|
}
|
||||||
|
|
||||||
func createSpan(ctx context.Context, tracer opentracing.Tracer, opName string) opentracing.Span {
|
func createSpan(ctx context.Context, tracer opentracing.Tracer, opName string) opentracing.Span {
|
||||||
|
@ -39,6 +39,7 @@ type User struct {
|
|||||||
Email string
|
Email string
|
||||||
Password string
|
Password string
|
||||||
Metadata Metadata
|
Metadata Metadata
|
||||||
|
Status string
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validate returns an error if user representation is invalid.
|
// Validate returns an error if user representation is invalid.
|
||||||
@ -65,10 +66,13 @@ type UserRepository interface {
|
|||||||
RetrieveByID(ctx context.Context, id string) (User, error)
|
RetrieveByID(ctx context.Context, id string) (User, error)
|
||||||
|
|
||||||
// RetrieveAll retrieves all users for given array of userIDs.
|
// RetrieveAll retrieves all users for given array of userIDs.
|
||||||
RetrieveAll(ctx context.Context, offset, limit uint64, userIDs []string, email string, m Metadata) (UserPage, error)
|
RetrieveAll(ctx context.Context, status string, offset, limit uint64, userIDs []string, email string, m Metadata) (UserPage, error)
|
||||||
|
|
||||||
// UpdatePassword updates password for user with given email
|
// UpdatePassword updates password for user with given email
|
||||||
UpdatePassword(ctx context.Context, email, password string) error
|
UpdatePassword(ctx context.Context, email, password string) error
|
||||||
|
|
||||||
|
// ChangeStatus changes users status to enabled or disabled
|
||||||
|
ChangeStatus(ctx context.Context, id, status string) error
|
||||||
}
|
}
|
||||||
|
|
||||||
func isEmail(email string) bool {
|
func isEmail(email string) bool {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user