mirror of
https://github.com/absmach/magistrala.git
synced 2026-06-23 04:10:28 +00:00
MG-2426 - Replace generic Clients in Users service (#2436)
Signed-off-by: Musilah <nataleigh.nk@gmail.com> Signed-off-by: Arvindh <arvindh91@gmail.com> Signed-off-by: Felix Gateru <felix.gateru@gmail.com> Signed-off-by: Dusan Borovcanin <borovcanindusan1@gmail.com> Co-authored-by: Arvindh <arvindh91@gmail.com> Co-authored-by: Felix Gateru <felix.gateru@gmail.com> Co-authored-by: Dusan Borovcanin <borovcanindusan1@gmail.com>
This commit is contained in:
+228
-57
@@ -76,8 +76,10 @@ paths:
|
||||
- $ref: "#/components/parameters/Offset"
|
||||
- $ref: "#/components/parameters/Metadata"
|
||||
- $ref: "#/components/parameters/Status"
|
||||
- $ref: "#/components/parameters/UserName"
|
||||
- $ref: "#/components/parameters/UserIdentity"
|
||||
- $ref: "#/components/parameters/FirstName"
|
||||
- $ref: "#/components/parameters/LastName"
|
||||
- $ref: "#/components/parameters/Username"
|
||||
- $ref: "#/components/parameters/Email"
|
||||
- $ref: "#/components/parameters/Tags"
|
||||
security:
|
||||
- bearerAuth: []
|
||||
@@ -204,9 +206,44 @@ paths:
|
||||
"500":
|
||||
$ref: "#/components/responses/ServiceError"
|
||||
|
||||
/users/{userID}/username:
|
||||
patch:
|
||||
operationId: updateUsername
|
||||
summary: Updates user's username.
|
||||
description: |
|
||||
Updates username of the user with provided ID. Username is
|
||||
updated using authorization token and the new received username.
|
||||
tags:
|
||||
- Users
|
||||
parameters:
|
||||
- $ref: "#/components/parameters/UserID"
|
||||
requestBody:
|
||||
$ref: "#/components/requestBodies/UpdateUsernameReq"
|
||||
security:
|
||||
- bearerAuth: []
|
||||
responses:
|
||||
"200":
|
||||
$ref: "#/components/responses/UserRes"
|
||||
"400":
|
||||
description: Failed due to malformed JSON.
|
||||
"403":
|
||||
description: Failed to perform authorization over the entity.
|
||||
"404":
|
||||
description: Failed due to non existing user.
|
||||
"401":
|
||||
description: Missing or invalid access token provided.
|
||||
"409":
|
||||
description: Failed due to using an existing username.
|
||||
"415":
|
||||
description: Missing or invalid content type.
|
||||
"422":
|
||||
description: Database can't process request.
|
||||
"500":
|
||||
$ref: "#/components/responses/ServiceError"
|
||||
|
||||
/users/{userID}/tags:
|
||||
patch:
|
||||
operationId: updateUserTags
|
||||
operationId: updateTags
|
||||
summary: Updates tags the user.
|
||||
description: |
|
||||
Updates tags of the user with provided ID. Tags is updated using
|
||||
@@ -237,19 +274,52 @@ paths:
|
||||
"500":
|
||||
$ref: "#/components/responses/ServiceError"
|
||||
|
||||
/users/{userID}/identity:
|
||||
/users/{userID}/picture:
|
||||
patch:
|
||||
operationId: updateUserIdentity
|
||||
summary: Updates Identity of the user.
|
||||
operationId: updateProfilePicture
|
||||
summary: Updates the user's profile picture.
|
||||
description: |
|
||||
Updates identity of the user with provided ID. Identity is
|
||||
updated using authorization token and the new received identity.
|
||||
Updates the user's profile picture with provided ID. Profile picture is
|
||||
updated using authorization token and the new received picture.
|
||||
tags:
|
||||
- Users
|
||||
parameters:
|
||||
- $ref: "#/components/parameters/UserID"
|
||||
requestBody:
|
||||
$ref: "#/components/requestBodies/UserUpdateIdentityReq"
|
||||
$ref: "#/components/requestBodies/UserUpdateProfilePictureReq"
|
||||
security:
|
||||
- bearerAuth: []
|
||||
responses:
|
||||
"200":
|
||||
$ref: "#/components/responses/UserRes"
|
||||
"400":
|
||||
description: Failed due to malformed JSON.
|
||||
"403":
|
||||
description: Failed to perform authorization over the entity.
|
||||
"404":
|
||||
description: Failed due to non existing user.
|
||||
"401":
|
||||
description: Missing or invalid access token provided.
|
||||
"415":
|
||||
description: Missing or invalid content type.
|
||||
"422":
|
||||
description: Database can't process request.
|
||||
"500":
|
||||
$ref: "#/components/responses/ServiceError"
|
||||
|
||||
/users/{userID}/email:
|
||||
patch:
|
||||
operationId: updateEmail
|
||||
summary: Updates email of the user.
|
||||
description: |
|
||||
Updates email of the user with provided ID. Email is
|
||||
updated using authorization token and the new received email.
|
||||
tags:
|
||||
- Users
|
||||
parameters:
|
||||
- $ref: "#/components/parameters/UserID"
|
||||
requestBody:
|
||||
$ref: "#/components/requestBodies/UserUpdateEmailReq"
|
||||
security:
|
||||
- bearerAuth: []
|
||||
responses:
|
||||
@@ -264,7 +334,7 @@ paths:
|
||||
"401":
|
||||
description: Missing or invalid access token provided.
|
||||
"409":
|
||||
description: Failed due to using an existing identity.
|
||||
description: Failed due to using an existing email.
|
||||
"415":
|
||||
description: Missing or invalid content type.
|
||||
"422":
|
||||
@@ -274,7 +344,7 @@ paths:
|
||||
|
||||
/users/{userID}/role:
|
||||
patch:
|
||||
operationId: updateUserRole
|
||||
operationId: updateRole
|
||||
summary: Updates the user role.
|
||||
description: |
|
||||
Updates role for the user with provided ID.
|
||||
@@ -370,7 +440,7 @@ paths:
|
||||
|
||||
/users/secret:
|
||||
patch:
|
||||
operationId: updateUserSecret
|
||||
operationId: updateSecret
|
||||
summary: Updates Secret of currently logged in user.
|
||||
description: |
|
||||
Updates secret of currently logged in user. Secret is updated using
|
||||
@@ -408,8 +478,10 @@ paths:
|
||||
parameters:
|
||||
- $ref: "#/components/parameters/Limit"
|
||||
- $ref: "#/components/parameters/Offset"
|
||||
- $ref: "#/components/parameters/UserName"
|
||||
- $ref: "#/components/parameters/UserIdentity"
|
||||
- $ref: "#/components/parameters/Username"
|
||||
- $ref: "#/components/parameters/FirstName"
|
||||
- $ref: "#/components/parameters/LastName"
|
||||
- $ref: "#/components/parameters/Email"
|
||||
- $ref: "#/components/parameters/UserID"
|
||||
security:
|
||||
- bearerAuth: []
|
||||
@@ -1092,10 +1164,18 @@ components:
|
||||
UserReqObj:
|
||||
type: object
|
||||
properties:
|
||||
name:
|
||||
first_name:
|
||||
type: string
|
||||
example: userName
|
||||
description: User name.
|
||||
example: firstName
|
||||
description: User's first name.
|
||||
last_name:
|
||||
type: string
|
||||
example: lastName
|
||||
description: User's last name.
|
||||
email:
|
||||
type: string
|
||||
example: "admin@example.com"
|
||||
description: User's email address will be used as its unique identifier.
|
||||
tags:
|
||||
type: array
|
||||
minItems: 0
|
||||
@@ -1106,10 +1186,10 @@ components:
|
||||
credentials:
|
||||
type: object
|
||||
properties:
|
||||
identity:
|
||||
username:
|
||||
type: string
|
||||
example: "admin@example.com"
|
||||
description: User's identity for example email address will be used as its unique identifier
|
||||
example: "admin"
|
||||
description: User's username for example 'admin' will be used as its unique identifier.
|
||||
secret:
|
||||
type: string
|
||||
format: password
|
||||
@@ -1120,6 +1200,10 @@ components:
|
||||
type: object
|
||||
example: { "domain": "example.com" }
|
||||
description: Arbitrary, object-encoded user's data.
|
||||
profile_picture:
|
||||
type: string
|
||||
example: "https://example.com/profile.jpg"
|
||||
description: User's profile picture URL that is represented as a string.
|
||||
status:
|
||||
type: string
|
||||
description: User Status
|
||||
@@ -1163,10 +1247,14 @@ components:
|
||||
format: uuid
|
||||
example: bb7edb32-2eac-4aad-aebe-ed96fe073879
|
||||
description: User unique identifier.
|
||||
name:
|
||||
first_name:
|
||||
type: string
|
||||
example: userName
|
||||
description: User name.
|
||||
example: John
|
||||
description: User's first name.
|
||||
last_name:
|
||||
type: string
|
||||
example: Doe
|
||||
description: User's last name.
|
||||
tags:
|
||||
type: array
|
||||
minItems: 0
|
||||
@@ -1174,17 +1262,25 @@ components:
|
||||
type: string
|
||||
example: ["tag1", "tag2"]
|
||||
description: User tags.
|
||||
email:
|
||||
type: string
|
||||
example: "john.doe@magistrala.com"
|
||||
description: User email for example email address.
|
||||
credentials:
|
||||
type: object
|
||||
properties:
|
||||
identity:
|
||||
username:
|
||||
type: string
|
||||
example: admin@magistrala.com
|
||||
description: User Identity for example email address.
|
||||
example: john_doe
|
||||
description: User's username for example john_doe for Mr John Doe.
|
||||
metadata:
|
||||
type: object
|
||||
example: { "address": "example" }
|
||||
description: Arbitrary, object-encoded user's data.
|
||||
profile_picture:
|
||||
type: string
|
||||
example: "https://example.com/profile.jpg"
|
||||
description: User's profile picture URL that is represented as a string.
|
||||
status:
|
||||
type: string
|
||||
description: User Status
|
||||
@@ -1269,10 +1365,18 @@ components:
|
||||
format: uuid
|
||||
example: bb7edb32-2eac-4aad-aebe-ed96fe073879
|
||||
description: User unique identifier.
|
||||
name:
|
||||
first_name:
|
||||
type: string
|
||||
example: userName
|
||||
description: User name.
|
||||
example: John
|
||||
description: User's first name.
|
||||
last_name:
|
||||
type: string
|
||||
example: Doe
|
||||
description: User's last name.
|
||||
email:
|
||||
type: string
|
||||
example: user@magistrala.com
|
||||
description: User's email address.
|
||||
tags:
|
||||
type: array
|
||||
minItems: 0
|
||||
@@ -1283,10 +1387,10 @@ components:
|
||||
credentials:
|
||||
type: object
|
||||
properties:
|
||||
identity:
|
||||
username:
|
||||
type: string
|
||||
example: user@magistrala.com
|
||||
description: User Identity for example email address.
|
||||
example: john_doe
|
||||
description: User's username.
|
||||
secret:
|
||||
type: string
|
||||
example: password
|
||||
@@ -1392,16 +1496,21 @@ components:
|
||||
UserUpdate:
|
||||
type: object
|
||||
properties:
|
||||
name:
|
||||
first_name:
|
||||
type: string
|
||||
example: userName
|
||||
description: User name.
|
||||
example: firstName
|
||||
description: User's first name.
|
||||
last_name:
|
||||
type: string
|
||||
example: lastName
|
||||
description: User's last name.
|
||||
metadata:
|
||||
type: object
|
||||
example: { "role": "general" }
|
||||
description: Arbitrary, object-encoded user's data.
|
||||
required:
|
||||
- name
|
||||
- first_name
|
||||
- last_name
|
||||
- metadata
|
||||
|
||||
UserTags:
|
||||
@@ -1416,15 +1525,25 @@ components:
|
||||
items:
|
||||
type: string
|
||||
|
||||
UserIdentity:
|
||||
UserProfilePicture:
|
||||
type: object
|
||||
properties:
|
||||
identity:
|
||||
profile_picture:
|
||||
type: string
|
||||
example: "https://example.com/profile.jpg"
|
||||
description: User's profile picture URL that is represented as a string.
|
||||
required:
|
||||
- profile_picture
|
||||
|
||||
Email:
|
||||
type: object
|
||||
properties:
|
||||
email:
|
||||
type: string
|
||||
example: user@magistrala.com
|
||||
description: User Identity for example email address.
|
||||
description: User email address.
|
||||
required:
|
||||
- identity
|
||||
- email
|
||||
|
||||
UserSecret:
|
||||
type: object
|
||||
@@ -1454,6 +1573,16 @@ components:
|
||||
required:
|
||||
- role
|
||||
|
||||
Username:
|
||||
type: object
|
||||
properties:
|
||||
username:
|
||||
type: string
|
||||
example: "admin"
|
||||
description: User's username for example 'admin' will be used as its unique identifier.
|
||||
required:
|
||||
- username
|
||||
|
||||
GroupUpdate:
|
||||
type: object
|
||||
properties:
|
||||
@@ -1529,7 +1658,7 @@ components:
|
||||
identity:
|
||||
type: string
|
||||
example: user@magistrala.com
|
||||
description: User Identity for example email address.
|
||||
description: User identity - email address.
|
||||
secret:
|
||||
type: string
|
||||
example: password
|
||||
@@ -1594,22 +1723,40 @@ components:
|
||||
required: true
|
||||
example: bb7edb32-2eac-4aad-aebe-ed96fe073879
|
||||
|
||||
UserName:
|
||||
name: name
|
||||
description: User's name.
|
||||
Username:
|
||||
name: username
|
||||
description: User's username.
|
||||
in: query
|
||||
schema:
|
||||
type: string
|
||||
required: false
|
||||
example: "userName"
|
||||
example: "username"
|
||||
|
||||
UserIdentity:
|
||||
name: identity
|
||||
description: User's identity.
|
||||
FirstName:
|
||||
name: first_name
|
||||
description: User's first name.
|
||||
in: query
|
||||
schema:
|
||||
type: string
|
||||
pattern: "^[^\u0000-\u001F]*$"
|
||||
required: false
|
||||
example: "Jane"
|
||||
|
||||
LastName:
|
||||
name: last_name
|
||||
description: User's last name.
|
||||
in: query
|
||||
schema:
|
||||
type: string
|
||||
required: false
|
||||
example: "Doe"
|
||||
|
||||
Email:
|
||||
name: email
|
||||
description: User's email address.
|
||||
in: query
|
||||
schema:
|
||||
type: string
|
||||
format: email
|
||||
required: false
|
||||
example: "admin@example.com"
|
||||
|
||||
@@ -1788,13 +1935,21 @@ components:
|
||||
schema:
|
||||
$ref: "#/components/schemas/UserTags"
|
||||
|
||||
UserUpdateIdentityReq:
|
||||
description: Identity change data. User can change its identity.
|
||||
UserUpdateProfilePictureReq:
|
||||
description: JSON-formated document describing the profile picture of user to be update
|
||||
required: true
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "#/components/schemas/UserIdentity"
|
||||
$ref: "#/components/schemas/UserProfilePicture"
|
||||
|
||||
UserUpdateEmailReq:
|
||||
description: Email change data. User can change its email.
|
||||
required: true
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "#/components/schemas/Email"
|
||||
|
||||
UserUpdateSecretReq:
|
||||
description: Secret change data. User can change its secret.
|
||||
@@ -1812,6 +1967,14 @@ components:
|
||||
schema:
|
||||
$ref: "#/components/schemas/UserRole"
|
||||
|
||||
UpdateUsernameReq:
|
||||
description: JSON-formated document describing the username of the user to be updated
|
||||
required: true
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "#/components/schemas/Username"
|
||||
|
||||
GroupCreateReq:
|
||||
description: JSON-formatted document describing the new group to be registered
|
||||
required: true
|
||||
@@ -1942,16 +2105,24 @@ components:
|
||||
operationId: updateUser
|
||||
parameters:
|
||||
userID: $response.body#/id
|
||||
update_tags:
|
||||
operationId: updateUserTags
|
||||
update_username:
|
||||
operationId: updateUsername
|
||||
parameters:
|
||||
userID: $response.body#/id
|
||||
update_identity:
|
||||
operationId: updateUserIdentity
|
||||
update_tags:
|
||||
operationId: updateTags
|
||||
parameters:
|
||||
userID: $response.body#/id
|
||||
update_profile_picture:
|
||||
operationId: updateProfilePicture
|
||||
parameters:
|
||||
userID: $response.body#/id
|
||||
update_email:
|
||||
operationId: updateEmail
|
||||
parameters:
|
||||
userID: $response.body#/id
|
||||
update_role:
|
||||
operationId: updateUserRole
|
||||
operationId: updateRole
|
||||
parameters:
|
||||
userID: $response.body#/id
|
||||
disable:
|
||||
|
||||
@@ -192,7 +192,7 @@ func TestAdd(t *testing.T) {
|
||||
lastID := "0"
|
||||
for _, tc := range cases {
|
||||
tc.session = mgauthn.Session{UserID: validID, DomainID: tc.domainID, DomainUserID: validID}
|
||||
sdkCall := tv.sdk.On("Thing", tc.config.ThingID, tc.domainID, tc.token).Return(mgsdk.Thing{ID: tc.config.ThingID, Credentials: mgsdk.Credentials{Secret: tc.config.ThingKey}}, errors.NewSDKError(tc.thingErr))
|
||||
sdkCall := tv.sdk.On("Thing", tc.config.ThingID, tc.domainID, tc.token).Return(mgsdk.Thing{ID: tc.config.ThingID, Credentials: mgsdk.ClientCredentials{Secret: tc.config.ThingKey}}, errors.NewSDKError(tc.thingErr))
|
||||
repoCall := tv.boot.On("ListExisting", context.Background(), domainID, mock.Anything).Return(tc.config.Channels, tc.listErr)
|
||||
repoCall1 := tv.boot.On("Save", context.Background(), mock.Anything, mock.Anything).Return(mock.Anything, tc.saveErr)
|
||||
|
||||
|
||||
@@ -152,7 +152,7 @@ func TestAdd(t *testing.T) {
|
||||
for _, tc := range cases {
|
||||
t.Run(tc.desc, func(t *testing.T) {
|
||||
tc.session = mgauthn.Session{UserID: tc.userID, DomainID: tc.domainID, DomainUserID: validID}
|
||||
repoCall := sdk.On("Thing", tc.config.ThingID, mock.Anything, tc.token).Return(mgsdk.Thing{ID: tc.config.ThingID, Credentials: mgsdk.Credentials{Secret: tc.config.ThingKey}}, tc.thingErr)
|
||||
repoCall := sdk.On("Thing", tc.config.ThingID, mock.Anything, tc.token).Return(mgsdk.Thing{ID: tc.config.ThingID, Credentials: mgsdk.ClientCredentials{Secret: tc.config.ThingKey}}, tc.thingErr)
|
||||
repoCall1 := sdk.On("CreateThing", mock.Anything, tc.domainID, tc.token).Return(mgsdk.Thing{}, tc.createThingErr)
|
||||
repoCall2 := sdk.On("DeleteThing", tc.config.ThingID, tc.domainID, tc.token).Return(tc.deleteThingErr)
|
||||
repoCall3 := boot.On("ListExisting", context.Background(), tc.domainID, mock.Anything).Return(tc.config.Channels, tc.listExistingErr)
|
||||
|
||||
@@ -26,7 +26,7 @@ func New(svc bootstrap.Service, tracer trace.Tracer) bootstrap.Service {
|
||||
|
||||
// Add traces the "Add" operation of the wrapped bootstrap.Service.
|
||||
func (tm *tracingMiddleware) Add(ctx context.Context, session mgauthn.Session, token string, cfg bootstrap.Config) (bootstrap.Config, error) {
|
||||
ctx, span := tm.tracer.Start(ctx, "svc_register_client", trace.WithAttributes(
|
||||
ctx, span := tm.tracer.Start(ctx, "svc_register_user", trace.WithAttributes(
|
||||
attribute.String("thing_id", cfg.ThingID),
|
||||
attribute.String("domain_id ", cfg.DomainID),
|
||||
attribute.String("name", cfg.Name),
|
||||
@@ -41,7 +41,7 @@ func (tm *tracingMiddleware) Add(ctx context.Context, session mgauthn.Session, t
|
||||
|
||||
// View traces the "View" operation of the wrapped bootstrap.Service.
|
||||
func (tm *tracingMiddleware) View(ctx context.Context, session mgauthn.Session, id string) (bootstrap.Config, error) {
|
||||
ctx, span := tm.tracer.Start(ctx, "svc_view_client", trace.WithAttributes(
|
||||
ctx, span := tm.tracer.Start(ctx, "svc_view_user", trace.WithAttributes(
|
||||
attribute.String("id", id),
|
||||
))
|
||||
defer span.End()
|
||||
@@ -51,7 +51,7 @@ func (tm *tracingMiddleware) View(ctx context.Context, session mgauthn.Session,
|
||||
|
||||
// Update traces the "Update" operation of the wrapped bootstrap.Service.
|
||||
func (tm *tracingMiddleware) Update(ctx context.Context, session mgauthn.Session, cfg bootstrap.Config) error {
|
||||
ctx, span := tm.tracer.Start(ctx, "svc_update_client", trace.WithAttributes(
|
||||
ctx, span := tm.tracer.Start(ctx, "svc_update_user", trace.WithAttributes(
|
||||
attribute.String("name", cfg.Name),
|
||||
attribute.String("content", cfg.Content),
|
||||
attribute.String("thing_id", cfg.ThingID),
|
||||
@@ -85,7 +85,7 @@ func (tm *tracingMiddleware) UpdateConnections(ctx context.Context, session mgau
|
||||
|
||||
// List traces the "List" operation of the wrapped bootstrap.Service.
|
||||
func (tm *tracingMiddleware) List(ctx context.Context, session mgauthn.Session, filter bootstrap.Filter, offset, limit uint64) (bootstrap.ConfigsPage, error) {
|
||||
ctx, span := tm.tracer.Start(ctx, "svc_list_clients", trace.WithAttributes(
|
||||
ctx, span := tm.tracer.Start(ctx, "svc_list_users", trace.WithAttributes(
|
||||
attribute.Int64("offset", int64(offset)),
|
||||
attribute.Int64("limit", int64(limit)),
|
||||
))
|
||||
@@ -96,7 +96,7 @@ func (tm *tracingMiddleware) List(ctx context.Context, session mgauthn.Session,
|
||||
|
||||
// Remove traces the "Remove" operation of the wrapped bootstrap.Service.
|
||||
func (tm *tracingMiddleware) Remove(ctx context.Context, session mgauthn.Session, id string) error {
|
||||
ctx, span := tm.tracer.Start(ctx, "svc_remove_client", trace.WithAttributes(
|
||||
ctx, span := tm.tracer.Start(ctx, "svc_remove_user", trace.WithAttributes(
|
||||
attribute.String("id", id),
|
||||
))
|
||||
defer span.End()
|
||||
@@ -106,7 +106,7 @@ func (tm *tracingMiddleware) Remove(ctx context.Context, session mgauthn.Session
|
||||
|
||||
// Bootstrap traces the "Bootstrap" operation of the wrapped bootstrap.Service.
|
||||
func (tm *tracingMiddleware) Bootstrap(ctx context.Context, externalKey, externalID string, secure bool) (bootstrap.Config, error) {
|
||||
ctx, span := tm.tracer.Start(ctx, "svc_bootstrap_client", trace.WithAttributes(
|
||||
ctx, span := tm.tracer.Start(ctx, "svc_bootstrap_user", trace.WithAttributes(
|
||||
attribute.String("external_key", externalKey),
|
||||
attribute.String("external_id", externalID),
|
||||
attribute.Bool("secure", secure),
|
||||
|
||||
@@ -106,7 +106,7 @@ func TestIssueCert(t *testing.T) {
|
||||
|
||||
for _, tc := range cases {
|
||||
t.Run(tc.desc, func(t *testing.T) {
|
||||
sdkCall := sdk.On("Thing", tc.thingID, tc.domainID, tc.token).Return(mgsdk.Thing{ID: tc.thingID, Credentials: mgsdk.Credentials{Secret: thingKey}}, tc.thingErr)
|
||||
sdkCall := sdk.On("Thing", tc.thingID, tc.domainID, tc.token).Return(mgsdk.Thing{ID: tc.thingID, Credentials: mgsdk.ClientCredentials{Secret: thingKey}}, tc.thingErr)
|
||||
agentCall := agent.On("Issue", thingID, tc.ttl, tc.ipAddr).Return(tc.cert, tc.issueCertErr)
|
||||
resp, err := svc.IssueCert(context.Background(), tc.domainID, tc.token, tc.thingID, tc.ttl)
|
||||
assert.True(t, errors.Contains(err, tc.err), fmt.Sprintf("%s: expected %s got %s\n", tc.desc, tc.err, err))
|
||||
@@ -169,7 +169,7 @@ func TestRevokeCert(t *testing.T) {
|
||||
|
||||
for _, tc := range cases {
|
||||
t.Run(tc.desc, func(t *testing.T) {
|
||||
sdkCall := sdk.On("Thing", tc.thingID, tc.domainID, tc.token).Return(mgsdk.Thing{ID: tc.thingID, Credentials: mgsdk.Credentials{Secret: thingKey}}, tc.thingErr)
|
||||
sdkCall := sdk.On("Thing", tc.thingID, tc.domainID, tc.token).Return(mgsdk.Thing{ID: tc.thingID, Credentials: mgsdk.ClientCredentials{Secret: thingKey}}, tc.thingErr)
|
||||
agentCall := agent.On("Revoke", mock.Anything).Return(tc.revokeErr)
|
||||
agentCall1 := agent.On("ListCerts", mock.Anything).Return(tc.page, tc.listErr)
|
||||
_, err := svc.RevokeCert(context.Background(), tc.domainID, tc.token, tc.thingID)
|
||||
|
||||
+5
-5
@@ -135,9 +135,10 @@ var cmdProvision = []cobra.Command{
|
||||
// Create test user
|
||||
name := namesgenerator.Generate()
|
||||
user := mgxsdk.User{
|
||||
Name: name,
|
||||
FirstName: name,
|
||||
Email: fmt.Sprintf("%s@email.com", name),
|
||||
Credentials: mgxsdk.Credentials{
|
||||
Identity: fmt.Sprintf("%s@email.com", name),
|
||||
Username: name,
|
||||
Secret: "12345678",
|
||||
},
|
||||
Status: mgxsdk.EnabledStatus,
|
||||
@@ -148,8 +149,7 @@ var cmdProvision = []cobra.Command{
|
||||
return
|
||||
}
|
||||
|
||||
user.Credentials.Secret = "12345678"
|
||||
ut, err := sdk.CreateToken(mgxsdk.Login{Identity: user.Credentials.Identity, Secret: user.Credentials.Secret})
|
||||
ut, err := sdk.CreateToken(mgxsdk.Login{Username: user.Username, Secret: user.Credentials.Secret})
|
||||
if err != nil {
|
||||
logErrorCmd(*cmd, err)
|
||||
return
|
||||
@@ -166,7 +166,7 @@ var cmdProvision = []cobra.Command{
|
||||
return
|
||||
}
|
||||
|
||||
ut, err = sdk.CreateToken(mgxsdk.Login{Identity: user.Credentials.Identity, Secret: user.Credentials.Secret})
|
||||
ut, err = sdk.CreateToken(mgxsdk.Login{Email: user.Email, Secret: user.Credentials.Secret})
|
||||
if err != nil {
|
||||
logErrorCmd(*cmd, err)
|
||||
return
|
||||
|
||||
+2
-2
@@ -33,7 +33,7 @@ var (
|
||||
var thing = sdk.Thing{
|
||||
ID: testsutil.GenerateUUID(&testing.T{}),
|
||||
Name: "testthing",
|
||||
Credentials: sdk.Credentials{
|
||||
Credentials: sdk.ClientCredentials{
|
||||
Secret: "secret",
|
||||
},
|
||||
DomainID: testsutil.GenerateUUID(&testing.T{}),
|
||||
@@ -385,7 +385,7 @@ func TestUpdateThingCmd(t *testing.T) {
|
||||
ID: thing.ID,
|
||||
DomainID: thing.DomainID,
|
||||
Status: thing.Status,
|
||||
Credentials: sdk.Credentials{
|
||||
Credentials: sdk.ClientCredentials{
|
||||
Secret: newSecret,
|
||||
},
|
||||
},
|
||||
|
||||
+41
-23
@@ -9,36 +9,38 @@ import (
|
||||
"net/url"
|
||||
"strconv"
|
||||
|
||||
mgclients "github.com/absmach/magistrala/pkg/clients"
|
||||
mgxsdk "github.com/absmach/magistrala/pkg/sdk/go"
|
||||
"github.com/absmach/magistrala/users"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
var cmdUsers = []cobra.Command{
|
||||
{
|
||||
Use: "create <name> <username> <password> <user_auth_token>",
|
||||
Use: "create <first_name> <last_name> <email> <username> <password> <user_auth_token>",
|
||||
Short: "Create user",
|
||||
Long: "Create user with provided name, username and password. Token is optional\n" +
|
||||
Long: "Create user with provided firstname, lastname, email, username and password. Token is optional\n" +
|
||||
"For example:\n" +
|
||||
"\tmagistrala-cli users create user user@example.com 12345678 $USER_AUTH_TOKEN\n",
|
||||
"\tmagistrala-cli users create jane doe janedoe@example.com jane_doe 12345678 $USER_AUTH_TOKEN\n",
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
if len(args) < 3 || len(args) > 4 {
|
||||
if len(args) < 5 || len(args) > 6 {
|
||||
logUsageCmd(*cmd, cmd.Use)
|
||||
return
|
||||
}
|
||||
if len(args) == 3 {
|
||||
if len(args) == 5 {
|
||||
args = append(args, "")
|
||||
}
|
||||
|
||||
user := mgxsdk.User{
|
||||
Name: args[0],
|
||||
FirstName: args[0],
|
||||
LastName: args[1],
|
||||
Email: args[2],
|
||||
Credentials: mgxsdk.Credentials{
|
||||
Identity: args[1],
|
||||
Secret: args[2],
|
||||
Username: args[3],
|
||||
Secret: args[4],
|
||||
},
|
||||
Status: mgclients.EnabledStatus.String(),
|
||||
Status: users.EnabledStatus.String(),
|
||||
}
|
||||
user, err := sdk.CreateUser(user, args[3])
|
||||
user, err := sdk.CreateUser(user, args[5])
|
||||
if err != nil {
|
||||
logErrorCmd(*cmd, err)
|
||||
return
|
||||
@@ -66,6 +68,7 @@ var cmdUsers = []cobra.Command{
|
||||
return
|
||||
}
|
||||
pageMetadata := mgxsdk.PageMetadata{
|
||||
Username: Username,
|
||||
Identity: Identity,
|
||||
Offset: Offset,
|
||||
Limit: Limit,
|
||||
@@ -93,21 +96,21 @@ var cmdUsers = []cobra.Command{
|
||||
{
|
||||
Use: "token <username> <password>",
|
||||
Short: "Get token",
|
||||
Long: "Generate new token from username and password\n" +
|
||||
Long: "Generate a new token with username and password\n" +
|
||||
"For example:\n" +
|
||||
"\tmagistrala-cli users token user@example.com 12345678\n",
|
||||
"\tmagistrala-cli users token jane.doe 12345678\n",
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
if len(args) != 2 {
|
||||
logUsageCmd(*cmd, cmd.Use)
|
||||
return
|
||||
}
|
||||
|
||||
lg := mgxsdk.Login{
|
||||
Identity: args[0],
|
||||
loginReq := mgxsdk.Login{
|
||||
Username: args[0],
|
||||
Secret: args[1],
|
||||
}
|
||||
|
||||
token, err := sdk.CreateToken(lg)
|
||||
token, err := sdk.CreateToken(loginReq)
|
||||
if err != nil {
|
||||
logErrorCmd(*cmd, err)
|
||||
return
|
||||
@@ -116,6 +119,7 @@ var cmdUsers = []cobra.Command{
|
||||
logJSONCmd(*cmd, token)
|
||||
},
|
||||
},
|
||||
|
||||
{
|
||||
Use: "refreshtoken <token>",
|
||||
Short: "Get token",
|
||||
@@ -138,13 +142,15 @@ var cmdUsers = []cobra.Command{
|
||||
},
|
||||
},
|
||||
{
|
||||
Use: "update [<user_id> <JSON_string> | tags <user_id> <tags> | identity <user_id> <identity> ] <user_auth_token>",
|
||||
Use: "update [<user_id> <JSON_string> | tags <user_id> <tags> | username <user_id> <username> | email <user_id> <email>] <user_auth_token>",
|
||||
Short: "Update user",
|
||||
Long: "Updates either user name and metadata or user tags or user identity\n" +
|
||||
Long: "Updates either user name and metadata or user tags or user email\n" +
|
||||
"Usage:\n" +
|
||||
"\tmagistrala-cli users update <user_id> '{\"name\":\"new name\", \"metadata\":{\"key\": \"value\"}}' $USERTOKEN - updates user name and metadata\n" +
|
||||
"\tmagistrala-cli users update <user_id> '{\"first_name\":\"new first_name\", \"metadata\":{\"key\": \"value\"}}' $USERTOKEN - updates user first and lastname and metadata\n" +
|
||||
"\tmagistrala-cli users update tags <user_id> '[\"tag1\", \"tag2\"]' $USERTOKEN - updates user tags\n" +
|
||||
"\tmagistrala-cli users update identity <user_id> newidentity@example.com $USERTOKEN - updates user identity\n",
|
||||
"\tmagistrala-cli users update username <user_id> newusername $USERTOKEN - updates user name\n" +
|
||||
"\tmagistrala-cli users update email <user_id> newemail@example.com $USERTOKEN - updates user email\n",
|
||||
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
if len(args) != 4 && len(args) != 3 {
|
||||
logUsageCmd(*cmd, cmd.Use)
|
||||
@@ -168,10 +174,22 @@ var cmdUsers = []cobra.Command{
|
||||
return
|
||||
}
|
||||
|
||||
if args[0] == "identity" {
|
||||
if args[0] == "email" {
|
||||
user.ID = args[1]
|
||||
user.Credentials.Identity = args[2]
|
||||
user, err := sdk.UpdateUserIdentity(user, args[3])
|
||||
user.Email = args[2]
|
||||
user, err := sdk.UpdateUserEmail(user, args[3])
|
||||
if err != nil {
|
||||
logErrorCmd(*cmd, err)
|
||||
return
|
||||
}
|
||||
logJSONCmd(*cmd, user)
|
||||
return
|
||||
}
|
||||
|
||||
if args[0] == "username" {
|
||||
user.ID = args[1]
|
||||
user.Credentials.Username = args[2]
|
||||
user, err := sdk.UpdateUser(user, args[3])
|
||||
if err != nil {
|
||||
logErrorCmd(*cmd, err)
|
||||
return
|
||||
|
||||
+41
-32
@@ -22,11 +22,12 @@ import (
|
||||
)
|
||||
|
||||
var user = mgsdk.User{
|
||||
ID: testsutil.GenerateUUID(&testing.T{}),
|
||||
Name: "testuser",
|
||||
ID: testsutil.GenerateUUID(&testing.T{}),
|
||||
FirstName: "testuserfirstname",
|
||||
LastName: "testuserfirstname",
|
||||
Credentials: mgsdk.Credentials{
|
||||
Secret: "testpassword",
|
||||
Identity: "identity@example.com",
|
||||
Username: "testusername",
|
||||
},
|
||||
Status: mgclients.EnabledStatus.String(),
|
||||
}
|
||||
@@ -57,9 +58,11 @@ func TestCreateUsersCmd(t *testing.T) {
|
||||
{
|
||||
desc: "create user successfully with token",
|
||||
args: []string{
|
||||
user.Name,
|
||||
user.Credentials.Identity,
|
||||
user.FirstName,
|
||||
user.LastName,
|
||||
user.Email,
|
||||
user.Credentials.Secret,
|
||||
user.Credentials.Username,
|
||||
validToken,
|
||||
},
|
||||
user: user,
|
||||
@@ -68,9 +71,11 @@ func TestCreateUsersCmd(t *testing.T) {
|
||||
{
|
||||
desc: "create user successfully without token",
|
||||
args: []string{
|
||||
user.Name,
|
||||
user.Credentials.Identity,
|
||||
user.FirstName,
|
||||
user.LastName,
|
||||
user.Email,
|
||||
user.Credentials.Secret,
|
||||
user.Credentials.Username,
|
||||
},
|
||||
user: user,
|
||||
logType: entityLog,
|
||||
@@ -78,9 +83,12 @@ func TestCreateUsersCmd(t *testing.T) {
|
||||
{
|
||||
desc: "failed to create user",
|
||||
args: []string{
|
||||
user.Name,
|
||||
user.Credentials.Identity,
|
||||
user.FirstName,
|
||||
user.LastName,
|
||||
user.Email,
|
||||
user.Credentials.Secret,
|
||||
user.Credentials.Username,
|
||||
validToken,
|
||||
},
|
||||
sdkerr: errors.NewSDKErrorWithStatus(svcerr.ErrCreateEntity, http.StatusUnprocessableEntity),
|
||||
errLogMessage: fmt.Sprintf("\nerror: %s\n\n", errors.NewSDKErrorWithStatus(svcerr.ErrCreateEntity, http.StatusUnprocessableEntity).Error()),
|
||||
@@ -88,7 +96,7 @@ func TestCreateUsersCmd(t *testing.T) {
|
||||
},
|
||||
{
|
||||
desc: "create user with invalid args",
|
||||
args: []string{user.Name, user.Credentials.Identity},
|
||||
args: []string{user.FirstName, user.Credentials.Username},
|
||||
logType: usageLog,
|
||||
},
|
||||
}
|
||||
@@ -96,12 +104,13 @@ func TestCreateUsersCmd(t *testing.T) {
|
||||
for _, tc := range cases {
|
||||
t.Run(tc.desc, func(t *testing.T) {
|
||||
sdkCall := sdkMock.On("CreateUser", mock.Anything, mock.Anything).Return(tc.user, tc.sdkerr)
|
||||
if len(tc.args) == 3 {
|
||||
if len(tc.args) == 4 {
|
||||
sdkUser := mgsdk.User{
|
||||
Name: tc.args[0],
|
||||
FirstName: tc.args[0],
|
||||
LastName: tc.args[1],
|
||||
Email: tc.args[2],
|
||||
Credentials: mgsdk.Credentials{
|
||||
Identity: tc.args[1],
|
||||
Secret: tc.args[2],
|
||||
Secret: tc.args[3],
|
||||
},
|
||||
}
|
||||
sdkCall = sdkMock.On("CreateUser", mock.Anything, sdkUser).Return(tc.user, tc.sdkerr)
|
||||
@@ -297,7 +306,7 @@ func TestIssueTokenCmd(t *testing.T) {
|
||||
{
|
||||
desc: "issue token successfully",
|
||||
args: []string{
|
||||
user.Credentials.Identity,
|
||||
user.Email,
|
||||
user.Credentials.Secret,
|
||||
},
|
||||
sdkerr: nil,
|
||||
@@ -307,7 +316,7 @@ func TestIssueTokenCmd(t *testing.T) {
|
||||
{
|
||||
desc: "issue token with failed authentication",
|
||||
args: []string{
|
||||
user.Credentials.Identity,
|
||||
user.Email,
|
||||
invalidPassword,
|
||||
},
|
||||
sdkerr: errors.NewSDKErrorWithStatus(svcerr.ErrAuthorization, http.StatusForbidden),
|
||||
@@ -318,7 +327,7 @@ func TestIssueTokenCmd(t *testing.T) {
|
||||
{
|
||||
desc: "issue token with invalid args",
|
||||
args: []string{
|
||||
user.Credentials.Identity,
|
||||
user.Email,
|
||||
user.Credentials.Secret,
|
||||
extraArg,
|
||||
},
|
||||
@@ -329,8 +338,8 @@ func TestIssueTokenCmd(t *testing.T) {
|
||||
for _, tc := range cases {
|
||||
t.Run(tc.desc, func(t *testing.T) {
|
||||
lg := mgsdk.Login{
|
||||
Identity: tc.args[0],
|
||||
Secret: tc.args[1],
|
||||
Email: tc.args[0],
|
||||
Secret: tc.args[1],
|
||||
}
|
||||
sdkCall := sdkMock.On("CreateToken", lg).Return(tc.token, tc.sdkerr)
|
||||
|
||||
@@ -391,7 +400,7 @@ func TestRefreshIssueTokenCmd(t *testing.T) {
|
||||
logType: usageLog,
|
||||
},
|
||||
{
|
||||
desc: "issue refresh token with invalid identity",
|
||||
desc: "issue refresh token with invalid Username",
|
||||
args: []string{
|
||||
"invalidToken",
|
||||
},
|
||||
@@ -435,9 +444,9 @@ func TestUpdateUserCmd(t *testing.T) {
|
||||
userID := testsutil.GenerateUUID(t)
|
||||
|
||||
tagUpdateType := "tags"
|
||||
identityUpdateType := "identity"
|
||||
emailUpdateType := "email"
|
||||
roleUpdateType := "role"
|
||||
newIdentity := "newidentity@example.com"
|
||||
newEmail := "newemail@example.com"
|
||||
newRole := "administrator"
|
||||
newTagsJSON := "[\"tag1\", \"tag2\"]"
|
||||
newNameMetadataJSON := "{\"name\":\"new name\", \"metadata\":{\"key\": \"value\"}}"
|
||||
@@ -487,22 +496,22 @@ func TestUpdateUserCmd(t *testing.T) {
|
||||
errLogMessage: fmt.Sprintf("\nerror: %s\n\n", errors.NewSDKErrorWithStatus(svcerr.ErrAuthorization, http.StatusForbidden)),
|
||||
},
|
||||
{
|
||||
desc: "update user identity successfully",
|
||||
desc: "update user email successfully",
|
||||
args: []string{
|
||||
identityUpdateType,
|
||||
emailUpdateType,
|
||||
userID,
|
||||
newIdentity,
|
||||
newEmail,
|
||||
validToken,
|
||||
},
|
||||
logType: entityLog,
|
||||
user: user,
|
||||
},
|
||||
{
|
||||
desc: "update user identity with invalid token",
|
||||
desc: "update user email with invalid token",
|
||||
args: []string{
|
||||
identityUpdateType,
|
||||
emailUpdateType,
|
||||
userID,
|
||||
newIdentity,
|
||||
newEmail,
|
||||
invalidToken,
|
||||
},
|
||||
logType: errLog,
|
||||
@@ -590,19 +599,19 @@ func TestUpdateUserCmd(t *testing.T) {
|
||||
u.ID = tc.args[1]
|
||||
|
||||
sdkCall1 = sdkMock.On("UpdateUserTags", u, tc.args[3]).Return(tc.user, tc.sdkerr)
|
||||
case tc.args[0] == identityUpdateType:
|
||||
case tc.args[0] == emailUpdateType:
|
||||
var u mgsdk.User
|
||||
u.Credentials.Identity = tc.args[2]
|
||||
u.Email = tc.args[2]
|
||||
u.ID = tc.args[1]
|
||||
|
||||
sdkCall2 = sdkMock.On("UpdateUserIdentity", u, tc.args[3]).Return(tc.user, tc.sdkerr)
|
||||
sdkCall2 = sdkMock.On("UpdateUserEmail", u, tc.args[3]).Return(tc.user, tc.sdkerr)
|
||||
case tc.args[0] == roleUpdateType && len(tc.args) == 4:
|
||||
sdkCall3 = sdkMock.On("UpdateUserRole", mgsdk.User{
|
||||
Role: tc.args[2],
|
||||
}, tc.args[3]).Return(tc.user, tc.sdkerr)
|
||||
case tc.args[0] == userID:
|
||||
sdkCall = sdkMock.On("UpdateUser", mgsdk.User{
|
||||
Name: "new name",
|
||||
FirstName: "new name",
|
||||
Metadata: mgsdk.Metadata{
|
||||
"key": "value",
|
||||
},
|
||||
|
||||
@@ -36,6 +36,12 @@ var (
|
||||
Contact string = ""
|
||||
// RawOutput raw output mode.
|
||||
RawOutput bool = false
|
||||
// Username query parameter.
|
||||
Username string = ""
|
||||
// FirstName query parameter.
|
||||
FirstName string = ""
|
||||
// LastName query parameter.
|
||||
LastName string = ""
|
||||
)
|
||||
|
||||
func logJSONCmd(cmd cobra.Command, iList ...interface{}) {
|
||||
|
||||
+25
-20
@@ -26,7 +26,6 @@ import (
|
||||
authsvcAuthn "github.com/absmach/magistrala/pkg/authn/authsvc"
|
||||
mgauthz "github.com/absmach/magistrala/pkg/authz"
|
||||
authsvcAuthz "github.com/absmach/magistrala/pkg/authz/authsvc"
|
||||
mgclients "github.com/absmach/magistrala/pkg/clients"
|
||||
"github.com/absmach/magistrala/pkg/groups"
|
||||
"github.com/absmach/magistrala/pkg/grpcclient"
|
||||
jaegerclient "github.com/absmach/magistrala/pkg/jaeger"
|
||||
@@ -75,6 +74,9 @@ type config struct {
|
||||
LogLevel string `env:"MG_USERS_LOG_LEVEL" envDefault:"info"`
|
||||
AdminEmail string `env:"MG_USERS_ADMIN_EMAIL" envDefault:"admin@example.com"`
|
||||
AdminPassword string `env:"MG_USERS_ADMIN_PASSWORD" envDefault:"12345678"`
|
||||
AdminUsername string `env:"MG_USERS_ADMIN_USERNAME" envDefault:"admin"`
|
||||
AdminFirstName string `env:"MG_USERS_ADMIN_FIRST_NAME" envDefault:"super"`
|
||||
AdminLastName string `env:"MG_USERS_ADMIN_LAST_NAME" envDefault:"admin"`
|
||||
PassRegexText string `env:"MG_USERS_PASS_REGEX" envDefault:"^.{8,}$"`
|
||||
ResetURL string `env:"MG_TOKEN_RESET_ENDPOINT" envDefault:"/reset-request"`
|
||||
JaegerURL url.URL `env:"MG_JAEGER_URL" envDefault:"http://localhost:4318/v1/traces"`
|
||||
@@ -256,6 +258,7 @@ func main() {
|
||||
|
||||
func newService(ctx context.Context, authz mgauthz.Authorization, token magistrala.TokenServiceClient, policyService policies.Service, domainsClient magistrala.DomainsServiceClient, db *sqlx.DB, dbConfig pgclient.Config, tracer trace.Tracer, c config, ec email.Config, logger *slog.Logger) (users.Service, groups.Service, error) {
|
||||
database := postgres.NewDatabase(db, dbConfig, tracer)
|
||||
|
||||
cRepo := clientspg.NewRepository(database)
|
||||
gRepo := gpostgres.New(database)
|
||||
|
||||
@@ -292,11 +295,11 @@ func newService(ctx context.Context, authz mgauthz.Authorization, token magistra
|
||||
counter, latency = prometheus.MakeMetrics("groups", "api")
|
||||
gsvc = gmiddleware.MetricsMiddleware(gsvc, counter, latency)
|
||||
|
||||
clientID, err := createAdmin(ctx, c, cRepo, hsr, csvc)
|
||||
userID, err := createAdmin(ctx, c, cRepo, hsr, csvc)
|
||||
if err != nil {
|
||||
logger.Error(fmt.Sprintf("failed to create admin client: %s", err))
|
||||
}
|
||||
if err := createAdminPolicy(ctx, clientID, authz, policyService); err != nil {
|
||||
if err := createAdminPolicy(ctx, userID, authz, policyService); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
@@ -305,7 +308,7 @@ func newService(ctx context.Context, authz mgauthz.Authorization, token magistra
|
||||
return csvc, gsvc, err
|
||||
}
|
||||
|
||||
func createAdmin(ctx context.Context, c config, crepo clientspg.Repository, hsr users.Hasher, svc users.Service) (string, error) {
|
||||
func createAdmin(ctx context.Context, c config, urepo users.Repository, hsr users.Hasher, svc users.Service) (string, error) {
|
||||
id, err := uuid.New().ID()
|
||||
if err != nil {
|
||||
return "", err
|
||||
@@ -315,47 +318,49 @@ func createAdmin(ctx context.Context, c config, crepo clientspg.Repository, hsr
|
||||
return "", err
|
||||
}
|
||||
|
||||
client := mgclients.Client{
|
||||
ID: id,
|
||||
Name: "admin",
|
||||
Credentials: mgclients.Credentials{
|
||||
Identity: c.AdminEmail,
|
||||
user := users.User{
|
||||
ID: id,
|
||||
Email: c.AdminEmail,
|
||||
FirstName: c.AdminFirstName,
|
||||
LastName: c.AdminLastName,
|
||||
Credentials: users.Credentials{
|
||||
Username: "admin",
|
||||
Secret: hash,
|
||||
},
|
||||
Metadata: mgclients.Metadata{
|
||||
Metadata: users.Metadata{
|
||||
"role": "admin",
|
||||
},
|
||||
CreatedAt: time.Now(),
|
||||
UpdatedAt: time.Now(),
|
||||
Role: mgclients.AdminRole,
|
||||
Status: mgclients.EnabledStatus,
|
||||
Role: users.AdminRole,
|
||||
Status: users.EnabledStatus,
|
||||
}
|
||||
|
||||
if c, err := crepo.RetrieveByIdentity(ctx, client.Credentials.Identity); err == nil {
|
||||
return c.ID, nil
|
||||
if u, err := urepo.RetrieveByEmail(ctx, user.Email); err == nil {
|
||||
return u.ID, nil
|
||||
}
|
||||
|
||||
// Create an admin
|
||||
if _, err = crepo.Save(ctx, client); err != nil {
|
||||
if _, err = urepo.Save(ctx, user); err != nil {
|
||||
return "", err
|
||||
}
|
||||
if _, err = svc.IssueToken(ctx, c.AdminEmail, c.AdminPassword); err != nil {
|
||||
if _, err = svc.IssueToken(ctx, c.AdminUsername, c.AdminPassword); err != nil {
|
||||
return "", err
|
||||
}
|
||||
return client.ID, nil
|
||||
return user.ID, nil
|
||||
}
|
||||
|
||||
func createAdminPolicy(ctx context.Context, clientID string, authz mgauthz.Authorization, policyService policies.Service) error {
|
||||
func createAdminPolicy(ctx context.Context, userID string, authz mgauthz.Authorization, policyService policies.Service) error {
|
||||
if err := authz.Authorize(ctx, mgauthz.PolicyReq{
|
||||
SubjectType: policies.UserType,
|
||||
Subject: clientID,
|
||||
Subject: userID,
|
||||
Permission: policies.AdministratorRelation,
|
||||
Object: policies.MagistralaObject,
|
||||
ObjectType: policies.PlatformType,
|
||||
}); err != nil {
|
||||
err := policyService.AddPolicy(ctx, policies.Policy{
|
||||
SubjectType: policies.UserType,
|
||||
Subject: clientID,
|
||||
Subject: userID,
|
||||
Relation: policies.AdministratorRelation,
|
||||
Object: policies.MagistralaObject,
|
||||
ObjectType: policies.PlatformType,
|
||||
|
||||
@@ -171,6 +171,9 @@ MG_USERS_LOG_LEVEL=debug
|
||||
MG_USERS_SECRET_KEY=HyE2D4RUt9nnKG6v8zKEqAp6g6ka8hhZsqUpzgKvnwpXrNVQSH
|
||||
MG_USERS_ADMIN_EMAIL=admin@example.com
|
||||
MG_USERS_ADMIN_PASSWORD=12345678
|
||||
MG_USERS_ADMIN_USERNAME=admin
|
||||
MG_USERS_ADMIN_FIRST_NAME=super
|
||||
MG_USERS_ADMIN_LAST_NAME=admin
|
||||
MG_USERS_PASS_REGEX=^.{8,}$
|
||||
MG_USERS_ACCESS_TOKEN_DURATION=15m
|
||||
MG_USERS_REFRESH_TOKEN_DURATION=24h
|
||||
@@ -310,6 +313,7 @@ MG_PROVISION_SERVER_KEY=
|
||||
MG_PROVISION_USERS_LOCATION=http://users:9002
|
||||
MG_PROVISION_THINGS_LOCATION=http://things:9000
|
||||
MG_PROVISION_USER=
|
||||
MG_PROVISION_USERNAME=
|
||||
MG_PROVISION_PASS=
|
||||
MG_PROVISION_API_KEY=
|
||||
MG_PROVISION_CERTS_SVC_URL=http://certs:9019
|
||||
|
||||
@@ -28,6 +28,7 @@ services:
|
||||
MG_PROVISION_USERS_LOCATION: ${MG_PROVISION_USERS_LOCATION}
|
||||
MG_PROVISION_THINGS_LOCATION: ${MG_PROVISION_THINGS_LOCATION}
|
||||
MG_PROVISION_USER: ${MG_PROVISION_USER}
|
||||
MG_PROVISION_USERNAME: ${MG_PROVISION_USERNAME}
|
||||
MG_PROVISION_PASS: ${MG_PROVISION_PASS}
|
||||
MG_PROVISION_API_KEY: ${MG_PROVISION_API_KEY}
|
||||
MG_PROVISION_CERTS_SVC_URL: ${MG_PROVISION_CERTS_SVC_URL}
|
||||
|
||||
@@ -412,6 +412,9 @@ services:
|
||||
MG_USERS_SECRET_KEY: ${MG_USERS_SECRET_KEY}
|
||||
MG_USERS_ADMIN_EMAIL: ${MG_USERS_ADMIN_EMAIL}
|
||||
MG_USERS_ADMIN_PASSWORD: ${MG_USERS_ADMIN_PASSWORD}
|
||||
MG_USERS_ADMIN_USERNAME: ${MG_USERS_ADMIN_USERNAME}
|
||||
MG_USERS_ADMIN_FIRST_NAME: ${MG_USERS_ADMIN_FIRST_NAME}
|
||||
MG_USERS_ADMIN_LAST_NAME: ${MG_USERS_ADMIN_LAST_NAME}
|
||||
MG_USERS_PASS_REGEX: ${MG_USERS_PASS_REGEX}
|
||||
MG_USERS_ACCESS_TOKEN_DURATION: ${MG_USERS_ACCESS_TOKEN_DURATION}
|
||||
MG_USERS_REFRESH_TOKEN_DURATION: ${MG_USERS_REFRESH_TOKEN_DURATION}
|
||||
|
||||
@@ -55,7 +55,7 @@ require (
|
||||
golang.org/x/oauth2 v0.23.0
|
||||
golang.org/x/sync v0.8.0
|
||||
gonum.org/v1/gonum v0.15.1
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20241007155032-5fefd90f89a9
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20241015192408-796eee8c2d53
|
||||
google.golang.org/grpc v1.67.1
|
||||
google.golang.org/protobuf v1.35.1
|
||||
gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df
|
||||
|
||||
@@ -612,8 +612,8 @@ google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98
|
||||
google.golang.org/genproto v0.0.0-20200423170343-7949de9c1215/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20241007155032-5fefd90f89a9 h1:T6rh4haD3GVYsgEfWExoCZA2o2FmbNyKpTuAxbEFPTg=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20241007155032-5fefd90f89a9/go.mod h1:wp2WsuBYj6j8wUdo3ToZsdxxixbvQNAHqVJrTgi5E5M=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20241007155032-5fefd90f89a9 h1:QCqS/PdaHTSWGvupk2F/ehwHtGc0/GYkT+3GAcR1CCc=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20241007155032-5fefd90f89a9/go.mod h1:GX3210XPVPUjJbTUbvwI8f2IpZDMZuPJWDzDuebbviI=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20241015192408-796eee8c2d53 h1:X58yt85/IXCx0Y3ZwN6sEIKZzQtDEYaBWrDvErdXrRE=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20241015192408-796eee8c2d53/go.mod h1:GX3210XPVPUjJbTUbvwI8f2IpZDMZuPJWDzDuebbviI=
|
||||
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
||||
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
|
||||
google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
|
||||
|
||||
+11
-4
@@ -30,11 +30,13 @@ const (
|
||||
ParentKey = "parent_id"
|
||||
OwnerKey = "owner_id"
|
||||
ClientKey = "client"
|
||||
IdentityKey = "identity"
|
||||
UsernameKey = "username"
|
||||
NameKey = "name"
|
||||
GroupKey = "group"
|
||||
ActionKey = "action"
|
||||
TagKey = "tag"
|
||||
NameKey = "name"
|
||||
FirstNameKey = "first_name"
|
||||
LastNameKey = "last_name"
|
||||
TotalKey = "total"
|
||||
SubjectKey = "subject"
|
||||
ObjectKey = "object"
|
||||
@@ -43,6 +45,7 @@ const (
|
||||
DirKey = "dir"
|
||||
ListPerms = "list_perms"
|
||||
VisibilityKey = "visibility"
|
||||
EmailKey = "email"
|
||||
SharedByKey = "shared_by"
|
||||
TokenKey = "token"
|
||||
DefPermission = "view"
|
||||
@@ -127,6 +130,7 @@ func EncodeError(_ context.Context, err error, w http.ResponseWriter) {
|
||||
errors.Contains(err, apiutil.ErrMissingName),
|
||||
errors.Contains(err, apiutil.ErrMissingAlias),
|
||||
errors.Contains(err, apiutil.ErrMissingEmail),
|
||||
errors.Contains(err, apiutil.ErrInvalidEmail),
|
||||
errors.Contains(err, apiutil.ErrMissingHost),
|
||||
errors.Contains(err, apiutil.ErrInvalidResetPass),
|
||||
errors.Contains(err, apiutil.ErrEmptyList),
|
||||
@@ -140,7 +144,6 @@ func EncodeError(_ context.Context, err error, w http.ResponseWriter) {
|
||||
errors.Contains(err, apiutil.ErrInvalidQueryParams),
|
||||
errors.Contains(err, apiutil.ErrMissingRelation),
|
||||
errors.Contains(err, apiutil.ErrValidation),
|
||||
errors.Contains(err, apiutil.ErrMissingIdentity),
|
||||
errors.Contains(err, apiutil.ErrMissingPass),
|
||||
errors.Contains(err, apiutil.ErrMissingConfPass),
|
||||
errors.Contains(err, apiutil.ErrPasswordFormat),
|
||||
@@ -165,7 +168,11 @@ func EncodeError(_ context.Context, err error, w http.ResponseWriter) {
|
||||
errors.Contains(err, apiutil.ErrEmptySearchQuery),
|
||||
errors.Contains(err, apiutil.ErrLenSearchQuery),
|
||||
errors.Contains(err, apiutil.ErrMissingDomainID),
|
||||
errors.Contains(err, certs.ErrFailedReadFromPKI):
|
||||
errors.Contains(err, certs.ErrFailedReadFromPKI),
|
||||
errors.Contains(err, apiutil.ErrMissingUsername),
|
||||
errors.Contains(err, apiutil.ErrMissingFirstName),
|
||||
errors.Contains(err, apiutil.ErrMissingLastName),
|
||||
errors.Contains(err, apiutil.ErrInvalidUsername):
|
||||
err = unwrap(err)
|
||||
w.WriteHeader(http.StatusBadRequest)
|
||||
|
||||
|
||||
@@ -87,6 +87,9 @@ var (
|
||||
// ErrMissingEmail indicates missing email.
|
||||
ErrMissingEmail = errors.New("missing email")
|
||||
|
||||
// ErrInvalidEmail indicates missing email.
|
||||
ErrInvalidEmail = errors.New("invalid email")
|
||||
|
||||
// ErrMissingHost indicates missing host.
|
||||
ErrMissingHost = errors.New("missing host")
|
||||
|
||||
@@ -188,4 +191,19 @@ var (
|
||||
|
||||
// ErrMissingDomainID indicates missing domainID.
|
||||
ErrMissingDomainID = errors.New("missing domainID")
|
||||
|
||||
// ErrMissingUsername indicates missing user name.
|
||||
ErrMissingUsername = errors.New("missing username")
|
||||
|
||||
// ErrInvalidUsername indicates missing user name.
|
||||
ErrInvalidUsername = errors.New("invalid username")
|
||||
|
||||
// ErrMissingFirstName indicates missing first name.
|
||||
ErrMissingFirstName = errors.New("missing first name")
|
||||
|
||||
// ErrMissingLastName indicates missing last name.
|
||||
ErrMissingLastName = errors.New("missing last name")
|
||||
|
||||
// ErrInvalidProfilePictureURL indicates that the profile picture url is invalid.
|
||||
ErrInvalidProfilePictureURL = errors.New("invalid profile picture url")
|
||||
)
|
||||
|
||||
@@ -49,7 +49,6 @@ type Client struct {
|
||||
UpdatedAt time.Time `json:"updated_at,omitempty"`
|
||||
UpdatedBy string `json:"updated_by,omitempty"`
|
||||
Status Status `json:"status,omitempty"` // 1 for enabled, 0 for disabled
|
||||
Role Role `json:"role,omitempty"` // 1 for admin, 0 for normal user
|
||||
Permissions []string `json:"permissions,omitempty"`
|
||||
}
|
||||
|
||||
|
||||
@@ -21,4 +21,7 @@ type Page struct {
|
||||
Identity string `json:"identity,omitempty"`
|
||||
Role Role `json:"-"`
|
||||
ListPerms bool `json:"-"`
|
||||
Username string `json:"username,omitempty"`
|
||||
FirstName string `json:"first_name,omitempty"`
|
||||
LastName string `json:"last_name,omitempty"`
|
||||
}
|
||||
|
||||
@@ -392,7 +392,6 @@ func ToDBClient(c clients.Client) (DBClient, error) {
|
||||
UpdatedAt: updatedAt,
|
||||
UpdatedBy: updatedBy,
|
||||
Status: c.Status,
|
||||
Role: &c.Role,
|
||||
}, nil
|
||||
}
|
||||
|
||||
@@ -431,9 +430,6 @@ func ToClient(c DBClient) (clients.Client, error) {
|
||||
UpdatedBy: updatedBy,
|
||||
Status: c.Status,
|
||||
}
|
||||
if c.Role != nil {
|
||||
cli.Role = *c.Role
|
||||
}
|
||||
return cli, nil
|
||||
}
|
||||
|
||||
@@ -491,9 +487,6 @@ func PageQuery(pm clients.Page) (string, error) {
|
||||
if pm.Tag != "" {
|
||||
query = append(query, "EXISTS (SELECT 1 FROM unnest(tags) AS tag WHERE tag ILIKE '%' || :tag || '%')")
|
||||
}
|
||||
if pm.Role != clients.AllRole {
|
||||
query = append(query, "c.role = :role")
|
||||
}
|
||||
// If there are search params presents, use search and ignore other options.
|
||||
// Always combine role with search params, so len(query) > 1.
|
||||
if len(query) > 1 {
|
||||
|
||||
@@ -13,9 +13,7 @@ import (
|
||||
|
||||
"github.com/0x6flab/namegenerator"
|
||||
"github.com/absmach/magistrala/internal/testsutil"
|
||||
"github.com/absmach/magistrala/pkg/clients"
|
||||
mgclients "github.com/absmach/magistrala/pkg/clients"
|
||||
"github.com/absmach/magistrala/pkg/clients/postgres"
|
||||
pgclients "github.com/absmach/magistrala/pkg/clients/postgres"
|
||||
"github.com/absmach/magistrala/pkg/errors"
|
||||
repoerr "github.com/absmach/magistrala/pkg/errors/repository"
|
||||
@@ -35,9 +33,9 @@ func TestRetrieveByID(t *testing.T) {
|
||||
_, err := db.Exec("DELETE FROM clients")
|
||||
require.Nil(t, err, fmt.Sprintf("clean clients unexpected error: %s", err))
|
||||
})
|
||||
repo := &postgres.Repository{database}
|
||||
repo := &pgclients.Repository{database}
|
||||
|
||||
client := generateClient(t, mgclients.EnabledStatus, mgclients.UserRole, repo)
|
||||
client := generateClient(t, mgclients.EnabledStatus, repo)
|
||||
|
||||
cases := []struct {
|
||||
desc string
|
||||
@@ -85,9 +83,9 @@ func TestRetrieveByIdentity(t *testing.T) {
|
||||
_, err := db.Exec("DELETE FROM clients")
|
||||
require.Nil(t, err, fmt.Sprintf("clean clients unexpected error: %s", err))
|
||||
})
|
||||
repo := &postgres.Repository{database}
|
||||
repo := &pgclients.Repository{database}
|
||||
|
||||
client := generateClient(t, mgclients.EnabledStatus, mgclients.UserRole, repo)
|
||||
client := generateClient(t, mgclients.EnabledStatus, repo)
|
||||
|
||||
cases := []struct {
|
||||
desc string
|
||||
@@ -135,7 +133,7 @@ func TestRetrieveAll(t *testing.T) {
|
||||
_, err := db.Exec("DELETE FROM clients")
|
||||
require.Nil(t, err, fmt.Sprintf("clean clients unexpected error: %s", err))
|
||||
})
|
||||
repo := &postgres.Repository{database}
|
||||
repo := &pgclients.Repository{database}
|
||||
|
||||
nClients := uint64(200)
|
||||
|
||||
@@ -156,11 +154,9 @@ func TestRetrieveAll(t *testing.T) {
|
||||
},
|
||||
Status: mgclients.EnabledStatus,
|
||||
CreatedAt: time.Now().UTC().Truncate(time.Millisecond),
|
||||
Role: mgclients.UserRole,
|
||||
}
|
||||
if i%50 == 0 {
|
||||
client.Status = mgclients.DisabledStatus
|
||||
client.Role = mgclients.AdminRole
|
||||
}
|
||||
client, err := save(context.Background(), repo, client)
|
||||
require.Nil(t, err, fmt.Sprintf("add new client: expected nil got %s\n", err))
|
||||
@@ -523,73 +519,6 @@ func TestRetrieveAll(t *testing.T) {
|
||||
Clients: []mgclients.Client(nil),
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "with user role",
|
||||
pm: mgclients.Page{
|
||||
Offset: 0,
|
||||
Limit: 10,
|
||||
Role: mgclients.UserRole,
|
||||
Status: mgclients.AllStatus,
|
||||
},
|
||||
response: mgclients.ClientsPage{
|
||||
Page: mgclients.Page{
|
||||
Total: 196,
|
||||
Offset: 0,
|
||||
Limit: 10,
|
||||
},
|
||||
Clients: expectedClients[1:11],
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "with admin role",
|
||||
pm: mgclients.Page{
|
||||
Offset: 0,
|
||||
Limit: nClients,
|
||||
Role: mgclients.AdminRole,
|
||||
Status: mgclients.AllStatus,
|
||||
},
|
||||
response: mgclients.ClientsPage{
|
||||
Page: mgclients.Page{
|
||||
Total: 4,
|
||||
Offset: 0,
|
||||
Limit: nClients,
|
||||
},
|
||||
Clients: disabledClients,
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "with combined role",
|
||||
pm: mgclients.Page{
|
||||
Offset: 0,
|
||||
Limit: nClients,
|
||||
Status: mgclients.AllStatus,
|
||||
Role: mgclients.AllRole,
|
||||
},
|
||||
response: mgclients.ClientsPage{
|
||||
Page: mgclients.Page{
|
||||
Total: nClients,
|
||||
Offset: 0,
|
||||
Limit: nClients,
|
||||
},
|
||||
Clients: expectedClients,
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "with the wrong role",
|
||||
pm: mgclients.Page{
|
||||
Offset: 0,
|
||||
Limit: nClients,
|
||||
Role: 10,
|
||||
},
|
||||
response: mgclients.ClientsPage{
|
||||
Page: mgclients.Page{
|
||||
Total: 0,
|
||||
Offset: 0,
|
||||
Limit: nClients,
|
||||
},
|
||||
Clients: []mgclients.Client(nil),
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "with tag",
|
||||
pm: mgclients.Page{
|
||||
@@ -668,7 +597,7 @@ func TestRetrieveByIDs(t *testing.T) {
|
||||
_, err := db.Exec("DELETE FROM clients")
|
||||
require.Nil(t, err, fmt.Sprintf("clean clients unexpected error: %s", err))
|
||||
})
|
||||
repo := &postgres.Repository{database}
|
||||
repo := &pgclients.Repository{database}
|
||||
|
||||
num := 200
|
||||
|
||||
@@ -681,12 +610,12 @@ func TestRetrieveByIDs(t *testing.T) {
|
||||
Name: name,
|
||||
Credentials: mgclients.Credentials{
|
||||
Identity: name + emailSuffix,
|
||||
Secret: password,
|
||||
Secret: testsutil.GenerateUUID(t),
|
||||
},
|
||||
Tags: namegen.GenerateMultiple(5),
|
||||
Metadata: map[string]interface{}{"name": name},
|
||||
CreatedAt: time.Now().UTC().Truncate(time.Millisecond),
|
||||
Status: clients.EnabledStatus,
|
||||
Status: mgclients.EnabledStatus,
|
||||
}
|
||||
client, err := save(context.Background(), repo, client)
|
||||
require.Nil(t, err, fmt.Sprintf("add new client: expected nil got %s\n", err))
|
||||
@@ -932,7 +861,7 @@ func TestSearchClients(t *testing.T) {
|
||||
_, err := db.Exec("DELETE FROM clients")
|
||||
require.Nil(t, err, fmt.Sprintf("clean clients unexpected error: %s", err))
|
||||
})
|
||||
repo := &postgres.Repository{database}
|
||||
repo := &pgclients.Repository{database}
|
||||
|
||||
name := namegen.Generate()
|
||||
|
||||
@@ -945,7 +874,7 @@ func TestSearchClients(t *testing.T) {
|
||||
Name: username,
|
||||
Credentials: mgclients.Credentials{
|
||||
Identity: username,
|
||||
Secret: password,
|
||||
Secret: fmt.Sprintf("%s%d", password, i),
|
||||
},
|
||||
Metadata: mgclients.Metadata{},
|
||||
Status: mgclients.EnabledStatus,
|
||||
@@ -1311,10 +1240,10 @@ func TestUpdate(t *testing.T) {
|
||||
_, err := db.Exec("DELETE FROM clients")
|
||||
require.Nil(t, err, fmt.Sprintf("clean clients unexpected error: %s", err))
|
||||
})
|
||||
repo := &postgres.Repository{database}
|
||||
repo := &pgclients.Repository{database}
|
||||
|
||||
client1 := generateClient(t, mgclients.EnabledStatus, mgclients.UserRole, repo)
|
||||
client2 := generateClient(t, mgclients.DisabledStatus, mgclients.UserRole, repo)
|
||||
client1 := generateClient(t, mgclients.EnabledStatus, repo)
|
||||
client2 := generateClient(t, mgclients.DisabledStatus, repo)
|
||||
|
||||
cases := []struct {
|
||||
desc string
|
||||
@@ -1487,10 +1416,10 @@ func TestUpdateTags(t *testing.T) {
|
||||
_, err := db.Exec("DELETE FROM clients")
|
||||
require.Nil(t, err, fmt.Sprintf("clean clients unexpected error: %s", err))
|
||||
})
|
||||
repo := &postgres.Repository{database}
|
||||
repo := &pgclients.Repository{database}
|
||||
|
||||
client1 := generateClient(t, mgclients.EnabledStatus, mgclients.UserRole, repo)
|
||||
client2 := generateClient(t, mgclients.DisabledStatus, mgclients.UserRole, repo)
|
||||
client1 := generateClient(t, mgclients.EnabledStatus, repo)
|
||||
client2 := generateClient(t, mgclients.DisabledStatus, repo)
|
||||
|
||||
cases := []struct {
|
||||
desc string
|
||||
@@ -1547,10 +1476,10 @@ func TestUpdateSecret(t *testing.T) {
|
||||
_, err := db.Exec("DELETE FROM clients")
|
||||
require.Nil(t, err, fmt.Sprintf("clean clients unexpected error: %s", err))
|
||||
})
|
||||
repo := &postgres.Repository{database}
|
||||
repo := &pgclients.Repository{database}
|
||||
|
||||
client1 := generateClient(t, mgclients.EnabledStatus, mgclients.UserRole, repo)
|
||||
client2 := generateClient(t, mgclients.DisabledStatus, mgclients.UserRole, repo)
|
||||
client1 := generateClient(t, mgclients.EnabledStatus, repo)
|
||||
client2 := generateClient(t, mgclients.DisabledStatus, repo)
|
||||
|
||||
cases := []struct {
|
||||
desc string
|
||||
@@ -1615,10 +1544,10 @@ func TestUpdateIdentity(t *testing.T) {
|
||||
_, err := db.Exec("DELETE FROM clients")
|
||||
require.Nil(t, err, fmt.Sprintf("clean clients unexpected error: %s", err))
|
||||
})
|
||||
repo := &postgres.Repository{database}
|
||||
repo := &pgclients.Repository{database}
|
||||
|
||||
client1 := generateClient(t, mgclients.EnabledStatus, mgclients.UserRole, repo)
|
||||
client2 := generateClient(t, mgclients.DisabledStatus, mgclients.UserRole, repo)
|
||||
client1 := generateClient(t, mgclients.EnabledStatus, repo)
|
||||
client2 := generateClient(t, mgclients.DisabledStatus, repo)
|
||||
|
||||
cases := []struct {
|
||||
desc string
|
||||
@@ -1681,10 +1610,10 @@ func TestChangeStatus(t *testing.T) {
|
||||
_, err := db.Exec("DELETE FROM clients")
|
||||
require.Nil(t, err, fmt.Sprintf("clean clients unexpected error: %s", err))
|
||||
})
|
||||
repo := &postgres.Repository{database}
|
||||
repo := &pgclients.Repository{database}
|
||||
|
||||
client1 := generateClient(t, mgclients.EnabledStatus, mgclients.UserRole, repo)
|
||||
client2 := generateClient(t, mgclients.DisabledStatus, mgclients.UserRole, repo)
|
||||
client1 := generateClient(t, mgclients.EnabledStatus, repo)
|
||||
client2 := generateClient(t, mgclients.DisabledStatus, repo)
|
||||
|
||||
cases := []struct {
|
||||
desc string
|
||||
@@ -1737,75 +1666,14 @@ func TestChangeStatus(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestUpdateRole(t *testing.T) {
|
||||
t.Cleanup(func() {
|
||||
_, err := db.Exec("DELETE FROM clients")
|
||||
require.Nil(t, err, fmt.Sprintf("clean clients unexpected error: %s", err))
|
||||
})
|
||||
repo := &postgres.Repository{database}
|
||||
|
||||
client1 := generateClient(t, mgclients.EnabledStatus, mgclients.UserRole, repo)
|
||||
client2 := generateClient(t, mgclients.DisabledStatus, mgclients.UserRole, repo)
|
||||
|
||||
cases := []struct {
|
||||
desc string
|
||||
client mgclients.Client
|
||||
err error
|
||||
}{
|
||||
{
|
||||
desc: "for an enabled client",
|
||||
client: mgclients.Client{
|
||||
ID: client1.ID,
|
||||
Role: mgclients.AdminRole,
|
||||
},
|
||||
err: nil,
|
||||
},
|
||||
{
|
||||
desc: "for a disabled client",
|
||||
client: mgclients.Client{
|
||||
ID: client2.ID,
|
||||
Role: mgclients.AdminRole,
|
||||
},
|
||||
err: repoerr.ErrNotFound,
|
||||
},
|
||||
{
|
||||
desc: "for invalid client",
|
||||
client: mgclients.Client{
|
||||
ID: testsutil.GenerateUUID(t),
|
||||
Role: mgclients.AdminRole,
|
||||
},
|
||||
err: repoerr.ErrNotFound,
|
||||
},
|
||||
{
|
||||
desc: "for empty client",
|
||||
client: mgclients.Client{},
|
||||
err: repoerr.ErrNotFound,
|
||||
},
|
||||
}
|
||||
|
||||
for _, c := range cases {
|
||||
t.Run(c.desc, func(t *testing.T) {
|
||||
c.client.UpdatedAt = time.Now().UTC().Truncate(time.Millisecond)
|
||||
c.client.UpdatedBy = testsutil.GenerateUUID(t)
|
||||
expected, err := repo.UpdateRole(context.Background(), c.client)
|
||||
assert.True(t, errors.Contains(err, c.err), fmt.Sprintf("expected %s to contain %s\n", err, c.err))
|
||||
if err == nil {
|
||||
assert.Equal(t, c.client.Role, expected.Role)
|
||||
assert.Equal(t, c.client.UpdatedAt, expected.UpdatedAt)
|
||||
assert.Equal(t, c.client.UpdatedBy, expected.UpdatedBy)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestDelete(t *testing.T) {
|
||||
t.Cleanup(func() {
|
||||
_, err := db.Exec("DELETE FROM clients")
|
||||
require.Nil(t, err, fmt.Sprintf("clean clients unexpected error: %s", err))
|
||||
})
|
||||
repo := &postgres.Repository{database}
|
||||
repo := &pgclients.Repository{database}
|
||||
|
||||
client := generateClient(t, mgclients.EnabledStatus, mgclients.UserRole, repo)
|
||||
client := generateClient(t, mgclients.EnabledStatus, repo)
|
||||
|
||||
cases := []struct {
|
||||
desc string
|
||||
@@ -1854,20 +1722,19 @@ func findClients(clis []mgclients.Client, query string, offset, limit uint64) []
|
||||
return rclients[offset:limit]
|
||||
}
|
||||
|
||||
func generateClient(t *testing.T, status mgclients.Status, role mgclients.Role, repo *postgres.Repository) mgclients.Client {
|
||||
func generateClient(t *testing.T, status mgclients.Status, repo *pgclients.Repository) mgclients.Client {
|
||||
client := mgclients.Client{
|
||||
ID: testsutil.GenerateUUID(t),
|
||||
Name: namegen.Generate(),
|
||||
Credentials: mgclients.Credentials{
|
||||
Identity: namegen.Generate() + emailSuffix,
|
||||
Secret: password,
|
||||
Secret: testsutil.GenerateUUID(t),
|
||||
},
|
||||
Tags: namegen.GenerateMultiple(5),
|
||||
Metadata: mgclients.Metadata{
|
||||
"name": namegen.Generate(),
|
||||
},
|
||||
Status: status,
|
||||
Role: role,
|
||||
CreatedAt: time.Now().UTC().Truncate(time.Millisecond),
|
||||
}
|
||||
_, err := save(context.Background(), repo, client)
|
||||
@@ -1876,9 +1743,9 @@ func generateClient(t *testing.T, status mgclients.Status, role mgclients.Role,
|
||||
return client
|
||||
}
|
||||
|
||||
func save(ctx context.Context, repo *postgres.Repository, c mgclients.Client) (mgclients.Client, error) {
|
||||
q := `INSERT INTO clients (id, name, tags, domain_id, identity, secret, metadata, created_at, status, role)
|
||||
VALUES (:id, :name, :tags, :domain_id, :identity, :secret, :metadata, :created_at, :status, :role)
|
||||
func save(ctx context.Context, repo *pgclients.Repository, c mgclients.Client) (mgclients.Client, error) {
|
||||
q := `INSERT INTO clients (id, name, tags, domain_id, identity, secret, metadata, created_at, status)
|
||||
VALUES (:id, :name, :tags, :domain_id, :identity, :secret, :metadata, :created_at, :status)
|
||||
RETURNING id, name, tags, identity, metadata, COALESCE(domain_id, '') AS domain_id, status, created_at`
|
||||
dbc, err := pgclients.ToDBClient(c)
|
||||
if err != nil {
|
||||
|
||||
@@ -13,7 +13,7 @@ import (
|
||||
|
||||
"github.com/absmach/magistrala/pkg/postgres"
|
||||
pgClient "github.com/absmach/magistrala/pkg/postgres"
|
||||
upostgres "github.com/absmach/magistrala/users/postgres"
|
||||
tpostgres "github.com/absmach/magistrala/things/postgres"
|
||||
"github.com/jmoiron/sqlx"
|
||||
dockertest "github.com/ory/dockertest/v3"
|
||||
"github.com/ory/dockertest/v3/docker"
|
||||
@@ -76,7 +76,7 @@ func TestMain(m *testing.M) {
|
||||
SSLRootCert: "",
|
||||
}
|
||||
|
||||
if db, err = pgClient.Setup(dbConfig, *upostgres.Migration()); err != nil {
|
||||
if db, err = pgClient.Setup(dbConfig, *tpostgres.Migration()); err != nil {
|
||||
log.Fatalf("Could not setup test DB connection: %s", err)
|
||||
}
|
||||
|
||||
|
||||
@@ -33,4 +33,7 @@ var (
|
||||
|
||||
// ErrFailedToRetrieveAllGroups failed to retrieve groups.
|
||||
ErrFailedToRetrieveAllGroups = errors.New("failed to retrieve all groups")
|
||||
|
||||
// ErrMissingNames indicates missing first and last names.
|
||||
ErrMissingNames = errors.New("missing first or last name")
|
||||
)
|
||||
|
||||
@@ -72,4 +72,7 @@ var (
|
||||
|
||||
// ErrParentGroupAuthorization indicates failure occurred while authorizing the parent group.
|
||||
ErrParentGroupAuthorization = errors.New("failed to authorize parent group")
|
||||
|
||||
// ErrMissingUsername indicates that the user's names are missing.
|
||||
ErrMissingUsername = errors.New("missing usernames")
|
||||
)
|
||||
|
||||
@@ -11,9 +11,9 @@ import (
|
||||
"net/url"
|
||||
"time"
|
||||
|
||||
mfclients "github.com/absmach/magistrala/pkg/clients"
|
||||
svcerr "github.com/absmach/magistrala/pkg/errors/service"
|
||||
mgoauth2 "github.com/absmach/magistrala/pkg/oauth2"
|
||||
uclient "github.com/absmach/magistrala/users"
|
||||
"golang.org/x/oauth2"
|
||||
googleoauth2 "golang.org/x/oauth2/google"
|
||||
)
|
||||
@@ -84,47 +84,48 @@ func (cfg *config) Exchange(ctx context.Context, code string) (oauth2.Token, err
|
||||
return *token, nil
|
||||
}
|
||||
|
||||
func (cfg *config) UserInfo(accessToken string) (mfclients.Client, error) {
|
||||
func (cfg *config) UserInfo(accessToken string) (uclient.User, error) {
|
||||
resp, err := http.Get(userInfoURL + url.QueryEscape(accessToken))
|
||||
if err != nil {
|
||||
return mfclients.Client{}, err
|
||||
return uclient.User{}, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
return mfclients.Client{}, svcerr.ErrAuthentication
|
||||
return uclient.User{}, svcerr.ErrAuthentication
|
||||
}
|
||||
|
||||
data, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return mfclients.Client{}, err
|
||||
return uclient.User{}, err
|
||||
}
|
||||
|
||||
var user struct {
|
||||
ID string `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Email string `json:"email"`
|
||||
Picture string `json:"picture"`
|
||||
ID string `json:"id"`
|
||||
FirstName string `json:"first_name"`
|
||||
LastName string `json:"last_name"`
|
||||
Username string `json:"username"`
|
||||
Email string `json:"email"`
|
||||
Picture string `json:"picture"`
|
||||
}
|
||||
if err := json.Unmarshal(data, &user); err != nil {
|
||||
return mfclients.Client{}, err
|
||||
return uclient.User{}, err
|
||||
}
|
||||
|
||||
if user.ID == "" || user.Name == "" || user.Email == "" {
|
||||
return mfclients.Client{}, svcerr.ErrAuthentication
|
||||
if user.ID == "" || user.FirstName == "" || user.LastName == "" || user.Email == "" {
|
||||
return uclient.User{}, svcerr.ErrAuthentication
|
||||
}
|
||||
|
||||
client := mfclients.Client{
|
||||
ID: user.ID,
|
||||
Name: user.Name,
|
||||
Credentials: mfclients.Credentials{
|
||||
Identity: user.Email,
|
||||
},
|
||||
client := uclient.User{
|
||||
ID: user.ID,
|
||||
FirstName: user.FirstName,
|
||||
LastName: user.LastName,
|
||||
Email: user.Email,
|
||||
Metadata: map[string]interface{}{
|
||||
"oauth_provider": providerName,
|
||||
"profile_picture": user.Picture,
|
||||
},
|
||||
Status: mfclients.EnabledStatus,
|
||||
Status: uclient.EnabledStatus,
|
||||
}
|
||||
|
||||
return client, nil
|
||||
|
||||
@@ -7,10 +7,10 @@ package mocks
|
||||
import (
|
||||
context "context"
|
||||
|
||||
clients "github.com/absmach/magistrala/pkg/clients"
|
||||
|
||||
mock "github.com/stretchr/testify/mock"
|
||||
|
||||
users "github.com/absmach/magistrala/users"
|
||||
|
||||
xoauth2 "golang.org/x/oauth2"
|
||||
)
|
||||
|
||||
@@ -138,22 +138,22 @@ func (_m *Provider) State() string {
|
||||
}
|
||||
|
||||
// UserInfo provides a mock function with given fields: accessToken
|
||||
func (_m *Provider) UserInfo(accessToken string) (clients.Client, error) {
|
||||
func (_m *Provider) UserInfo(accessToken string) (users.User, error) {
|
||||
ret := _m.Called(accessToken)
|
||||
|
||||
if len(ret) == 0 {
|
||||
panic("no return value specified for UserInfo")
|
||||
}
|
||||
|
||||
var r0 clients.Client
|
||||
var r0 users.User
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(0).(func(string) (clients.Client, error)); ok {
|
||||
if rf, ok := ret.Get(0).(func(string) (users.User, error)); ok {
|
||||
return rf(accessToken)
|
||||
}
|
||||
if rf, ok := ret.Get(0).(func(string) clients.Client); ok {
|
||||
if rf, ok := ret.Get(0).(func(string) users.User); ok {
|
||||
r0 = rf(accessToken)
|
||||
} else {
|
||||
r0 = ret.Get(0).(clients.Client)
|
||||
r0 = ret.Get(0).(users.User)
|
||||
}
|
||||
|
||||
if rf, ok := ret.Get(1).(func(string) error); ok {
|
||||
|
||||
@@ -6,7 +6,7 @@ package oauth2
|
||||
import (
|
||||
"context"
|
||||
|
||||
mfclients "github.com/absmach/magistrala/pkg/clients"
|
||||
"github.com/absmach/magistrala/users"
|
||||
"golang.org/x/oauth2"
|
||||
)
|
||||
|
||||
@@ -42,5 +42,5 @@ type Provider interface {
|
||||
Exchange(ctx context.Context, code string) (oauth2.Token, error)
|
||||
|
||||
// UserInfo retrieves the user's information using the access token.
|
||||
UserInfo(accessToken string) (mfclients.Client, error)
|
||||
UserInfo(accessToken string) (users.User, error)
|
||||
}
|
||||
|
||||
+12
-7
@@ -3,8 +3,8 @@
|
||||
|
||||
package sdk
|
||||
|
||||
// updateClientSecretReq is used to update the client secret.
|
||||
type updateClientSecretReq struct {
|
||||
// updateUserSecretReq is used to update the user secret.
|
||||
type updateUserSecretReq struct {
|
||||
OldSecret string `json:"old_secret,omitempty"`
|
||||
NewSecret string `json:"new_secret,omitempty"`
|
||||
}
|
||||
@@ -24,11 +24,11 @@ type updateThingSecretReq struct {
|
||||
Secret string `json:"secret,omitempty"`
|
||||
}
|
||||
|
||||
// updateClientIdentityReq is used to update the client identity.
|
||||
type updateClientIdentityReq struct {
|
||||
token string
|
||||
id string
|
||||
Identity string `json:"identity,omitempty"`
|
||||
// updateUserEmailReq is used to update the user email.
|
||||
type updateUserEmailReq struct {
|
||||
token string
|
||||
id string
|
||||
Email string `json:"email,omitempty"`
|
||||
}
|
||||
|
||||
// UserPasswordReq contains old and new passwords.
|
||||
@@ -51,3 +51,8 @@ type UsersRelationRequest struct {
|
||||
type UserGroupsRequest struct {
|
||||
UserGroupIDs []string `json:"group_ids"`
|
||||
}
|
||||
|
||||
type UpdateUsernameReq struct {
|
||||
id string
|
||||
Username string `json:"username"`
|
||||
}
|
||||
|
||||
+50
-9
@@ -95,6 +95,10 @@ type PageMetadata struct {
|
||||
Direction string `json:"direction,omitempty"`
|
||||
Level uint64 `json:"level,omitempty"`
|
||||
Identity string `json:"identity,omitempty"`
|
||||
Email string `json:"email,omitempty"`
|
||||
Username string `json:"username,omitempty"`
|
||||
LastName string `json:"last_name,omitempty"`
|
||||
FirstName string `json:"first_name,omitempty"`
|
||||
Name string `json:"name,omitempty"`
|
||||
Type string `json:"type,omitempty"`
|
||||
Metadata Metadata `json:"metadata,omitempty"`
|
||||
@@ -125,10 +129,10 @@ type PageMetadata struct {
|
||||
}
|
||||
|
||||
// Credentials represent client credentials: it contains
|
||||
// "identity" which can be a username, email, generated name;
|
||||
// "username" which can be a username, generated name;
|
||||
// and "secret" which can be a password or access token.
|
||||
type Credentials struct {
|
||||
Identity string `json:"identity,omitempty"` // username or generated login ID
|
||||
Username string `json:"username,omitempty"` // username or generated login ID
|
||||
Secret string `json:"secret,omitempty"` // password or token
|
||||
}
|
||||
|
||||
@@ -141,8 +145,9 @@ type SDK interface {
|
||||
// example:
|
||||
// user := sdk.User{
|
||||
// Name: "John Doe",
|
||||
// Email: "john.doe@example",
|
||||
// Credentials: sdk.Credentials{
|
||||
// Identity: "john.doe@example",
|
||||
// Username: "john.doe",
|
||||
// Secret: "12345678",
|
||||
// },
|
||||
// }
|
||||
@@ -202,6 +207,19 @@ type SDK interface {
|
||||
// fmt.Println(user)
|
||||
UpdateUser(user User, token string) (User, errors.SDKError)
|
||||
|
||||
// UpdateUserEmail updates the user's email
|
||||
//
|
||||
// example:
|
||||
// user := sdk.User{
|
||||
// ID: "userID",
|
||||
// Credentials: sdk.Credentials{
|
||||
// Email: "john.doe@example",
|
||||
// },
|
||||
// }
|
||||
// user, _ := sdk.UpdateUserEmail(user, "token")
|
||||
// fmt.Println(user)
|
||||
UpdateUserEmail(user User, token string) (User, errors.SDKError)
|
||||
|
||||
// UpdateUserTags updates the user's tags.
|
||||
//
|
||||
// example:
|
||||
@@ -213,18 +231,29 @@ type SDK interface {
|
||||
// fmt.Println(user)
|
||||
UpdateUserTags(user User, token string) (User, errors.SDKError)
|
||||
|
||||
// UpdateUserIdentity updates the user's identity
|
||||
// UpdateUsername updates the user's Username.
|
||||
//
|
||||
// example:
|
||||
// user := sdk.User{
|
||||
// ID: "userID",
|
||||
// Credentials: sdk.Credentials{
|
||||
// Identity: "john.doe@example",
|
||||
// },
|
||||
// Username: "john.doe",
|
||||
// },
|
||||
// }
|
||||
// user, _ := sdk.UpdateUserIdentity(user, "token")
|
||||
// user, _ := sdk.UpdateUsername(user, "token")
|
||||
// fmt.Println(user)
|
||||
UpdateUserIdentity(user User, token string) (User, errors.SDKError)
|
||||
UpdateUsername(user User, token string) (User, errors.SDKError)
|
||||
|
||||
// UpdateProfilePicture updates the user's profile picture.
|
||||
//
|
||||
// example:
|
||||
// user := sdk.User{
|
||||
// ID: "userID",
|
||||
// ProfilePicture: "https://cloudstorage.example.com/bucket-name/user-images/profile-picture.jpg",
|
||||
// }
|
||||
// user, _ := sdk.UpdateProfilePicture(user, "token")
|
||||
// fmt.Println(user)
|
||||
UpdateProfilePicture(user User, token string) (User, errors.SDKError)
|
||||
|
||||
// UpdateUserRole updates the user's role.
|
||||
//
|
||||
@@ -283,7 +312,7 @@ type SDK interface {
|
||||
//
|
||||
// example:
|
||||
// lt := sdk.Login{
|
||||
// Identity: "john.doe@example",
|
||||
// Email: "john.doe@example",
|
||||
// Secret: "12345678",
|
||||
// }
|
||||
// token, _ := sdk.CreateToken(lt)
|
||||
@@ -1326,9 +1355,21 @@ func (pm PageMetadata) query() (string, error) {
|
||||
if pm.Level != 0 {
|
||||
q.Add("level", strconv.FormatUint(pm.Level, 10))
|
||||
}
|
||||
if pm.Email != "" {
|
||||
q.Add("email", pm.Email)
|
||||
}
|
||||
if pm.Identity != "" {
|
||||
q.Add("identity", pm.Identity)
|
||||
}
|
||||
if pm.Username != "" {
|
||||
q.Add("username", pm.Username)
|
||||
}
|
||||
if pm.FirstName != "" {
|
||||
q.Add("first_name", pm.FirstName)
|
||||
}
|
||||
if pm.LastName != "" {
|
||||
q.Add("last_name", pm.LastName)
|
||||
}
|
||||
if pm.Name != "" {
|
||||
q.Add("name", pm.Name)
|
||||
}
|
||||
|
||||
+32
-24
@@ -17,12 +17,15 @@ import (
|
||||
mggroups "github.com/absmach/magistrala/pkg/groups"
|
||||
sdk "github.com/absmach/magistrala/pkg/sdk/go"
|
||||
"github.com/absmach/magistrala/pkg/uuid"
|
||||
"github.com/absmach/magistrala/users"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
const (
|
||||
invalidIdentity = "invalididentity"
|
||||
Identity = "identity"
|
||||
Email = "email"
|
||||
InvalidEmail = "invalidemail"
|
||||
secret = "strongsecret"
|
||||
invalidToken = "invalid"
|
||||
contentType = "application/senml+json"
|
||||
@@ -51,11 +54,11 @@ func generateUUID(t *testing.T) string {
|
||||
return ulid
|
||||
}
|
||||
|
||||
func convertClients(cs []sdk.User) []mgclients.Client {
|
||||
ccs := []mgclients.Client{}
|
||||
func convertUsers(cs []sdk.User) []users.User {
|
||||
ccs := []users.User{}
|
||||
|
||||
for _, c := range cs {
|
||||
ccs = append(ccs, convertClient(c))
|
||||
ccs = append(ccs, convertUser(c))
|
||||
}
|
||||
|
||||
return ccs
|
||||
@@ -131,29 +134,31 @@ func convertChildren(gs []*sdk.Group) []*mggroups.Group {
|
||||
return cg
|
||||
}
|
||||
|
||||
func convertClient(c sdk.User) mgclients.Client {
|
||||
func convertUser(c sdk.User) users.User {
|
||||
if c.Status == "" {
|
||||
c.Status = mgclients.EnabledStatus.String()
|
||||
c.Status = users.EnabledStatus.String()
|
||||
}
|
||||
status, err := mgclients.ToStatus(c.Status)
|
||||
status, err := users.ToStatus(c.Status)
|
||||
if err != nil {
|
||||
return mgclients.Client{}
|
||||
return users.User{}
|
||||
}
|
||||
role, err := mgclients.ToRole(c.Role)
|
||||
role, err := users.ToRole(c.Role)
|
||||
if err != nil {
|
||||
return mgclients.Client{}
|
||||
return users.User{}
|
||||
}
|
||||
return mgclients.Client{
|
||||
ID: c.ID,
|
||||
Name: c.Name,
|
||||
Tags: c.Tags,
|
||||
Domain: c.Domain,
|
||||
Credentials: mgclients.Credentials(c.Credentials),
|
||||
Metadata: mgclients.Metadata(c.Metadata),
|
||||
CreatedAt: c.CreatedAt,
|
||||
UpdatedAt: c.UpdatedAt,
|
||||
Status: status,
|
||||
Role: role,
|
||||
return users.User{
|
||||
ID: c.ID,
|
||||
FirstName: c.FirstName,
|
||||
LastName: c.LastName,
|
||||
Tags: c.Tags,
|
||||
Email: c.Email,
|
||||
Credentials: users.Credentials(c.Credentials),
|
||||
Metadata: users.Metadata(c.Metadata),
|
||||
CreatedAt: c.CreatedAt,
|
||||
UpdatedAt: c.UpdatedAt,
|
||||
Status: status,
|
||||
Role: role,
|
||||
ProfilePicture: c.ProfilePicture,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -229,17 +234,20 @@ func generateTestUser(t *testing.T) sdk.User {
|
||||
createdAt, err := time.Parse(time.RFC3339, "2024-01-01T00:00:00Z")
|
||||
assert.Nil(t, err, fmt.Sprintf("Unexpected error parsing time: %v", err))
|
||||
return sdk.User{
|
||||
ID: generateUUID(t),
|
||||
Name: "clientname",
|
||||
ID: generateUUID(t),
|
||||
FirstName: "userfirstname",
|
||||
LastName: "userlastname",
|
||||
Email: "useremail@example.com",
|
||||
Credentials: sdk.Credentials{
|
||||
Identity: "clientidentity@email.com",
|
||||
Username: "username",
|
||||
Secret: secret,
|
||||
},
|
||||
Tags: []string{"tag1", "tag2"},
|
||||
Metadata: validMetadata,
|
||||
CreatedAt: createdAt,
|
||||
UpdatedAt: createdAt,
|
||||
Status: mgclients.EnabledStatus.String(),
|
||||
Status: users.EnabledStatus.String(),
|
||||
Role: users.UserRole.String(),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -27,7 +27,7 @@ const (
|
||||
type Thing struct {
|
||||
ID string `json:"id,omitempty"`
|
||||
Name string `json:"name,omitempty"`
|
||||
Credentials Credentials `json:"credentials"`
|
||||
Credentials ClientCredentials `json:"credentials"`
|
||||
Tags []string `json:"tags,omitempty"`
|
||||
DomainID string `json:"domain_id,omitempty"`
|
||||
Metadata map[string]interface{} `json:"metadata,omitempty"`
|
||||
@@ -37,6 +37,11 @@ type Thing struct {
|
||||
Permissions []string `json:"permissions,omitempty"`
|
||||
}
|
||||
|
||||
type ClientCredentials struct {
|
||||
Identity string `json:"identity,omitempty"`
|
||||
Secret string `json:"secret,omitempty"`
|
||||
}
|
||||
|
||||
func (sdk mgSDK) CreateThing(thing Thing, domainID, token string) (Thing, errors.SDKError) {
|
||||
data, err := json.Marshal(thing)
|
||||
if err != nil {
|
||||
|
||||
@@ -2203,7 +2203,7 @@ func generateTestThing(t *testing.T) sdk.Thing {
|
||||
return sdk.Thing{
|
||||
ID: testsutil.GenerateUUID(t),
|
||||
Name: "clientname",
|
||||
Credentials: sdk.Credentials{
|
||||
Credentials: sdk.ClientCredentials{
|
||||
Identity: "thing@example.com",
|
||||
Secret: generateUUID(t),
|
||||
},
|
||||
|
||||
@@ -20,7 +20,8 @@ type Token struct {
|
||||
}
|
||||
|
||||
type Login struct {
|
||||
Identity string `json:"identity"`
|
||||
Email string `json:"email"`
|
||||
Username string `json:"username,omitempty"`
|
||||
Secret string `json:"secret"`
|
||||
}
|
||||
|
||||
|
||||
+10
-10
@@ -41,7 +41,7 @@ func TestIssueToken(t *testing.T) {
|
||||
{
|
||||
desc: "issue token successfully",
|
||||
login: sdk.Login{
|
||||
Identity: client.Credentials.Identity,
|
||||
Username: client.Credentials.Username,
|
||||
Secret: client.Credentials.Secret,
|
||||
},
|
||||
svcRes: &magistrala.Token{
|
||||
@@ -54,9 +54,9 @@ func TestIssueToken(t *testing.T) {
|
||||
err: nil,
|
||||
},
|
||||
{
|
||||
desc: "issue token with invalid identity",
|
||||
desc: "issue token with invalid username",
|
||||
login: sdk.Login{
|
||||
Identity: invalidIdentity,
|
||||
Username: invalidIdentity,
|
||||
Secret: client.Credentials.Secret,
|
||||
},
|
||||
svcRes: &magistrala.Token{},
|
||||
@@ -67,7 +67,7 @@ func TestIssueToken(t *testing.T) {
|
||||
{
|
||||
desc: "issue token with invalid secret",
|
||||
login: sdk.Login{
|
||||
Identity: client.Credentials.Identity,
|
||||
Username: client.Credentials.Username,
|
||||
Secret: "invalid",
|
||||
},
|
||||
svcRes: &magistrala.Token{},
|
||||
@@ -76,20 +76,20 @@ func TestIssueToken(t *testing.T) {
|
||||
err: errors.NewSDKErrorWithStatus(svcerr.ErrLogin, http.StatusUnauthorized),
|
||||
},
|
||||
{
|
||||
desc: "issue token with empty identity",
|
||||
desc: "issue token with empty username",
|
||||
login: sdk.Login{
|
||||
Identity: "",
|
||||
Username: "",
|
||||
Secret: client.Credentials.Secret,
|
||||
},
|
||||
svcRes: &magistrala.Token{},
|
||||
svcErr: nil,
|
||||
response: sdk.Token{},
|
||||
err: errors.NewSDKErrorWithStatus(errors.Wrap(apiutil.ErrValidation, apiutil.ErrMissingIdentity), http.StatusBadRequest),
|
||||
err: errors.NewSDKErrorWithStatus(errors.Wrap(apiutil.ErrValidation, apiutil.ErrMissingUsername), http.StatusBadRequest),
|
||||
},
|
||||
{
|
||||
desc: "issue token with empty secret",
|
||||
login: sdk.Login{
|
||||
Identity: client.Credentials.Identity,
|
||||
Username: client.Credentials.Username,
|
||||
Secret: "",
|
||||
},
|
||||
svcRes: &magistrala.Token{},
|
||||
@@ -100,12 +100,12 @@ func TestIssueToken(t *testing.T) {
|
||||
}
|
||||
for _, tc := range cases {
|
||||
t.Run(tc.desc, func(t *testing.T) {
|
||||
svcCall := svc.On("IssueToken", mock.Anything, tc.login.Identity, tc.login.Secret).Return(tc.svcRes, tc.svcErr)
|
||||
svcCall := svc.On("IssueToken", mock.Anything, tc.login.Username, tc.login.Secret).Return(tc.svcRes, tc.svcErr)
|
||||
resp, err := mgsdk.CreateToken(tc.login)
|
||||
assert.Equal(t, tc.err, err)
|
||||
assert.Equal(t, tc.response, resp)
|
||||
if tc.err == nil {
|
||||
ok := svcCall.Parent.AssertCalled(t, "IssueToken", mock.Anything, tc.login.Identity, tc.login.Secret)
|
||||
ok := svcCall.Parent.AssertCalled(t, "IssueToken", mock.Anything, tc.login.Username, tc.login.Secret)
|
||||
assert.True(t, ok)
|
||||
}
|
||||
svcCall.Unset()
|
||||
|
||||
+63
-17
@@ -27,16 +27,19 @@ const (
|
||||
|
||||
// User represents magistrala user its credentials.
|
||||
type User struct {
|
||||
ID string `json:"id"`
|
||||
Name string `json:"name,omitempty"`
|
||||
Credentials Credentials `json:"credentials"`
|
||||
Tags []string `json:"tags,omitempty"`
|
||||
Domain string `json:"-"` // ignoring Domain Field, since it will be always empty for users
|
||||
Metadata Metadata `json:"metadata,omitempty"`
|
||||
CreatedAt time.Time `json:"created_at,omitempty"`
|
||||
UpdatedAt time.Time `json:"updated_at,omitempty"`
|
||||
Status string `json:"status,omitempty"`
|
||||
Role string `json:"role,omitempty"`
|
||||
ID string `json:"id"`
|
||||
Username string `json:"username,omitempty"`
|
||||
FirstName string `json:"first_name,omitempty"`
|
||||
LastName string `json:"last_name,omitempty"`
|
||||
Email string `json:"email,omitempty"`
|
||||
Credentials Credentials `json:"credentials"`
|
||||
Tags []string `json:"tags,omitempty"`
|
||||
Metadata Metadata `json:"metadata,omitempty"`
|
||||
CreatedAt time.Time `json:"created_at,omitempty"`
|
||||
UpdatedAt time.Time `json:"updated_at,omitempty"`
|
||||
Status string `json:"status,omitempty"`
|
||||
Role string `json:"role,omitempty"`
|
||||
ProfilePicture string `json:"profile_picture,omitempty"`
|
||||
}
|
||||
|
||||
func (sdk mgSDK) CreateUser(user User, token string) (User, errors.SDKError) {
|
||||
@@ -178,15 +181,15 @@ func (sdk mgSDK) UpdateUserTags(user User, token string) (User, errors.SDKError)
|
||||
return user, nil
|
||||
}
|
||||
|
||||
func (sdk mgSDK) UpdateUserIdentity(user User, token string) (User, errors.SDKError) {
|
||||
ucir := updateClientIdentityReq{token: token, id: user.ID, Identity: user.Credentials.Identity}
|
||||
func (sdk mgSDK) UpdateUserEmail(user User, token string) (User, errors.SDKError) {
|
||||
ucir := updateUserEmailReq{token: token, id: user.ID, Email: user.Email}
|
||||
|
||||
data, err := json.Marshal(ucir)
|
||||
if err != nil {
|
||||
return User{}, errors.NewSDKError(err)
|
||||
}
|
||||
|
||||
url := fmt.Sprintf("%s/%s/%s/identity", sdk.usersURL, usersEndpoint, user.ID)
|
||||
url := fmt.Sprintf("%s/%s/%s/email", sdk.usersURL, usersEndpoint, user.ID)
|
||||
|
||||
_, body, sdkerr := sdk.processRequest(http.MethodPatch, url, token, data, nil, http.StatusOK)
|
||||
if sdkerr != nil {
|
||||
@@ -233,7 +236,7 @@ func (sdk mgSDK) ResetPassword(password, confPass, token string) errors.SDKError
|
||||
}
|
||||
|
||||
func (sdk mgSDK) UpdatePassword(oldPass, newPass, token string) (User, errors.SDKError) {
|
||||
ucsr := updateClientSecretReq{OldSecret: oldPass, NewSecret: newPass}
|
||||
ucsr := updateUserSecretReq{OldSecret: oldPass, NewSecret: newPass}
|
||||
|
||||
data, err := json.Marshal(ucsr)
|
||||
if err != nil {
|
||||
@@ -276,6 +279,49 @@ func (sdk mgSDK) UpdateUserRole(user User, token string) (User, errors.SDKError)
|
||||
return user, nil
|
||||
}
|
||||
|
||||
func (sdk mgSDK) UpdateUsername(user User, token string) (User, errors.SDKError) {
|
||||
uur := UpdateUsernameReq{id: user.ID, Username: user.Credentials.Username}
|
||||
data, err := json.Marshal(uur)
|
||||
if err != nil {
|
||||
return User{}, errors.NewSDKError(err)
|
||||
}
|
||||
|
||||
url := fmt.Sprintf("%s/%s/%s/username", sdk.usersURL, usersEndpoint, user.ID)
|
||||
|
||||
_, body, sdkerr := sdk.processRequest(http.MethodPatch, url, token, data, nil, http.StatusOK)
|
||||
if sdkerr != nil {
|
||||
return User{}, sdkerr
|
||||
}
|
||||
|
||||
user = User{}
|
||||
if err = json.Unmarshal(body, &user); err != nil {
|
||||
return User{}, errors.NewSDKError(err)
|
||||
}
|
||||
|
||||
return user, nil
|
||||
}
|
||||
|
||||
func (sdk mgSDK) UpdateProfilePicture(user User, token string) (User, errors.SDKError) {
|
||||
data, err := json.Marshal(user)
|
||||
if err != nil {
|
||||
return User{}, errors.NewSDKError(err)
|
||||
}
|
||||
|
||||
url := fmt.Sprintf("%s/%s/%s/picture", sdk.usersURL, usersEndpoint, user.ID)
|
||||
|
||||
_, body, sdkerr := sdk.processRequest(http.MethodPatch, url, token, data, nil, http.StatusOK)
|
||||
if sdkerr != nil {
|
||||
return User{}, sdkerr
|
||||
}
|
||||
|
||||
user = User{}
|
||||
if err = json.Unmarshal(body, &user); err != nil {
|
||||
return User{}, errors.NewSDKError(err)
|
||||
}
|
||||
|
||||
return user, nil
|
||||
}
|
||||
|
||||
func (sdk mgSDK) ListUserChannels(userID string, pm PageMetadata, token string) (ChannelsPage, errors.SDKError) {
|
||||
url, err := sdk.withQueryParams(sdk.thingsURL, fmt.Sprintf("%s/%s/%s/%s", pm.DomainID, usersEndpoint, userID, channelsEndpoint), pm)
|
||||
if err != nil {
|
||||
@@ -348,14 +394,14 @@ func (sdk mgSDK) SearchUsers(pm PageMetadata, token string) (UsersPage, errors.S
|
||||
}
|
||||
|
||||
func (sdk mgSDK) EnableUser(id, token string) (User, errors.SDKError) {
|
||||
return sdk.changeClientStatus(token, id, enableEndpoint)
|
||||
return sdk.changeUserStatus(token, id, enableEndpoint)
|
||||
}
|
||||
|
||||
func (sdk mgSDK) DisableUser(id, token string) (User, errors.SDKError) {
|
||||
return sdk.changeClientStatus(token, id, disableEndpoint)
|
||||
return sdk.changeUserStatus(token, id, disableEndpoint)
|
||||
}
|
||||
|
||||
func (sdk mgSDK) changeClientStatus(token, id, status string) (User, errors.SDKError) {
|
||||
func (sdk mgSDK) changeUserStatus(token, id, status string) (User, errors.SDKError) {
|
||||
url := fmt.Sprintf("%s/%s/%s/%s", sdk.usersURL, usersEndpoint, id, status)
|
||||
|
||||
_, body, sdkerr := sdk.processRequest(http.MethodPost, url, token, nil, nil, http.StatusOK)
|
||||
|
||||
+466
-440
File diff suppressed because it is too large
Load Diff
+63
-3
@@ -2506,6 +2506,36 @@ func (_m *SDK) UpdatePassword(oldPass string, newPass string, token string) (sdk
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// UpdateProfilePicture provides a mock function with given fields: user, token
|
||||
func (_m *SDK) UpdateProfilePicture(user sdk.User, token string) (sdk.User, errors.SDKError) {
|
||||
ret := _m.Called(user, token)
|
||||
|
||||
if len(ret) == 0 {
|
||||
panic("no return value specified for UpdateProfilePicture")
|
||||
}
|
||||
|
||||
var r0 sdk.User
|
||||
var r1 errors.SDKError
|
||||
if rf, ok := ret.Get(0).(func(sdk.User, string) (sdk.User, errors.SDKError)); ok {
|
||||
return rf(user, token)
|
||||
}
|
||||
if rf, ok := ret.Get(0).(func(sdk.User, string) sdk.User); ok {
|
||||
r0 = rf(user, token)
|
||||
} else {
|
||||
r0 = ret.Get(0).(sdk.User)
|
||||
}
|
||||
|
||||
if rf, ok := ret.Get(1).(func(sdk.User, string) errors.SDKError); ok {
|
||||
r1 = rf(user, token)
|
||||
} else {
|
||||
if ret.Get(1) != nil {
|
||||
r1 = ret.Get(1).(errors.SDKError)
|
||||
}
|
||||
}
|
||||
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// UpdateThing provides a mock function with given fields: thing, domainID, token
|
||||
func (_m *SDK) UpdateThing(thing sdk.Thing, domainID string, token string) (sdk.Thing, errors.SDKError) {
|
||||
ret := _m.Called(thing, domainID, token)
|
||||
@@ -2626,12 +2656,12 @@ func (_m *SDK) UpdateUser(user sdk.User, token string) (sdk.User, errors.SDKErro
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// UpdateUserIdentity provides a mock function with given fields: user, token
|
||||
func (_m *SDK) UpdateUserIdentity(user sdk.User, token string) (sdk.User, errors.SDKError) {
|
||||
// UpdateUserEmail provides a mock function with given fields: user, token
|
||||
func (_m *SDK) UpdateUserEmail(user sdk.User, token string) (sdk.User, errors.SDKError) {
|
||||
ret := _m.Called(user, token)
|
||||
|
||||
if len(ret) == 0 {
|
||||
panic("no return value specified for UpdateUserIdentity")
|
||||
panic("no return value specified for UpdateUserEmail")
|
||||
}
|
||||
|
||||
var r0 sdk.User
|
||||
@@ -2716,6 +2746,36 @@ func (_m *SDK) UpdateUserTags(user sdk.User, token string) (sdk.User, errors.SDK
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// UpdateUsername provides a mock function with given fields: user, token
|
||||
func (_m *SDK) UpdateUsername(user sdk.User, token string) (sdk.User, errors.SDKError) {
|
||||
ret := _m.Called(user, token)
|
||||
|
||||
if len(ret) == 0 {
|
||||
panic("no return value specified for UpdateUsername")
|
||||
}
|
||||
|
||||
var r0 sdk.User
|
||||
var r1 errors.SDKError
|
||||
if rf, ok := ret.Get(0).(func(sdk.User, string) (sdk.User, errors.SDKError)); ok {
|
||||
return rf(user, token)
|
||||
}
|
||||
if rf, ok := ret.Get(0).(func(sdk.User, string) sdk.User); ok {
|
||||
r0 = rf(user, token)
|
||||
} else {
|
||||
r0 = ret.Get(0).(sdk.User)
|
||||
}
|
||||
|
||||
if rf, ok := ret.Get(1).(func(sdk.User, string) errors.SDKError); ok {
|
||||
r1 = rf(user, token)
|
||||
} else {
|
||||
if ret.Get(1) != nil {
|
||||
r1 = ret.Get(1).(errors.SDKError)
|
||||
}
|
||||
}
|
||||
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// User provides a mock function with given fields: id, token
|
||||
func (_m *SDK) User(id string, token string) (sdk.User, errors.SDKError) {
|
||||
ret := _m.Called(id, token)
|
||||
|
||||
+2
-1
@@ -25,7 +25,8 @@ type ServiceConf struct {
|
||||
ThingsURL string `toml:"things_url" env:"MG_PROVISION_THINGS_LOCATION" envDefault:"http://localhost"`
|
||||
UsersURL string `toml:"users_url" env:"MG_PROVISION_USERS_LOCATION" envDefault:"http://localhost"`
|
||||
HTTPPort string `toml:"http_port" env:"MG_PROVISION_HTTP_PORT" envDefault:"9016"`
|
||||
MgUser string `toml:"mg_user" env:"MG_PROVISION_USER" envDefault:"test@example.com"`
|
||||
MgEmail string `toml:"mg_email" env:"MG_PROVISION_EMAIL" envDefault:"test@example.com"`
|
||||
MgUsername string `toml:"mg_username" env:"MG_PROVISION_USERNAME" envDefault:"user"`
|
||||
MgPass string `toml:"mg_pass" env:"MG_PROVISION_PASS" envDefault:"test"`
|
||||
MgDomainID string `toml:"mg_domain_id" env:"MG_PROVISION_DOMAIN_ID" envDefault:""`
|
||||
MgAPIKey string `toml:"mg_api_key" env:"MG_PROVISION_API_KEY" envDefault:""`
|
||||
|
||||
@@ -289,12 +289,13 @@ func (ps *provisionService) createTokenIfEmpty(token string) (string, error) {
|
||||
}
|
||||
|
||||
// If no API key use username and password provided to create access token.
|
||||
if ps.conf.Server.MgUser == "" || ps.conf.Server.MgPass == "" {
|
||||
if ps.conf.Server.MgUsername == "" || ps.conf.Server.MgPass == "" {
|
||||
return token, ErrMissingCredentials
|
||||
}
|
||||
|
||||
u := sdk.Login{
|
||||
Identity: ps.conf.Server.MgUser,
|
||||
Email: ps.conf.Server.MgEmail,
|
||||
Username: ps.conf.Server.MgUsername,
|
||||
Secret: ps.conf.Server.MgPass,
|
||||
}
|
||||
tkn, err := ps.sdk.CreateToken(u)
|
||||
|
||||
@@ -111,7 +111,7 @@ func TestCert(t *testing.T) {
|
||||
desc: "empty token with username and password",
|
||||
config: provision.Config{
|
||||
Server: provision.ServiceConf{
|
||||
MgUser: "test@example.com",
|
||||
MgUsername: "testUsername",
|
||||
MgPass: "12345678",
|
||||
MgDomainID: testsutil.GenerateUUID(t),
|
||||
},
|
||||
@@ -132,7 +132,7 @@ func TestCert(t *testing.T) {
|
||||
desc: "empty token with username and invalid password",
|
||||
config: provision.Config{
|
||||
Server: provision.ServiceConf{
|
||||
MgUser: "test@example.com",
|
||||
MgUsername: "testUsername",
|
||||
MgPass: "12345678",
|
||||
MgDomainID: testsutil.GenerateUUID(t),
|
||||
},
|
||||
@@ -219,7 +219,7 @@ func TestCert(t *testing.T) {
|
||||
mgsdk.On("IssueCert", c.thingID, c.config.Cert.TTL, c.domainID, mock.Anything).Return(sdk.Cert{SerialNumber: c.serial}, c.sdkCertErr)
|
||||
mgsdk.On("ViewCert", c.serial, mock.Anything, mock.Anything).Return(sdk.Cert{Certificate: c.cert, Key: c.key}, c.sdkCertErr)
|
||||
login := sdk.Login{
|
||||
Identity: c.config.Server.MgUser,
|
||||
Username: c.config.Server.MgUsername,
|
||||
Secret: c.config.Server.MgPass,
|
||||
}
|
||||
mgsdk.On("CreateToken", login).Return(sdk.Token{AccessToken: validToken}, c.sdkTokenErr)
|
||||
|
||||
+1
-1
@@ -38,7 +38,7 @@ done
|
||||
###
|
||||
# Users
|
||||
###
|
||||
MG_USERS_LOG_LEVEL=info MG_USERS_HTTP_PORT=9002 MG_USERS_GRPC_PORT=7001 MG_USERS_ADMIN_EMAIL=admin@magistrala.com MG_USERS_ADMIN_PASSWORD=12345678 MG_EMAIL_TEMPLATE=../docker/templates/users.tmpl $BUILD_DIR/magistrala-users &
|
||||
MG_USERS_LOG_LEVEL=info MG_USERS_HTTP_PORT=9002 MG_USERS_GRPC_PORT=7001 MG_USERS_ADMIN_EMAIL=admin@magistrala.com MG_USERS_ADMIN_PASSWORD=12345678 MG_USERS_ADMIN_USERNAME=admin MG_EMAIL_TEMPLATE=../docker/templates/users.tmpl $BUILD_DIR/magistrala-users &
|
||||
|
||||
###
|
||||
# Things
|
||||
|
||||
@@ -11,7 +11,6 @@ import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/absmach/magistrala/pkg/postgres"
|
||||
pgclient "github.com/absmach/magistrala/pkg/postgres"
|
||||
cpostgres "github.com/absmach/magistrala/things/postgres"
|
||||
"github.com/jmoiron/sqlx"
|
||||
@@ -22,7 +21,7 @@ import (
|
||||
|
||||
var (
|
||||
db *sqlx.DB
|
||||
database postgres.Database
|
||||
database pgclient.Database
|
||||
tracer = otel.Tracer("repo_tests")
|
||||
)
|
||||
|
||||
@@ -84,7 +83,7 @@ func TestMain(m *testing.M) {
|
||||
log.Fatalf("Could not setup test DB connection: %s", err)
|
||||
}
|
||||
|
||||
database = postgres.NewDatabase(db, dbConfig, tracer)
|
||||
database = pgclient.NewDatabase(db, dbConfig, tracer)
|
||||
|
||||
code := m.Run()
|
||||
|
||||
|
||||
+31
-19
@@ -133,9 +133,11 @@ func errExit(err error) {
|
||||
|
||||
func createUser(s sdk.SDK, conf Config) (string, string, error) {
|
||||
user := sdk.User{
|
||||
Name: fmt.Sprintf("%s%s", conf.Prefix, namesgenerator.Generate()),
|
||||
FirstName: fmt.Sprintf("%s%s", conf.Prefix, namesgenerator.Generate()),
|
||||
LastName: fmt.Sprintf("%s%s", conf.Prefix, namesgenerator.Generate()),
|
||||
Email: fmt.Sprintf("%s%s@email.com", conf.Prefix, namesgenerator.Generate()),
|
||||
Credentials: sdk.Credentials{
|
||||
Identity: fmt.Sprintf("%s%s@email.com", conf.Prefix, namesgenerator.Generate()),
|
||||
Username: fmt.Sprintf("%s%s", conf.Prefix, namesgenerator.Generate()),
|
||||
Secret: defPass,
|
||||
},
|
||||
Status: sdk.EnabledStatus,
|
||||
@@ -147,7 +149,7 @@ func createUser(s sdk.SDK, conf Config) (string, string, error) {
|
||||
}
|
||||
|
||||
login := sdk.Login{
|
||||
Identity: user.Credentials.Identity,
|
||||
Username: user.Credentials.Username,
|
||||
Secret: user.Credentials.Secret,
|
||||
}
|
||||
token, err := s.CreateToken(login)
|
||||
@@ -168,7 +170,7 @@ func createUser(s sdk.SDK, conf Config) (string, string, error) {
|
||||
}
|
||||
|
||||
login = sdk.Login{
|
||||
Identity: user.Credentials.Identity,
|
||||
Username: user.Credentials.Username,
|
||||
Secret: user.Credentials.Secret,
|
||||
}
|
||||
token, err = s.CreateToken(login)
|
||||
@@ -185,9 +187,11 @@ func createUsers(s sdk.SDK, conf Config, token string) ([]sdk.User, error) {
|
||||
|
||||
for i := uint64(0); i < conf.Num; i++ {
|
||||
user := sdk.User{
|
||||
Name: fmt.Sprintf("%s%s", conf.Prefix, namesgenerator.Generate()),
|
||||
FirstName: fmt.Sprintf("%s%s", conf.Prefix, namesgenerator.Generate()),
|
||||
LastName: fmt.Sprintf("%s%s", conf.Prefix, namesgenerator.Generate()),
|
||||
Email: fmt.Sprintf("%s%s@email.com", conf.Prefix, namesgenerator.Generate()),
|
||||
Credentials: sdk.Credentials{
|
||||
Identity: fmt.Sprintf("%s%s@email.com", conf.Prefix, namesgenerator.Generate()),
|
||||
Username: fmt.Sprintf("%s%s", conf.Prefix, namesgenerator.Generate()),
|
||||
Secret: defPass,
|
||||
},
|
||||
Status: sdk.EnabledStatus,
|
||||
@@ -249,19 +253,19 @@ func createThings(s sdk.SDK, conf Config, domainID, token string) ([]sdk.Thing,
|
||||
for i := 0; i < batches; i++ {
|
||||
ths, err := createThingsInBatch(s, conf, domainID, token, batchSize)
|
||||
if err != nil {
|
||||
return []sdk.Thing{}, fmt.Errorf("Failed to create the things: %w", err)
|
||||
return []sdk.Thing{}, fmt.Errorf("failed to create the things: %w", err)
|
||||
}
|
||||
things = append(things, ths...)
|
||||
}
|
||||
ths, err := createThingsInBatch(s, conf, domainID, token, conf.Num%uint64(batchSize))
|
||||
if err != nil {
|
||||
return []sdk.Thing{}, fmt.Errorf("Failed to create the things: %w", err)
|
||||
return []sdk.Thing{}, fmt.Errorf("failed to create the things: %w", err)
|
||||
}
|
||||
things = append(things, ths...)
|
||||
} else {
|
||||
ths, err := createThingsInBatch(s, conf, domainID, token, conf.Num)
|
||||
if err != nil {
|
||||
return []sdk.Thing{}, fmt.Errorf("Failed to create the things: %w", err)
|
||||
return []sdk.Thing{}, fmt.Errorf("failed to create the things: %w", err)
|
||||
}
|
||||
things = append(things, ths...)
|
||||
}
|
||||
@@ -294,19 +298,19 @@ func createChannels(s sdk.SDK, conf Config, domainID, token string) ([]sdk.Chann
|
||||
for i := 0; i < batches; i++ {
|
||||
chs, err := createChannelsInBatch(s, conf, token, domainID, batchSize)
|
||||
if err != nil {
|
||||
return []sdk.Channel{}, fmt.Errorf("Failed to create the channels: %w", err)
|
||||
return []sdk.Channel{}, fmt.Errorf("failed to create the channels: %w", err)
|
||||
}
|
||||
channels = append(channels, chs...)
|
||||
}
|
||||
chs, err := createChannelsInBatch(s, conf, domainID, token, conf.Num%uint64(batchSize))
|
||||
if err != nil {
|
||||
return []sdk.Channel{}, fmt.Errorf("Failed to create the channels: %w", err)
|
||||
return []sdk.Channel{}, fmt.Errorf("failed to create the channels: %w", err)
|
||||
}
|
||||
channels = append(channels, chs...)
|
||||
} else {
|
||||
chs, err := createChannelsInBatch(s, conf, domainID, token, conf.Num)
|
||||
if err != nil {
|
||||
return []sdk.Channel{}, fmt.Errorf("Failed to create the channels: %w", err)
|
||||
return []sdk.Channel{}, fmt.Errorf("failed to create the channels: %w", err)
|
||||
}
|
||||
channels = append(channels, chs...)
|
||||
}
|
||||
@@ -369,26 +373,34 @@ func read(s sdk.SDK, conf Config, domainID, token string, users []sdk.User, grou
|
||||
|
||||
func update(s sdk.SDK, domainID, token string, users []sdk.User, groups []sdk.Group, things []sdk.Thing, channels []sdk.Channel) error {
|
||||
for _, user := range users {
|
||||
user.Name = namesgenerator.Generate()
|
||||
user.FirstName = namesgenerator.Generate()
|
||||
user.Metadata = sdk.Metadata{"Update": namesgenerator.Generate()}
|
||||
rUser, err := s.UpdateUser(user, token)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to update user %w", err)
|
||||
}
|
||||
if rUser.Name != user.Name {
|
||||
return fmt.Errorf("failed to update user name before %s after %s", user.Name, rUser.Name)
|
||||
if rUser.FirstName != user.FirstName {
|
||||
return fmt.Errorf("failed to update user name before %s after %s", user.FirstName, rUser.FirstName)
|
||||
}
|
||||
if rUser.Metadata["Update"] != user.Metadata["Update"] {
|
||||
return fmt.Errorf("failed to update user metadata before %s after %s", user.Metadata["Update"], rUser.Metadata["Update"])
|
||||
}
|
||||
user = rUser
|
||||
user.Credentials.Identity = namesgenerator.Generate()
|
||||
rUser, err = s.UpdateUserIdentity(user, token)
|
||||
user.Credentials.Username = namesgenerator.Generate()
|
||||
rUser, err = s.UpdateUsername(user, token)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to update username %w", err)
|
||||
}
|
||||
if rUser.Credentials.Username != user.Credentials.Username {
|
||||
return fmt.Errorf("failed to update user name before %s after %s", user.Credentials.Username, rUser.Credentials.Username)
|
||||
}
|
||||
user = rUser
|
||||
rUser, err = s.UpdateUserEmail(user, token)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to update user identity %w", err)
|
||||
}
|
||||
if rUser.Credentials.Identity != user.Credentials.Identity {
|
||||
return fmt.Errorf("failed to update user identity before %s after %s", user.Credentials.Identity, rUser.Credentials.Identity)
|
||||
if rUser.Email != user.Email {
|
||||
return fmt.Errorf("failed to update user identity before %s after %s", user.Email, rUser.Email)
|
||||
}
|
||||
user = rUser
|
||||
user.Tags = []string{namesgenerator.Generate()}
|
||||
|
||||
@@ -44,6 +44,7 @@ type MgConn struct {
|
||||
type Config struct {
|
||||
Host string
|
||||
Username string
|
||||
Email string
|
||||
Password string
|
||||
Num int
|
||||
SSL bool
|
||||
@@ -74,14 +75,15 @@ func Provision(conf Config) error {
|
||||
s := sdk.NewSDK(sdkConf)
|
||||
|
||||
user := sdk.User{
|
||||
Email: conf.Email,
|
||||
Credentials: sdk.Credentials{
|
||||
Identity: conf.Username,
|
||||
Username: conf.Username,
|
||||
Secret: conf.Password,
|
||||
},
|
||||
}
|
||||
|
||||
if user.Credentials.Identity == "" {
|
||||
user.Credentials.Identity = fmt.Sprintf("%s@email.com", namesgenerator.Generate())
|
||||
if user.Email == "" {
|
||||
user.Email = fmt.Sprintf("%s@email.com", namesgenerator.Generate())
|
||||
user.Credentials.Secret = defPass
|
||||
}
|
||||
|
||||
@@ -93,7 +95,7 @@ func Provision(conf Config) error {
|
||||
var err error
|
||||
|
||||
// Login user
|
||||
token, err := s.CreateToken(sdk.Login{Identity: user.Credentials.Identity, Secret: user.Credentials.Secret})
|
||||
token, err := s.CreateToken(sdk.Login{Username: user.Credentials.Username, Secret: user.Credentials.Secret})
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to login user: %s", err.Error())
|
||||
}
|
||||
@@ -112,7 +114,7 @@ func Provision(conf Config) error {
|
||||
}
|
||||
// Login to domain
|
||||
token, err = s.CreateToken(sdk.Login{
|
||||
Identity: user.Credentials.Identity,
|
||||
Username: user.Credentials.Username,
|
||||
Secret: user.Credentials.Secret,
|
||||
})
|
||||
if err != nil {
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
# Clients
|
||||
# Users
|
||||
|
||||
Users service provides an HTTP API for managing users. Through this API clients are able to do the following actions:
|
||||
|
||||
|
||||
+601
-569
File diff suppressed because it is too large
Load Diff
+141
-90
@@ -9,7 +9,6 @@ import (
|
||||
"github.com/absmach/magistrala/internal/api"
|
||||
"github.com/absmach/magistrala/pkg/apiutil"
|
||||
"github.com/absmach/magistrala/pkg/authn"
|
||||
mgclients "github.com/absmach/magistrala/pkg/clients"
|
||||
"github.com/absmach/magistrala/pkg/errors"
|
||||
svcerr "github.com/absmach/magistrala/pkg/errors/service"
|
||||
"github.com/absmach/magistrala/users"
|
||||
@@ -18,7 +17,7 @@ import (
|
||||
|
||||
func registrationEndpoint(svc users.Service, selfRegister bool) endpoint.Endpoint {
|
||||
return func(ctx context.Context, request interface{}) (interface{}, error) {
|
||||
req := request.(createClientReq)
|
||||
req := request.(createUserReq)
|
||||
if err := req.validate(); err != nil {
|
||||
return nil, errors.Wrap(apiutil.ErrValidation, err)
|
||||
}
|
||||
@@ -32,21 +31,21 @@ func registrationEndpoint(svc users.Service, selfRegister bool) endpoint.Endpoin
|
||||
}
|
||||
}
|
||||
|
||||
client, err := svc.RegisterClient(ctx, session, req.client, selfRegister)
|
||||
user, err := svc.Register(ctx, session, req.User, selfRegister)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return createClientRes{
|
||||
Client: client,
|
||||
return createUserRes{
|
||||
User: user,
|
||||
created: true,
|
||||
}, nil
|
||||
}
|
||||
}
|
||||
|
||||
func viewClientEndpoint(svc users.Service) endpoint.Endpoint {
|
||||
func viewEndpoint(svc users.Service) endpoint.Endpoint {
|
||||
return func(ctx context.Context, request interface{}) (interface{}, error) {
|
||||
req := request.(viewClientReq)
|
||||
req := request.(viewUserReq)
|
||||
if err := req.validate(); err != nil {
|
||||
return nil, errors.Wrap(apiutil.ErrValidation, err)
|
||||
}
|
||||
@@ -55,12 +54,12 @@ func viewClientEndpoint(svc users.Service) endpoint.Endpoint {
|
||||
if !ok {
|
||||
return nil, svcerr.ErrAuthorization
|
||||
}
|
||||
client, err := svc.ViewClient(ctx, session, req.id)
|
||||
user, err := svc.View(ctx, session, req.id)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return viewClientRes{Client: client}, nil
|
||||
return viewUserRes{User: user}, nil
|
||||
}
|
||||
}
|
||||
|
||||
@@ -75,13 +74,13 @@ func viewProfileEndpoint(svc users.Service) endpoint.Endpoint {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return viewClientRes{Client: client}, nil
|
||||
return viewUserRes{User: client}, nil
|
||||
}
|
||||
}
|
||||
|
||||
func listClientsEndpoint(svc users.Service) endpoint.Endpoint {
|
||||
func listUsersEndpoint(svc users.Service) endpoint.Endpoint {
|
||||
return func(ctx context.Context, request interface{}) (interface{}, error) {
|
||||
req := request.(listClientsReq)
|
||||
req := request.(listUsersReq)
|
||||
if err := req.validate(); err != nil {
|
||||
return nil, errors.Wrap(apiutil.ErrValidation, err)
|
||||
}
|
||||
@@ -91,70 +90,74 @@ func listClientsEndpoint(svc users.Service) endpoint.Endpoint {
|
||||
return nil, svcerr.ErrAuthorization
|
||||
}
|
||||
|
||||
pm := mgclients.Page{
|
||||
Status: req.status,
|
||||
Offset: req.offset,
|
||||
Limit: req.limit,
|
||||
Name: req.name,
|
||||
Tag: req.tag,
|
||||
Metadata: req.metadata,
|
||||
Identity: req.identity,
|
||||
Order: req.order,
|
||||
Dir: req.dir,
|
||||
Id: req.id,
|
||||
pm := users.Page{
|
||||
Status: req.status,
|
||||
Offset: req.offset,
|
||||
Limit: req.limit,
|
||||
Username: req.userName,
|
||||
Tag: req.tag,
|
||||
Metadata: req.metadata,
|
||||
FirstName: req.firstName,
|
||||
LastName: req.lastName,
|
||||
Email: req.email,
|
||||
Order: req.order,
|
||||
Dir: req.dir,
|
||||
Id: req.id,
|
||||
}
|
||||
|
||||
page, err := svc.ListClients(ctx, session, pm)
|
||||
page, err := svc.ListUsers(ctx, session, pm)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
res := clientsPageRes{
|
||||
res := usersPageRes{
|
||||
pageRes: pageRes{
|
||||
Total: page.Total,
|
||||
Offset: page.Offset,
|
||||
Limit: page.Limit,
|
||||
},
|
||||
Clients: []viewClientRes{},
|
||||
Users: []viewUserRes{},
|
||||
}
|
||||
for _, client := range page.Clients {
|
||||
res.Clients = append(res.Clients, viewClientRes{Client: client})
|
||||
for _, user := range page.Users {
|
||||
res.Users = append(res.Users, viewUserRes{User: user})
|
||||
}
|
||||
|
||||
return res, nil
|
||||
}
|
||||
}
|
||||
|
||||
func searchClientsEndpoint(svc users.Service) endpoint.Endpoint {
|
||||
func searchUsersEndpoint(svc users.Service) endpoint.Endpoint {
|
||||
return func(ctx context.Context, request interface{}) (interface{}, error) {
|
||||
req := request.(searchClientsReq)
|
||||
req := request.(searchUsersReq)
|
||||
if err := req.validate(); err != nil {
|
||||
return nil, errors.Wrap(apiutil.ErrValidation, err)
|
||||
}
|
||||
|
||||
pm := mgclients.Page{
|
||||
Offset: req.Offset,
|
||||
Limit: req.Limit,
|
||||
Name: req.Name,
|
||||
Id: req.Id,
|
||||
Order: req.Order,
|
||||
Dir: req.Dir,
|
||||
pm := users.Page{
|
||||
Offset: req.Offset,
|
||||
Limit: req.Limit,
|
||||
Username: req.Username,
|
||||
FirstName: req.FirstName,
|
||||
LastName: req.LastName,
|
||||
Id: req.Id,
|
||||
Order: req.Order,
|
||||
Dir: req.Dir,
|
||||
}
|
||||
page, err := svc.SearchUsers(ctx, pm)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
res := clientsPageRes{
|
||||
res := usersPageRes{
|
||||
pageRes: pageRes{
|
||||
Total: page.Total,
|
||||
Offset: page.Offset,
|
||||
Limit: page.Limit,
|
||||
},
|
||||
Clients: []viewClientRes{},
|
||||
Users: []viewUserRes{},
|
||||
}
|
||||
for _, client := range page.Clients {
|
||||
res.Clients = append(res.Clients, viewClientRes{Client: client})
|
||||
for _, user := range page.Users {
|
||||
res.Users = append(res.Users, viewUserRes{User: user})
|
||||
}
|
||||
|
||||
return res, nil
|
||||
@@ -179,7 +182,7 @@ func listMembersByGroupEndpoint(svc users.Service) endpoint.Endpoint {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return buildClientsResponse(page), nil
|
||||
return buildUsersResponse(page), nil
|
||||
}
|
||||
}
|
||||
|
||||
@@ -202,7 +205,7 @@ func listMembersByChannelEndpoint(svc users.Service) endpoint.Endpoint {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return buildClientsResponse(page), nil
|
||||
return buildUsersResponse(page), nil
|
||||
}
|
||||
}
|
||||
|
||||
@@ -224,7 +227,7 @@ func listMembersByThingEndpoint(svc users.Service) endpoint.Endpoint {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return buildClientsResponse(page), nil
|
||||
return buildUsersResponse(page), nil
|
||||
}
|
||||
}
|
||||
|
||||
@@ -246,13 +249,13 @@ func listMembersByDomainEndpoint(svc users.Service) endpoint.Endpoint {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return buildClientsResponse(page), nil
|
||||
return buildUsersResponse(page), nil
|
||||
}
|
||||
}
|
||||
|
||||
func updateClientEndpoint(svc users.Service) endpoint.Endpoint {
|
||||
func updateEndpoint(svc users.Service) endpoint.Endpoint {
|
||||
return func(ctx context.Context, request interface{}) (interface{}, error) {
|
||||
req := request.(updateClientReq)
|
||||
req := request.(updateUserReq)
|
||||
if err := req.validate(); err != nil {
|
||||
return nil, errors.Wrap(apiutil.ErrValidation, err)
|
||||
}
|
||||
@@ -262,24 +265,25 @@ func updateClientEndpoint(svc users.Service) endpoint.Endpoint {
|
||||
return nil, svcerr.ErrAuthorization
|
||||
}
|
||||
|
||||
client := mgclients.Client{
|
||||
ID: req.id,
|
||||
Name: req.Name,
|
||||
Metadata: req.Metadata,
|
||||
user := users.User{
|
||||
ID: req.id,
|
||||
FirstName: req.FirstName,
|
||||
LastName: req.LastName,
|
||||
Metadata: req.Metadata,
|
||||
}
|
||||
|
||||
client, err := svc.UpdateClient(ctx, session, client)
|
||||
user, err := svc.Update(ctx, session, user)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return updateClientRes{Client: client}, nil
|
||||
return updateUserRes{User: user}, nil
|
||||
}
|
||||
}
|
||||
|
||||
func updateClientTagsEndpoint(svc users.Service) endpoint.Endpoint {
|
||||
func updateTagsEndpoint(svc users.Service) endpoint.Endpoint {
|
||||
return func(ctx context.Context, request interface{}) (interface{}, error) {
|
||||
req := request.(updateClientTagsReq)
|
||||
req := request.(updateUserTagsReq)
|
||||
if err := req.validate(); err != nil {
|
||||
return nil, errors.Wrap(apiutil.ErrValidation, err)
|
||||
}
|
||||
@@ -289,23 +293,23 @@ func updateClientTagsEndpoint(svc users.Service) endpoint.Endpoint {
|
||||
return nil, svcerr.ErrAuthorization
|
||||
}
|
||||
|
||||
client := mgclients.Client{
|
||||
user := users.User{
|
||||
ID: req.id,
|
||||
Tags: req.Tags,
|
||||
}
|
||||
|
||||
client, err := svc.UpdateClientTags(ctx, session, client)
|
||||
user, err := svc.UpdateTags(ctx, session, user)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return updateClientRes{Client: client}, nil
|
||||
return updateUserRes{User: user}, nil
|
||||
}
|
||||
}
|
||||
|
||||
func updateClientIdentityEndpoint(svc users.Service) endpoint.Endpoint {
|
||||
func updateEmailEndpoint(svc users.Service) endpoint.Endpoint {
|
||||
return func(ctx context.Context, request interface{}) (interface{}, error) {
|
||||
req := request.(updateClientIdentityReq)
|
||||
req := request.(updateEmailReq)
|
||||
if err := req.validate(); err != nil {
|
||||
return nil, errors.Wrap(apiutil.ErrValidation, err)
|
||||
}
|
||||
@@ -315,12 +319,12 @@ func updateClientIdentityEndpoint(svc users.Service) endpoint.Endpoint {
|
||||
return nil, svcerr.ErrAuthorization
|
||||
}
|
||||
|
||||
client, err := svc.UpdateClientIdentity(ctx, session, req.id, req.Identity)
|
||||
user, err := svc.UpdateEmail(ctx, session, req.id, req.Email)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return updateClientRes{Client: client}, nil
|
||||
return updateUserRes{User: user}, nil
|
||||
}
|
||||
}
|
||||
|
||||
@@ -371,9 +375,9 @@ func passwordResetEndpoint(svc users.Service) endpoint.Endpoint {
|
||||
}
|
||||
}
|
||||
|
||||
func updateClientSecretEndpoint(svc users.Service) endpoint.Endpoint {
|
||||
func updateSecretEndpoint(svc users.Service) endpoint.Endpoint {
|
||||
return func(ctx context.Context, request interface{}) (interface{}, error) {
|
||||
req := request.(updateClientSecretReq)
|
||||
req := request.(updateUserSecretReq)
|
||||
if err := req.validate(); err != nil {
|
||||
return nil, errors.Wrap(apiutil.ErrValidation, err)
|
||||
}
|
||||
@@ -382,23 +386,70 @@ func updateClientSecretEndpoint(svc users.Service) endpoint.Endpoint {
|
||||
if !ok {
|
||||
return nil, svcerr.ErrAuthorization
|
||||
}
|
||||
client, err := svc.UpdateClientSecret(ctx, session, req.OldSecret, req.NewSecret)
|
||||
user, err := svc.UpdateSecret(ctx, session, req.OldSecret, req.NewSecret)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return updateClientRes{Client: client}, nil
|
||||
return updateUserRes{User: user}, nil
|
||||
}
|
||||
}
|
||||
|
||||
func updateClientRoleEndpoint(svc users.Service) endpoint.Endpoint {
|
||||
func updateUsernameEndpoint(svc users.Service) endpoint.Endpoint {
|
||||
return func(ctx context.Context, request interface{}) (interface{}, error) {
|
||||
req := request.(updateClientRoleReq)
|
||||
req := request.(updateUsernameReq)
|
||||
if err := req.validate(); err != nil {
|
||||
return nil, errors.Wrap(apiutil.ErrValidation, err)
|
||||
}
|
||||
|
||||
client := mgclients.Client{
|
||||
session, ok := ctx.Value(api.SessionKey).(authn.Session)
|
||||
if !ok {
|
||||
return nil, svcerr.ErrAuthorization
|
||||
}
|
||||
|
||||
user, err := svc.UpdateUsername(ctx, session, req.id, req.Username)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return updateUserRes{User: user}, nil
|
||||
}
|
||||
}
|
||||
|
||||
func updateProfilePictureEndpoint(svc users.Service) endpoint.Endpoint {
|
||||
return func(ctx context.Context, request interface{}) (interface{}, error) {
|
||||
req := request.(updateProfilePictureReq)
|
||||
if err := req.validate(); err != nil {
|
||||
return nil, errors.Wrap(apiutil.ErrValidation, err)
|
||||
}
|
||||
|
||||
user := users.User{
|
||||
ID: req.id,
|
||||
ProfilePicture: req.ProfilePicture,
|
||||
}
|
||||
|
||||
session, ok := ctx.Value(api.SessionKey).(authn.Session)
|
||||
if !ok {
|
||||
return nil, svcerr.ErrAuthorization
|
||||
}
|
||||
|
||||
user, err := svc.Update(ctx, session, user)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return updateUserRes{User: user}, nil
|
||||
}
|
||||
}
|
||||
|
||||
func updateRoleEndpoint(svc users.Service) endpoint.Endpoint {
|
||||
return func(ctx context.Context, request interface{}) (interface{}, error) {
|
||||
req := request.(updateUserRoleReq)
|
||||
if err := req.validate(); err != nil {
|
||||
return nil, errors.Wrap(apiutil.ErrValidation, err)
|
||||
}
|
||||
|
||||
user := users.User{
|
||||
ID: req.id,
|
||||
Role: req.role,
|
||||
}
|
||||
@@ -408,23 +459,23 @@ func updateClientRoleEndpoint(svc users.Service) endpoint.Endpoint {
|
||||
return nil, svcerr.ErrAuthorization
|
||||
}
|
||||
|
||||
client, err := svc.UpdateClientRole(ctx, session, client)
|
||||
user, err := svc.UpdateRole(ctx, session, user)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return updateClientRes{Client: client}, nil
|
||||
return updateUserRes{User: user}, nil
|
||||
}
|
||||
}
|
||||
|
||||
func issueTokenEndpoint(svc users.Service) endpoint.Endpoint {
|
||||
return func(ctx context.Context, request interface{}) (interface{}, error) {
|
||||
req := request.(loginClientReq)
|
||||
req := request.(loginUserReq)
|
||||
if err := req.validate(); err != nil {
|
||||
return nil, errors.Wrap(apiutil.ErrValidation, err)
|
||||
}
|
||||
|
||||
token, err := svc.IssueToken(ctx, req.Identity, req.Secret)
|
||||
token, err := svc.IssueToken(ctx, req.Username, req.Secret)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -462,9 +513,9 @@ func refreshTokenEndpoint(svc users.Service) endpoint.Endpoint {
|
||||
}
|
||||
}
|
||||
|
||||
func enableClientEndpoint(svc users.Service) endpoint.Endpoint {
|
||||
func enableEndpoint(svc users.Service) endpoint.Endpoint {
|
||||
return func(ctx context.Context, request interface{}) (interface{}, error) {
|
||||
req := request.(changeClientStatusReq)
|
||||
req := request.(changeUserStatusReq)
|
||||
if err := req.validate(); err != nil {
|
||||
return nil, errors.Wrap(apiutil.ErrValidation, err)
|
||||
}
|
||||
@@ -474,18 +525,18 @@ func enableClientEndpoint(svc users.Service) endpoint.Endpoint {
|
||||
return nil, svcerr.ErrAuthorization
|
||||
}
|
||||
|
||||
client, err := svc.EnableClient(ctx, session, req.id)
|
||||
user, err := svc.Enable(ctx, session, req.id)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return changeClientStatusClientRes{Client: client}, nil
|
||||
return changeUserStatusRes{User: user}, nil
|
||||
}
|
||||
}
|
||||
|
||||
func disableClientEndpoint(svc users.Service) endpoint.Endpoint {
|
||||
func disableEndpoint(svc users.Service) endpoint.Endpoint {
|
||||
return func(ctx context.Context, request interface{}) (interface{}, error) {
|
||||
req := request.(changeClientStatusReq)
|
||||
req := request.(changeUserStatusReq)
|
||||
if err := req.validate(); err != nil {
|
||||
return nil, errors.Wrap(apiutil.ErrValidation, err)
|
||||
}
|
||||
@@ -495,18 +546,18 @@ func disableClientEndpoint(svc users.Service) endpoint.Endpoint {
|
||||
return nil, svcerr.ErrAuthorization
|
||||
}
|
||||
|
||||
client, err := svc.DisableClient(ctx, session, req.id)
|
||||
user, err := svc.Disable(ctx, session, req.id)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return changeClientStatusClientRes{Client: client}, nil
|
||||
return changeUserStatusRes{User: user}, nil
|
||||
}
|
||||
}
|
||||
|
||||
func deleteClientEndpoint(svc users.Service) endpoint.Endpoint {
|
||||
func deleteEndpoint(svc users.Service) endpoint.Endpoint {
|
||||
return func(ctx context.Context, request interface{}) (interface{}, error) {
|
||||
req := request.(changeClientStatusReq)
|
||||
req := request.(changeUserStatusReq)
|
||||
if err := req.validate(); err != nil {
|
||||
return nil, errors.Wrap(apiutil.ErrValidation, err)
|
||||
}
|
||||
@@ -516,26 +567,26 @@ func deleteClientEndpoint(svc users.Service) endpoint.Endpoint {
|
||||
return nil, svcerr.ErrAuthorization
|
||||
}
|
||||
|
||||
if err := svc.DeleteClient(ctx, session, req.id); err != nil {
|
||||
if err := svc.Delete(ctx, session, req.id); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return deleteClientRes{true}, nil
|
||||
return deleteUserRes{true}, nil
|
||||
}
|
||||
}
|
||||
|
||||
func buildClientsResponse(cp mgclients.MembersPage) clientsPageRes {
|
||||
res := clientsPageRes{
|
||||
func buildUsersResponse(cp users.MembersPage) usersPageRes {
|
||||
res := usersPageRes{
|
||||
pageRes: pageRes{
|
||||
Total: cp.Total,
|
||||
Offset: cp.Offset,
|
||||
Limit: cp.Limit,
|
||||
},
|
||||
Clients: []viewClientRes{},
|
||||
Users: []viewUserRes{},
|
||||
}
|
||||
|
||||
for _, client := range cp.Members {
|
||||
res.Clients = append(res.Clients, viewClientRes{Client: client})
|
||||
for _, user := range cp.Members {
|
||||
res.Users = append(res.Users, viewUserRes{User: user})
|
||||
}
|
||||
|
||||
return res
|
||||
|
||||
+128
-57
@@ -4,39 +4,71 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"net/mail"
|
||||
"net/url"
|
||||
|
||||
"github.com/absmach/magistrala/internal/api"
|
||||
"github.com/absmach/magistrala/pkg/apiutil"
|
||||
mgclients "github.com/absmach/magistrala/pkg/clients"
|
||||
svcerr "github.com/absmach/magistrala/pkg/errors/service"
|
||||
"github.com/absmach/magistrala/users"
|
||||
)
|
||||
|
||||
const maxLimitSize = 100
|
||||
|
||||
type createClientReq struct {
|
||||
client mgclients.Client
|
||||
type createUserReq struct {
|
||||
users.User
|
||||
}
|
||||
|
||||
func (req createClientReq) validate() error {
|
||||
if len(req.client.Name) > api.MaxNameSize {
|
||||
func (req createUserReq) validate() error {
|
||||
if len(req.User.FirstName) > api.MaxNameSize {
|
||||
return apiutil.ErrNameSize
|
||||
}
|
||||
if req.client.Credentials.Identity == "" {
|
||||
return apiutil.ErrMissingIdentity
|
||||
if len(req.User.LastName) > api.MaxNameSize {
|
||||
return apiutil.ErrNameSize
|
||||
}
|
||||
if req.client.Credentials.Secret == "" {
|
||||
if req.User.FirstName == "" {
|
||||
return apiutil.ErrMissingFirstName
|
||||
}
|
||||
if req.User.LastName == "" {
|
||||
return apiutil.ErrMissingLastName
|
||||
}
|
||||
if req.User.Credentials.Username == "" {
|
||||
return apiutil.ErrMissingUsername
|
||||
}
|
||||
// Username must not be a valid email format due to username/email login.
|
||||
if _, err := mail.ParseAddress(req.User.Credentials.Username); err == nil {
|
||||
return apiutil.ErrInvalidUsername
|
||||
}
|
||||
if req.User.Email == "" {
|
||||
return apiutil.ErrMissingEmail
|
||||
}
|
||||
// Email must be in a valid format.
|
||||
if _, err := mail.ParseAddress(req.User.Email); err != nil {
|
||||
return apiutil.ErrInvalidEmail
|
||||
}
|
||||
if req.User.Credentials.Secret == "" {
|
||||
return apiutil.ErrMissingPass
|
||||
}
|
||||
if !passRegex.MatchString(req.client.Credentials.Secret) {
|
||||
if !passRegex.MatchString(req.User.Credentials.Secret) {
|
||||
return apiutil.ErrPasswordFormat
|
||||
}
|
||||
if req.User.Status == users.AllStatus {
|
||||
return svcerr.ErrInvalidStatus
|
||||
}
|
||||
if req.User.ProfilePicture != "" {
|
||||
if _, err := url.Parse(req.User.ProfilePicture); err != nil {
|
||||
return apiutil.ErrInvalidProfilePictureURL
|
||||
}
|
||||
}
|
||||
|
||||
return req.client.Validate()
|
||||
return req.User.Validate()
|
||||
}
|
||||
|
||||
type viewClientReq struct {
|
||||
type viewUserReq struct {
|
||||
id string
|
||||
}
|
||||
|
||||
func (req viewClientReq) validate() error {
|
||||
func (req viewUserReq) validate() error {
|
||||
if req.id == "" {
|
||||
return apiutil.ErrMissingID
|
||||
}
|
||||
@@ -44,20 +76,22 @@ func (req viewClientReq) validate() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
type listClientsReq struct {
|
||||
status mgclients.Status
|
||||
offset uint64
|
||||
limit uint64
|
||||
name string
|
||||
tag string
|
||||
identity string
|
||||
metadata mgclients.Metadata
|
||||
order string
|
||||
dir string
|
||||
id string
|
||||
type listUsersReq struct {
|
||||
status users.Status
|
||||
offset uint64
|
||||
limit uint64
|
||||
userName string
|
||||
tag string
|
||||
firstName string
|
||||
lastName string
|
||||
email string
|
||||
metadata users.Metadata
|
||||
order string
|
||||
dir string
|
||||
id string
|
||||
}
|
||||
|
||||
func (req listClientsReq) validate() error {
|
||||
func (req listUsersReq) validate() error {
|
||||
if req.limit > maxLimitSize || req.limit < 1 {
|
||||
return apiutil.ErrLimitSize
|
||||
}
|
||||
@@ -68,17 +102,19 @@ func (req listClientsReq) validate() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
type searchClientsReq struct {
|
||||
Offset uint64
|
||||
Limit uint64
|
||||
Name string
|
||||
Id string
|
||||
Order string
|
||||
Dir string
|
||||
type searchUsersReq struct {
|
||||
Offset uint64
|
||||
Limit uint64
|
||||
Username string
|
||||
FirstName string
|
||||
LastName string
|
||||
Id string
|
||||
Order string
|
||||
Dir string
|
||||
}
|
||||
|
||||
func (req searchClientsReq) validate() error {
|
||||
if req.Name == "" && req.Id == "" {
|
||||
func (req searchUsersReq) validate() error {
|
||||
if req.Username == "" && req.Id == "" && req.FirstName == "" && req.LastName == "" {
|
||||
return apiutil.ErrEmptySearchQuery
|
||||
}
|
||||
|
||||
@@ -86,7 +122,7 @@ func (req searchClientsReq) validate() error {
|
||||
}
|
||||
|
||||
type listMembersByObjectReq struct {
|
||||
mgclients.Page
|
||||
users.Page
|
||||
objectKind string
|
||||
objectID string
|
||||
}
|
||||
@@ -102,13 +138,14 @@ func (req listMembersByObjectReq) validate() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
type updateClientReq struct {
|
||||
id string
|
||||
Name string `json:"name,omitempty"`
|
||||
Metadata mgclients.Metadata `json:"metadata,omitempty"`
|
||||
type updateUserReq struct {
|
||||
id string
|
||||
FirstName string `json:"first_name,omitempty"`
|
||||
LastName string `json:"last_name,omitempty"`
|
||||
Metadata users.Metadata `json:"metadata,omitempty"`
|
||||
}
|
||||
|
||||
func (req updateClientReq) validate() error {
|
||||
func (req updateUserReq) validate() error {
|
||||
if req.id == "" {
|
||||
return apiutil.ErrMissingID
|
||||
}
|
||||
@@ -116,12 +153,12 @@ func (req updateClientReq) validate() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
type updateClientTagsReq struct {
|
||||
type updateUserTagsReq struct {
|
||||
id string
|
||||
Tags []string `json:"tags,omitempty"`
|
||||
}
|
||||
|
||||
func (req updateClientTagsReq) validate() error {
|
||||
func (req updateUserTagsReq) validate() error {
|
||||
if req.id == "" {
|
||||
return apiutil.ErrMissingID
|
||||
}
|
||||
@@ -129,13 +166,13 @@ func (req updateClientTagsReq) validate() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
type updateClientRoleReq struct {
|
||||
type updateUserRoleReq struct {
|
||||
id string
|
||||
role mgclients.Role
|
||||
role users.Role
|
||||
Role string `json:"role,omitempty"`
|
||||
}
|
||||
|
||||
func (req updateClientRoleReq) validate() error {
|
||||
func (req updateUserRoleReq) validate() error {
|
||||
if req.id == "" {
|
||||
return apiutil.ErrMissingID
|
||||
}
|
||||
@@ -143,25 +180,28 @@ func (req updateClientRoleReq) validate() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
type updateClientIdentityReq struct {
|
||||
id string
|
||||
Identity string `json:"identity,omitempty"`
|
||||
type updateEmailReq struct {
|
||||
id string
|
||||
Email string `json:"email,omitempty"`
|
||||
}
|
||||
|
||||
func (req updateClientIdentityReq) validate() error {
|
||||
func (req updateEmailReq) validate() error {
|
||||
if req.id == "" {
|
||||
return apiutil.ErrMissingID
|
||||
}
|
||||
if _, err := mail.ParseAddress(req.Email); err != nil {
|
||||
return apiutil.ErrInvalidEmail
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
type updateClientSecretReq struct {
|
||||
type updateUserSecretReq struct {
|
||||
OldSecret string `json:"old_secret,omitempty"`
|
||||
NewSecret string `json:"new_secret,omitempty"`
|
||||
}
|
||||
|
||||
func (req updateClientSecretReq) validate() error {
|
||||
func (req updateUserSecretReq) validate() error {
|
||||
if req.OldSecret == "" || req.NewSecret == "" {
|
||||
return apiutil.ErrMissingPass
|
||||
}
|
||||
@@ -172,11 +212,42 @@ func (req updateClientSecretReq) validate() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
type changeClientStatusReq struct {
|
||||
type updateUsernameReq struct {
|
||||
id string
|
||||
Username string
|
||||
}
|
||||
|
||||
func (req updateUsernameReq) validate() error {
|
||||
if req.id == "" {
|
||||
return apiutil.ErrMissingID
|
||||
}
|
||||
if len(req.Username) > api.MaxNameSize {
|
||||
return apiutil.ErrNameSize
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
type updateProfilePictureReq struct {
|
||||
id string
|
||||
ProfilePicture string `json:"profile_picture,omitempty"`
|
||||
}
|
||||
|
||||
func (req updateProfilePictureReq) validate() error {
|
||||
if req.id == "" {
|
||||
return apiutil.ErrMissingID
|
||||
}
|
||||
if _, err := url.Parse(req.ProfilePicture); err != nil {
|
||||
return apiutil.ErrInvalidProfilePictureURL
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type changeUserStatusReq struct {
|
||||
id string
|
||||
}
|
||||
|
||||
func (req changeClientStatusReq) validate() error {
|
||||
func (req changeUserStatusReq) validate() error {
|
||||
if req.id == "" {
|
||||
return apiutil.ErrMissingID
|
||||
}
|
||||
@@ -184,14 +255,14 @@ func (req changeClientStatusReq) validate() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
type loginClientReq struct {
|
||||
Identity string `json:"identity,omitempty"`
|
||||
type loginUserReq struct {
|
||||
Username string `json:"username,omitempty"`
|
||||
Secret string `json:"secret,omitempty"`
|
||||
}
|
||||
|
||||
func (req loginClientReq) validate() error {
|
||||
if req.Identity == "" {
|
||||
return apiutil.ErrMissingIdentity
|
||||
func (req loginUserReq) validate() error {
|
||||
if req.Username == "" {
|
||||
return apiutil.ErrMissingUsername
|
||||
}
|
||||
if req.Secret == "" {
|
||||
return apiutil.ErrMissingPass
|
||||
|
||||
+199
-119
@@ -4,13 +4,14 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"net/url"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/absmach/magistrala/internal/api"
|
||||
"github.com/absmach/magistrala/internal/testsutil"
|
||||
"github.com/absmach/magistrala/pkg/apiutil"
|
||||
mgclients "github.com/absmach/magistrala/pkg/clients"
|
||||
"github.com/absmach/magistrala/users"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
@@ -18,7 +19,7 @@ const (
|
||||
valid = "valid"
|
||||
invalid = "invalid"
|
||||
secret = "QJg58*aMan7j"
|
||||
name = "client"
|
||||
name = "user"
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -26,20 +27,22 @@ var (
|
||||
domain = testsutil.GenerateUUID(&testing.T{})
|
||||
)
|
||||
|
||||
func TestCreateClientReqValidate(t *testing.T) {
|
||||
func TestCreateUserReqValidate(t *testing.T) {
|
||||
cases := []struct {
|
||||
desc string
|
||||
req createClientReq
|
||||
req createUserReq
|
||||
err error
|
||||
}{
|
||||
{
|
||||
desc: "valid request",
|
||||
req: createClientReq{
|
||||
client: mgclients.Client{
|
||||
ID: validID,
|
||||
Name: valid,
|
||||
Credentials: mgclients.Credentials{
|
||||
Identity: "example@example.com",
|
||||
req: createUserReq{
|
||||
User: users.User{
|
||||
ID: validID,
|
||||
FirstName: valid,
|
||||
LastName: valid,
|
||||
Email: "example@domain.com",
|
||||
Credentials: users.Credentials{
|
||||
Username: "example",
|
||||
Secret: secret,
|
||||
},
|
||||
},
|
||||
@@ -48,35 +51,40 @@ func TestCreateClientReqValidate(t *testing.T) {
|
||||
},
|
||||
{
|
||||
desc: "name too long",
|
||||
req: createClientReq{
|
||||
client: mgclients.Client{
|
||||
ID: validID,
|
||||
Name: strings.Repeat("a", api.MaxNameSize+1),
|
||||
req: createUserReq{
|
||||
User: users.User{
|
||||
ID: validID,
|
||||
FirstName: strings.Repeat("a", api.MaxNameSize+1),
|
||||
LastName: valid,
|
||||
},
|
||||
},
|
||||
err: apiutil.ErrNameSize,
|
||||
},
|
||||
{
|
||||
desc: "missing identity in request",
|
||||
req: createClientReq{
|
||||
client: mgclients.Client{
|
||||
ID: validID,
|
||||
Name: valid,
|
||||
Credentials: mgclients.Credentials{
|
||||
Secret: valid,
|
||||
desc: "missing email in request",
|
||||
req: createUserReq{
|
||||
User: users.User{
|
||||
ID: validID,
|
||||
FirstName: valid,
|
||||
LastName: valid,
|
||||
Credentials: users.Credentials{
|
||||
Username: "example",
|
||||
Secret: secret,
|
||||
},
|
||||
},
|
||||
},
|
||||
err: apiutil.ErrMissingIdentity,
|
||||
err: apiutil.ErrMissingEmail,
|
||||
},
|
||||
{
|
||||
desc: "missing secret in request",
|
||||
req: createClientReq{
|
||||
client: mgclients.Client{
|
||||
ID: validID,
|
||||
Name: valid,
|
||||
Credentials: mgclients.Credentials{
|
||||
Identity: "example@example.com",
|
||||
req: createUserReq{
|
||||
User: users.User{
|
||||
ID: validID,
|
||||
FirstName: valid,
|
||||
LastName: valid,
|
||||
Email: "example@domain.com",
|
||||
Credentials: users.Credentials{
|
||||
Username: "example",
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -84,12 +92,14 @@ func TestCreateClientReqValidate(t *testing.T) {
|
||||
},
|
||||
{
|
||||
desc: "invalid secret in request",
|
||||
req: createClientReq{
|
||||
client: mgclients.Client{
|
||||
ID: validID,
|
||||
Name: valid,
|
||||
Credentials: mgclients.Credentials{
|
||||
Identity: "example@example.com",
|
||||
req: createUserReq{
|
||||
User: users.User{
|
||||
ID: validID,
|
||||
FirstName: valid,
|
||||
LastName: valid,
|
||||
Email: "example@domain.com",
|
||||
Credentials: users.Credentials{
|
||||
Username: "example",
|
||||
Secret: "invalid",
|
||||
},
|
||||
},
|
||||
@@ -99,26 +109,26 @@ func TestCreateClientReqValidate(t *testing.T) {
|
||||
}
|
||||
for _, tc := range cases {
|
||||
err := tc.req.validate()
|
||||
assert.Equal(t, tc.err, err)
|
||||
assert.Equal(t, tc.err, err, "%s: expected %s got %s\n", tc.desc, tc.err, err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestViewClientReqValidate(t *testing.T) {
|
||||
func TestViewUserReqValidate(t *testing.T) {
|
||||
cases := []struct {
|
||||
desc string
|
||||
req viewClientReq
|
||||
req viewUserReq
|
||||
err error
|
||||
}{
|
||||
{
|
||||
desc: "valid request",
|
||||
req: viewClientReq{
|
||||
req: viewUserReq{
|
||||
id: validID,
|
||||
},
|
||||
err: nil,
|
||||
},
|
||||
{
|
||||
desc: "empty id",
|
||||
req: viewClientReq{
|
||||
req: viewUserReq{
|
||||
id: "",
|
||||
},
|
||||
err: apiutil.ErrMissingID,
|
||||
@@ -130,36 +140,36 @@ func TestViewClientReqValidate(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestListClientsReqValidate(t *testing.T) {
|
||||
func TestListUsersReqValidate(t *testing.T) {
|
||||
cases := []struct {
|
||||
desc string
|
||||
req listClientsReq
|
||||
req listUsersReq
|
||||
err error
|
||||
}{
|
||||
{
|
||||
desc: "valid request",
|
||||
req: listClientsReq{
|
||||
req: listUsersReq{
|
||||
limit: 10,
|
||||
},
|
||||
err: nil,
|
||||
},
|
||||
{
|
||||
desc: "limit too big",
|
||||
req: listClientsReq{
|
||||
req: listUsersReq{
|
||||
limit: api.MaxLimitSize + 1,
|
||||
},
|
||||
err: apiutil.ErrLimitSize,
|
||||
},
|
||||
{
|
||||
desc: "limit too small",
|
||||
req: listClientsReq{
|
||||
req: listUsersReq{
|
||||
limit: 0,
|
||||
},
|
||||
err: apiutil.ErrLimitSize,
|
||||
},
|
||||
{
|
||||
desc: "invalid direction",
|
||||
req: listClientsReq{
|
||||
req: listUsersReq{
|
||||
limit: 10,
|
||||
dir: "invalid",
|
||||
},
|
||||
@@ -172,22 +182,22 @@ func TestListClientsReqValidate(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestSearchClientsReqValidate(t *testing.T) {
|
||||
func TestSearchUsersReqValidate(t *testing.T) {
|
||||
cases := []struct {
|
||||
desc string
|
||||
req searchClientsReq
|
||||
req searchUsersReq
|
||||
err error
|
||||
}{
|
||||
{
|
||||
desc: "valid request",
|
||||
req: searchClientsReq{
|
||||
Name: name,
|
||||
req: searchUsersReq{
|
||||
Username: name,
|
||||
},
|
||||
err: nil,
|
||||
},
|
||||
{
|
||||
desc: "empty query",
|
||||
req: searchClientsReq{},
|
||||
req: searchUsersReq{},
|
||||
err: apiutil.ErrEmptySearchQuery,
|
||||
},
|
||||
}
|
||||
@@ -234,25 +244,23 @@ func TestListMembersByObjectReqValidate(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestUpdateClientReqValidate(t *testing.T) {
|
||||
func TestUpdateUserReqValidate(t *testing.T) {
|
||||
cases := []struct {
|
||||
desc string
|
||||
req updateClientReq
|
||||
req updateUserReq
|
||||
err error
|
||||
}{
|
||||
{
|
||||
desc: "valid request",
|
||||
req: updateClientReq{
|
||||
id: validID,
|
||||
Name: valid,
|
||||
req: updateUserReq{
|
||||
id: validID,
|
||||
},
|
||||
err: nil,
|
||||
},
|
||||
{
|
||||
desc: "empty id",
|
||||
req: updateClientReq{
|
||||
id: "",
|
||||
Name: valid,
|
||||
req: updateUserReq{
|
||||
id: "",
|
||||
},
|
||||
err: apiutil.ErrMissingID,
|
||||
},
|
||||
@@ -263,15 +271,15 @@ func TestUpdateClientReqValidate(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestUpdateClientTagsReqValidate(t *testing.T) {
|
||||
func TestUpdateUserTagsReqValidate(t *testing.T) {
|
||||
cases := []struct {
|
||||
desc string
|
||||
req updateClientTagsReq
|
||||
req updateUserTagsReq
|
||||
err error
|
||||
}{
|
||||
{
|
||||
desc: "valid request",
|
||||
req: updateClientTagsReq{
|
||||
req: updateUserTagsReq{
|
||||
id: validID,
|
||||
Tags: []string{"tag1", "tag2"},
|
||||
},
|
||||
@@ -279,7 +287,7 @@ func TestUpdateClientTagsReqValidate(t *testing.T) {
|
||||
},
|
||||
{
|
||||
desc: "empty id",
|
||||
req: updateClientTagsReq{
|
||||
req: updateUserTagsReq{
|
||||
id: "",
|
||||
Tags: []string{"tag1", "tag2"},
|
||||
},
|
||||
@@ -292,54 +300,97 @@ func TestUpdateClientTagsReqValidate(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestUpdateClientRoleReqValidate(t *testing.T) {
|
||||
func TestUpdateUsernameReqValidate(t *testing.T) {
|
||||
cases := []struct {
|
||||
desc string
|
||||
req updateClientRoleReq
|
||||
req updateUsernameReq
|
||||
err error
|
||||
}{
|
||||
{
|
||||
desc: "valid request",
|
||||
req: updateClientRoleReq{
|
||||
id: validID,
|
||||
Role: "admin",
|
||||
},
|
||||
err: nil,
|
||||
},
|
||||
{
|
||||
desc: "empty id",
|
||||
req: updateClientRoleReq{
|
||||
id: "",
|
||||
Role: "admin",
|
||||
},
|
||||
err: apiutil.ErrMissingID,
|
||||
},
|
||||
}
|
||||
for _, c := range cases {
|
||||
err := c.req.validate()
|
||||
assert.Equal(t, c.err, err, "%s: expected %s got %s\n", c.desc, c.err, err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestUpdateClientIdentityReqValidate(t *testing.T) {
|
||||
cases := []struct {
|
||||
desc string
|
||||
req updateClientIdentityReq
|
||||
err error
|
||||
}{
|
||||
{
|
||||
desc: "valid request",
|
||||
req: updateClientIdentityReq{
|
||||
req: updateUsernameReq{
|
||||
id: validID,
|
||||
Identity: "example@example.com",
|
||||
Username: "validUsername",
|
||||
},
|
||||
err: nil,
|
||||
},
|
||||
{
|
||||
desc: "missing user ID",
|
||||
req: updateUsernameReq{
|
||||
id: "",
|
||||
Username: "validUsername",
|
||||
},
|
||||
err: apiutil.ErrMissingID,
|
||||
},
|
||||
{
|
||||
desc: "name too long",
|
||||
req: updateUsernameReq{
|
||||
id: validID,
|
||||
Username: strings.Repeat("a", api.MaxNameSize+1),
|
||||
},
|
||||
err: apiutil.ErrNameSize,
|
||||
},
|
||||
}
|
||||
for _, tc := range cases {
|
||||
err := tc.req.validate()
|
||||
assert.Equal(t, tc.err, err, "%s: expected %s got %s\n", tc.desc, tc.err, err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestUpdateProfilePictureReqValidate(t *testing.T) {
|
||||
base64EncodedString := "https://example.com/profile.jpg"
|
||||
|
||||
parsedURL, err := url.Parse(base64EncodedString)
|
||||
if err != nil {
|
||||
t.Fatalf("Error parsing URL: %v", err)
|
||||
}
|
||||
cases := []struct {
|
||||
desc string
|
||||
req updateProfilePictureReq
|
||||
err error
|
||||
}{
|
||||
{
|
||||
desc: "valid request",
|
||||
req: updateProfilePictureReq{
|
||||
id: validID,
|
||||
ProfilePicture: parsedURL.String(),
|
||||
},
|
||||
err: nil,
|
||||
},
|
||||
{
|
||||
desc: "empty ID",
|
||||
req: updateProfilePictureReq{
|
||||
id: "",
|
||||
ProfilePicture: parsedURL.String(),
|
||||
},
|
||||
err: apiutil.ErrMissingID,
|
||||
},
|
||||
}
|
||||
for _, tc := range cases {
|
||||
err := tc.req.validate()
|
||||
assert.Equal(t, tc.err, err, "%s: expected %s got %s\n", tc.desc, tc.err, err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestUpdateUserRoleReqValidate(t *testing.T) {
|
||||
cases := []struct {
|
||||
desc string
|
||||
req updateUserRoleReq
|
||||
err error
|
||||
}{
|
||||
{
|
||||
desc: "valid request",
|
||||
req: updateUserRoleReq{
|
||||
id: validID,
|
||||
Role: "admin",
|
||||
},
|
||||
err: nil,
|
||||
},
|
||||
{
|
||||
desc: "empty id",
|
||||
req: updateClientIdentityReq{
|
||||
id: "",
|
||||
Identity: "example@example.com",
|
||||
req: updateUserRoleReq{
|
||||
id: "",
|
||||
Role: "admin",
|
||||
},
|
||||
err: apiutil.ErrMissingID,
|
||||
},
|
||||
@@ -350,15 +401,44 @@ func TestUpdateClientIdentityReqValidate(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestUpdateClientSecretReqValidate(t *testing.T) {
|
||||
func TestUpdateUserEmailReqValidate(t *testing.T) {
|
||||
cases := []struct {
|
||||
desc string
|
||||
req updateClientSecretReq
|
||||
req updateEmailReq
|
||||
err error
|
||||
}{
|
||||
{
|
||||
desc: "valid request",
|
||||
req: updateClientSecretReq{
|
||||
req: updateEmailReq{
|
||||
id: validID,
|
||||
Email: "example@example.com",
|
||||
},
|
||||
err: nil,
|
||||
},
|
||||
{
|
||||
desc: "empty id",
|
||||
req: updateEmailReq{
|
||||
id: "",
|
||||
Email: "example@example.com",
|
||||
},
|
||||
err: apiutil.ErrMissingID,
|
||||
},
|
||||
}
|
||||
for _, c := range cases {
|
||||
err := c.req.validate()
|
||||
assert.Equal(t, c.err, err, "%s: expected %s got %s\n", c.desc, c.err, err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestUpdateUserSecretReqValidate(t *testing.T) {
|
||||
cases := []struct {
|
||||
desc string
|
||||
req updateUserSecretReq
|
||||
err error
|
||||
}{
|
||||
{
|
||||
desc: "valid request",
|
||||
req: updateUserSecretReq{
|
||||
OldSecret: secret,
|
||||
NewSecret: secret,
|
||||
},
|
||||
@@ -366,7 +446,7 @@ func TestUpdateClientSecretReqValidate(t *testing.T) {
|
||||
},
|
||||
{
|
||||
desc: "missing old secret",
|
||||
req: updateClientSecretReq{
|
||||
req: updateUserSecretReq{
|
||||
OldSecret: "",
|
||||
NewSecret: secret,
|
||||
},
|
||||
@@ -374,7 +454,7 @@ func TestUpdateClientSecretReqValidate(t *testing.T) {
|
||||
},
|
||||
{
|
||||
desc: "missing new secret",
|
||||
req: updateClientSecretReq{
|
||||
req: updateUserSecretReq{
|
||||
OldSecret: secret,
|
||||
NewSecret: "",
|
||||
},
|
||||
@@ -382,7 +462,7 @@ func TestUpdateClientSecretReqValidate(t *testing.T) {
|
||||
},
|
||||
{
|
||||
desc: "invalid new secret",
|
||||
req: updateClientSecretReq{
|
||||
req: updateUserSecretReq{
|
||||
OldSecret: secret,
|
||||
NewSecret: "invalid",
|
||||
},
|
||||
@@ -395,22 +475,22 @@ func TestUpdateClientSecretReqValidate(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestChangeClientStatusReqValidate(t *testing.T) {
|
||||
func TestChangeUserStatusReqValidate(t *testing.T) {
|
||||
cases := []struct {
|
||||
desc string
|
||||
req changeClientStatusReq
|
||||
req changeUserStatusReq
|
||||
err error
|
||||
}{
|
||||
{
|
||||
desc: "valid request",
|
||||
req: changeClientStatusReq{
|
||||
req: changeUserStatusReq{
|
||||
id: validID,
|
||||
},
|
||||
err: nil,
|
||||
},
|
||||
{
|
||||
desc: "empty id",
|
||||
req: changeClientStatusReq{
|
||||
req: changeUserStatusReq{
|
||||
id: "",
|
||||
},
|
||||
err: apiutil.ErrMissingID,
|
||||
@@ -422,33 +502,33 @@ func TestChangeClientStatusReqValidate(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestLoginClientReqValidate(t *testing.T) {
|
||||
func TestLoginUserReqValidate(t *testing.T) {
|
||||
cases := []struct {
|
||||
desc string
|
||||
req loginClientReq
|
||||
req loginUserReq
|
||||
err error
|
||||
}{
|
||||
{
|
||||
desc: "valid request",
|
||||
req: loginClientReq{
|
||||
Identity: "eaxmple,example.com",
|
||||
desc: "valid request with username",
|
||||
req: loginUserReq{
|
||||
Username: "example",
|
||||
Secret: secret,
|
||||
},
|
||||
err: nil,
|
||||
},
|
||||
{
|
||||
desc: "empty identity",
|
||||
req: loginClientReq{
|
||||
Identity: "",
|
||||
desc: "empty Username",
|
||||
req: loginUserReq{
|
||||
Username: "",
|
||||
Secret: secret,
|
||||
},
|
||||
err: apiutil.ErrMissingIdentity,
|
||||
err: apiutil.ErrMissingUsername,
|
||||
},
|
||||
{
|
||||
desc: "empty secret",
|
||||
req: loginClientReq{
|
||||
Identity: "eaxmple,example.com",
|
||||
req: loginUserReq{
|
||||
Secret: "",
|
||||
Username: "example",
|
||||
},
|
||||
err: apiutil.ErrMissingPass,
|
||||
},
|
||||
|
||||
+38
-38
@@ -8,7 +8,7 @@ import (
|
||||
"net/http"
|
||||
|
||||
"github.com/absmach/magistrala"
|
||||
mgclients "github.com/absmach/magistrala/pkg/clients"
|
||||
"github.com/absmach/magistrala/users"
|
||||
)
|
||||
|
||||
// MailSent message response when link is sent.
|
||||
@@ -16,18 +16,18 @@ const MailSent = "Email with reset link is sent"
|
||||
|
||||
var (
|
||||
_ magistrala.Response = (*tokenRes)(nil)
|
||||
_ magistrala.Response = (*viewClientRes)(nil)
|
||||
_ magistrala.Response = (*createClientRes)(nil)
|
||||
_ magistrala.Response = (*changeClientStatusClientRes)(nil)
|
||||
_ magistrala.Response = (*clientsPageRes)(nil)
|
||||
_ magistrala.Response = (*viewUserRes)(nil)
|
||||
_ magistrala.Response = (*createUserRes)(nil)
|
||||
_ magistrala.Response = (*changeUserStatusRes)(nil)
|
||||
_ magistrala.Response = (*usersPageRes)(nil)
|
||||
_ magistrala.Response = (*viewMembersRes)(nil)
|
||||
_ magistrala.Response = (*passwResetReqRes)(nil)
|
||||
_ magistrala.Response = (*passwChangeRes)(nil)
|
||||
_ magistrala.Response = (*assignUsersRes)(nil)
|
||||
_ magistrala.Response = (*unassignUsersRes)(nil)
|
||||
_ magistrala.Response = (*updateClientRes)(nil)
|
||||
_ magistrala.Response = (*updateUserRes)(nil)
|
||||
_ magistrala.Response = (*tokenRes)(nil)
|
||||
_ magistrala.Response = (*deleteClientRes)(nil)
|
||||
_ magistrala.Response = (*deleteUserRes)(nil)
|
||||
)
|
||||
|
||||
type pageRes struct {
|
||||
@@ -36,12 +36,12 @@ type pageRes struct {
|
||||
Total uint64 `json:"total"`
|
||||
}
|
||||
|
||||
type createClientRes struct {
|
||||
mgclients.Client `json:",inline"`
|
||||
created bool
|
||||
type createUserRes struct {
|
||||
users.User
|
||||
created bool
|
||||
}
|
||||
|
||||
func (res createClientRes) Code() int {
|
||||
func (res createUserRes) Code() int {
|
||||
if res.created {
|
||||
return http.StatusCreated
|
||||
}
|
||||
@@ -49,7 +49,7 @@ func (res createClientRes) Code() int {
|
||||
return http.StatusOK
|
||||
}
|
||||
|
||||
func (res createClientRes) Headers() map[string]string {
|
||||
func (res createUserRes) Headers() map[string]string {
|
||||
if res.created {
|
||||
return map[string]string{
|
||||
"Location": fmt.Sprintf("/users/%s", res.ID),
|
||||
@@ -59,7 +59,7 @@ func (res createClientRes) Headers() map[string]string {
|
||||
return map[string]string{}
|
||||
}
|
||||
|
||||
func (res createClientRes) Empty() bool {
|
||||
func (res createUserRes) Empty() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
@@ -81,57 +81,57 @@ func (res tokenRes) Empty() bool {
|
||||
return res.AccessToken == "" || res.RefreshToken == ""
|
||||
}
|
||||
|
||||
type updateClientRes struct {
|
||||
mgclients.Client `json:",inline"`
|
||||
type updateUserRes struct {
|
||||
users.User `json:",inline"`
|
||||
}
|
||||
|
||||
func (res updateClientRes) Code() int {
|
||||
func (res updateUserRes) Code() int {
|
||||
return http.StatusOK
|
||||
}
|
||||
|
||||
func (res updateClientRes) Headers() map[string]string {
|
||||
func (res updateUserRes) Headers() map[string]string {
|
||||
return map[string]string{}
|
||||
}
|
||||
|
||||
func (res updateClientRes) Empty() bool {
|
||||
func (res updateUserRes) Empty() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
type viewClientRes struct {
|
||||
mgclients.Client `json:",inline"`
|
||||
type viewUserRes struct {
|
||||
users.User `json:",inline"`
|
||||
}
|
||||
|
||||
func (res viewClientRes) Code() int {
|
||||
func (res viewUserRes) Code() int {
|
||||
return http.StatusOK
|
||||
}
|
||||
|
||||
func (res viewClientRes) Headers() map[string]string {
|
||||
func (res viewUserRes) Headers() map[string]string {
|
||||
return map[string]string{}
|
||||
}
|
||||
|
||||
func (res viewClientRes) Empty() bool {
|
||||
func (res viewUserRes) Empty() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
type clientsPageRes struct {
|
||||
type usersPageRes struct {
|
||||
pageRes
|
||||
Clients []viewClientRes `json:"users"`
|
||||
Users []viewUserRes `json:"users"`
|
||||
}
|
||||
|
||||
func (res clientsPageRes) Code() int {
|
||||
func (res usersPageRes) Code() int {
|
||||
return http.StatusOK
|
||||
}
|
||||
|
||||
func (res clientsPageRes) Headers() map[string]string {
|
||||
func (res usersPageRes) Headers() map[string]string {
|
||||
return map[string]string{}
|
||||
}
|
||||
|
||||
func (res clientsPageRes) Empty() bool {
|
||||
func (res usersPageRes) Empty() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
type viewMembersRes struct {
|
||||
mgclients.Client `json:",inline"`
|
||||
users.User `json:",inline"`
|
||||
}
|
||||
|
||||
func (res viewMembersRes) Code() int {
|
||||
@@ -146,19 +146,19 @@ func (res viewMembersRes) Empty() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
type changeClientStatusClientRes struct {
|
||||
mgclients.Client `json:",inline"`
|
||||
type changeUserStatusRes struct {
|
||||
users.User `json:",inline"`
|
||||
}
|
||||
|
||||
func (res changeClientStatusClientRes) Code() int {
|
||||
func (res changeUserStatusRes) Code() int {
|
||||
return http.StatusOK
|
||||
}
|
||||
|
||||
func (res changeClientStatusClientRes) Headers() map[string]string {
|
||||
func (res changeUserStatusRes) Headers() map[string]string {
|
||||
return map[string]string{}
|
||||
}
|
||||
|
||||
func (res changeClientStatusClientRes) Empty() bool {
|
||||
func (res changeUserStatusRes) Empty() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
@@ -220,11 +220,11 @@ func (res unassignUsersRes) Empty() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
type deleteClientRes struct {
|
||||
type deleteUserRes struct {
|
||||
deleted bool
|
||||
}
|
||||
|
||||
func (res deleteClientRes) Code() int {
|
||||
func (res deleteUserRes) Code() int {
|
||||
if res.deleted {
|
||||
return http.StatusNoContent
|
||||
}
|
||||
@@ -232,10 +232,10 @@ func (res deleteClientRes) Code() int {
|
||||
return http.StatusOK
|
||||
}
|
||||
|
||||
func (res deleteClientRes) Headers() map[string]string {
|
||||
func (res deleteUserRes) Headers() map[string]string {
|
||||
return map[string]string{}
|
||||
}
|
||||
|
||||
func (res deleteClientRes) Empty() bool {
|
||||
func (res deleteUserRes) Empty() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
@@ -19,7 +19,7 @@ import (
|
||||
|
||||
// MakeHandler returns a HTTP handler for Users and Groups API endpoints.
|
||||
func MakeHandler(cls users.Service, authn mgauthn.Authentication, tokenClient magistrala.TokenServiceClient, selfRegister bool, grps groups.Service, mux *chi.Mux, logger *slog.Logger, instanceID string, pr *regexp.Regexp, providers ...oauth2.Provider) http.Handler {
|
||||
clientsHandler(cls, authn, tokenClient, selfRegister, mux, logger, pr, providers...)
|
||||
usersHandler(cls, authn, tokenClient, selfRegister, mux, logger, pr, providers...)
|
||||
groupsHandler(grps, authn, mux, logger)
|
||||
|
||||
mux.Get("/health", magistrala.Health("users", instanceID))
|
||||
|
||||
@@ -16,7 +16,6 @@ import (
|
||||
"github.com/absmach/magistrala/internal/api"
|
||||
"github.com/absmach/magistrala/pkg/apiutil"
|
||||
mgauthn "github.com/absmach/magistrala/pkg/authn"
|
||||
mgclients "github.com/absmach/magistrala/pkg/clients"
|
||||
"github.com/absmach/magistrala/pkg/errors"
|
||||
"github.com/absmach/magistrala/pkg/oauth2"
|
||||
"github.com/absmach/magistrala/pkg/policies"
|
||||
@@ -28,8 +27,8 @@ import (
|
||||
|
||||
var passRegex = regexp.MustCompile("^.{8,}$")
|
||||
|
||||
// MakeHandler returns a HTTP handler for API endpoints.
|
||||
func clientsHandler(svc users.Service, authn mgauthn.Authentication, tokenClient magistrala.TokenServiceClient, selfRegister bool, r *chi.Mux, logger *slog.Logger, pr *regexp.Regexp, providers ...oauth2.Provider) http.Handler {
|
||||
// usersHandler returns a HTTP handler for API endpoints.
|
||||
func usersHandler(svc users.Service, authn mgauthn.Authentication, tokenClient magistrala.TokenServiceClient, selfRegister bool, r *chi.Mux, logger *slog.Logger, pr *regexp.Regexp, providers ...oauth2.Provider) http.Handler {
|
||||
passRegex = pr
|
||||
|
||||
opts := []kithttp.ServerOption{
|
||||
@@ -41,17 +40,17 @@ func clientsHandler(svc users.Service, authn mgauthn.Authentication, tokenClient
|
||||
case true:
|
||||
r.Post("/", otelhttp.NewHandler(kithttp.NewServer(
|
||||
registrationEndpoint(svc, selfRegister),
|
||||
decodeCreateClientReq,
|
||||
decodeCreateUserReq,
|
||||
api.EncodeResponse,
|
||||
opts...,
|
||||
), "register_client").ServeHTTP)
|
||||
), "register_user").ServeHTTP)
|
||||
default:
|
||||
r.With(api.AuthenticateMiddleware(authn, false)).Post("/", otelhttp.NewHandler(kithttp.NewServer(
|
||||
registrationEndpoint(svc, selfRegister),
|
||||
decodeCreateClientReq,
|
||||
decodeCreateUserReq,
|
||||
api.EncodeResponse,
|
||||
opts...,
|
||||
), "register_client").ServeHTTP)
|
||||
), "register_user").ServeHTTP)
|
||||
}
|
||||
|
||||
r.Group(func(r chi.Router) {
|
||||
@@ -65,88 +64,95 @@ func clientsHandler(svc users.Service, authn mgauthn.Authentication, tokenClient
|
||||
), "view_profile").ServeHTTP)
|
||||
|
||||
r.Get("/{id}", otelhttp.NewHandler(kithttp.NewServer(
|
||||
viewClientEndpoint(svc),
|
||||
decodeViewClient,
|
||||
viewEndpoint(svc),
|
||||
decodeViewUser,
|
||||
api.EncodeResponse,
|
||||
opts...,
|
||||
), "view_client").ServeHTTP)
|
||||
), "view_user").ServeHTTP)
|
||||
|
||||
r.Get("/", otelhttp.NewHandler(kithttp.NewServer(
|
||||
listClientsEndpoint(svc),
|
||||
decodeListClients,
|
||||
listUsersEndpoint(svc),
|
||||
decodeListUsers,
|
||||
api.EncodeResponse,
|
||||
opts...,
|
||||
), "list_clients").ServeHTTP)
|
||||
), "list_users").ServeHTTP)
|
||||
|
||||
r.Get("/search", otelhttp.NewHandler(kithttp.NewServer(
|
||||
searchClientsEndpoint(svc),
|
||||
decodeSearchClients,
|
||||
searchUsersEndpoint(svc),
|
||||
decodeSearchUsers,
|
||||
api.EncodeResponse,
|
||||
opts...,
|
||||
), "search_clients").ServeHTTP)
|
||||
), "search_users").ServeHTTP)
|
||||
|
||||
r.Patch("/secret", otelhttp.NewHandler(kithttp.NewServer(
|
||||
updateClientSecretEndpoint(svc),
|
||||
decodeUpdateClientSecret,
|
||||
updateSecretEndpoint(svc),
|
||||
decodeUpdateUserSecret,
|
||||
api.EncodeResponse,
|
||||
opts...,
|
||||
), "update_client_secret").ServeHTTP)
|
||||
), "update_user_secret").ServeHTTP)
|
||||
|
||||
r.Patch("/{id}", otelhttp.NewHandler(kithttp.NewServer(
|
||||
updateClientEndpoint(svc),
|
||||
decodeUpdateClient,
|
||||
updateEndpoint(svc),
|
||||
decodeUpdateUser,
|
||||
api.EncodeResponse,
|
||||
opts...,
|
||||
), "update_client").ServeHTTP)
|
||||
), "update_user").ServeHTTP)
|
||||
|
||||
r.Patch("/{id}/username", otelhttp.NewHandler(kithttp.NewServer(
|
||||
updateUsernameEndpoint(svc),
|
||||
decodeUpdateUsername,
|
||||
api.EncodeResponse,
|
||||
opts...,
|
||||
), "update_username").ServeHTTP)
|
||||
|
||||
r.Patch("/{id}/picture", otelhttp.NewHandler(kithttp.NewServer(
|
||||
updateProfilePictureEndpoint(svc),
|
||||
decodeUpdateUserProfilePicture,
|
||||
api.EncodeResponse,
|
||||
opts...,
|
||||
), "update_profile_picture").ServeHTTP)
|
||||
|
||||
r.Patch("/{id}/tags", otelhttp.NewHandler(kithttp.NewServer(
|
||||
updateClientTagsEndpoint(svc),
|
||||
decodeUpdateClientTags,
|
||||
updateTagsEndpoint(svc),
|
||||
decodeUpdateUserTags,
|
||||
api.EncodeResponse,
|
||||
opts...,
|
||||
), "update_client_tags").ServeHTTP)
|
||||
), "update_user_tags").ServeHTTP)
|
||||
|
||||
r.Patch("/{id}/identity", otelhttp.NewHandler(kithttp.NewServer(
|
||||
updateClientIdentityEndpoint(svc),
|
||||
decodeUpdateClientIdentity,
|
||||
r.Patch("/{id}/email", otelhttp.NewHandler(kithttp.NewServer(
|
||||
updateEmailEndpoint(svc),
|
||||
decodeUpdateUserEmail,
|
||||
api.EncodeResponse,
|
||||
opts...,
|
||||
), "update_client_identity").ServeHTTP)
|
||||
), "update_user_email").ServeHTTP)
|
||||
|
||||
r.Patch("/{id}/role", otelhttp.NewHandler(kithttp.NewServer(
|
||||
updateClientRoleEndpoint(svc),
|
||||
decodeUpdateClientRole,
|
||||
updateRoleEndpoint(svc),
|
||||
decodeUpdateUserRole,
|
||||
api.EncodeResponse,
|
||||
opts...,
|
||||
), "update_client_role").ServeHTTP)
|
||||
|
||||
r.Patch("/{id}/role", otelhttp.NewHandler(kithttp.NewServer(
|
||||
updateClientRoleEndpoint(svc),
|
||||
decodeUpdateClientRole,
|
||||
api.EncodeResponse,
|
||||
opts...,
|
||||
), "update_client_role").ServeHTTP)
|
||||
), "update_user_role").ServeHTTP)
|
||||
|
||||
r.Post("/{id}/enable", otelhttp.NewHandler(kithttp.NewServer(
|
||||
enableClientEndpoint(svc),
|
||||
decodeChangeClientStatus,
|
||||
enableEndpoint(svc),
|
||||
decodeChangeUserStatus,
|
||||
api.EncodeResponse,
|
||||
opts...,
|
||||
), "enable_client").ServeHTTP)
|
||||
), "enable_user").ServeHTTP)
|
||||
|
||||
r.Post("/{id}/disable", otelhttp.NewHandler(kithttp.NewServer(
|
||||
disableClientEndpoint(svc),
|
||||
decodeChangeClientStatus,
|
||||
disableEndpoint(svc),
|
||||
decodeChangeUserStatus,
|
||||
api.EncodeResponse,
|
||||
opts...,
|
||||
), "disable_client").ServeHTTP)
|
||||
), "disable_user").ServeHTTP)
|
||||
|
||||
r.Delete("/{id}", otelhttp.NewHandler(kithttp.NewServer(
|
||||
deleteClientEndpoint(svc),
|
||||
decodeChangeClientStatus,
|
||||
deleteEndpoint(svc),
|
||||
decodeChangeUserStatus,
|
||||
api.EncodeResponse,
|
||||
opts...,
|
||||
), "delete_client").ServeHTTP)
|
||||
), "delete_user").ServeHTTP)
|
||||
|
||||
r.Post("/tokens/refresh", otelhttp.NewHandler(kithttp.NewServer(
|
||||
refreshTokenEndpoint(svc),
|
||||
@@ -230,8 +236,8 @@ func clientsHandler(svc users.Service, authn mgauthn.Authentication, tokenClient
|
||||
return r
|
||||
}
|
||||
|
||||
func decodeViewClient(_ context.Context, r *http.Request) (interface{}, error) {
|
||||
req := viewClientReq{
|
||||
func decodeViewUser(_ context.Context, r *http.Request) (interface{}, error) {
|
||||
req := viewUserReq{
|
||||
id: chi.URLParam(r, "id"),
|
||||
}
|
||||
|
||||
@@ -242,7 +248,7 @@ func decodeViewProfile(_ context.Context, r *http.Request) (interface{}, error)
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func decodeListClients(_ context.Context, r *http.Request) (interface{}, error) {
|
||||
func decodeListUsers(_ context.Context, r *http.Request) (interface{}, error) {
|
||||
s, err := apiutil.ReadStringQuery(r, api.StatusKey, api.DefClientStatus)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(apiutil.ErrValidation, err)
|
||||
@@ -259,11 +265,19 @@ func decodeListClients(_ context.Context, r *http.Request) (interface{}, error)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(apiutil.ErrValidation, err)
|
||||
}
|
||||
n, err := apiutil.ReadStringQuery(r, api.NameKey, "")
|
||||
n, err := apiutil.ReadStringQuery(r, api.UsernameKey, "")
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(apiutil.ErrValidation, err)
|
||||
}
|
||||
i, err := apiutil.ReadStringQuery(r, api.IdentityKey, "")
|
||||
d, err := apiutil.ReadStringQuery(r, api.EmailKey, "")
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(apiutil.ErrValidation, err)
|
||||
}
|
||||
i, err := apiutil.ReadStringQuery(r, api.FirstNameKey, "")
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(apiutil.ErrValidation, err)
|
||||
}
|
||||
f, err := apiutil.ReadStringQuery(r, api.LastNameKey, "")
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(apiutil.ErrValidation, err)
|
||||
}
|
||||
@@ -284,27 +298,29 @@ func decodeListClients(_ context.Context, r *http.Request) (interface{}, error)
|
||||
return nil, errors.Wrap(apiutil.ErrValidation, err)
|
||||
}
|
||||
|
||||
st, err := mgclients.ToStatus(s)
|
||||
st, err := users.ToStatus(s)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(apiutil.ErrValidation, err)
|
||||
}
|
||||
req := listClientsReq{
|
||||
status: st,
|
||||
offset: o,
|
||||
limit: l,
|
||||
metadata: m,
|
||||
name: n,
|
||||
identity: i,
|
||||
tag: t,
|
||||
order: order,
|
||||
dir: dir,
|
||||
id: id,
|
||||
req := listUsersReq{
|
||||
status: st,
|
||||
offset: o,
|
||||
limit: l,
|
||||
metadata: m,
|
||||
userName: n,
|
||||
firstName: i,
|
||||
lastName: f,
|
||||
tag: t,
|
||||
order: order,
|
||||
dir: dir,
|
||||
id: id,
|
||||
email: d,
|
||||
}
|
||||
|
||||
return req, nil
|
||||
}
|
||||
|
||||
func decodeSearchClients(_ context.Context, r *http.Request) (interface{}, error) {
|
||||
func decodeSearchUsers(_ context.Context, r *http.Request) (interface{}, error) {
|
||||
o, err := apiutil.ReadNumQuery[uint64](r, api.OffsetKey, api.DefOffset)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(apiutil.ErrValidation, err)
|
||||
@@ -313,7 +329,15 @@ func decodeSearchClients(_ context.Context, r *http.Request) (interface{}, error
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(apiutil.ErrValidation, err)
|
||||
}
|
||||
n, err := apiutil.ReadStringQuery(r, api.NameKey, "")
|
||||
n, err := apiutil.ReadStringQuery(r, api.UsernameKey, "")
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(apiutil.ErrValidation, err)
|
||||
}
|
||||
f, err := apiutil.ReadStringQuery(r, api.FirstNameKey, "")
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(apiutil.ErrValidation, err)
|
||||
}
|
||||
e, err := apiutil.ReadStringQuery(r, api.LastNameKey, "")
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(apiutil.ErrValidation, err)
|
||||
}
|
||||
@@ -330,18 +354,20 @@ func decodeSearchClients(_ context.Context, r *http.Request) (interface{}, error
|
||||
return nil, errors.Wrap(apiutil.ErrValidation, err)
|
||||
}
|
||||
|
||||
req := searchClientsReq{
|
||||
Offset: o,
|
||||
Limit: l,
|
||||
Name: n,
|
||||
Id: id,
|
||||
Order: order,
|
||||
Dir: dir,
|
||||
req := searchUsersReq{
|
||||
Offset: o,
|
||||
Limit: l,
|
||||
Username: n,
|
||||
FirstName: f,
|
||||
LastName: e,
|
||||
Id: id,
|
||||
Order: order,
|
||||
Dir: dir,
|
||||
}
|
||||
|
||||
for _, field := range []string{req.Name, req.Id} {
|
||||
for _, field := range []string{req.Username, req.Id} {
|
||||
if field != "" && len(field) < 3 {
|
||||
req = searchClientsReq{}
|
||||
req = searchUsersReq{}
|
||||
return req, errors.Wrap(apiutil.ErrLenSearchQuery, apiutil.ErrValidation)
|
||||
}
|
||||
}
|
||||
@@ -349,12 +375,12 @@ func decodeSearchClients(_ context.Context, r *http.Request) (interface{}, error
|
||||
return req, nil
|
||||
}
|
||||
|
||||
func decodeUpdateClient(_ context.Context, r *http.Request) (interface{}, error) {
|
||||
func decodeUpdateUser(_ context.Context, r *http.Request) (interface{}, error) {
|
||||
if !strings.Contains(r.Header.Get("Content-Type"), api.ContentType) {
|
||||
return nil, errors.Wrap(apiutil.ErrValidation, apiutil.ErrUnsupportedContentType)
|
||||
}
|
||||
|
||||
req := updateClientReq{
|
||||
req := updateUserReq{
|
||||
id: chi.URLParam(r, "id"),
|
||||
}
|
||||
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
|
||||
@@ -364,12 +390,12 @@ func decodeUpdateClient(_ context.Context, r *http.Request) (interface{}, error)
|
||||
return req, nil
|
||||
}
|
||||
|
||||
func decodeUpdateClientTags(_ context.Context, r *http.Request) (interface{}, error) {
|
||||
func decodeUpdateUserTags(_ context.Context, r *http.Request) (interface{}, error) {
|
||||
if !strings.Contains(r.Header.Get("Content-Type"), api.ContentType) {
|
||||
return nil, errors.Wrap(apiutil.ErrValidation, apiutil.ErrUnsupportedContentType)
|
||||
}
|
||||
|
||||
req := updateClientTagsReq{
|
||||
req := updateUserTagsReq{
|
||||
id: chi.URLParam(r, "id"),
|
||||
}
|
||||
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
|
||||
@@ -379,12 +405,12 @@ func decodeUpdateClientTags(_ context.Context, r *http.Request) (interface{}, er
|
||||
return req, nil
|
||||
}
|
||||
|
||||
func decodeUpdateClientIdentity(_ context.Context, r *http.Request) (interface{}, error) {
|
||||
func decodeUpdateUserEmail(_ context.Context, r *http.Request) (interface{}, error) {
|
||||
if !strings.Contains(r.Header.Get("Content-Type"), api.ContentType) {
|
||||
return nil, errors.Wrap(apiutil.ErrValidation, apiutil.ErrUnsupportedContentType)
|
||||
}
|
||||
|
||||
req := updateClientIdentityReq{
|
||||
req := updateEmailReq{
|
||||
id: chi.URLParam(r, "id"),
|
||||
}
|
||||
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
|
||||
@@ -394,12 +420,43 @@ func decodeUpdateClientIdentity(_ context.Context, r *http.Request) (interface{}
|
||||
return req, nil
|
||||
}
|
||||
|
||||
func decodeUpdateClientSecret(_ context.Context, r *http.Request) (interface{}, error) {
|
||||
func decodeUpdateUserSecret(_ context.Context, r *http.Request) (interface{}, error) {
|
||||
if !strings.Contains(r.Header.Get("Content-Type"), api.ContentType) {
|
||||
return nil, errors.Wrap(apiutil.ErrValidation, apiutil.ErrUnsupportedContentType)
|
||||
}
|
||||
|
||||
req := updateClientSecretReq{}
|
||||
req := updateUserSecretReq{}
|
||||
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
|
||||
return nil, errors.Wrap(apiutil.ErrValidation, errors.Wrap(err, errors.ErrMalformedEntity))
|
||||
}
|
||||
|
||||
return req, nil
|
||||
}
|
||||
|
||||
func decodeUpdateUsername(_ context.Context, r *http.Request) (interface{}, error) {
|
||||
if !strings.Contains(r.Header.Get("Content-Type"), api.ContentType) {
|
||||
return nil, errors.Wrap(apiutil.ErrValidation, apiutil.ErrUnsupportedContentType)
|
||||
}
|
||||
|
||||
req := updateUsernameReq{
|
||||
id: chi.URLParam(r, "id"),
|
||||
}
|
||||
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
|
||||
return nil, errors.Wrap(apiutil.ErrValidation, errors.Wrap(err, errors.ErrMalformedEntity))
|
||||
}
|
||||
|
||||
return req, nil
|
||||
}
|
||||
|
||||
func decodeUpdateUserProfilePicture(_ context.Context, r *http.Request) (interface{}, error) {
|
||||
if !strings.Contains(r.Header.Get("Content-Type"), api.ContentType) {
|
||||
return nil, errors.Wrap(apiutil.ErrValidation, apiutil.ErrUnsupportedContentType)
|
||||
}
|
||||
|
||||
req := updateProfilePictureReq{
|
||||
id: chi.URLParam(r, "id"),
|
||||
}
|
||||
|
||||
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
|
||||
return nil, errors.Wrap(apiutil.ErrValidation, errors.Wrap(err, errors.ErrMalformedEntity))
|
||||
}
|
||||
@@ -434,19 +491,19 @@ func decodePasswordReset(_ context.Context, r *http.Request) (interface{}, error
|
||||
return req, nil
|
||||
}
|
||||
|
||||
func decodeUpdateClientRole(_ context.Context, r *http.Request) (interface{}, error) {
|
||||
func decodeUpdateUserRole(_ context.Context, r *http.Request) (interface{}, error) {
|
||||
if !strings.Contains(r.Header.Get("Content-Type"), api.ContentType) {
|
||||
return nil, errors.Wrap(apiutil.ErrValidation, apiutil.ErrUnsupportedContentType)
|
||||
}
|
||||
|
||||
req := updateClientRoleReq{
|
||||
req := updateUserRoleReq{
|
||||
id: chi.URLParam(r, "id"),
|
||||
}
|
||||
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
|
||||
return nil, errors.Wrap(apiutil.ErrValidation, errors.Wrap(err, errors.ErrMalformedEntity))
|
||||
}
|
||||
var err error
|
||||
req.role, err = mgclients.ToRole(req.Role)
|
||||
req.role, err = users.ToRole(req.Role)
|
||||
return req, err
|
||||
}
|
||||
|
||||
@@ -455,7 +512,7 @@ func decodeCredentials(_ context.Context, r *http.Request) (interface{}, error)
|
||||
return nil, errors.Wrap(apiutil.ErrValidation, apiutil.ErrUnsupportedContentType)
|
||||
}
|
||||
|
||||
req := loginClientReq{}
|
||||
req := loginUserReq{}
|
||||
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
|
||||
return nil, errors.Wrap(apiutil.ErrValidation, errors.Wrap(err, errors.ErrMalformedEntity))
|
||||
}
|
||||
@@ -472,24 +529,21 @@ func decodeRefreshToken(_ context.Context, r *http.Request) (interface{}, error)
|
||||
return req, nil
|
||||
}
|
||||
|
||||
func decodeCreateClientReq(_ context.Context, r *http.Request) (interface{}, error) {
|
||||
func decodeCreateUserReq(_ context.Context, r *http.Request) (interface{}, error) {
|
||||
if !strings.Contains(r.Header.Get("Content-Type"), api.ContentType) {
|
||||
return nil, errors.Wrap(apiutil.ErrValidation, apiutil.ErrUnsupportedContentType)
|
||||
}
|
||||
|
||||
var c mgclients.Client
|
||||
if err := json.NewDecoder(r.Body).Decode(&c); err != nil {
|
||||
var req createUserReq
|
||||
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
|
||||
return nil, errors.Wrap(apiutil.ErrValidation, errors.Wrap(err, errors.ErrMalformedEntity))
|
||||
}
|
||||
req := createClientReq{
|
||||
client: c,
|
||||
}
|
||||
|
||||
return req, nil
|
||||
}
|
||||
|
||||
func decodeChangeClientStatus(_ context.Context, r *http.Request) (interface{}, error) {
|
||||
req := changeClientStatusReq{
|
||||
func decodeChangeUserStatus(_ context.Context, r *http.Request) (interface{}, error) {
|
||||
req := changeUserStatusReq{
|
||||
id: chi.URLParam(r, "id"),
|
||||
}
|
||||
|
||||
@@ -549,54 +603,64 @@ func decodeListMembersByDomain(_ context.Context, r *http.Request) (interface{},
|
||||
return req, nil
|
||||
}
|
||||
|
||||
func queryPageParams(r *http.Request, defPermission string) (mgclients.Page, error) {
|
||||
func queryPageParams(r *http.Request, defPermission string) (users.Page, error) {
|
||||
s, err := apiutil.ReadStringQuery(r, api.StatusKey, api.DefClientStatus)
|
||||
if err != nil {
|
||||
return mgclients.Page{}, errors.Wrap(apiutil.ErrValidation, err)
|
||||
return users.Page{}, errors.Wrap(apiutil.ErrValidation, err)
|
||||
}
|
||||
o, err := apiutil.ReadNumQuery[uint64](r, api.OffsetKey, api.DefOffset)
|
||||
if err != nil {
|
||||
return mgclients.Page{}, errors.Wrap(apiutil.ErrValidation, err)
|
||||
return users.Page{}, errors.Wrap(apiutil.ErrValidation, err)
|
||||
}
|
||||
l, err := apiutil.ReadNumQuery[uint64](r, api.LimitKey, api.DefLimit)
|
||||
if err != nil {
|
||||
return mgclients.Page{}, errors.Wrap(apiutil.ErrValidation, err)
|
||||
return users.Page{}, errors.Wrap(apiutil.ErrValidation, err)
|
||||
}
|
||||
m, err := apiutil.ReadMetadataQuery(r, api.MetadataKey, nil)
|
||||
if err != nil {
|
||||
return mgclients.Page{}, errors.Wrap(apiutil.ErrValidation, err)
|
||||
return users.Page{}, errors.Wrap(apiutil.ErrValidation, err)
|
||||
}
|
||||
n, err := apiutil.ReadStringQuery(r, api.NameKey, "")
|
||||
n, err := apiutil.ReadStringQuery(r, api.UsernameKey, "")
|
||||
if err != nil {
|
||||
return mgclients.Page{}, errors.Wrap(apiutil.ErrValidation, err)
|
||||
return users.Page{}, errors.Wrap(apiutil.ErrValidation, err)
|
||||
}
|
||||
i, err := apiutil.ReadStringQuery(r, api.IdentityKey, "")
|
||||
f, err := apiutil.ReadStringQuery(r, api.FirstNameKey, "")
|
||||
if err != nil {
|
||||
return mgclients.Page{}, errors.Wrap(apiutil.ErrValidation, err)
|
||||
return users.Page{}, errors.Wrap(apiutil.ErrValidation, err)
|
||||
}
|
||||
a, err := apiutil.ReadStringQuery(r, api.LastNameKey, "")
|
||||
if err != nil {
|
||||
return users.Page{}, errors.Wrap(apiutil.ErrValidation, err)
|
||||
}
|
||||
i, err := apiutil.ReadStringQuery(r, api.EmailKey, "")
|
||||
if err != nil {
|
||||
return users.Page{}, errors.Wrap(apiutil.ErrValidation, err)
|
||||
}
|
||||
t, err := apiutil.ReadStringQuery(r, api.TagKey, "")
|
||||
if err != nil {
|
||||
return mgclients.Page{}, errors.Wrap(apiutil.ErrValidation, err)
|
||||
return users.Page{}, errors.Wrap(apiutil.ErrValidation, err)
|
||||
}
|
||||
st, err := mgclients.ToStatus(s)
|
||||
st, err := users.ToStatus(s)
|
||||
if err != nil {
|
||||
return mgclients.Page{}, errors.Wrap(apiutil.ErrValidation, err)
|
||||
return users.Page{}, errors.Wrap(apiutil.ErrValidation, err)
|
||||
}
|
||||
p, err := apiutil.ReadStringQuery(r, api.PermissionKey, defPermission)
|
||||
if err != nil {
|
||||
return mgclients.Page{}, errors.Wrap(apiutil.ErrValidation, err)
|
||||
return users.Page{}, errors.Wrap(apiutil.ErrValidation, err)
|
||||
}
|
||||
lp, err := apiutil.ReadBoolQuery(r, api.ListPerms, api.DefListPerms)
|
||||
if err != nil {
|
||||
return mgclients.Page{}, errors.Wrap(apiutil.ErrValidation, err)
|
||||
return users.Page{}, errors.Wrap(apiutil.ErrValidation, err)
|
||||
}
|
||||
return mgclients.Page{
|
||||
return users.Page{
|
||||
Status: st,
|
||||
Offset: o,
|
||||
Limit: l,
|
||||
Metadata: m,
|
||||
Identity: i,
|
||||
Name: n,
|
||||
FirstName: f,
|
||||
Username: n,
|
||||
LastName: a,
|
||||
Email: i,
|
||||
Tag: t,
|
||||
Permission: p,
|
||||
ListPerms: lp,
|
||||
@@ -623,24 +687,24 @@ func oauth2CallbackHandler(oauth oauth2.Provider, svc users.Service, tokenClient
|
||||
return
|
||||
}
|
||||
|
||||
client, err := oauth.UserInfo(token.AccessToken)
|
||||
user, err := oauth.UserInfo(token.AccessToken)
|
||||
if err != nil {
|
||||
http.Redirect(w, r, oauth.ErrorURL()+"?error="+err.Error(), http.StatusSeeOther)
|
||||
return
|
||||
}
|
||||
|
||||
client, err = svc.OAuthCallback(r.Context(), client)
|
||||
user, err = svc.OAuthCallback(r.Context(), user)
|
||||
if err != nil {
|
||||
http.Redirect(w, r, oauth.ErrorURL()+"?error="+err.Error(), http.StatusSeeOther)
|
||||
return
|
||||
}
|
||||
if err := svc.OAuthAddClientPolicy(r.Context(), client); err != nil {
|
||||
if err := svc.OAuthAddUserPolicy(r.Context(), user); err != nil {
|
||||
http.Redirect(w, r, oauth.ErrorURL()+"?error="+err.Error(), http.StatusSeeOther)
|
||||
return
|
||||
}
|
||||
|
||||
jwt, err := tokenClient.Issue(r.Context(), &magistrala.IssueReq{
|
||||
UserId: client.ID,
|
||||
UserId: user.ID,
|
||||
Type: uint32(mgauth.AccessKey),
|
||||
})
|
||||
if err != nil {
|
||||
@@ -1,90 +0,0 @@
|
||||
// Copyright (c) Abstract Machines
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package users
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/absmach/magistrala"
|
||||
"github.com/absmach/magistrala/pkg/authn"
|
||||
"github.com/absmach/magistrala/pkg/clients"
|
||||
)
|
||||
|
||||
// Service specifies an API that must be fullfiled by the domain service
|
||||
// implementation, and all of its decorators (e.g. logging & metrics).
|
||||
//
|
||||
//go:generate mockery --name Service --output=./mocks --filename service.go --quiet --note "Copyright (c) Abstract Machines"
|
||||
type Service interface {
|
||||
// RegisterClient creates new client. In case of the failed registration, a
|
||||
// non-nil error value is returned.
|
||||
RegisterClient(ctx context.Context, session authn.Session, client clients.Client, selfRegister bool) (clients.Client, error)
|
||||
|
||||
// ViewClient retrieves client info for a given client ID and an authorized token.
|
||||
ViewClient(ctx context.Context, session authn.Session, id string) (clients.Client, error)
|
||||
|
||||
// ViewProfile retrieves client info for a given token.
|
||||
ViewProfile(ctx context.Context, session authn.Session) (clients.Client, error)
|
||||
|
||||
// ListClients retrieves clients list for a valid auth token.
|
||||
ListClients(ctx context.Context, session authn.Session, pm clients.Page) (clients.ClientsPage, error)
|
||||
|
||||
// ListMembers retrieves everything that is assigned to a group/thing identified by objectID.
|
||||
ListMembers(ctx context.Context, session authn.Session, objectKind, objectID string, pm clients.Page) (clients.MembersPage, error)
|
||||
|
||||
// SearchClients searches for users with provided filters for a valid auth token.
|
||||
SearchUsers(ctx context.Context, pm clients.Page) (clients.ClientsPage, error)
|
||||
|
||||
// UpdateClient updates the client's name and metadata.
|
||||
UpdateClient(ctx context.Context, session authn.Session, client clients.Client) (clients.Client, error)
|
||||
|
||||
// UpdateClientTags updates the client's tags.
|
||||
UpdateClientTags(ctx context.Context, session authn.Session, client clients.Client) (clients.Client, error)
|
||||
|
||||
// UpdateClientIdentity updates the client's identity.
|
||||
UpdateClientIdentity(ctx context.Context, session authn.Session, id, identity string) (clients.Client, error)
|
||||
|
||||
// GenerateResetToken email where mail will be sent.
|
||||
// host is used for generating reset link.
|
||||
GenerateResetToken(ctx context.Context, email, host string) error
|
||||
|
||||
// UpdateClientSecret updates the client's secret.
|
||||
UpdateClientSecret(ctx context.Context, session authn.Session, oldSecret, newSecret string) (clients.Client, 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
|
||||
|
||||
// SendPasswordReset sends reset password link to email.
|
||||
SendPasswordReset(ctx context.Context, host, email, user, token string) error
|
||||
|
||||
// UpdateClientRole updates the client's Role.
|
||||
UpdateClientRole(ctx context.Context, session authn.Session, client clients.Client) (clients.Client, error)
|
||||
|
||||
// EnableClient logically enableds the client identified with the provided ID.
|
||||
EnableClient(ctx context.Context, session authn.Session, id string) (clients.Client, error)
|
||||
|
||||
// DisableClient logically disables the client identified with the provided ID.
|
||||
DisableClient(ctx context.Context, session authn.Session, id string) (clients.Client, error)
|
||||
|
||||
// DeleteClient deletes client with given ID.
|
||||
DeleteClient(ctx context.Context, session authn.Session, id string) error
|
||||
|
||||
// Identify returns the client id from the given token.
|
||||
Identify(ctx context.Context, session authn.Session) (string, error)
|
||||
|
||||
// IssueToken issues a new access and refresh token.
|
||||
IssueToken(ctx context.Context, identity, secret string) (*magistrala.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) (*magistrala.Token, 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, client clients.Client) (clients.Client, error)
|
||||
|
||||
// OAuthAddClientPolicy adds a policy to the client for an OAuth request.
|
||||
OAuthAddClientPolicy(ctx context.Context, client clients.Client) error
|
||||
}
|
||||
+9
-10
@@ -15,16 +15,14 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/absmach/magistrala"
|
||||
mgclients "github.com/absmach/magistrala/pkg/clients"
|
||||
svcerr "github.com/absmach/magistrala/pkg/errors/service"
|
||||
"github.com/absmach/magistrala/pkg/policies"
|
||||
"github.com/absmach/magistrala/users/postgres"
|
||||
)
|
||||
|
||||
const defLimit = uint64(100)
|
||||
|
||||
type handler struct {
|
||||
clients postgres.Repository
|
||||
users Repository
|
||||
domains magistrala.DomainsServiceClient
|
||||
policies policies.Service
|
||||
checkInterval time.Duration
|
||||
@@ -32,9 +30,9 @@ type handler struct {
|
||||
logger *slog.Logger
|
||||
}
|
||||
|
||||
func NewDeleteHandler(ctx context.Context, clients postgres.Repository, policyService policies.Service, domainsClient magistrala.DomainsServiceClient, defCheckInterval, deleteAfter time.Duration, logger *slog.Logger) {
|
||||
func NewDeleteHandler(ctx context.Context, users Repository, policyService policies.Service, domainsClient magistrala.DomainsServiceClient, defCheckInterval, deleteAfter time.Duration, logger *slog.Logger) {
|
||||
handler := &handler{
|
||||
clients: clients,
|
||||
users: users,
|
||||
domains: domainsClient,
|
||||
policies: policyService,
|
||||
checkInterval: defCheckInterval,
|
||||
@@ -58,10 +56,10 @@ func NewDeleteHandler(ctx context.Context, clients postgres.Repository, policySe
|
||||
}
|
||||
|
||||
func (h *handler) handle(ctx context.Context) {
|
||||
pm := mgclients.Page{Limit: defLimit, Offset: 0, Status: mgclients.DeletedStatus}
|
||||
pm := Page{Limit: defLimit, Offset: 0, Status: DeletedStatus}
|
||||
|
||||
for {
|
||||
dbUsers, err := h.clients.RetrieveAll(ctx, pm)
|
||||
dbUsers, err := h.users.RetrieveAll(ctx, pm)
|
||||
if err != nil {
|
||||
h.logger.Error("failed to retrieve users", slog.Any("error", err))
|
||||
break
|
||||
@@ -70,7 +68,7 @@ func (h *handler) handle(ctx context.Context) {
|
||||
break
|
||||
}
|
||||
|
||||
for _, u := range dbUsers.Clients {
|
||||
for _, u := range dbUsers.Users {
|
||||
if time.Since(u.UpdatedAt) < h.deleteAfter {
|
||||
continue
|
||||
}
|
||||
@@ -96,14 +94,15 @@ func (h *handler) handle(ctx context.Context) {
|
||||
continue
|
||||
}
|
||||
|
||||
if err := h.clients.Delete(ctx, u.ID); err != nil {
|
||||
if err := h.users.Delete(ctx, u.ID); err != nil {
|
||||
h.logger.Error("failed to delete user", slog.Any("error", err))
|
||||
continue
|
||||
}
|
||||
|
||||
h.logger.Info("user deleted", slog.Group("user",
|
||||
slog.String("id", u.ID),
|
||||
slog.String("name", u.Name),
|
||||
slog.String("first_name", u.FirstName),
|
||||
slog.String("last_name", u.LastName),
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
+225
-142
@@ -6,107 +6,121 @@ package events
|
||||
import (
|
||||
"time"
|
||||
|
||||
mgclients "github.com/absmach/magistrala/pkg/clients"
|
||||
"github.com/absmach/magistrala/pkg/events"
|
||||
"github.com/absmach/magistrala/users"
|
||||
)
|
||||
|
||||
const (
|
||||
clientPrefix = "user."
|
||||
clientCreate = clientPrefix + "create"
|
||||
clientUpdate = clientPrefix + "update"
|
||||
clientRemove = clientPrefix + "remove"
|
||||
clientView = clientPrefix + "view"
|
||||
profileView = clientPrefix + "view_profile"
|
||||
clientList = clientPrefix + "list"
|
||||
clientSearch = clientPrefix + "search"
|
||||
clientListByGroup = clientPrefix + "list_by_group"
|
||||
clientIdentify = clientPrefix + "identify"
|
||||
generateResetToken = clientPrefix + "generate_reset_token"
|
||||
issueToken = clientPrefix + "issue_token"
|
||||
refreshToken = clientPrefix + "refresh_token"
|
||||
resetSecret = clientPrefix + "reset_secret"
|
||||
sendPasswordReset = clientPrefix + "send_password_reset"
|
||||
oauthCallback = clientPrefix + "oauth_callback"
|
||||
deleteClient = clientPrefix + "delete"
|
||||
addClientPolicy = clientPrefix + "add_policy"
|
||||
userPrefix = "user."
|
||||
userCreate = userPrefix + "create"
|
||||
userUpdate = userPrefix + "update"
|
||||
userRemove = userPrefix + "remove"
|
||||
userView = userPrefix + "view"
|
||||
profileView = userPrefix + "view_profile"
|
||||
userList = userPrefix + "list"
|
||||
userSearch = userPrefix + "search"
|
||||
userListByGroup = userPrefix + "list_by_group"
|
||||
userIdentify = userPrefix + "identify"
|
||||
generateResetToken = userPrefix + "generate_reset_token"
|
||||
issueToken = userPrefix + "issue_token"
|
||||
refreshToken = userPrefix + "refresh_token"
|
||||
resetSecret = userPrefix + "reset_secret"
|
||||
sendPasswordReset = userPrefix + "send_password_reset"
|
||||
oauthCallback = userPrefix + "oauth_callback"
|
||||
addClientPolicy = userPrefix + "add_policy"
|
||||
deleteUser = userPrefix + "delete"
|
||||
userUpdateUsername = userPrefix + "update_username"
|
||||
userUpdateProfilePicture = userPrefix + "update_profile_picture"
|
||||
)
|
||||
|
||||
var (
|
||||
_ events.Event = (*createClientEvent)(nil)
|
||||
_ events.Event = (*updateClientEvent)(nil)
|
||||
_ events.Event = (*removeClientEvent)(nil)
|
||||
_ events.Event = (*viewClientEvent)(nil)
|
||||
_ events.Event = (*createUserEvent)(nil)
|
||||
_ events.Event = (*updateUserEvent)(nil)
|
||||
_ events.Event = (*updateProfilePictureEvent)(nil)
|
||||
_ events.Event = (*updateUsernameEvent)(nil)
|
||||
_ events.Event = (*removeUserEvent)(nil)
|
||||
_ events.Event = (*viewUserEvent)(nil)
|
||||
_ events.Event = (*viewProfileEvent)(nil)
|
||||
_ events.Event = (*listClientEvent)(nil)
|
||||
_ events.Event = (*listClientByGroupEvent)(nil)
|
||||
_ events.Event = (*searchClientEvent)(nil)
|
||||
_ events.Event = (*identifyClientEvent)(nil)
|
||||
_ events.Event = (*listUserEvent)(nil)
|
||||
_ events.Event = (*listUserByGroupEvent)(nil)
|
||||
_ events.Event = (*searchUserEvent)(nil)
|
||||
_ events.Event = (*identifyUserEvent)(nil)
|
||||
_ events.Event = (*generateResetTokenEvent)(nil)
|
||||
_ events.Event = (*issueTokenEvent)(nil)
|
||||
_ events.Event = (*refreshTokenEvent)(nil)
|
||||
_ events.Event = (*resetSecretEvent)(nil)
|
||||
_ events.Event = (*sendPasswordResetEvent)(nil)
|
||||
_ events.Event = (*oauthCallbackEvent)(nil)
|
||||
_ events.Event = (*deleteClientEvent)(nil)
|
||||
_ events.Event = (*deleteUserEvent)(nil)
|
||||
_ events.Event = (*addUserPolicyEvent)(nil)
|
||||
)
|
||||
|
||||
type createClientEvent struct {
|
||||
mgclients.Client
|
||||
type createUserEvent struct {
|
||||
users.User
|
||||
}
|
||||
|
||||
func (cce createClientEvent) Encode() (map[string]interface{}, error) {
|
||||
func (uce createUserEvent) Encode() (map[string]interface{}, error) {
|
||||
val := map[string]interface{}{
|
||||
"operation": clientCreate,
|
||||
"id": cce.ID,
|
||||
"status": cce.Status.String(),
|
||||
"created_at": cce.CreatedAt,
|
||||
"operation": userCreate,
|
||||
"id": uce.ID,
|
||||
"status": uce.Status.String(),
|
||||
"created_at": uce.CreatedAt,
|
||||
}
|
||||
|
||||
if cce.Name != "" {
|
||||
val["name"] = cce.Name
|
||||
if uce.FirstName != "" {
|
||||
val["first_name"] = uce.FirstName
|
||||
}
|
||||
if len(cce.Tags) > 0 {
|
||||
val["tags"] = cce.Tags
|
||||
if uce.LastName != "" {
|
||||
val["last_name"] = uce.LastName
|
||||
}
|
||||
if cce.Domain != "" {
|
||||
val["domain"] = cce.Domain
|
||||
if len(uce.Tags) > 0 {
|
||||
val["tags"] = uce.Tags
|
||||
}
|
||||
if cce.Metadata != nil {
|
||||
val["metadata"] = cce.Metadata
|
||||
if uce.Metadata != nil {
|
||||
val["metadata"] = uce.Metadata
|
||||
}
|
||||
if cce.Credentials.Identity != "" {
|
||||
val["identity"] = cce.Credentials.Identity
|
||||
if uce.Credentials.Username != "" {
|
||||
val["username"] = uce.Credentials.Username
|
||||
}
|
||||
if uce.Email != "" {
|
||||
val["email"] = uce.Email
|
||||
}
|
||||
|
||||
return val, nil
|
||||
}
|
||||
|
||||
type updateClientEvent struct {
|
||||
mgclients.Client
|
||||
type updateUserEvent struct {
|
||||
users.User
|
||||
operation string
|
||||
}
|
||||
|
||||
func (uce updateClientEvent) Encode() (map[string]interface{}, error) {
|
||||
func (uce updateUserEvent) Encode() (map[string]interface{}, error) {
|
||||
val := map[string]interface{}{
|
||||
"operation": clientUpdate,
|
||||
"operation": userUpdate,
|
||||
"updated_at": uce.UpdatedAt,
|
||||
"updated_by": uce.UpdatedBy,
|
||||
}
|
||||
if uce.operation != "" {
|
||||
val["operation"] = clientUpdate + "_" + uce.operation
|
||||
val["operation"] = userUpdate + "_" + uce.operation
|
||||
}
|
||||
|
||||
if uce.ID != "" {
|
||||
val["id"] = uce.ID
|
||||
}
|
||||
if uce.Name != "" {
|
||||
val["name"] = uce.Name
|
||||
if uce.FirstName != "" {
|
||||
val["first_name"] = uce.FirstName
|
||||
}
|
||||
if uce.LastName != "" {
|
||||
val["last_name"] = uce.LastName
|
||||
}
|
||||
if len(uce.Tags) > 0 {
|
||||
val["tags"] = uce.Tags
|
||||
}
|
||||
if uce.Credentials.Identity != "" {
|
||||
val["identity"] = uce.Credentials.Identity
|
||||
if uce.Credentials.Username != "" {
|
||||
val["username"] = uce.Credentials.Username
|
||||
}
|
||||
if uce.Email != "" {
|
||||
val["email"] = uce.Email
|
||||
}
|
||||
if uce.Metadata != nil {
|
||||
val["metadata"] = uce.Metadata
|
||||
@@ -121,16 +135,64 @@ func (uce updateClientEvent) Encode() (map[string]interface{}, error) {
|
||||
return val, nil
|
||||
}
|
||||
|
||||
type removeClientEvent struct {
|
||||
type updateUsernameEvent struct {
|
||||
users.User
|
||||
}
|
||||
|
||||
func (une updateUsernameEvent) Encode() (map[string]interface{}, error) {
|
||||
val := map[string]interface{}{
|
||||
"operation": userUpdateUsername,
|
||||
"updated_at": une.UpdatedAt,
|
||||
"updated_by": une.UpdatedBy,
|
||||
}
|
||||
|
||||
if une.ID != "" {
|
||||
val["id"] = une.ID
|
||||
}
|
||||
if une.FirstName != "" {
|
||||
val["first_name"] = une.FirstName
|
||||
}
|
||||
if une.LastName != "" {
|
||||
val["last_name"] = une.LastName
|
||||
}
|
||||
if une.Credentials.Username != "" {
|
||||
val["username"] = une.Credentials.Username
|
||||
}
|
||||
|
||||
return val, nil
|
||||
}
|
||||
|
||||
type updateProfilePictureEvent struct {
|
||||
users.User
|
||||
}
|
||||
|
||||
func (uppe updateProfilePictureEvent) Encode() (map[string]interface{}, error) {
|
||||
val := map[string]interface{}{
|
||||
"operation": userUpdateProfilePicture,
|
||||
"updated_at": uppe.UpdatedAt,
|
||||
"updated_by": uppe.UpdatedBy,
|
||||
}
|
||||
|
||||
if uppe.ID != "" {
|
||||
val["id"] = uppe.ID
|
||||
}
|
||||
if uppe.ProfilePicture != "" {
|
||||
val["profile_picture"] = uppe.ProfilePicture
|
||||
}
|
||||
|
||||
return val, nil
|
||||
}
|
||||
|
||||
type removeUserEvent struct {
|
||||
id string
|
||||
status string
|
||||
updatedAt time.Time
|
||||
updatedBy string
|
||||
}
|
||||
|
||||
func (rce removeClientEvent) Encode() (map[string]interface{}, error) {
|
||||
func (rce removeUserEvent) Encode() (map[string]interface{}, error) {
|
||||
return map[string]interface{}{
|
||||
"operation": clientRemove,
|
||||
"operation": userRemove,
|
||||
"id": rce.id,
|
||||
"status": rce.status,
|
||||
"updated_at": rce.updatedAt,
|
||||
@@ -138,49 +200,52 @@ func (rce removeClientEvent) Encode() (map[string]interface{}, error) {
|
||||
}, nil
|
||||
}
|
||||
|
||||
type viewClientEvent struct {
|
||||
mgclients.Client
|
||||
type viewUserEvent struct {
|
||||
users.User
|
||||
}
|
||||
|
||||
func (vce viewClientEvent) Encode() (map[string]interface{}, error) {
|
||||
func (vue viewUserEvent) Encode() (map[string]interface{}, error) {
|
||||
val := map[string]interface{}{
|
||||
"operation": clientView,
|
||||
"id": vce.ID,
|
||||
"operation": userView,
|
||||
"id": vue.ID,
|
||||
}
|
||||
|
||||
if vce.Name != "" {
|
||||
val["name"] = vce.Name
|
||||
if vue.LastName != "" {
|
||||
val["last_name"] = vue.LastName
|
||||
}
|
||||
if len(vce.Tags) > 0 {
|
||||
val["tags"] = vce.Tags
|
||||
if vue.FirstName != "" {
|
||||
val["first_name"] = vue.FirstName
|
||||
}
|
||||
if vce.Domain != "" {
|
||||
val["domain"] = vce.Domain
|
||||
if len(vue.Tags) > 0 {
|
||||
val["tags"] = vue.Tags
|
||||
}
|
||||
if vce.Credentials.Identity != "" {
|
||||
val["identity"] = vce.Credentials.Identity
|
||||
if vue.Email != "" {
|
||||
val["email"] = vue.Email
|
||||
}
|
||||
if vce.Metadata != nil {
|
||||
val["metadata"] = vce.Metadata
|
||||
if vue.Credentials.Username != "" {
|
||||
val["email"] = vue.Credentials.Username
|
||||
}
|
||||
if !vce.CreatedAt.IsZero() {
|
||||
val["created_at"] = vce.CreatedAt
|
||||
if vue.Metadata != nil {
|
||||
val["metadata"] = vue.Metadata
|
||||
}
|
||||
if !vce.UpdatedAt.IsZero() {
|
||||
val["updated_at"] = vce.UpdatedAt
|
||||
if !vue.CreatedAt.IsZero() {
|
||||
val["created_at"] = vue.CreatedAt
|
||||
}
|
||||
if vce.UpdatedBy != "" {
|
||||
val["updated_by"] = vce.UpdatedBy
|
||||
if !vue.UpdatedAt.IsZero() {
|
||||
val["updated_at"] = vue.UpdatedAt
|
||||
}
|
||||
if vce.Status.String() != "" {
|
||||
val["status"] = vce.Status.String()
|
||||
if vue.UpdatedBy != "" {
|
||||
val["updated_by"] = vue.UpdatedBy
|
||||
}
|
||||
if vue.Status.String() != "" {
|
||||
val["status"] = vue.Status.String()
|
||||
}
|
||||
|
||||
return val, nil
|
||||
}
|
||||
|
||||
type viewProfileEvent struct {
|
||||
mgclients.Client
|
||||
users.User
|
||||
}
|
||||
|
||||
func (vpe viewProfileEvent) Encode() (map[string]interface{}, error) {
|
||||
@@ -189,17 +254,14 @@ func (vpe viewProfileEvent) Encode() (map[string]interface{}, error) {
|
||||
"id": vpe.ID,
|
||||
}
|
||||
|
||||
if vpe.Name != "" {
|
||||
val["name"] = vpe.Name
|
||||
if vpe.FirstName != "" {
|
||||
val["first_name"] = vpe.FirstName
|
||||
}
|
||||
if len(vpe.Tags) > 0 {
|
||||
val["tags"] = vpe.Tags
|
||||
}
|
||||
if vpe.Domain != "" {
|
||||
val["domain"] = vpe.Domain
|
||||
}
|
||||
if vpe.Credentials.Identity != "" {
|
||||
val["identity"] = vpe.Credentials.Identity
|
||||
if vpe.Credentials.Username != "" {
|
||||
val["username"] = vpe.Credentials.Username
|
||||
}
|
||||
if vpe.Metadata != nil {
|
||||
val["metadata"] = vpe.Metadata
|
||||
@@ -216,62 +278,71 @@ func (vpe viewProfileEvent) Encode() (map[string]interface{}, error) {
|
||||
if vpe.Status.String() != "" {
|
||||
val["status"] = vpe.Status.String()
|
||||
}
|
||||
if vpe.Email != "" {
|
||||
val["email"] = vpe.Email
|
||||
}
|
||||
|
||||
return val, nil
|
||||
}
|
||||
|
||||
type listClientEvent struct {
|
||||
mgclients.Page
|
||||
type listUserEvent struct {
|
||||
users.Page
|
||||
}
|
||||
|
||||
func (lce listClientEvent) Encode() (map[string]interface{}, error) {
|
||||
func (lue listUserEvent) Encode() (map[string]interface{}, error) {
|
||||
val := map[string]interface{}{
|
||||
"operation": clientList,
|
||||
"total": lce.Total,
|
||||
"offset": lce.Offset,
|
||||
"limit": lce.Limit,
|
||||
"operation": userList,
|
||||
"total": lue.Total,
|
||||
"offset": lue.Offset,
|
||||
"limit": lue.Limit,
|
||||
}
|
||||
|
||||
if lce.Name != "" {
|
||||
val["name"] = lce.Name
|
||||
if lue.FirstName != "" {
|
||||
val["first_name"] = lue.FirstName
|
||||
}
|
||||
if lce.Order != "" {
|
||||
val["order"] = lce.Order
|
||||
if lue.LastName != "" {
|
||||
val["last_name"] = lue.LastName
|
||||
}
|
||||
if lce.Dir != "" {
|
||||
val["dir"] = lce.Dir
|
||||
if lue.Order != "" {
|
||||
val["order"] = lue.Order
|
||||
}
|
||||
if lce.Metadata != nil {
|
||||
val["metadata"] = lce.Metadata
|
||||
if lue.Dir != "" {
|
||||
val["dir"] = lue.Dir
|
||||
}
|
||||
if lce.Domain != "" {
|
||||
val["domain"] = lce.Domain
|
||||
if lue.Metadata != nil {
|
||||
val["metadata"] = lue.Metadata
|
||||
}
|
||||
if lce.Tag != "" {
|
||||
val["tag"] = lce.Tag
|
||||
if lue.Domain != "" {
|
||||
val["domain"] = lue.Domain
|
||||
}
|
||||
if lce.Permission != "" {
|
||||
val["permission"] = lce.Permission
|
||||
if lue.Tag != "" {
|
||||
val["tag"] = lue.Tag
|
||||
}
|
||||
if lce.Status.String() != "" {
|
||||
val["status"] = lce.Status.String()
|
||||
if lue.Permission != "" {
|
||||
val["permission"] = lue.Permission
|
||||
}
|
||||
if lce.Identity != "" {
|
||||
val["identity"] = lce.Identity
|
||||
if lue.Status.String() != "" {
|
||||
val["status"] = lue.Status.String()
|
||||
}
|
||||
if lue.Username != "" {
|
||||
val["username"] = lue.Username
|
||||
}
|
||||
if lue.Email != "" {
|
||||
val["email"] = lue.Email
|
||||
}
|
||||
|
||||
return val, nil
|
||||
}
|
||||
|
||||
type listClientByGroupEvent struct {
|
||||
mgclients.Page
|
||||
type listUserByGroupEvent struct {
|
||||
users.Page
|
||||
objectKind string
|
||||
objectID string
|
||||
}
|
||||
|
||||
func (lcge listClientByGroupEvent) Encode() (map[string]interface{}, error) {
|
||||
func (lcge listUserByGroupEvent) Encode() (map[string]interface{}, error) {
|
||||
val := map[string]interface{}{
|
||||
"operation": clientListByGroup,
|
||||
"operation": userListByGroup,
|
||||
"total": lcge.Total,
|
||||
"offset": lcge.Offset,
|
||||
"limit": lcge.Limit,
|
||||
@@ -279,8 +350,8 @@ func (lcge listClientByGroupEvent) Encode() (map[string]interface{}, error) {
|
||||
"object_id": lcge.objectID,
|
||||
}
|
||||
|
||||
if lcge.Name != "" {
|
||||
val["name"] = lcge.Name
|
||||
if lcge.Username != "" {
|
||||
val["username"] = lcge.Username
|
||||
}
|
||||
if lcge.Order != "" {
|
||||
val["order"] = lcge.Order
|
||||
@@ -303,29 +374,41 @@ func (lcge listClientByGroupEvent) Encode() (map[string]interface{}, error) {
|
||||
if lcge.Status.String() != "" {
|
||||
val["status"] = lcge.Status.String()
|
||||
}
|
||||
if lcge.Identity != "" {
|
||||
val["identity"] = lcge.Identity
|
||||
if lcge.FirstName != "" {
|
||||
val["first_name"] = lcge.FirstName
|
||||
}
|
||||
if lcge.LastName != "" {
|
||||
val["last_name"] = lcge.LastName
|
||||
}
|
||||
if lcge.Email != "" {
|
||||
val["email"] = lcge.Email
|
||||
}
|
||||
|
||||
return val, nil
|
||||
}
|
||||
|
||||
type searchClientEvent struct {
|
||||
mgclients.Page
|
||||
type searchUserEvent struct {
|
||||
users.Page
|
||||
}
|
||||
|
||||
func (sce searchClientEvent) Encode() (map[string]interface{}, error) {
|
||||
func (sce searchUserEvent) Encode() (map[string]interface{}, error) {
|
||||
val := map[string]interface{}{
|
||||
"operation": clientSearch,
|
||||
"operation": userSearch,
|
||||
"total": sce.Total,
|
||||
"offset": sce.Offset,
|
||||
"limit": sce.Limit,
|
||||
}
|
||||
if sce.Name != "" {
|
||||
val["name"] = sce.Name
|
||||
if sce.Username != "" {
|
||||
val["username"] = sce.Username
|
||||
}
|
||||
if sce.Identity != "" {
|
||||
val["identity"] = sce.Identity
|
||||
if sce.FirstName != "" {
|
||||
val["first_name"] = sce.FirstName
|
||||
}
|
||||
if sce.LastName != "" {
|
||||
val["last_name"] = sce.LastName
|
||||
}
|
||||
if sce.Email != "" {
|
||||
val["email"] = sce.Email
|
||||
}
|
||||
if sce.Id != "" {
|
||||
val["id"] = sce.Id
|
||||
@@ -334,14 +417,14 @@ func (sce searchClientEvent) Encode() (map[string]interface{}, error) {
|
||||
return val, nil
|
||||
}
|
||||
|
||||
type identifyClientEvent struct {
|
||||
type identifyUserEvent struct {
|
||||
userID string
|
||||
}
|
||||
|
||||
func (ice identifyClientEvent) Encode() (map[string]interface{}, error) {
|
||||
func (ise identifyUserEvent) Encode() (map[string]interface{}, error) {
|
||||
return map[string]interface{}{
|
||||
"operation": clientIdentify,
|
||||
"id": ice.userID,
|
||||
"operation": userIdentify,
|
||||
"id": ise.userID,
|
||||
}, nil
|
||||
}
|
||||
|
||||
@@ -359,13 +442,13 @@ func (grte generateResetTokenEvent) Encode() (map[string]interface{}, error) {
|
||||
}
|
||||
|
||||
type issueTokenEvent struct {
|
||||
identity string
|
||||
username string
|
||||
}
|
||||
|
||||
func (ite issueTokenEvent) Encode() (map[string]interface{}, error) {
|
||||
return map[string]interface{}{
|
||||
"operation": issueToken,
|
||||
"identity": ite.identity,
|
||||
"username": ite.username,
|
||||
}, nil
|
||||
}
|
||||
|
||||
@@ -401,33 +484,33 @@ func (spre sendPasswordResetEvent) Encode() (map[string]interface{}, error) {
|
||||
}
|
||||
|
||||
type oauthCallbackEvent struct {
|
||||
clientID string
|
||||
userID string
|
||||
}
|
||||
|
||||
func (oce oauthCallbackEvent) Encode() (map[string]interface{}, error) {
|
||||
return map[string]interface{}{
|
||||
"operation": oauthCallback,
|
||||
"client_id": oce.clientID,
|
||||
"user_id": oce.userID,
|
||||
}, nil
|
||||
}
|
||||
|
||||
type deleteClientEvent struct {
|
||||
type deleteUserEvent struct {
|
||||
id string
|
||||
}
|
||||
|
||||
func (dce deleteClientEvent) Encode() (map[string]interface{}, error) {
|
||||
func (dce deleteUserEvent) Encode() (map[string]interface{}, error) {
|
||||
return map[string]interface{}{
|
||||
"operation": deleteClient,
|
||||
"operation": deleteUser,
|
||||
"id": dce.id,
|
||||
}, nil
|
||||
}
|
||||
|
||||
type addClientPolicyEvent struct {
|
||||
type addUserPolicyEvent struct {
|
||||
id string
|
||||
role string
|
||||
}
|
||||
|
||||
func (acpe addClientPolicyEvent) Encode() (map[string]interface{}, error) {
|
||||
func (acpe addUserPolicyEvent) Encode() (map[string]interface{}, error) {
|
||||
return map[string]interface{}{
|
||||
"operation": addClientPolicy,
|
||||
"id": acpe.id,
|
||||
|
||||
+82
-49
@@ -8,7 +8,6 @@ import (
|
||||
|
||||
"github.com/absmach/magistrala"
|
||||
"github.com/absmach/magistrala/pkg/authn"
|
||||
mgclients "github.com/absmach/magistrala/pkg/clients"
|
||||
"github.com/absmach/magistrala/pkg/events"
|
||||
"github.com/absmach/magistrala/pkg/events/store"
|
||||
"github.com/absmach/magistrala/users"
|
||||
@@ -37,13 +36,13 @@ func NewEventStoreMiddleware(ctx context.Context, svc users.Service, url string)
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (es *eventStore) RegisterClient(ctx context.Context, session authn.Session, user mgclients.Client, selfRegister bool) (mgclients.Client, error) {
|
||||
user, err := es.svc.RegisterClient(ctx, session, user, selfRegister)
|
||||
func (es *eventStore) Register(ctx context.Context, session authn.Session, user users.User, selfRegister bool) (users.User, error) {
|
||||
user, err := es.svc.Register(ctx, session, user, selfRegister)
|
||||
if err != nil {
|
||||
return user, err
|
||||
}
|
||||
|
||||
event := createClientEvent{
|
||||
event := createUserEvent{
|
||||
user,
|
||||
}
|
||||
|
||||
@@ -54,8 +53,8 @@ func (es *eventStore) RegisterClient(ctx context.Context, session authn.Session,
|
||||
return user, nil
|
||||
}
|
||||
|
||||
func (es *eventStore) UpdateClient(ctx context.Context, session authn.Session, user mgclients.Client) (mgclients.Client, error) {
|
||||
user, err := es.svc.UpdateClient(ctx, session, user)
|
||||
func (es *eventStore) Update(ctx context.Context, session authn.Session, user users.User) (users.User, error) {
|
||||
user, err := es.svc.Update(ctx, session, user)
|
||||
if err != nil {
|
||||
return user, err
|
||||
}
|
||||
@@ -63,8 +62,8 @@ func (es *eventStore) UpdateClient(ctx context.Context, session authn.Session, u
|
||||
return es.update(ctx, "", user)
|
||||
}
|
||||
|
||||
func (es *eventStore) UpdateClientRole(ctx context.Context, session authn.Session, user mgclients.Client) (mgclients.Client, error) {
|
||||
user, err := es.svc.UpdateClientRole(ctx, session, user)
|
||||
func (es *eventStore) UpdateRole(ctx context.Context, session authn.Session, user users.User) (users.User, error) {
|
||||
user, err := es.svc.UpdateRole(ctx, session, user)
|
||||
if err != nil {
|
||||
return user, err
|
||||
}
|
||||
@@ -72,8 +71,8 @@ func (es *eventStore) UpdateClientRole(ctx context.Context, session authn.Sessio
|
||||
return es.update(ctx, "role", user)
|
||||
}
|
||||
|
||||
func (es *eventStore) UpdateClientTags(ctx context.Context, session authn.Session, user mgclients.Client) (mgclients.Client, error) {
|
||||
user, err := es.svc.UpdateClientTags(ctx, session, user)
|
||||
func (es *eventStore) UpdateTags(ctx context.Context, session authn.Session, user users.User) (users.User, error) {
|
||||
user, err := es.svc.UpdateTags(ctx, session, user)
|
||||
if err != nil {
|
||||
return user, err
|
||||
}
|
||||
@@ -81,8 +80,8 @@ func (es *eventStore) UpdateClientTags(ctx context.Context, session authn.Sessio
|
||||
return es.update(ctx, "tags", user)
|
||||
}
|
||||
|
||||
func (es *eventStore) UpdateClientSecret(ctx context.Context, session authn.Session, oldSecret, newSecret string) (mgclients.Client, error) {
|
||||
user, err := es.svc.UpdateClientSecret(ctx, session, oldSecret, newSecret)
|
||||
func (es *eventStore) UpdateSecret(ctx context.Context, session authn.Session, oldSecret, newSecret string) (users.User, error) {
|
||||
user, err := es.svc.UpdateSecret(ctx, session, oldSecret, newSecret)
|
||||
if err != nil {
|
||||
return user, err
|
||||
}
|
||||
@@ -90,17 +89,51 @@ func (es *eventStore) UpdateClientSecret(ctx context.Context, session authn.Sess
|
||||
return es.update(ctx, "secret", user)
|
||||
}
|
||||
|
||||
func (es *eventStore) UpdateClientIdentity(ctx context.Context, session authn.Session, id, identity string) (mgclients.Client, error) {
|
||||
user, err := es.svc.UpdateClientIdentity(ctx, session, id, identity)
|
||||
func (es *eventStore) UpdateUsername(ctx context.Context, session authn.Session, id, username string) (users.User, error) {
|
||||
user, err := es.svc.UpdateUsername(ctx, session, id, username)
|
||||
if err != nil {
|
||||
return user, err
|
||||
}
|
||||
|
||||
return es.update(ctx, "identity", user)
|
||||
event := updateUsernameEvent{
|
||||
user,
|
||||
}
|
||||
|
||||
if err := es.Publish(ctx, event); err != nil {
|
||||
return user, err
|
||||
}
|
||||
|
||||
return user, nil
|
||||
}
|
||||
|
||||
func (es *eventStore) update(ctx context.Context, operation string, user mgclients.Client) (mgclients.Client, error) {
|
||||
event := updateClientEvent{
|
||||
func (es *eventStore) UpdateProfilePicture(ctx context.Context, session authn.Session, user users.User) (users.User, error) {
|
||||
user, err := es.svc.Update(ctx, session, user)
|
||||
if err != nil {
|
||||
return user, err
|
||||
}
|
||||
|
||||
event := updateProfilePictureEvent{
|
||||
user,
|
||||
}
|
||||
|
||||
if err := es.Publish(ctx, event); err != nil {
|
||||
return user, err
|
||||
}
|
||||
|
||||
return es.update(ctx, "profile_picture", user)
|
||||
}
|
||||
|
||||
func (es *eventStore) UpdateEmail(ctx context.Context, session authn.Session, id, email string) (users.User, error) {
|
||||
user, err := es.svc.UpdateEmail(ctx, session, id, email)
|
||||
if err != nil {
|
||||
return user, err
|
||||
}
|
||||
|
||||
return es.update(ctx, "email", user)
|
||||
}
|
||||
|
||||
func (es *eventStore) update(ctx context.Context, operation string, user users.User) (users.User, error) {
|
||||
event := updateUserEvent{
|
||||
user, operation,
|
||||
}
|
||||
|
||||
@@ -111,13 +144,13 @@ func (es *eventStore) update(ctx context.Context, operation string, user mgclien
|
||||
return user, nil
|
||||
}
|
||||
|
||||
func (es *eventStore) ViewClient(ctx context.Context, session authn.Session, id string) (mgclients.Client, error) {
|
||||
user, err := es.svc.ViewClient(ctx, session, id)
|
||||
func (es *eventStore) View(ctx context.Context, session authn.Session, id string) (users.User, error) {
|
||||
user, err := es.svc.View(ctx, session, id)
|
||||
if err != nil {
|
||||
return user, err
|
||||
}
|
||||
|
||||
event := viewClientEvent{
|
||||
event := viewUserEvent{
|
||||
user,
|
||||
}
|
||||
|
||||
@@ -128,7 +161,7 @@ func (es *eventStore) ViewClient(ctx context.Context, session authn.Session, id
|
||||
return user, nil
|
||||
}
|
||||
|
||||
func (es *eventStore) ViewProfile(ctx context.Context, session authn.Session) (mgclients.Client, error) {
|
||||
func (es *eventStore) ViewProfile(ctx context.Context, session authn.Session) (users.User, error) {
|
||||
user, err := es.svc.ViewProfile(ctx, session)
|
||||
if err != nil {
|
||||
return user, err
|
||||
@@ -145,12 +178,12 @@ func (es *eventStore) ViewProfile(ctx context.Context, session authn.Session) (m
|
||||
return user, nil
|
||||
}
|
||||
|
||||
func (es *eventStore) ListClients(ctx context.Context, session authn.Session, pm mgclients.Page) (mgclients.ClientsPage, error) {
|
||||
cp, err := es.svc.ListClients(ctx, session, pm)
|
||||
func (es *eventStore) ListUsers(ctx context.Context, session authn.Session, pm users.Page) (users.UsersPage, error) {
|
||||
cp, err := es.svc.ListUsers(ctx, session, pm)
|
||||
if err != nil {
|
||||
return cp, err
|
||||
}
|
||||
event := listClientEvent{
|
||||
event := listUserEvent{
|
||||
pm,
|
||||
}
|
||||
|
||||
@@ -161,12 +194,12 @@ func (es *eventStore) ListClients(ctx context.Context, session authn.Session, pm
|
||||
return cp, nil
|
||||
}
|
||||
|
||||
func (es *eventStore) SearchUsers(ctx context.Context, pm mgclients.Page) (mgclients.ClientsPage, error) {
|
||||
func (es *eventStore) SearchUsers(ctx context.Context, pm users.Page) (users.UsersPage, error) {
|
||||
cp, err := es.svc.SearchUsers(ctx, pm)
|
||||
if err != nil {
|
||||
return cp, err
|
||||
}
|
||||
event := searchClientEvent{
|
||||
event := searchUserEvent{
|
||||
pm,
|
||||
}
|
||||
|
||||
@@ -177,12 +210,12 @@ func (es *eventStore) SearchUsers(ctx context.Context, pm mgclients.Page) (mgcli
|
||||
return cp, nil
|
||||
}
|
||||
|
||||
func (es *eventStore) ListMembers(ctx context.Context, session authn.Session, objectKind, objectID string, pm mgclients.Page) (mgclients.MembersPage, error) {
|
||||
func (es *eventStore) ListMembers(ctx context.Context, session authn.Session, objectKind, objectID string, pm users.Page) (users.MembersPage, error) {
|
||||
mp, err := es.svc.ListMembers(ctx, session, objectKind, objectID, pm)
|
||||
if err != nil {
|
||||
return mp, err
|
||||
}
|
||||
event := listClientByGroupEvent{
|
||||
event := listUserByGroupEvent{
|
||||
pm, objectKind, objectID,
|
||||
}
|
||||
|
||||
@@ -193,8 +226,8 @@ func (es *eventStore) ListMembers(ctx context.Context, session authn.Session, ob
|
||||
return mp, nil
|
||||
}
|
||||
|
||||
func (es *eventStore) EnableClient(ctx context.Context, session authn.Session, id string) (mgclients.Client, error) {
|
||||
user, err := es.svc.EnableClient(ctx, session, id)
|
||||
func (es *eventStore) Enable(ctx context.Context, session authn.Session, id string) (users.User, error) {
|
||||
user, err := es.svc.Enable(ctx, session, id)
|
||||
if err != nil {
|
||||
return user, err
|
||||
}
|
||||
@@ -202,8 +235,8 @@ func (es *eventStore) EnableClient(ctx context.Context, session authn.Session, i
|
||||
return es.delete(ctx, user)
|
||||
}
|
||||
|
||||
func (es *eventStore) DisableClient(ctx context.Context, session authn.Session, id string) (mgclients.Client, error) {
|
||||
user, err := es.svc.DisableClient(ctx, session, id)
|
||||
func (es *eventStore) Disable(ctx context.Context, session authn.Session, id string) (users.User, error) {
|
||||
user, err := es.svc.Disable(ctx, session, id)
|
||||
if err != nil {
|
||||
return user, err
|
||||
}
|
||||
@@ -211,8 +244,8 @@ func (es *eventStore) DisableClient(ctx context.Context, session authn.Session,
|
||||
return es.delete(ctx, user)
|
||||
}
|
||||
|
||||
func (es *eventStore) delete(ctx context.Context, user mgclients.Client) (mgclients.Client, error) {
|
||||
event := removeClientEvent{
|
||||
func (es *eventStore) delete(ctx context.Context, user users.User) (users.User, error) {
|
||||
event := removeUserEvent{
|
||||
id: user.ID,
|
||||
updatedAt: user.UpdatedAt,
|
||||
updatedBy: user.UpdatedBy,
|
||||
@@ -232,7 +265,7 @@ func (es *eventStore) Identify(ctx context.Context, session authn.Session) (stri
|
||||
return userID, err
|
||||
}
|
||||
|
||||
event := identifyClientEvent{
|
||||
event := identifyUserEvent{
|
||||
userID: userID,
|
||||
}
|
||||
|
||||
@@ -257,14 +290,14 @@ func (es *eventStore) GenerateResetToken(ctx context.Context, email, host string
|
||||
return es.Publish(ctx, event)
|
||||
}
|
||||
|
||||
func (es *eventStore) IssueToken(ctx context.Context, identity, secret string) (*magistrala.Token, error) {
|
||||
token, err := es.svc.IssueToken(ctx, identity, secret)
|
||||
func (es *eventStore) IssueToken(ctx context.Context, username, secret string) (*magistrala.Token, error) {
|
||||
token, err := es.svc.IssueToken(ctx, username, secret)
|
||||
if err != nil {
|
||||
return token, err
|
||||
}
|
||||
|
||||
event := issueTokenEvent{
|
||||
identity: identity,
|
||||
username: username,
|
||||
}
|
||||
|
||||
if err := es.Publish(ctx, event); err != nil {
|
||||
@@ -313,14 +346,14 @@ func (es *eventStore) SendPasswordReset(ctx context.Context, host, email, user,
|
||||
return es.Publish(ctx, event)
|
||||
}
|
||||
|
||||
func (es *eventStore) OAuthCallback(ctx context.Context, client mgclients.Client) (mgclients.Client, error) {
|
||||
token, err := es.svc.OAuthCallback(ctx, client)
|
||||
func (es *eventStore) OAuthCallback(ctx context.Context, user users.User) (users.User, error) {
|
||||
token, err := es.svc.OAuthCallback(ctx, user)
|
||||
if err != nil {
|
||||
return token, err
|
||||
}
|
||||
|
||||
event := oauthCallbackEvent{
|
||||
clientID: client.ID,
|
||||
userID: user.ID,
|
||||
}
|
||||
|
||||
if err := es.Publish(ctx, event); err != nil {
|
||||
@@ -330,26 +363,26 @@ func (es *eventStore) OAuthCallback(ctx context.Context, client mgclients.Client
|
||||
return token, nil
|
||||
}
|
||||
|
||||
func (es *eventStore) DeleteClient(ctx context.Context, session authn.Session, id string) error {
|
||||
if err := es.svc.DeleteClient(ctx, session, id); err != nil {
|
||||
func (es *eventStore) Delete(ctx context.Context, session authn.Session, id string) error {
|
||||
if err := es.svc.Delete(ctx, session, id); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
event := deleteClientEvent{
|
||||
event := deleteUserEvent{
|
||||
id: id,
|
||||
}
|
||||
|
||||
return es.Publish(ctx, event)
|
||||
}
|
||||
|
||||
func (es *eventStore) OAuthAddClientPolicy(ctx context.Context, client mgclients.Client) error {
|
||||
if err := es.svc.OAuthAddClientPolicy(ctx, client); err != nil {
|
||||
func (es *eventStore) OAuthAddUserPolicy(ctx context.Context, user users.User) error {
|
||||
if err := es.svc.OAuthAddUserPolicy(ctx, user); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
event := addClientPolicyEvent{
|
||||
id: client.ID,
|
||||
role: client.Role.String(),
|
||||
event := addUserPolicyEvent{
|
||||
id: user.ID,
|
||||
role: user.Role.String(),
|
||||
}
|
||||
|
||||
return es.Publish(ctx, event)
|
||||
|
||||
@@ -11,7 +11,6 @@ import (
|
||||
"github.com/absmach/magistrala/pkg/authn"
|
||||
"github.com/absmach/magistrala/pkg/authz"
|
||||
mgauthz "github.com/absmach/magistrala/pkg/authz"
|
||||
"github.com/absmach/magistrala/pkg/clients"
|
||||
svcerr "github.com/absmach/magistrala/pkg/errors/service"
|
||||
"github.com/absmach/magistrala/pkg/policies"
|
||||
"github.com/absmach/magistrala/users"
|
||||
@@ -34,94 +33,106 @@ func AuthorizationMiddleware(svc users.Service, authz mgauthz.Authorization, sel
|
||||
}
|
||||
}
|
||||
|
||||
func (am *authorizationMiddleware) RegisterClient(ctx context.Context, session authn.Session, client clients.Client, selfRegister bool) (clients.Client, error) {
|
||||
func (am *authorizationMiddleware) Register(ctx context.Context, session authn.Session, user users.User, selfRegister bool) (users.User, error) {
|
||||
if selfRegister {
|
||||
if err := am.checkSuperAdmin(ctx, session.UserID); err == nil {
|
||||
session.SuperAdmin = true
|
||||
}
|
||||
}
|
||||
|
||||
return am.svc.RegisterClient(ctx, session, client, selfRegister)
|
||||
return am.svc.Register(ctx, session, user, selfRegister)
|
||||
}
|
||||
|
||||
func (am *authorizationMiddleware) ViewClient(ctx context.Context, session authn.Session, id string) (clients.Client, error) {
|
||||
func (am *authorizationMiddleware) View(ctx context.Context, session authn.Session, id string) (users.User, error) {
|
||||
if err := am.checkSuperAdmin(ctx, session.UserID); err == nil {
|
||||
session.SuperAdmin = true
|
||||
}
|
||||
|
||||
return am.svc.ViewClient(ctx, session, id)
|
||||
return am.svc.View(ctx, session, id)
|
||||
}
|
||||
|
||||
func (am *authorizationMiddleware) ViewProfile(ctx context.Context, session authn.Session) (clients.Client, error) {
|
||||
func (am *authorizationMiddleware) ViewProfile(ctx context.Context, session authn.Session) (users.User, error) {
|
||||
return am.svc.ViewProfile(ctx, session)
|
||||
}
|
||||
|
||||
func (am *authorizationMiddleware) ListClients(ctx context.Context, session authn.Session, pm clients.Page) (clients.ClientsPage, error) {
|
||||
func (am *authorizationMiddleware) ListUsers(ctx context.Context, session authn.Session, pm users.Page) (users.UsersPage, error) {
|
||||
if err := am.checkSuperAdmin(ctx, session.UserID); err == nil {
|
||||
session.SuperAdmin = true
|
||||
}
|
||||
|
||||
return am.svc.ListClients(ctx, session, pm)
|
||||
return am.svc.ListUsers(ctx, session, pm)
|
||||
}
|
||||
|
||||
func (am *authorizationMiddleware) ListMembers(ctx context.Context, session authn.Session, objectKind, objectID string, pm clients.Page) (clients.MembersPage, error) {
|
||||
func (am *authorizationMiddleware) ListMembers(ctx context.Context, session authn.Session, objectKind, objectID string, pm users.Page) (users.MembersPage, error) {
|
||||
if session.DomainUserID == "" {
|
||||
return clients.MembersPage{}, svcerr.ErrDomainAuthorization
|
||||
return users.MembersPage{}, svcerr.ErrDomainAuthorization
|
||||
}
|
||||
switch objectKind {
|
||||
case policies.GroupsKind:
|
||||
if err := am.authorize(ctx, session.DomainID, policies.UserType, policies.UsersKind, session.UserID, mgauth.SwitchToPermission(pm.Permission), policies.GroupType, objectID); err != nil {
|
||||
return clients.MembersPage{}, err
|
||||
return users.MembersPage{}, err
|
||||
}
|
||||
case policies.DomainsKind:
|
||||
if err := am.authorize(ctx, session.DomainID, policies.UserType, policies.UsersKind, session.UserID, mgauth.SwitchToPermission(pm.Permission), policies.DomainType, objectID); err != nil {
|
||||
return clients.MembersPage{}, err
|
||||
return users.MembersPage{}, err
|
||||
}
|
||||
case policies.ThingsKind:
|
||||
if err := am.authorize(ctx, session.DomainID, policies.UserType, policies.UsersKind, session.UserID, mgauth.SwitchToPermission(pm.Permission), policies.ThingType, objectID); err != nil {
|
||||
return clients.MembersPage{}, err
|
||||
return users.MembersPage{}, err
|
||||
}
|
||||
default:
|
||||
return clients.MembersPage{}, svcerr.ErrAuthorization
|
||||
return users.MembersPage{}, svcerr.ErrAuthorization
|
||||
}
|
||||
|
||||
return am.svc.ListMembers(ctx, session, objectKind, objectID, pm)
|
||||
}
|
||||
|
||||
func (am *authorizationMiddleware) SearchUsers(ctx context.Context, pm clients.Page) (clients.ClientsPage, error) {
|
||||
func (am *authorizationMiddleware) SearchUsers(ctx context.Context, pm users.Page) (users.UsersPage, error) {
|
||||
return am.svc.SearchUsers(ctx, pm)
|
||||
}
|
||||
|
||||
func (am *authorizationMiddleware) UpdateClient(ctx context.Context, session authn.Session, client clients.Client) (clients.Client, error) {
|
||||
func (am *authorizationMiddleware) Update(ctx context.Context, session authn.Session, user users.User) (users.User, error) {
|
||||
if err := am.checkSuperAdmin(ctx, session.UserID); err == nil {
|
||||
session.SuperAdmin = true
|
||||
}
|
||||
|
||||
return am.svc.UpdateClient(ctx, session, client)
|
||||
return am.svc.Update(ctx, session, user)
|
||||
}
|
||||
|
||||
func (am *authorizationMiddleware) UpdateClientTags(ctx context.Context, session authn.Session, client clients.Client) (clients.Client, error) {
|
||||
func (am *authorizationMiddleware) UpdateTags(ctx context.Context, session authn.Session, user users.User) (users.User, error) {
|
||||
if err := am.checkSuperAdmin(ctx, session.UserID); err == nil {
|
||||
session.SuperAdmin = true
|
||||
}
|
||||
|
||||
return am.svc.UpdateClientTags(ctx, session, client)
|
||||
return am.svc.UpdateTags(ctx, session, user)
|
||||
}
|
||||
|
||||
func (am *authorizationMiddleware) UpdateClientIdentity(ctx context.Context, session authn.Session, id, identity string) (clients.Client, error) {
|
||||
func (am *authorizationMiddleware) UpdateEmail(ctx context.Context, session authn.Session, id, email string) (users.User, error) {
|
||||
if err := am.checkSuperAdmin(ctx, session.UserID); err == nil {
|
||||
session.SuperAdmin = true
|
||||
}
|
||||
|
||||
return am.svc.UpdateClientIdentity(ctx, session, id, identity)
|
||||
return am.svc.UpdateEmail(ctx, session, id, email)
|
||||
}
|
||||
|
||||
func (am *authorizationMiddleware) UpdateUsername(ctx context.Context, session authn.Session, id, username string) (users.User, error) {
|
||||
if err := am.checkSuperAdmin(ctx, session.UserID); err == nil {
|
||||
session.SuperAdmin = true
|
||||
}
|
||||
|
||||
return am.svc.UpdateUsername(ctx, session, id, username)
|
||||
}
|
||||
|
||||
func (am *authorizationMiddleware) UpdateProfilePicture(ctx context.Context, session authn.Session, user users.User) (users.User, error) {
|
||||
return am.svc.UpdateProfilePicture(ctx, session, user)
|
||||
}
|
||||
|
||||
func (am *authorizationMiddleware) GenerateResetToken(ctx context.Context, email, host string) error {
|
||||
return am.svc.GenerateResetToken(ctx, email, host)
|
||||
}
|
||||
|
||||
func (am *authorizationMiddleware) UpdateClientSecret(ctx context.Context, session authn.Session, oldSecret, newSecret string) (clients.Client, error) {
|
||||
return am.svc.UpdateClientSecret(ctx, session, oldSecret, newSecret)
|
||||
func (am *authorizationMiddleware) UpdateSecret(ctx context.Context, session authn.Session, oldSecret, newSecret string) (users.User, error) {
|
||||
return am.svc.UpdateSecret(ctx, session, oldSecret, newSecret)
|
||||
}
|
||||
|
||||
func (am *authorizationMiddleware) ResetSecret(ctx context.Context, session authn.Session, secret string) error {
|
||||
@@ -132,62 +143,62 @@ func (am *authorizationMiddleware) SendPasswordReset(ctx context.Context, host,
|
||||
return am.svc.SendPasswordReset(ctx, host, email, user, token)
|
||||
}
|
||||
|
||||
func (am *authorizationMiddleware) UpdateClientRole(ctx context.Context, session authn.Session, client clients.Client) (clients.Client, error) {
|
||||
func (am *authorizationMiddleware) UpdateRole(ctx context.Context, session authn.Session, user users.User) (users.User, error) {
|
||||
if err := am.checkSuperAdmin(ctx, session.UserID); err == nil {
|
||||
session.SuperAdmin = true
|
||||
}
|
||||
if err := am.authorize(ctx, "", policies.UserType, policies.UsersKind, client.ID, policies.MembershipPermission, policies.PlatformType, policies.MagistralaObject); err != nil {
|
||||
return clients.Client{}, err
|
||||
if err := am.authorize(ctx, "", policies.UserType, policies.UsersKind, user.ID, policies.MembershipPermission, policies.PlatformType, policies.MagistralaObject); err != nil {
|
||||
return users.User{}, err
|
||||
}
|
||||
|
||||
return am.svc.UpdateClientRole(ctx, session, client)
|
||||
return am.svc.UpdateRole(ctx, session, user)
|
||||
}
|
||||
|
||||
func (am *authorizationMiddleware) EnableClient(ctx context.Context, session authn.Session, id string) (clients.Client, error) {
|
||||
func (am *authorizationMiddleware) Enable(ctx context.Context, session authn.Session, id string) (users.User, error) {
|
||||
if err := am.checkSuperAdmin(ctx, session.UserID); err == nil {
|
||||
session.SuperAdmin = true
|
||||
}
|
||||
|
||||
return am.svc.EnableClient(ctx, session, id)
|
||||
return am.svc.Enable(ctx, session, id)
|
||||
}
|
||||
|
||||
func (am *authorizationMiddleware) DisableClient(ctx context.Context, session authn.Session, id string) (clients.Client, error) {
|
||||
func (am *authorizationMiddleware) Disable(ctx context.Context, session authn.Session, id string) (users.User, error) {
|
||||
if err := am.checkSuperAdmin(ctx, session.UserID); err == nil {
|
||||
session.SuperAdmin = true
|
||||
}
|
||||
|
||||
return am.svc.DisableClient(ctx, session, id)
|
||||
return am.svc.Disable(ctx, session, id)
|
||||
}
|
||||
|
||||
func (am *authorizationMiddleware) DeleteClient(ctx context.Context, session authn.Session, id string) error {
|
||||
func (am *authorizationMiddleware) Delete(ctx context.Context, session authn.Session, id string) error {
|
||||
if err := am.checkSuperAdmin(ctx, session.UserID); err == nil {
|
||||
session.SuperAdmin = true
|
||||
}
|
||||
|
||||
return am.svc.DeleteClient(ctx, session, id)
|
||||
return am.svc.Delete(ctx, session, id)
|
||||
}
|
||||
|
||||
func (am *authorizationMiddleware) Identify(ctx context.Context, session authn.Session) (string, error) {
|
||||
return am.svc.Identify(ctx, session)
|
||||
}
|
||||
|
||||
func (am *authorizationMiddleware) IssueToken(ctx context.Context, identity, secret string) (*magistrala.Token, error) {
|
||||
return am.svc.IssueToken(ctx, identity, secret)
|
||||
func (am *authorizationMiddleware) IssueToken(ctx context.Context, username, secret string) (*magistrala.Token, error) {
|
||||
return am.svc.IssueToken(ctx, username, secret)
|
||||
}
|
||||
|
||||
func (am *authorizationMiddleware) RefreshToken(ctx context.Context, session authn.Session, refreshToken string) (*magistrala.Token, error) {
|
||||
return am.svc.RefreshToken(ctx, session, refreshToken)
|
||||
}
|
||||
|
||||
func (am *authorizationMiddleware) OAuthCallback(ctx context.Context, client clients.Client) (clients.Client, error) {
|
||||
return am.svc.OAuthCallback(ctx, client)
|
||||
func (am *authorizationMiddleware) OAuthCallback(ctx context.Context, user users.User) (users.User, error) {
|
||||
return am.svc.OAuthCallback(ctx, user)
|
||||
}
|
||||
|
||||
func (am *authorizationMiddleware) OAuthAddClientPolicy(ctx context.Context, client clients.Client) error {
|
||||
if err := am.authorize(ctx, "", policies.UserType, policies.UsersKind, client.ID, policies.MembershipPermission, policies.PlatformType, policies.MagistralaObject); err == nil {
|
||||
func (am *authorizationMiddleware) OAuthAddUserPolicy(ctx context.Context, user users.User) error {
|
||||
if err := am.authorize(ctx, "", policies.UserType, policies.UsersKind, user.ID, policies.MembershipPermission, policies.PlatformType, policies.MagistralaObject); err == nil {
|
||||
return nil
|
||||
}
|
||||
return am.svc.OAuthAddClientPolicy(ctx, client)
|
||||
return am.svc.OAuthAddUserPolicy(ctx, user)
|
||||
}
|
||||
|
||||
func (am *authorizationMiddleware) checkSuperAdmin(ctx context.Context, adminID string) error {
|
||||
|
||||
+108
-70
@@ -10,7 +10,6 @@ import (
|
||||
|
||||
"github.com/absmach/magistrala"
|
||||
"github.com/absmach/magistrala/pkg/authn"
|
||||
mgclients "github.com/absmach/magistrala/pkg/clients"
|
||||
"github.com/absmach/magistrala/users"
|
||||
)
|
||||
|
||||
@@ -21,20 +20,21 @@ type loggingMiddleware struct {
|
||||
svc users.Service
|
||||
}
|
||||
|
||||
// LoggingMiddleware adds logging facilities to the clients service.
|
||||
// LoggingMiddleware adds logging facilities to the users service.
|
||||
func LoggingMiddleware(svc users.Service, logger *slog.Logger) users.Service {
|
||||
return &loggingMiddleware{logger, svc}
|
||||
}
|
||||
|
||||
// RegisterClient logs the register_client request. It logs the client id and the time it took to complete the request.
|
||||
// Register logs the user request. It logs the user id and the time it took to complete the request.
|
||||
// If the request fails, it logs the error.
|
||||
func (lm *loggingMiddleware) RegisterClient(ctx context.Context, session authn.Session, client mgclients.Client, selfRegister bool) (c mgclients.Client, err error) {
|
||||
func (lm *loggingMiddleware) Register(ctx context.Context, session authn.Session, user users.User, selfRegister bool) (u users.User, err error) {
|
||||
defer func(begin time.Time) {
|
||||
args := []any{
|
||||
slog.String("duration", time.Since(begin).String()),
|
||||
slog.Group("user",
|
||||
slog.String("id", c.ID),
|
||||
slog.String("name", c.Name),
|
||||
slog.String("username", user.Credentials.Username),
|
||||
slog.String("first_name", user.FirstName),
|
||||
slog.String("last_name", user.LastName),
|
||||
),
|
||||
}
|
||||
if err != nil {
|
||||
@@ -42,14 +42,15 @@ func (lm *loggingMiddleware) RegisterClient(ctx context.Context, session authn.S
|
||||
lm.logger.Warn("Register user failed", args...)
|
||||
return
|
||||
}
|
||||
args = append(args, slog.String("user_id", u.ID))
|
||||
lm.logger.Info("Register user completed successfully", args...)
|
||||
}(time.Now())
|
||||
return lm.svc.RegisterClient(ctx, session, client, selfRegister)
|
||||
return lm.svc.Register(ctx, session, user, selfRegister)
|
||||
}
|
||||
|
||||
// IssueToken logs the issue_token request. It logs the client identity type and the time it took to complete the request.
|
||||
// IssueToken logs the issue_token request. It logs the username type and the time it took to complete the request.
|
||||
// If the request fails, it logs the error.
|
||||
func (lm *loggingMiddleware) IssueToken(ctx context.Context, identity, secret string) (t *magistrala.Token, err error) {
|
||||
func (lm *loggingMiddleware) IssueToken(ctx context.Context, username, secret string) (t *magistrala.Token, err error) {
|
||||
defer func(begin time.Time) {
|
||||
args := []any{
|
||||
slog.String("duration", time.Since(begin).String()),
|
||||
@@ -64,7 +65,7 @@ func (lm *loggingMiddleware) IssueToken(ctx context.Context, identity, secret st
|
||||
}
|
||||
lm.logger.Info("Issue token completed successfully", args...)
|
||||
}(time.Now())
|
||||
return lm.svc.IssueToken(ctx, identity, secret)
|
||||
return lm.svc.IssueToken(ctx, username, secret)
|
||||
}
|
||||
|
||||
// RefreshToken logs the refresh_token request. It logs the refreshtoken, token type and the time it took to complete the request.
|
||||
@@ -87,15 +88,14 @@ func (lm *loggingMiddleware) RefreshToken(ctx context.Context, session authn.Ses
|
||||
return lm.svc.RefreshToken(ctx, session, refreshToken)
|
||||
}
|
||||
|
||||
// ViewClient logs the view_client request. It logs the client id and the time it took to complete the request.
|
||||
// View logs the view_user request. It logs the user id and the time it took to complete the request.
|
||||
// If the request fails, it logs the error.
|
||||
func (lm *loggingMiddleware) ViewClient(ctx context.Context, session authn.Session, id string) (c mgclients.Client, err error) {
|
||||
func (lm *loggingMiddleware) View(ctx context.Context, session authn.Session, id string) (c users.User, err error) {
|
||||
defer func(begin time.Time) {
|
||||
args := []any{
|
||||
slog.String("duration", time.Since(begin).String()),
|
||||
slog.Group("user",
|
||||
slog.String("id", id),
|
||||
slog.String("name", c.Name),
|
||||
),
|
||||
}
|
||||
if err != nil {
|
||||
@@ -105,18 +105,18 @@ func (lm *loggingMiddleware) ViewClient(ctx context.Context, session authn.Sessi
|
||||
}
|
||||
lm.logger.Info("View user completed successfully", args...)
|
||||
}(time.Now())
|
||||
return lm.svc.ViewClient(ctx, session, id)
|
||||
return lm.svc.View(ctx, session, id)
|
||||
}
|
||||
|
||||
// ViewProfile logs the view_profile request. It logs the client id and the time it took to complete the request.
|
||||
// ViewProfile logs the view_profile request. It logs the user id and the time it took to complete the request.
|
||||
// If the request fails, it logs the error.
|
||||
func (lm *loggingMiddleware) ViewProfile(ctx context.Context, session authn.Session) (c mgclients.Client, err error) {
|
||||
func (lm *loggingMiddleware) ViewProfile(ctx context.Context, session authn.Session) (c users.User, err error) {
|
||||
defer func(begin time.Time) {
|
||||
args := []any{
|
||||
slog.String("duration", time.Since(begin).String()),
|
||||
slog.Group("user",
|
||||
slog.String("id", c.ID),
|
||||
slog.String("name", c.Name),
|
||||
slog.String("username", c.Credentials.Username),
|
||||
),
|
||||
}
|
||||
if err != nil {
|
||||
@@ -129,9 +129,9 @@ func (lm *loggingMiddleware) ViewProfile(ctx context.Context, session authn.Sess
|
||||
return lm.svc.ViewProfile(ctx, session)
|
||||
}
|
||||
|
||||
// ListClients logs the list_clients request. It logs the page metadata and the time it took to complete the request.
|
||||
// ListUsers logs the list_users request. It logs the page metadata and the time it took to complete the request.
|
||||
// If the request fails, it logs the error.
|
||||
func (lm *loggingMiddleware) ListClients(ctx context.Context, session authn.Session, pm mgclients.Page) (cp mgclients.ClientsPage, err error) {
|
||||
func (lm *loggingMiddleware) ListUsers(ctx context.Context, session authn.Session, pm users.Page) (cp users.UsersPage, err error) {
|
||||
defer func(begin time.Time) {
|
||||
args := []any{
|
||||
slog.String("duration", time.Since(begin).String()),
|
||||
@@ -148,11 +148,11 @@ func (lm *loggingMiddleware) ListClients(ctx context.Context, session authn.Sess
|
||||
}
|
||||
lm.logger.Info("List users completed successfully", args...)
|
||||
}(time.Now())
|
||||
return lm.svc.ListClients(ctx, session, pm)
|
||||
return lm.svc.ListUsers(ctx, session, pm)
|
||||
}
|
||||
|
||||
// SearchUsers logs the search_users request. It logs the page metadata and the time it took to complete the request.
|
||||
func (lm *loggingMiddleware) SearchUsers(ctx context.Context, cp mgclients.Page) (mp mgclients.ClientsPage, err error) {
|
||||
func (lm *loggingMiddleware) SearchUsers(ctx context.Context, cp users.Page) (mp users.UsersPage, err error) {
|
||||
defer func(begin time.Time) {
|
||||
args := []any{
|
||||
slog.String("duration", time.Since(begin).String()),
|
||||
@@ -164,24 +164,26 @@ func (lm *loggingMiddleware) SearchUsers(ctx context.Context, cp mgclients.Page)
|
||||
}
|
||||
if err != nil {
|
||||
args = append(args, slog.Any("error", err))
|
||||
lm.logger.Warn("Search clients failed to complete successfully", args...)
|
||||
lm.logger.Warn("Search users failed to complete successfully", args...)
|
||||
return
|
||||
}
|
||||
lm.logger.Info("Search clients completed successfully", args...)
|
||||
lm.logger.Info("Search users completed successfully", args...)
|
||||
}(time.Now())
|
||||
return lm.svc.SearchUsers(ctx, cp)
|
||||
}
|
||||
|
||||
// UpdateClient logs the update_client request. It logs the client id and the time it took to complete the request.
|
||||
// Update logs the update_user request. It logs the user id and the time it took to complete the request.
|
||||
// If the request fails, it logs the error.
|
||||
func (lm *loggingMiddleware) UpdateClient(ctx context.Context, session authn.Session, client mgclients.Client) (c mgclients.Client, err error) {
|
||||
func (lm *loggingMiddleware) Update(ctx context.Context, session authn.Session, user users.User) (u users.User, err error) {
|
||||
defer func(begin time.Time) {
|
||||
args := []any{
|
||||
slog.String("duration", time.Since(begin).String()),
|
||||
slog.Group("user",
|
||||
slog.String("id", c.ID),
|
||||
slog.String("name", c.Name),
|
||||
slog.Any("metadata", c.Metadata),
|
||||
slog.String("id", u.ID),
|
||||
slog.String("username", u.Credentials.Username),
|
||||
slog.String("first_name", u.FirstName),
|
||||
slog.String("last_name", u.LastName),
|
||||
slog.Any("metadata", u.Metadata),
|
||||
),
|
||||
}
|
||||
if err != nil {
|
||||
@@ -191,18 +193,17 @@ func (lm *loggingMiddleware) UpdateClient(ctx context.Context, session authn.Ses
|
||||
}
|
||||
lm.logger.Info("Update user completed successfully", args...)
|
||||
}(time.Now())
|
||||
return lm.svc.UpdateClient(ctx, session, client)
|
||||
return lm.svc.Update(ctx, session, user)
|
||||
}
|
||||
|
||||
// UpdateClientTags logs the update_client_tags request. It logs the client id and the time it took to complete the request.
|
||||
// UpdateTags logs the update_user_tags request. It logs the user id and the time it took to complete the request.
|
||||
// If the request fails, it logs the error.
|
||||
func (lm *loggingMiddleware) UpdateClientTags(ctx context.Context, session authn.Session, client mgclients.Client) (c mgclients.Client, err error) {
|
||||
func (lm *loggingMiddleware) UpdateTags(ctx context.Context, session authn.Session, user users.User) (c users.User, err error) {
|
||||
defer func(begin time.Time) {
|
||||
args := []any{
|
||||
slog.String("duration", time.Since(begin).String()),
|
||||
slog.Group("user",
|
||||
slog.String("id", c.ID),
|
||||
slog.String("name", c.Name),
|
||||
slog.Any("tags", c.Tags),
|
||||
),
|
||||
}
|
||||
@@ -213,39 +214,38 @@ func (lm *loggingMiddleware) UpdateClientTags(ctx context.Context, session authn
|
||||
}
|
||||
lm.logger.Info("Update user tags completed successfully", args...)
|
||||
}(time.Now())
|
||||
return lm.svc.UpdateClientTags(ctx, session, client)
|
||||
return lm.svc.UpdateTags(ctx, session, user)
|
||||
}
|
||||
|
||||
// UpdateClientIdentity logs the update_identity request. It logs the client id and the time it took to complete the request.
|
||||
// UpdateEmail logs the update_user_email request. It logs the user id and the time it took to complete the request.
|
||||
// If the request fails, it logs the error.
|
||||
func (lm *loggingMiddleware) UpdateClientIdentity(ctx context.Context, session authn.Session, id, identity string) (c mgclients.Client, err error) {
|
||||
func (lm *loggingMiddleware) UpdateEmail(ctx context.Context, session authn.Session, id, email string) (c users.User, err error) {
|
||||
defer func(begin time.Time) {
|
||||
args := []any{
|
||||
slog.String("duration", time.Since(begin).String()),
|
||||
slog.Group("user",
|
||||
slog.String("id", c.ID),
|
||||
slog.String("name", c.Name),
|
||||
slog.String("email", c.Email),
|
||||
),
|
||||
}
|
||||
if err != nil {
|
||||
args = append(args, slog.Any("error", err))
|
||||
lm.logger.Warn("Update client identity failed", args...)
|
||||
lm.logger.Warn("Update user email failed", args...)
|
||||
return
|
||||
}
|
||||
lm.logger.Info("Update client identity completed successfully", args...)
|
||||
lm.logger.Info("Update user email completed successfully", args...)
|
||||
}(time.Now())
|
||||
return lm.svc.UpdateClientIdentity(ctx, session, id, identity)
|
||||
return lm.svc.UpdateEmail(ctx, session, id, email)
|
||||
}
|
||||
|
||||
// UpdateClientSecret logs the update_client_secret request. It logs the client id and the time it took to complete the request.
|
||||
// UpdateSecret logs the update_user_secret request. It logs the user id and the time it took to complete the request.
|
||||
// If the request fails, it logs the error.
|
||||
func (lm *loggingMiddleware) UpdateClientSecret(ctx context.Context, session authn.Session, oldSecret, newSecret string) (c mgclients.Client, err error) {
|
||||
func (lm *loggingMiddleware) UpdateSecret(ctx context.Context, session authn.Session, oldSecret, newSecret string) (c users.User, err error) {
|
||||
defer func(begin time.Time) {
|
||||
args := []any{
|
||||
slog.String("duration", time.Since(begin).String()),
|
||||
slog.Group("user",
|
||||
slog.String("id", c.ID),
|
||||
slog.String("name", c.Name),
|
||||
),
|
||||
}
|
||||
if err != nil {
|
||||
@@ -255,7 +255,48 @@ func (lm *loggingMiddleware) UpdateClientSecret(ctx context.Context, session aut
|
||||
}
|
||||
lm.logger.Info("Update user secret completed successfully", args...)
|
||||
}(time.Now())
|
||||
return lm.svc.UpdateClientSecret(ctx, session, oldSecret, newSecret)
|
||||
return lm.svc.UpdateSecret(ctx, session, oldSecret, newSecret)
|
||||
}
|
||||
|
||||
// UpdateUsername logs the update_usernames request. It logs the user id and the time it took to complete the request.
|
||||
// If the request fails, it logs the error.
|
||||
func (lm *loggingMiddleware) UpdateUsername(ctx context.Context, session authn.Session, id, username string) (u users.User, err error) {
|
||||
defer func(begin time.Time) {
|
||||
args := []any{
|
||||
slog.String("duration", time.Since(begin).String()),
|
||||
slog.Group("user",
|
||||
slog.String("id", u.ID),
|
||||
slog.String("username", u.Credentials.Username),
|
||||
),
|
||||
}
|
||||
if err != nil {
|
||||
args = append(args, slog.Any("error", err))
|
||||
lm.logger.Warn("Update user names failed", args...)
|
||||
return
|
||||
}
|
||||
lm.logger.Info("Update user names completed successfully", args...)
|
||||
}(time.Now())
|
||||
return lm.svc.UpdateUsername(ctx, session, id, username)
|
||||
}
|
||||
|
||||
// UpdateProfilePicture logs the update_profile_picture request. It logs the user id and the time it took to complete the request.
|
||||
// If the request fails, it logs the error.
|
||||
func (lm *loggingMiddleware) UpdateProfilePicture(ctx context.Context, session authn.Session, user users.User) (u users.User, err error) {
|
||||
defer func(begin time.Time) {
|
||||
args := []any{
|
||||
slog.String("duration", time.Since(begin).String()),
|
||||
slog.Group("user",
|
||||
slog.String("id", u.ID),
|
||||
),
|
||||
}
|
||||
if err != nil {
|
||||
args = append(args, slog.Any("error", err))
|
||||
lm.logger.Warn("Update profile picture failed", args...)
|
||||
return
|
||||
}
|
||||
lm.logger.Info("Update profile picture completed successfully", args...)
|
||||
}(time.Now())
|
||||
return lm.svc.Update(ctx, session, user)
|
||||
}
|
||||
|
||||
// GenerateResetToken logs the generate_reset_token request. It logs the time it took to complete the request.
|
||||
@@ -311,16 +352,15 @@ func (lm *loggingMiddleware) SendPasswordReset(ctx context.Context, host, email,
|
||||
return lm.svc.SendPasswordReset(ctx, host, email, user, token)
|
||||
}
|
||||
|
||||
// UpdateClientRole logs the update_client_role request. It logs the client id and the time it took to complete the request.
|
||||
// UpdateRole logs the update_user_role request. It logs the user id and the time it took to complete the request.
|
||||
// If the request fails, it logs the error.
|
||||
func (lm *loggingMiddleware) UpdateClientRole(ctx context.Context, session authn.Session, client mgclients.Client) (c mgclients.Client, err error) {
|
||||
func (lm *loggingMiddleware) UpdateRole(ctx context.Context, session authn.Session, user users.User) (c users.User, err error) {
|
||||
defer func(begin time.Time) {
|
||||
args := []any{
|
||||
slog.String("duration", time.Since(begin).String()),
|
||||
slog.Group("user",
|
||||
slog.String("id", c.ID),
|
||||
slog.String("name", c.Name),
|
||||
slog.String("role", client.Role.String()),
|
||||
slog.String("id", user.ID),
|
||||
slog.String("role", user.Role.String()),
|
||||
),
|
||||
}
|
||||
if err != nil {
|
||||
@@ -330,18 +370,17 @@ func (lm *loggingMiddleware) UpdateClientRole(ctx context.Context, session authn
|
||||
}
|
||||
lm.logger.Info("Update user role completed successfully", args...)
|
||||
}(time.Now())
|
||||
return lm.svc.UpdateClientRole(ctx, session, client)
|
||||
return lm.svc.UpdateRole(ctx, session, user)
|
||||
}
|
||||
|
||||
// EnableClient logs the enable_client request. It logs the client id and the time it took to complete the request.
|
||||
// Enable logs the enable_user request. It logs the user id and the time it took to complete the request.
|
||||
// If the request fails, it logs the error.
|
||||
func (lm *loggingMiddleware) EnableClient(ctx context.Context, session authn.Session, id string) (c mgclients.Client, err error) {
|
||||
func (lm *loggingMiddleware) Enable(ctx context.Context, session authn.Session, id string) (c users.User, err error) {
|
||||
defer func(begin time.Time) {
|
||||
args := []any{
|
||||
slog.String("duration", time.Since(begin).String()),
|
||||
slog.Group("user",
|
||||
slog.String("id", id),
|
||||
slog.String("name", c.Name),
|
||||
),
|
||||
}
|
||||
if err != nil {
|
||||
@@ -351,18 +390,17 @@ func (lm *loggingMiddleware) EnableClient(ctx context.Context, session authn.Ses
|
||||
}
|
||||
lm.logger.Info("Enable user completed successfully", args...)
|
||||
}(time.Now())
|
||||
return lm.svc.EnableClient(ctx, session, id)
|
||||
return lm.svc.Enable(ctx, session, id)
|
||||
}
|
||||
|
||||
// DisableClient logs the disable_client request. It logs the client id and the time it took to complete the request.
|
||||
// Disable logs the disable_user request. It logs the user id and the time it took to complete the request.
|
||||
// If the request fails, it logs the error.
|
||||
func (lm *loggingMiddleware) DisableClient(ctx context.Context, session authn.Session, id string) (c mgclients.Client, err error) {
|
||||
func (lm *loggingMiddleware) Disable(ctx context.Context, session authn.Session, id string) (c users.User, err error) {
|
||||
defer func(begin time.Time) {
|
||||
args := []any{
|
||||
slog.String("duration", time.Since(begin).String()),
|
||||
slog.Group("user",
|
||||
slog.String("id", id),
|
||||
slog.String("name", c.Name),
|
||||
),
|
||||
}
|
||||
if err != nil {
|
||||
@@ -372,12 +410,12 @@ func (lm *loggingMiddleware) DisableClient(ctx context.Context, session authn.Se
|
||||
}
|
||||
lm.logger.Info("Disable user completed successfully", args...)
|
||||
}(time.Now())
|
||||
return lm.svc.DisableClient(ctx, session, id)
|
||||
return lm.svc.Disable(ctx, session, id)
|
||||
}
|
||||
|
||||
// ListMembers logs the list_members request. It logs the group id, and the time it took to complete the request.
|
||||
// If the request fails, it logs the error.
|
||||
func (lm *loggingMiddleware) ListMembers(ctx context.Context, session authn.Session, objectKind, objectID string, cp mgclients.Page) (mp mgclients.MembersPage, err error) {
|
||||
func (lm *loggingMiddleware) ListMembers(ctx context.Context, session authn.Session, objectKind, objectID string, cp users.Page) (mp users.MembersPage, err error) {
|
||||
defer func(begin time.Time) {
|
||||
args := []any{
|
||||
slog.String("duration", time.Since(begin).String()),
|
||||
@@ -418,11 +456,11 @@ func (lm *loggingMiddleware) Identify(ctx context.Context, session authn.Session
|
||||
return lm.svc.Identify(ctx, session)
|
||||
}
|
||||
|
||||
func (lm *loggingMiddleware) OAuthCallback(ctx context.Context, client mgclients.Client) (c mgclients.Client, err error) {
|
||||
func (lm *loggingMiddleware) OAuthCallback(ctx context.Context, user users.User) (c users.User, err error) {
|
||||
defer func(begin time.Time) {
|
||||
args := []any{
|
||||
slog.String("duration", time.Since(begin).String()),
|
||||
slog.String("user_id", client.ID),
|
||||
slog.String("user_id", user.ID),
|
||||
}
|
||||
if err != nil {
|
||||
args = append(args, slog.Any("error", err))
|
||||
@@ -431,11 +469,11 @@ func (lm *loggingMiddleware) OAuthCallback(ctx context.Context, client mgclients
|
||||
}
|
||||
lm.logger.Info("OAuth callback completed successfully", args...)
|
||||
}(time.Now())
|
||||
return lm.svc.OAuthCallback(ctx, client)
|
||||
return lm.svc.OAuthCallback(ctx, user)
|
||||
}
|
||||
|
||||
// DeleteClient logs the delete_client request. It logs the client id and token and the time it took to complete the request.
|
||||
func (lm *loggingMiddleware) DeleteClient(ctx context.Context, session authn.Session, id string) (err error) {
|
||||
// Delete logs the delete_user request. It logs the user id and token and the time it took to complete the request.
|
||||
func (lm *loggingMiddleware) Delete(ctx context.Context, session authn.Session, id string) (err error) {
|
||||
defer func(begin time.Time) {
|
||||
args := []any{
|
||||
slog.String("duration", time.Since(begin).String()),
|
||||
@@ -448,22 +486,22 @@ func (lm *loggingMiddleware) DeleteClient(ctx context.Context, session authn.Ses
|
||||
}
|
||||
lm.logger.Info("Delete user completed successfully", args...)
|
||||
}(time.Now())
|
||||
return lm.svc.DeleteClient(ctx, session, id)
|
||||
return lm.svc.Delete(ctx, session, id)
|
||||
}
|
||||
|
||||
// OAuthAddClientPolicy logs the add_client_policy request. It logs the client id and the time it took to complete the request.
|
||||
func (lm *loggingMiddleware) OAuthAddClientPolicy(ctx context.Context, client mgclients.Client) (err error) {
|
||||
// OAuthAddUserPolicy logs the add_user_policy request. It logs the user id and the time it took to complete the request.
|
||||
func (lm *loggingMiddleware) OAuthAddUserPolicy(ctx context.Context, user users.User) (err error) {
|
||||
defer func(begin time.Time) {
|
||||
args := []any{
|
||||
slog.String("duration", time.Since(begin).String()),
|
||||
slog.String("user_id", client.ID),
|
||||
slog.String("user_id", user.ID),
|
||||
}
|
||||
if err != nil {
|
||||
args = append(args, slog.Any("error", err))
|
||||
lm.logger.Warn("Add client policy failed", args...)
|
||||
lm.logger.Warn("Add user policy failed", args...)
|
||||
return
|
||||
}
|
||||
lm.logger.Info("Add client policy completed successfully", args...)
|
||||
lm.logger.Info("Add user policy completed successfully", args...)
|
||||
}(time.Now())
|
||||
return lm.svc.OAuthAddClientPolicy(ctx, client)
|
||||
return lm.svc.OAuthAddUserPolicy(ctx, user)
|
||||
}
|
||||
|
||||
+86
-69
@@ -9,7 +9,6 @@ import (
|
||||
|
||||
"github.com/absmach/magistrala"
|
||||
"github.com/absmach/magistrala/pkg/authn"
|
||||
mgclients "github.com/absmach/magistrala/pkg/clients"
|
||||
"github.com/absmach/magistrala/users"
|
||||
"github.com/go-kit/kit/metrics"
|
||||
)
|
||||
@@ -31,22 +30,22 @@ func MetricsMiddleware(svc users.Service, counter metrics.Counter, latency metri
|
||||
}
|
||||
}
|
||||
|
||||
// RegisterClient instruments RegisterClient method with metrics.
|
||||
func (ms *metricsMiddleware) RegisterClient(ctx context.Context, session authn.Session, client mgclients.Client, selfRegister bool) (mgclients.Client, error) {
|
||||
// Register instruments Register method with metrics.
|
||||
func (ms *metricsMiddleware) Register(ctx context.Context, session authn.Session, user users.User, selfRegister bool) (users.User, error) {
|
||||
defer func(begin time.Time) {
|
||||
ms.counter.With("method", "register_client").Add(1)
|
||||
ms.latency.With("method", "register_client").Observe(time.Since(begin).Seconds())
|
||||
ms.counter.With("method", "register_user").Add(1)
|
||||
ms.latency.With("method", "register_user").Observe(time.Since(begin).Seconds())
|
||||
}(time.Now())
|
||||
return ms.svc.RegisterClient(ctx, session, client, selfRegister)
|
||||
return ms.svc.Register(ctx, session, user, selfRegister)
|
||||
}
|
||||
|
||||
// IssueToken instruments IssueToken method with metrics.
|
||||
func (ms *metricsMiddleware) IssueToken(ctx context.Context, identity, secret string) (*magistrala.Token, error) {
|
||||
func (ms *metricsMiddleware) IssueToken(ctx context.Context, username, secret string) (*magistrala.Token, error) {
|
||||
defer func(begin time.Time) {
|
||||
ms.counter.With("method", "issue_token").Add(1)
|
||||
ms.latency.With("method", "issue_token").Observe(time.Since(begin).Seconds())
|
||||
}(time.Now())
|
||||
return ms.svc.IssueToken(ctx, identity, secret)
|
||||
return ms.svc.IssueToken(ctx, username, secret)
|
||||
}
|
||||
|
||||
// RefreshToken instruments RefreshToken method with metrics.
|
||||
@@ -58,17 +57,17 @@ func (ms *metricsMiddleware) RefreshToken(ctx context.Context, session authn.Ses
|
||||
return ms.svc.RefreshToken(ctx, session, refreshToken)
|
||||
}
|
||||
|
||||
// ViewClient instruments ViewClient method with metrics.
|
||||
func (ms *metricsMiddleware) ViewClient(ctx context.Context, session authn.Session, id string) (mgclients.Client, error) {
|
||||
// View instruments View method with metrics.
|
||||
func (ms *metricsMiddleware) View(ctx context.Context, session authn.Session, id string) (users.User, error) {
|
||||
defer func(begin time.Time) {
|
||||
ms.counter.With("method", "view_client").Add(1)
|
||||
ms.latency.With("method", "view_client").Observe(time.Since(begin).Seconds())
|
||||
ms.counter.With("method", "view_user").Add(1)
|
||||
ms.latency.With("method", "view_user").Observe(time.Since(begin).Seconds())
|
||||
}(time.Now())
|
||||
return ms.svc.ViewClient(ctx, session, id)
|
||||
return ms.svc.View(ctx, session, id)
|
||||
}
|
||||
|
||||
// ViewProfile instruments ViewProfile method with metrics.
|
||||
func (ms *metricsMiddleware) ViewProfile(ctx context.Context, session authn.Session) (mgclients.Client, error) {
|
||||
func (ms *metricsMiddleware) ViewProfile(ctx context.Context, session authn.Session) (users.User, error) {
|
||||
defer func(begin time.Time) {
|
||||
ms.counter.With("method", "view_profile").Add(1)
|
||||
ms.latency.With("method", "view_profile").Observe(time.Since(begin).Seconds())
|
||||
@@ -76,17 +75,17 @@ func (ms *metricsMiddleware) ViewProfile(ctx context.Context, session authn.Sess
|
||||
return ms.svc.ViewProfile(ctx, session)
|
||||
}
|
||||
|
||||
// ListClients instruments ListClients method with metrics.
|
||||
func (ms *metricsMiddleware) ListClients(ctx context.Context, session authn.Session, pm mgclients.Page) (mgclients.ClientsPage, error) {
|
||||
// ListUsers instruments ListUsers method with metrics.
|
||||
func (ms *metricsMiddleware) ListUsers(ctx context.Context, session authn.Session, pm users.Page) (users.UsersPage, error) {
|
||||
defer func(begin time.Time) {
|
||||
ms.counter.With("method", "list_clients").Add(1)
|
||||
ms.latency.With("method", "list_clients").Observe(time.Since(begin).Seconds())
|
||||
ms.counter.With("method", "list_users").Add(1)
|
||||
ms.latency.With("method", "list_users").Observe(time.Since(begin).Seconds())
|
||||
}(time.Now())
|
||||
return ms.svc.ListClients(ctx, session, pm)
|
||||
return ms.svc.ListUsers(ctx, session, pm)
|
||||
}
|
||||
|
||||
// SearchUsers instruments SearchClients method with metrics.
|
||||
func (ms *metricsMiddleware) SearchUsers(ctx context.Context, pm mgclients.Page) (mp mgclients.ClientsPage, err error) {
|
||||
// SearchUsers instruments SearchUsers method with metrics.
|
||||
func (ms *metricsMiddleware) SearchUsers(ctx context.Context, pm users.Page) (mp users.UsersPage, err error) {
|
||||
defer func(begin time.Time) {
|
||||
ms.counter.With("method", "search_users").Add(1)
|
||||
ms.latency.With("method", "search_users").Observe(time.Since(begin).Seconds())
|
||||
@@ -94,40 +93,58 @@ func (ms *metricsMiddleware) SearchUsers(ctx context.Context, pm mgclients.Page)
|
||||
return ms.svc.SearchUsers(ctx, pm)
|
||||
}
|
||||
|
||||
// UpdateClient instruments UpdateClient method with metrics.
|
||||
func (ms *metricsMiddleware) UpdateClient(ctx context.Context, session authn.Session, client mgclients.Client) (mgclients.Client, error) {
|
||||
// Update instruments Update method with metrics.
|
||||
func (ms *metricsMiddleware) Update(ctx context.Context, session authn.Session, user users.User) (users.User, error) {
|
||||
defer func(begin time.Time) {
|
||||
ms.counter.With("method", "update_client_name_and_metadata").Add(1)
|
||||
ms.latency.With("method", "update_client_name_and_metadata").Observe(time.Since(begin).Seconds())
|
||||
ms.counter.With("method", "update_user").Add(1)
|
||||
ms.latency.With("method", "update_user").Observe(time.Since(begin).Seconds())
|
||||
}(time.Now())
|
||||
return ms.svc.UpdateClient(ctx, session, client)
|
||||
return ms.svc.Update(ctx, session, user)
|
||||
}
|
||||
|
||||
// UpdateClientTags instruments UpdateClientTags method with metrics.
|
||||
func (ms *metricsMiddleware) UpdateClientTags(ctx context.Context, session authn.Session, client mgclients.Client) (mgclients.Client, error) {
|
||||
// UpdateTags instruments UpdateTags method with metrics.
|
||||
func (ms *metricsMiddleware) UpdateTags(ctx context.Context, session authn.Session, user users.User) (users.User, error) {
|
||||
defer func(begin time.Time) {
|
||||
ms.counter.With("method", "update_client_tags").Add(1)
|
||||
ms.latency.With("method", "update_client_tags").Observe(time.Since(begin).Seconds())
|
||||
ms.counter.With("method", "update_user_tags").Add(1)
|
||||
ms.latency.With("method", "update_user_tags").Observe(time.Since(begin).Seconds())
|
||||
}(time.Now())
|
||||
return ms.svc.UpdateClientTags(ctx, session, client)
|
||||
return ms.svc.UpdateTags(ctx, session, user)
|
||||
}
|
||||
|
||||
// UpdateClientIdentity instruments UpdateClientIdentity method with metrics.
|
||||
func (ms *metricsMiddleware) UpdateClientIdentity(ctx context.Context, session authn.Session, id, identity string) (mgclients.Client, error) {
|
||||
// UpdateEmail instruments UpdateEmail method with metrics.
|
||||
func (ms *metricsMiddleware) UpdateEmail(ctx context.Context, session authn.Session, id, email string) (users.User, error) {
|
||||
defer func(begin time.Time) {
|
||||
ms.counter.With("method", "update_client_identity").Add(1)
|
||||
ms.latency.With("method", "update_client_identity").Observe(time.Since(begin).Seconds())
|
||||
ms.counter.With("method", "update_user_email").Add(1)
|
||||
ms.latency.With("method", "update_user_email").Observe(time.Since(begin).Seconds())
|
||||
}(time.Now())
|
||||
return ms.svc.UpdateClientIdentity(ctx, session, id, identity)
|
||||
return ms.svc.UpdateEmail(ctx, session, id, email)
|
||||
}
|
||||
|
||||
// UpdateClientSecret instruments UpdateClientSecret method with metrics.
|
||||
func (ms *metricsMiddleware) UpdateClientSecret(ctx context.Context, session authn.Session, oldSecret, newSecret string) (mgclients.Client, error) {
|
||||
// UpdateSecret instruments UpdateSecret method with metrics.
|
||||
func (ms *metricsMiddleware) UpdateSecret(ctx context.Context, session authn.Session, oldSecret, newSecret string) (users.User, error) {
|
||||
defer func(begin time.Time) {
|
||||
ms.counter.With("method", "update_client_secret").Add(1)
|
||||
ms.latency.With("method", "update_client_secret").Observe(time.Since(begin).Seconds())
|
||||
ms.counter.With("method", "update_user_secret").Add(1)
|
||||
ms.latency.With("method", "update_user_secret").Observe(time.Since(begin).Seconds())
|
||||
}(time.Now())
|
||||
return ms.svc.UpdateClientSecret(ctx, session, oldSecret, newSecret)
|
||||
return ms.svc.UpdateSecret(ctx, session, oldSecret, newSecret)
|
||||
}
|
||||
|
||||
// UpdateUsername instruments UpdateUsername method with metrics.
|
||||
func (ms *metricsMiddleware) UpdateUsername(ctx context.Context, session authn.Session, id, username string) (users.User, error) {
|
||||
defer func(begin time.Time) {
|
||||
ms.counter.With("method", "update_usernames").Add(1)
|
||||
ms.latency.With("method", "update_usernames").Observe(time.Since(begin).Seconds())
|
||||
}(time.Now())
|
||||
return ms.svc.UpdateUsername(ctx, session, id, username)
|
||||
}
|
||||
|
||||
// UpdateProfilePicture instruments UpdateProfilePicture method with metrics.
|
||||
func (ms *metricsMiddleware) UpdateProfilePicture(ctx context.Context, session authn.Session, user users.User) (users.User, error) {
|
||||
defer func(begin time.Time) {
|
||||
ms.counter.With("method", "update_profile_picture").Add(1)
|
||||
ms.latency.With("method", "update_profile_picture").Observe(time.Since(begin).Seconds())
|
||||
}(time.Now())
|
||||
return ms.svc.Update(ctx, session, user)
|
||||
}
|
||||
|
||||
// GenerateResetToken instruments GenerateResetToken method with metrics.
|
||||
@@ -157,35 +174,35 @@ func (ms *metricsMiddleware) SendPasswordReset(ctx context.Context, host, email,
|
||||
return ms.svc.SendPasswordReset(ctx, host, email, user, token)
|
||||
}
|
||||
|
||||
// UpdateClientRole instruments UpdateClientRole method with metrics.
|
||||
func (ms *metricsMiddleware) UpdateClientRole(ctx context.Context, session authn.Session, client mgclients.Client) (mgclients.Client, error) {
|
||||
// UpdateRole instruments UpdateRole method with metrics.
|
||||
func (ms *metricsMiddleware) UpdateRole(ctx context.Context, session authn.Session, user users.User) (users.User, error) {
|
||||
defer func(begin time.Time) {
|
||||
ms.counter.With("method", "update_client_role").Add(1)
|
||||
ms.latency.With("method", "update_client_role").Observe(time.Since(begin).Seconds())
|
||||
ms.counter.With("method", "update_user_role").Add(1)
|
||||
ms.latency.With("method", "update_user_role").Observe(time.Since(begin).Seconds())
|
||||
}(time.Now())
|
||||
return ms.svc.UpdateClientRole(ctx, session, client)
|
||||
return ms.svc.UpdateRole(ctx, session, user)
|
||||
}
|
||||
|
||||
// EnableClient instruments EnableClient method with metrics.
|
||||
func (ms *metricsMiddleware) EnableClient(ctx context.Context, session authn.Session, id string) (mgclients.Client, error) {
|
||||
// Enable instruments Enable method with metrics.
|
||||
func (ms *metricsMiddleware) Enable(ctx context.Context, session authn.Session, id string) (users.User, error) {
|
||||
defer func(begin time.Time) {
|
||||
ms.counter.With("method", "enable_client").Add(1)
|
||||
ms.latency.With("method", "enable_client").Observe(time.Since(begin).Seconds())
|
||||
ms.counter.With("method", "enable_user").Add(1)
|
||||
ms.latency.With("method", "enable_user").Observe(time.Since(begin).Seconds())
|
||||
}(time.Now())
|
||||
return ms.svc.EnableClient(ctx, session, id)
|
||||
return ms.svc.Enable(ctx, session, id)
|
||||
}
|
||||
|
||||
// DisableClient instruments DisableClient method with metrics.
|
||||
func (ms *metricsMiddleware) DisableClient(ctx context.Context, session authn.Session, id string) (mgclients.Client, error) {
|
||||
// Disable instruments Disable method with metrics.
|
||||
func (ms *metricsMiddleware) Disable(ctx context.Context, session authn.Session, id string) (users.User, error) {
|
||||
defer func(begin time.Time) {
|
||||
ms.counter.With("method", "disable_client").Add(1)
|
||||
ms.latency.With("method", "disable_client").Observe(time.Since(begin).Seconds())
|
||||
ms.counter.With("method", "disable_user").Add(1)
|
||||
ms.latency.With("method", "disable_user").Observe(time.Since(begin).Seconds())
|
||||
}(time.Now())
|
||||
return ms.svc.DisableClient(ctx, session, id)
|
||||
return ms.svc.Disable(ctx, session, id)
|
||||
}
|
||||
|
||||
// ListMembers instruments ListMembers method with metrics.
|
||||
func (ms *metricsMiddleware) ListMembers(ctx context.Context, session authn.Session, objectKind, objectID string, pm mgclients.Page) (mp mgclients.MembersPage, err error) {
|
||||
func (ms *metricsMiddleware) ListMembers(ctx context.Context, session authn.Session, objectKind, objectID string, pm users.Page) (mp users.MembersPage, err error) {
|
||||
defer func(begin time.Time) {
|
||||
ms.counter.With("method", "list_members").Add(1)
|
||||
ms.latency.With("method", "list_members").Observe(time.Since(begin).Seconds())
|
||||
@@ -203,28 +220,28 @@ func (ms *metricsMiddleware) Identify(ctx context.Context, session authn.Session
|
||||
}
|
||||
|
||||
// OAuthCallback instruments OAuthCallback method with metrics.
|
||||
func (ms *metricsMiddleware) OAuthCallback(ctx context.Context, client mgclients.Client) (mgclients.Client, error) {
|
||||
func (ms *metricsMiddleware) OAuthCallback(ctx context.Context, user users.User) (users.User, error) {
|
||||
defer func(begin time.Time) {
|
||||
ms.counter.With("method", "oauth_callback").Add(1)
|
||||
ms.latency.With("method", "oauth_callback").Observe(time.Since(begin).Seconds())
|
||||
}(time.Now())
|
||||
return ms.svc.OAuthCallback(ctx, client)
|
||||
return ms.svc.OAuthCallback(ctx, user)
|
||||
}
|
||||
|
||||
// DeleteClient instruments DeleteClient method with metrics.
|
||||
func (ms *metricsMiddleware) DeleteClient(ctx context.Context, session authn.Session, id string) error {
|
||||
// Delete instruments Delete method with metrics.
|
||||
func (ms *metricsMiddleware) Delete(ctx context.Context, session authn.Session, id string) error {
|
||||
defer func(begin time.Time) {
|
||||
ms.counter.With("method", "delete_client").Add(1)
|
||||
ms.latency.With("method", "delete_client").Observe(time.Since(begin).Seconds())
|
||||
ms.counter.With("method", "delete_user").Add(1)
|
||||
ms.latency.With("method", "delete_user").Observe(time.Since(begin).Seconds())
|
||||
}(time.Now())
|
||||
return ms.svc.DeleteClient(ctx, session, id)
|
||||
return ms.svc.Delete(ctx, session, id)
|
||||
}
|
||||
|
||||
// OAuthAddClientPolicy instruments OAuthAddClientPolicy method with metrics.
|
||||
func (ms *metricsMiddleware) OAuthAddClientPolicy(ctx context.Context, client mgclients.Client) error {
|
||||
// OAuthAddUserPolicy instruments OAuthAddUserPolicy method with metrics.
|
||||
func (ms *metricsMiddleware) OAuthAddUserPolicy(ctx context.Context, user users.User) error {
|
||||
defer func(begin time.Time) {
|
||||
ms.counter.With("method", "add_client_policy").Add(1)
|
||||
ms.latency.With("method", "add_client_policy").Observe(time.Since(begin).Seconds())
|
||||
ms.counter.With("method", "add_user_policy").Add(1)
|
||||
ms.latency.With("method", "add_user_policy").Observe(time.Since(begin).Seconds())
|
||||
}(time.Now())
|
||||
return ms.svc.OAuthAddClientPolicy(ctx, client)
|
||||
return ms.svc.OAuthAddUserPolicy(ctx, user)
|
||||
}
|
||||
|
||||
+121
-150
@@ -7,8 +7,7 @@ package mocks
|
||||
import (
|
||||
context "context"
|
||||
|
||||
clients "github.com/absmach/magistrala/pkg/clients"
|
||||
|
||||
users "github.com/absmach/magistrala/users"
|
||||
mock "github.com/stretchr/testify/mock"
|
||||
)
|
||||
|
||||
@@ -17,27 +16,27 @@ type Repository struct {
|
||||
mock.Mock
|
||||
}
|
||||
|
||||
// ChangeStatus provides a mock function with given fields: ctx, client
|
||||
func (_m *Repository) ChangeStatus(ctx context.Context, client clients.Client) (clients.Client, error) {
|
||||
ret := _m.Called(ctx, client)
|
||||
// ChangeStatus provides a mock function with given fields: ctx, user
|
||||
func (_m *Repository) ChangeStatus(ctx context.Context, user users.User) (users.User, error) {
|
||||
ret := _m.Called(ctx, user)
|
||||
|
||||
if len(ret) == 0 {
|
||||
panic("no return value specified for ChangeStatus")
|
||||
}
|
||||
|
||||
var r0 clients.Client
|
||||
var r0 users.User
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(0).(func(context.Context, clients.Client) (clients.Client, error)); ok {
|
||||
return rf(ctx, client)
|
||||
if rf, ok := ret.Get(0).(func(context.Context, users.User) (users.User, error)); ok {
|
||||
return rf(ctx, user)
|
||||
}
|
||||
if rf, ok := ret.Get(0).(func(context.Context, clients.Client) clients.Client); ok {
|
||||
r0 = rf(ctx, client)
|
||||
if rf, ok := ret.Get(0).(func(context.Context, users.User) users.User); ok {
|
||||
r0 = rf(ctx, user)
|
||||
} else {
|
||||
r0 = ret.Get(0).(clients.Client)
|
||||
r0 = ret.Get(0).(users.User)
|
||||
}
|
||||
|
||||
if rf, ok := ret.Get(1).(func(context.Context, clients.Client) error); ok {
|
||||
r1 = rf(ctx, client)
|
||||
if rf, ok := ret.Get(1).(func(context.Context, users.User) error); ok {
|
||||
r1 = rf(ctx, user)
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
@@ -82,25 +81,25 @@ func (_m *Repository) Delete(ctx context.Context, id string) error {
|
||||
}
|
||||
|
||||
// RetrieveAll provides a mock function with given fields: ctx, pm
|
||||
func (_m *Repository) RetrieveAll(ctx context.Context, pm clients.Page) (clients.ClientsPage, error) {
|
||||
func (_m *Repository) RetrieveAll(ctx context.Context, pm users.Page) (users.UsersPage, error) {
|
||||
ret := _m.Called(ctx, pm)
|
||||
|
||||
if len(ret) == 0 {
|
||||
panic("no return value specified for RetrieveAll")
|
||||
}
|
||||
|
||||
var r0 clients.ClientsPage
|
||||
var r0 users.UsersPage
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(0).(func(context.Context, clients.Page) (clients.ClientsPage, error)); ok {
|
||||
if rf, ok := ret.Get(0).(func(context.Context, users.Page) (users.UsersPage, error)); ok {
|
||||
return rf(ctx, pm)
|
||||
}
|
||||
if rf, ok := ret.Get(0).(func(context.Context, clients.Page) clients.ClientsPage); ok {
|
||||
if rf, ok := ret.Get(0).(func(context.Context, users.Page) users.UsersPage); ok {
|
||||
r0 = rf(ctx, pm)
|
||||
} else {
|
||||
r0 = ret.Get(0).(clients.ClientsPage)
|
||||
r0 = ret.Get(0).(users.UsersPage)
|
||||
}
|
||||
|
||||
if rf, ok := ret.Get(1).(func(context.Context, clients.Page) error); ok {
|
||||
if rf, ok := ret.Get(1).(func(context.Context, users.Page) error); ok {
|
||||
r1 = rf(ctx, pm)
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
@@ -110,25 +109,25 @@ func (_m *Repository) RetrieveAll(ctx context.Context, pm clients.Page) (clients
|
||||
}
|
||||
|
||||
// RetrieveAllByIDs provides a mock function with given fields: ctx, pm
|
||||
func (_m *Repository) RetrieveAllByIDs(ctx context.Context, pm clients.Page) (clients.ClientsPage, error) {
|
||||
func (_m *Repository) RetrieveAllByIDs(ctx context.Context, pm users.Page) (users.UsersPage, error) {
|
||||
ret := _m.Called(ctx, pm)
|
||||
|
||||
if len(ret) == 0 {
|
||||
panic("no return value specified for RetrieveAllByIDs")
|
||||
}
|
||||
|
||||
var r0 clients.ClientsPage
|
||||
var r0 users.UsersPage
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(0).(func(context.Context, clients.Page) (clients.ClientsPage, error)); ok {
|
||||
if rf, ok := ret.Get(0).(func(context.Context, users.Page) (users.UsersPage, error)); ok {
|
||||
return rf(ctx, pm)
|
||||
}
|
||||
if rf, ok := ret.Get(0).(func(context.Context, clients.Page) clients.ClientsPage); ok {
|
||||
if rf, ok := ret.Get(0).(func(context.Context, users.Page) users.UsersPage); ok {
|
||||
r0 = rf(ctx, pm)
|
||||
} else {
|
||||
r0 = ret.Get(0).(clients.ClientsPage)
|
||||
r0 = ret.Get(0).(users.UsersPage)
|
||||
}
|
||||
|
||||
if rf, ok := ret.Get(1).(func(context.Context, clients.Page) error); ok {
|
||||
if rf, ok := ret.Get(1).(func(context.Context, users.Page) error); ok {
|
||||
r1 = rf(ctx, pm)
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
@@ -137,23 +136,51 @@ func (_m *Repository) RetrieveAllByIDs(ctx context.Context, pm clients.Page) (cl
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// RetrieveByEmail provides a mock function with given fields: ctx, email
|
||||
func (_m *Repository) RetrieveByEmail(ctx context.Context, email string) (users.User, error) {
|
||||
ret := _m.Called(ctx, email)
|
||||
|
||||
if len(ret) == 0 {
|
||||
panic("no return value specified for RetrieveByEmail")
|
||||
}
|
||||
|
||||
var r0 users.User
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(0).(func(context.Context, string) (users.User, error)); ok {
|
||||
return rf(ctx, email)
|
||||
}
|
||||
if rf, ok := ret.Get(0).(func(context.Context, string) users.User); ok {
|
||||
r0 = rf(ctx, email)
|
||||
} else {
|
||||
r0 = ret.Get(0).(users.User)
|
||||
}
|
||||
|
||||
if rf, ok := ret.Get(1).(func(context.Context, string) error); ok {
|
||||
r1 = rf(ctx, email)
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// RetrieveByID provides a mock function with given fields: ctx, id
|
||||
func (_m *Repository) RetrieveByID(ctx context.Context, id string) (clients.Client, error) {
|
||||
func (_m *Repository) RetrieveByID(ctx context.Context, id string) (users.User, error) {
|
||||
ret := _m.Called(ctx, id)
|
||||
|
||||
if len(ret) == 0 {
|
||||
panic("no return value specified for RetrieveByID")
|
||||
}
|
||||
|
||||
var r0 clients.Client
|
||||
var r0 users.User
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(0).(func(context.Context, string) (clients.Client, error)); ok {
|
||||
if rf, ok := ret.Get(0).(func(context.Context, string) (users.User, error)); ok {
|
||||
return rf(ctx, id)
|
||||
}
|
||||
if rf, ok := ret.Get(0).(func(context.Context, string) clients.Client); ok {
|
||||
if rf, ok := ret.Get(0).(func(context.Context, string) users.User); ok {
|
||||
r0 = rf(ctx, id)
|
||||
} else {
|
||||
r0 = ret.Get(0).(clients.Client)
|
||||
r0 = ret.Get(0).(users.User)
|
||||
}
|
||||
|
||||
if rf, ok := ret.Get(1).(func(context.Context, string) error); ok {
|
||||
@@ -165,27 +192,27 @@ func (_m *Repository) RetrieveByID(ctx context.Context, id string) (clients.Clie
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// RetrieveByIdentity provides a mock function with given fields: ctx, identity
|
||||
func (_m *Repository) RetrieveByIdentity(ctx context.Context, identity string) (clients.Client, error) {
|
||||
ret := _m.Called(ctx, identity)
|
||||
// RetrieveByUsername provides a mock function with given fields: ctx, username
|
||||
func (_m *Repository) RetrieveByUsername(ctx context.Context, username string) (users.User, error) {
|
||||
ret := _m.Called(ctx, username)
|
||||
|
||||
if len(ret) == 0 {
|
||||
panic("no return value specified for RetrieveByIdentity")
|
||||
panic("no return value specified for RetrieveByUsername")
|
||||
}
|
||||
|
||||
var r0 clients.Client
|
||||
var r0 users.User
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(0).(func(context.Context, string) (clients.Client, error)); ok {
|
||||
return rf(ctx, identity)
|
||||
if rf, ok := ret.Get(0).(func(context.Context, string) (users.User, error)); ok {
|
||||
return rf(ctx, username)
|
||||
}
|
||||
if rf, ok := ret.Get(0).(func(context.Context, string) clients.Client); ok {
|
||||
r0 = rf(ctx, identity)
|
||||
if rf, ok := ret.Get(0).(func(context.Context, string) users.User); ok {
|
||||
r0 = rf(ctx, username)
|
||||
} else {
|
||||
r0 = ret.Get(0).(clients.Client)
|
||||
r0 = ret.Get(0).(users.User)
|
||||
}
|
||||
|
||||
if rf, ok := ret.Get(1).(func(context.Context, string) error); ok {
|
||||
r1 = rf(ctx, identity)
|
||||
r1 = rf(ctx, username)
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
@@ -193,27 +220,27 @@ func (_m *Repository) RetrieveByIdentity(ctx context.Context, identity string) (
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// Save provides a mock function with given fields: ctx, client
|
||||
func (_m *Repository) Save(ctx context.Context, client clients.Client) (clients.Client, error) {
|
||||
ret := _m.Called(ctx, client)
|
||||
// Save provides a mock function with given fields: ctx, user
|
||||
func (_m *Repository) Save(ctx context.Context, user users.User) (users.User, error) {
|
||||
ret := _m.Called(ctx, user)
|
||||
|
||||
if len(ret) == 0 {
|
||||
panic("no return value specified for Save")
|
||||
}
|
||||
|
||||
var r0 clients.Client
|
||||
var r0 users.User
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(0).(func(context.Context, clients.Client) (clients.Client, error)); ok {
|
||||
return rf(ctx, client)
|
||||
if rf, ok := ret.Get(0).(func(context.Context, users.User) (users.User, error)); ok {
|
||||
return rf(ctx, user)
|
||||
}
|
||||
if rf, ok := ret.Get(0).(func(context.Context, clients.Client) clients.Client); ok {
|
||||
r0 = rf(ctx, client)
|
||||
if rf, ok := ret.Get(0).(func(context.Context, users.User) users.User); ok {
|
||||
r0 = rf(ctx, user)
|
||||
} else {
|
||||
r0 = ret.Get(0).(clients.Client)
|
||||
r0 = ret.Get(0).(users.User)
|
||||
}
|
||||
|
||||
if rf, ok := ret.Get(1).(func(context.Context, clients.Client) error); ok {
|
||||
r1 = rf(ctx, client)
|
||||
if rf, ok := ret.Get(1).(func(context.Context, users.User) error); ok {
|
||||
r1 = rf(ctx, user)
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
@@ -221,26 +248,26 @@ func (_m *Repository) Save(ctx context.Context, client clients.Client) (clients.
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// SearchClients provides a mock function with given fields: ctx, pm
|
||||
func (_m *Repository) SearchClients(ctx context.Context, pm clients.Page) (clients.ClientsPage, error) {
|
||||
// SearchUsers provides a mock function with given fields: ctx, pm
|
||||
func (_m *Repository) SearchUsers(ctx context.Context, pm users.Page) (users.UsersPage, error) {
|
||||
ret := _m.Called(ctx, pm)
|
||||
|
||||
if len(ret) == 0 {
|
||||
panic("no return value specified for SearchClients")
|
||||
panic("no return value specified for SearchUsers")
|
||||
}
|
||||
|
||||
var r0 clients.ClientsPage
|
||||
var r0 users.UsersPage
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(0).(func(context.Context, clients.Page) (clients.ClientsPage, error)); ok {
|
||||
if rf, ok := ret.Get(0).(func(context.Context, users.Page) (users.UsersPage, error)); ok {
|
||||
return rf(ctx, pm)
|
||||
}
|
||||
if rf, ok := ret.Get(0).(func(context.Context, clients.Page) clients.ClientsPage); ok {
|
||||
if rf, ok := ret.Get(0).(func(context.Context, users.Page) users.UsersPage); ok {
|
||||
r0 = rf(ctx, pm)
|
||||
} else {
|
||||
r0 = ret.Get(0).(clients.ClientsPage)
|
||||
r0 = ret.Get(0).(users.UsersPage)
|
||||
}
|
||||
|
||||
if rf, ok := ret.Get(1).(func(context.Context, clients.Page) error); ok {
|
||||
if rf, ok := ret.Get(1).(func(context.Context, users.Page) error); ok {
|
||||
r1 = rf(ctx, pm)
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
@@ -249,27 +276,27 @@ func (_m *Repository) SearchClients(ctx context.Context, pm clients.Page) (clien
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// Update provides a mock function with given fields: ctx, client
|
||||
func (_m *Repository) Update(ctx context.Context, client clients.Client) (clients.Client, error) {
|
||||
ret := _m.Called(ctx, client)
|
||||
// Update provides a mock function with given fields: ctx, user
|
||||
func (_m *Repository) Update(ctx context.Context, user users.User) (users.User, error) {
|
||||
ret := _m.Called(ctx, user)
|
||||
|
||||
if len(ret) == 0 {
|
||||
panic("no return value specified for Update")
|
||||
}
|
||||
|
||||
var r0 clients.Client
|
||||
var r0 users.User
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(0).(func(context.Context, clients.Client) (clients.Client, error)); ok {
|
||||
return rf(ctx, client)
|
||||
if rf, ok := ret.Get(0).(func(context.Context, users.User) (users.User, error)); ok {
|
||||
return rf(ctx, user)
|
||||
}
|
||||
if rf, ok := ret.Get(0).(func(context.Context, clients.Client) clients.Client); ok {
|
||||
r0 = rf(ctx, client)
|
||||
if rf, ok := ret.Get(0).(func(context.Context, users.User) users.User); ok {
|
||||
r0 = rf(ctx, user)
|
||||
} else {
|
||||
r0 = ret.Get(0).(clients.Client)
|
||||
r0 = ret.Get(0).(users.User)
|
||||
}
|
||||
|
||||
if rf, ok := ret.Get(1).(func(context.Context, clients.Client) error); ok {
|
||||
r1 = rf(ctx, client)
|
||||
if rf, ok := ret.Get(1).(func(context.Context, users.User) error); ok {
|
||||
r1 = rf(ctx, user)
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
@@ -277,83 +304,27 @@ func (_m *Repository) Update(ctx context.Context, client clients.Client) (client
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// UpdateIdentity provides a mock function with given fields: ctx, client
|
||||
func (_m *Repository) UpdateIdentity(ctx context.Context, client clients.Client) (clients.Client, error) {
|
||||
ret := _m.Called(ctx, client)
|
||||
|
||||
if len(ret) == 0 {
|
||||
panic("no return value specified for UpdateIdentity")
|
||||
}
|
||||
|
||||
var r0 clients.Client
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(0).(func(context.Context, clients.Client) (clients.Client, error)); ok {
|
||||
return rf(ctx, client)
|
||||
}
|
||||
if rf, ok := ret.Get(0).(func(context.Context, clients.Client) clients.Client); ok {
|
||||
r0 = rf(ctx, client)
|
||||
} else {
|
||||
r0 = ret.Get(0).(clients.Client)
|
||||
}
|
||||
|
||||
if rf, ok := ret.Get(1).(func(context.Context, clients.Client) error); ok {
|
||||
r1 = rf(ctx, client)
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// UpdateRole provides a mock function with given fields: ctx, client
|
||||
func (_m *Repository) UpdateRole(ctx context.Context, client clients.Client) (clients.Client, error) {
|
||||
ret := _m.Called(ctx, client)
|
||||
|
||||
if len(ret) == 0 {
|
||||
panic("no return value specified for UpdateRole")
|
||||
}
|
||||
|
||||
var r0 clients.Client
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(0).(func(context.Context, clients.Client) (clients.Client, error)); ok {
|
||||
return rf(ctx, client)
|
||||
}
|
||||
if rf, ok := ret.Get(0).(func(context.Context, clients.Client) clients.Client); ok {
|
||||
r0 = rf(ctx, client)
|
||||
} else {
|
||||
r0 = ret.Get(0).(clients.Client)
|
||||
}
|
||||
|
||||
if rf, ok := ret.Get(1).(func(context.Context, clients.Client) error); ok {
|
||||
r1 = rf(ctx, client)
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// UpdateSecret provides a mock function with given fields: ctx, client
|
||||
func (_m *Repository) UpdateSecret(ctx context.Context, client clients.Client) (clients.Client, error) {
|
||||
ret := _m.Called(ctx, client)
|
||||
// UpdateSecret provides a mock function with given fields: ctx, user
|
||||
func (_m *Repository) UpdateSecret(ctx context.Context, user users.User) (users.User, error) {
|
||||
ret := _m.Called(ctx, user)
|
||||
|
||||
if len(ret) == 0 {
|
||||
panic("no return value specified for UpdateSecret")
|
||||
}
|
||||
|
||||
var r0 clients.Client
|
||||
var r0 users.User
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(0).(func(context.Context, clients.Client) (clients.Client, error)); ok {
|
||||
return rf(ctx, client)
|
||||
if rf, ok := ret.Get(0).(func(context.Context, users.User) (users.User, error)); ok {
|
||||
return rf(ctx, user)
|
||||
}
|
||||
if rf, ok := ret.Get(0).(func(context.Context, clients.Client) clients.Client); ok {
|
||||
r0 = rf(ctx, client)
|
||||
if rf, ok := ret.Get(0).(func(context.Context, users.User) users.User); ok {
|
||||
r0 = rf(ctx, user)
|
||||
} else {
|
||||
r0 = ret.Get(0).(clients.Client)
|
||||
r0 = ret.Get(0).(users.User)
|
||||
}
|
||||
|
||||
if rf, ok := ret.Get(1).(func(context.Context, clients.Client) error); ok {
|
||||
r1 = rf(ctx, client)
|
||||
if rf, ok := ret.Get(1).(func(context.Context, users.User) error); ok {
|
||||
r1 = rf(ctx, user)
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
@@ -361,27 +332,27 @@ func (_m *Repository) UpdateSecret(ctx context.Context, client clients.Client) (
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// UpdateTags provides a mock function with given fields: ctx, client
|
||||
func (_m *Repository) UpdateTags(ctx context.Context, client clients.Client) (clients.Client, error) {
|
||||
ret := _m.Called(ctx, client)
|
||||
// UpdateUsername provides a mock function with given fields: ctx, user
|
||||
func (_m *Repository) UpdateUsername(ctx context.Context, user users.User) (users.User, error) {
|
||||
ret := _m.Called(ctx, user)
|
||||
|
||||
if len(ret) == 0 {
|
||||
panic("no return value specified for UpdateTags")
|
||||
panic("no return value specified for UpdateUsername")
|
||||
}
|
||||
|
||||
var r0 clients.Client
|
||||
var r0 users.User
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(0).(func(context.Context, clients.Client) (clients.Client, error)); ok {
|
||||
return rf(ctx, client)
|
||||
if rf, ok := ret.Get(0).(func(context.Context, users.User) (users.User, error)); ok {
|
||||
return rf(ctx, user)
|
||||
}
|
||||
if rf, ok := ret.Get(0).(func(context.Context, clients.Client) clients.Client); ok {
|
||||
r0 = rf(ctx, client)
|
||||
if rf, ok := ret.Get(0).(func(context.Context, users.User) users.User); ok {
|
||||
r0 = rf(ctx, user)
|
||||
} else {
|
||||
r0 = ret.Get(0).(clients.Client)
|
||||
r0 = ret.Get(0).(users.User)
|
||||
}
|
||||
|
||||
if rf, ok := ret.Get(1).(func(context.Context, clients.Client) error); ok {
|
||||
r1 = rf(ctx, client)
|
||||
if rf, ok := ret.Get(1).(func(context.Context, users.User) error); ok {
|
||||
r1 = rf(ctx, user)
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
|
||||
+212
-155
@@ -5,14 +5,15 @@
|
||||
package mocks
|
||||
|
||||
import (
|
||||
authn "github.com/absmach/magistrala/pkg/authn"
|
||||
clients "github.com/absmach/magistrala/pkg/clients"
|
||||
|
||||
context "context"
|
||||
|
||||
authn "github.com/absmach/magistrala/pkg/authn"
|
||||
|
||||
magistrala "github.com/absmach/magistrala"
|
||||
|
||||
mock "github.com/stretchr/testify/mock"
|
||||
|
||||
users "github.com/absmach/magistrala/users"
|
||||
)
|
||||
|
||||
// Service is an autogenerated mock type for the Service type
|
||||
@@ -20,12 +21,12 @@ type Service struct {
|
||||
mock.Mock
|
||||
}
|
||||
|
||||
// DeleteClient provides a mock function with given fields: ctx, session, id
|
||||
func (_m *Service) DeleteClient(ctx context.Context, session authn.Session, id string) error {
|
||||
// Delete provides a mock function with given fields: ctx, session, id
|
||||
func (_m *Service) Delete(ctx context.Context, session authn.Session, id string) error {
|
||||
ret := _m.Called(ctx, session, id)
|
||||
|
||||
if len(ret) == 0 {
|
||||
panic("no return value specified for DeleteClient")
|
||||
panic("no return value specified for Delete")
|
||||
}
|
||||
|
||||
var r0 error
|
||||
@@ -38,23 +39,23 @@ func (_m *Service) DeleteClient(ctx context.Context, session authn.Session, id s
|
||||
return r0
|
||||
}
|
||||
|
||||
// DisableClient provides a mock function with given fields: ctx, session, id
|
||||
func (_m *Service) DisableClient(ctx context.Context, session authn.Session, id string) (clients.Client, error) {
|
||||
// Disable provides a mock function with given fields: ctx, session, id
|
||||
func (_m *Service) Disable(ctx context.Context, session authn.Session, id string) (users.User, error) {
|
||||
ret := _m.Called(ctx, session, id)
|
||||
|
||||
if len(ret) == 0 {
|
||||
panic("no return value specified for DisableClient")
|
||||
panic("no return value specified for Disable")
|
||||
}
|
||||
|
||||
var r0 clients.Client
|
||||
var r0 users.User
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(0).(func(context.Context, authn.Session, string) (clients.Client, error)); ok {
|
||||
if rf, ok := ret.Get(0).(func(context.Context, authn.Session, string) (users.User, error)); ok {
|
||||
return rf(ctx, session, id)
|
||||
}
|
||||
if rf, ok := ret.Get(0).(func(context.Context, authn.Session, string) clients.Client); ok {
|
||||
if rf, ok := ret.Get(0).(func(context.Context, authn.Session, string) users.User); ok {
|
||||
r0 = rf(ctx, session, id)
|
||||
} else {
|
||||
r0 = ret.Get(0).(clients.Client)
|
||||
r0 = ret.Get(0).(users.User)
|
||||
}
|
||||
|
||||
if rf, ok := ret.Get(1).(func(context.Context, authn.Session, string) error); ok {
|
||||
@@ -66,23 +67,23 @@ func (_m *Service) DisableClient(ctx context.Context, session authn.Session, id
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// EnableClient provides a mock function with given fields: ctx, session, id
|
||||
func (_m *Service) EnableClient(ctx context.Context, session authn.Session, id string) (clients.Client, error) {
|
||||
// Enable provides a mock function with given fields: ctx, session, id
|
||||
func (_m *Service) Enable(ctx context.Context, session authn.Session, id string) (users.User, error) {
|
||||
ret := _m.Called(ctx, session, id)
|
||||
|
||||
if len(ret) == 0 {
|
||||
panic("no return value specified for EnableClient")
|
||||
panic("no return value specified for Enable")
|
||||
}
|
||||
|
||||
var r0 clients.Client
|
||||
var r0 users.User
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(0).(func(context.Context, authn.Session, string) (clients.Client, error)); ok {
|
||||
if rf, ok := ret.Get(0).(func(context.Context, authn.Session, string) (users.User, error)); ok {
|
||||
return rf(ctx, session, id)
|
||||
}
|
||||
if rf, ok := ret.Get(0).(func(context.Context, authn.Session, string) clients.Client); ok {
|
||||
if rf, ok := ret.Get(0).(func(context.Context, authn.Session, string) users.User); ok {
|
||||
r0 = rf(ctx, session, id)
|
||||
} else {
|
||||
r0 = ret.Get(0).(clients.Client)
|
||||
r0 = ret.Get(0).(users.User)
|
||||
}
|
||||
|
||||
if rf, ok := ret.Get(1).(func(context.Context, authn.Session, string) error); ok {
|
||||
@@ -170,54 +171,26 @@ func (_m *Service) IssueToken(ctx context.Context, identity string, secret strin
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// ListClients provides a mock function with given fields: ctx, session, pm
|
||||
func (_m *Service) ListClients(ctx context.Context, session authn.Session, pm clients.Page) (clients.ClientsPage, error) {
|
||||
ret := _m.Called(ctx, session, pm)
|
||||
|
||||
if len(ret) == 0 {
|
||||
panic("no return value specified for ListClients")
|
||||
}
|
||||
|
||||
var r0 clients.ClientsPage
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(0).(func(context.Context, authn.Session, clients.Page) (clients.ClientsPage, error)); ok {
|
||||
return rf(ctx, session, pm)
|
||||
}
|
||||
if rf, ok := ret.Get(0).(func(context.Context, authn.Session, clients.Page) clients.ClientsPage); ok {
|
||||
r0 = rf(ctx, session, pm)
|
||||
} else {
|
||||
r0 = ret.Get(0).(clients.ClientsPage)
|
||||
}
|
||||
|
||||
if rf, ok := ret.Get(1).(func(context.Context, authn.Session, clients.Page) error); ok {
|
||||
r1 = rf(ctx, session, pm)
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// ListMembers provides a mock function with given fields: ctx, session, objectKind, objectID, pm
|
||||
func (_m *Service) ListMembers(ctx context.Context, session authn.Session, objectKind string, objectID string, pm clients.Page) (clients.MembersPage, error) {
|
||||
func (_m *Service) ListMembers(ctx context.Context, session authn.Session, objectKind string, objectID string, pm users.Page) (users.MembersPage, error) {
|
||||
ret := _m.Called(ctx, session, objectKind, objectID, pm)
|
||||
|
||||
if len(ret) == 0 {
|
||||
panic("no return value specified for ListMembers")
|
||||
}
|
||||
|
||||
var r0 clients.MembersPage
|
||||
var r0 users.MembersPage
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(0).(func(context.Context, authn.Session, string, string, clients.Page) (clients.MembersPage, error)); ok {
|
||||
if rf, ok := ret.Get(0).(func(context.Context, authn.Session, string, string, users.Page) (users.MembersPage, error)); ok {
|
||||
return rf(ctx, session, objectKind, objectID, pm)
|
||||
}
|
||||
if rf, ok := ret.Get(0).(func(context.Context, authn.Session, string, string, clients.Page) clients.MembersPage); ok {
|
||||
if rf, ok := ret.Get(0).(func(context.Context, authn.Session, string, string, users.Page) users.MembersPage); ok {
|
||||
r0 = rf(ctx, session, objectKind, objectID, pm)
|
||||
} else {
|
||||
r0 = ret.Get(0).(clients.MembersPage)
|
||||
r0 = ret.Get(0).(users.MembersPage)
|
||||
}
|
||||
|
||||
if rf, ok := ret.Get(1).(func(context.Context, authn.Session, string, string, clients.Page) error); ok {
|
||||
if rf, ok := ret.Get(1).(func(context.Context, authn.Session, string, string, users.Page) error); ok {
|
||||
r1 = rf(ctx, session, objectKind, objectID, pm)
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
@@ -226,17 +199,45 @@ func (_m *Service) ListMembers(ctx context.Context, session authn.Session, objec
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// OAuthAddClientPolicy provides a mock function with given fields: ctx, client
|
||||
func (_m *Service) OAuthAddClientPolicy(ctx context.Context, client clients.Client) error {
|
||||
ret := _m.Called(ctx, client)
|
||||
// ListUsers provides a mock function with given fields: ctx, session, pm
|
||||
func (_m *Service) ListUsers(ctx context.Context, session authn.Session, pm users.Page) (users.UsersPage, error) {
|
||||
ret := _m.Called(ctx, session, pm)
|
||||
|
||||
if len(ret) == 0 {
|
||||
panic("no return value specified for OAuthAddClientPolicy")
|
||||
panic("no return value specified for ListUsers")
|
||||
}
|
||||
|
||||
var r0 users.UsersPage
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(0).(func(context.Context, authn.Session, users.Page) (users.UsersPage, error)); ok {
|
||||
return rf(ctx, session, pm)
|
||||
}
|
||||
if rf, ok := ret.Get(0).(func(context.Context, authn.Session, users.Page) users.UsersPage); ok {
|
||||
r0 = rf(ctx, session, pm)
|
||||
} else {
|
||||
r0 = ret.Get(0).(users.UsersPage)
|
||||
}
|
||||
|
||||
if rf, ok := ret.Get(1).(func(context.Context, authn.Session, users.Page) error); ok {
|
||||
r1 = rf(ctx, session, pm)
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// OAuthAddUserPolicy provides a mock function with given fields: ctx, user
|
||||
func (_m *Service) OAuthAddUserPolicy(ctx context.Context, user users.User) error {
|
||||
ret := _m.Called(ctx, user)
|
||||
|
||||
if len(ret) == 0 {
|
||||
panic("no return value specified for OAuthAddUserPolicy")
|
||||
}
|
||||
|
||||
var r0 error
|
||||
if rf, ok := ret.Get(0).(func(context.Context, clients.Client) error); ok {
|
||||
r0 = rf(ctx, client)
|
||||
if rf, ok := ret.Get(0).(func(context.Context, users.User) error); ok {
|
||||
r0 = rf(ctx, user)
|
||||
} else {
|
||||
r0 = ret.Error(0)
|
||||
}
|
||||
@@ -244,27 +245,27 @@ func (_m *Service) OAuthAddClientPolicy(ctx context.Context, client clients.Clie
|
||||
return r0
|
||||
}
|
||||
|
||||
// OAuthCallback provides a mock function with given fields: ctx, client
|
||||
func (_m *Service) OAuthCallback(ctx context.Context, client clients.Client) (clients.Client, error) {
|
||||
ret := _m.Called(ctx, client)
|
||||
// OAuthCallback provides a mock function with given fields: ctx, user
|
||||
func (_m *Service) OAuthCallback(ctx context.Context, user users.User) (users.User, error) {
|
||||
ret := _m.Called(ctx, user)
|
||||
|
||||
if len(ret) == 0 {
|
||||
panic("no return value specified for OAuthCallback")
|
||||
}
|
||||
|
||||
var r0 clients.Client
|
||||
var r0 users.User
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(0).(func(context.Context, clients.Client) (clients.Client, error)); ok {
|
||||
return rf(ctx, client)
|
||||
if rf, ok := ret.Get(0).(func(context.Context, users.User) (users.User, error)); ok {
|
||||
return rf(ctx, user)
|
||||
}
|
||||
if rf, ok := ret.Get(0).(func(context.Context, clients.Client) clients.Client); ok {
|
||||
r0 = rf(ctx, client)
|
||||
if rf, ok := ret.Get(0).(func(context.Context, users.User) users.User); ok {
|
||||
r0 = rf(ctx, user)
|
||||
} else {
|
||||
r0 = ret.Get(0).(clients.Client)
|
||||
r0 = ret.Get(0).(users.User)
|
||||
}
|
||||
|
||||
if rf, ok := ret.Get(1).(func(context.Context, clients.Client) error); ok {
|
||||
r1 = rf(ctx, client)
|
||||
if rf, ok := ret.Get(1).(func(context.Context, users.User) error); ok {
|
||||
r1 = rf(ctx, user)
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
@@ -302,27 +303,27 @@ func (_m *Service) RefreshToken(ctx context.Context, session authn.Session, refr
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// RegisterClient provides a mock function with given fields: ctx, session, client, selfRegister
|
||||
func (_m *Service) RegisterClient(ctx context.Context, session authn.Session, client clients.Client, selfRegister bool) (clients.Client, error) {
|
||||
ret := _m.Called(ctx, session, client, selfRegister)
|
||||
// Register provides a mock function with given fields: ctx, session, user, selfRegister
|
||||
func (_m *Service) Register(ctx context.Context, session authn.Session, user users.User, selfRegister bool) (users.User, error) {
|
||||
ret := _m.Called(ctx, session, user, selfRegister)
|
||||
|
||||
if len(ret) == 0 {
|
||||
panic("no return value specified for RegisterClient")
|
||||
panic("no return value specified for Register")
|
||||
}
|
||||
|
||||
var r0 clients.Client
|
||||
var r0 users.User
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(0).(func(context.Context, authn.Session, clients.Client, bool) (clients.Client, error)); ok {
|
||||
return rf(ctx, session, client, selfRegister)
|
||||
if rf, ok := ret.Get(0).(func(context.Context, authn.Session, users.User, bool) (users.User, error)); ok {
|
||||
return rf(ctx, session, user, selfRegister)
|
||||
}
|
||||
if rf, ok := ret.Get(0).(func(context.Context, authn.Session, clients.Client, bool) clients.Client); ok {
|
||||
r0 = rf(ctx, session, client, selfRegister)
|
||||
if rf, ok := ret.Get(0).(func(context.Context, authn.Session, users.User, bool) users.User); ok {
|
||||
r0 = rf(ctx, session, user, selfRegister)
|
||||
} else {
|
||||
r0 = ret.Get(0).(clients.Client)
|
||||
r0 = ret.Get(0).(users.User)
|
||||
}
|
||||
|
||||
if rf, ok := ret.Get(1).(func(context.Context, authn.Session, clients.Client, bool) error); ok {
|
||||
r1 = rf(ctx, session, client, selfRegister)
|
||||
if rf, ok := ret.Get(1).(func(context.Context, authn.Session, users.User, bool) error); ok {
|
||||
r1 = rf(ctx, session, user, selfRegister)
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
@@ -349,25 +350,25 @@ func (_m *Service) ResetSecret(ctx context.Context, session authn.Session, secre
|
||||
}
|
||||
|
||||
// SearchUsers provides a mock function with given fields: ctx, pm
|
||||
func (_m *Service) SearchUsers(ctx context.Context, pm clients.Page) (clients.ClientsPage, error) {
|
||||
func (_m *Service) SearchUsers(ctx context.Context, pm users.Page) (users.UsersPage, error) {
|
||||
ret := _m.Called(ctx, pm)
|
||||
|
||||
if len(ret) == 0 {
|
||||
panic("no return value specified for SearchUsers")
|
||||
}
|
||||
|
||||
var r0 clients.ClientsPage
|
||||
var r0 users.UsersPage
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(0).(func(context.Context, clients.Page) (clients.ClientsPage, error)); ok {
|
||||
if rf, ok := ret.Get(0).(func(context.Context, users.Page) (users.UsersPage, error)); ok {
|
||||
return rf(ctx, pm)
|
||||
}
|
||||
if rf, ok := ret.Get(0).(func(context.Context, clients.Page) clients.ClientsPage); ok {
|
||||
if rf, ok := ret.Get(0).(func(context.Context, users.Page) users.UsersPage); ok {
|
||||
r0 = rf(ctx, pm)
|
||||
} else {
|
||||
r0 = ret.Get(0).(clients.ClientsPage)
|
||||
r0 = ret.Get(0).(users.UsersPage)
|
||||
}
|
||||
|
||||
if rf, ok := ret.Get(1).(func(context.Context, clients.Page) error); ok {
|
||||
if rf, ok := ret.Get(1).(func(context.Context, users.Page) error); ok {
|
||||
r1 = rf(ctx, pm)
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
@@ -394,27 +395,27 @@ func (_m *Service) SendPasswordReset(ctx context.Context, host string, email str
|
||||
return r0
|
||||
}
|
||||
|
||||
// UpdateClient provides a mock function with given fields: ctx, session, client
|
||||
func (_m *Service) UpdateClient(ctx context.Context, session authn.Session, client clients.Client) (clients.Client, error) {
|
||||
ret := _m.Called(ctx, session, client)
|
||||
// Update provides a mock function with given fields: ctx, session, user
|
||||
func (_m *Service) Update(ctx context.Context, session authn.Session, user users.User) (users.User, error) {
|
||||
ret := _m.Called(ctx, session, user)
|
||||
|
||||
if len(ret) == 0 {
|
||||
panic("no return value specified for UpdateClient")
|
||||
panic("no return value specified for Update")
|
||||
}
|
||||
|
||||
var r0 clients.Client
|
||||
var r0 users.User
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(0).(func(context.Context, authn.Session, clients.Client) (clients.Client, error)); ok {
|
||||
return rf(ctx, session, client)
|
||||
if rf, ok := ret.Get(0).(func(context.Context, authn.Session, users.User) (users.User, error)); ok {
|
||||
return rf(ctx, session, user)
|
||||
}
|
||||
if rf, ok := ret.Get(0).(func(context.Context, authn.Session, clients.Client) clients.Client); ok {
|
||||
r0 = rf(ctx, session, client)
|
||||
if rf, ok := ret.Get(0).(func(context.Context, authn.Session, users.User) users.User); ok {
|
||||
r0 = rf(ctx, session, user)
|
||||
} else {
|
||||
r0 = ret.Get(0).(clients.Client)
|
||||
r0 = ret.Get(0).(users.User)
|
||||
}
|
||||
|
||||
if rf, ok := ret.Get(1).(func(context.Context, authn.Session, clients.Client) error); ok {
|
||||
r1 = rf(ctx, session, client)
|
||||
if rf, ok := ret.Get(1).(func(context.Context, authn.Session, users.User) error); ok {
|
||||
r1 = rf(ctx, session, user)
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
@@ -422,27 +423,27 @@ func (_m *Service) UpdateClient(ctx context.Context, session authn.Session, clie
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// UpdateClientIdentity provides a mock function with given fields: ctx, session, id, identity
|
||||
func (_m *Service) UpdateClientIdentity(ctx context.Context, session authn.Session, id string, identity string) (clients.Client, error) {
|
||||
ret := _m.Called(ctx, session, id, identity)
|
||||
// UpdateEmail provides a mock function with given fields: ctx, session, id, email
|
||||
func (_m *Service) UpdateEmail(ctx context.Context, session authn.Session, id string, email string) (users.User, error) {
|
||||
ret := _m.Called(ctx, session, id, email)
|
||||
|
||||
if len(ret) == 0 {
|
||||
panic("no return value specified for UpdateClientIdentity")
|
||||
panic("no return value specified for UpdateEmail")
|
||||
}
|
||||
|
||||
var r0 clients.Client
|
||||
var r0 users.User
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(0).(func(context.Context, authn.Session, string, string) (clients.Client, error)); ok {
|
||||
return rf(ctx, session, id, identity)
|
||||
if rf, ok := ret.Get(0).(func(context.Context, authn.Session, string, string) (users.User, error)); ok {
|
||||
return rf(ctx, session, id, email)
|
||||
}
|
||||
if rf, ok := ret.Get(0).(func(context.Context, authn.Session, string, string) clients.Client); ok {
|
||||
r0 = rf(ctx, session, id, identity)
|
||||
if rf, ok := ret.Get(0).(func(context.Context, authn.Session, string, string) users.User); ok {
|
||||
r0 = rf(ctx, session, id, email)
|
||||
} else {
|
||||
r0 = ret.Get(0).(clients.Client)
|
||||
r0 = ret.Get(0).(users.User)
|
||||
}
|
||||
|
||||
if rf, ok := ret.Get(1).(func(context.Context, authn.Session, string, string) error); ok {
|
||||
r1 = rf(ctx, session, id, identity)
|
||||
r1 = rf(ctx, session, id, email)
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
@@ -450,27 +451,27 @@ func (_m *Service) UpdateClientIdentity(ctx context.Context, session authn.Sessi
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// UpdateClientRole provides a mock function with given fields: ctx, session, client
|
||||
func (_m *Service) UpdateClientRole(ctx context.Context, session authn.Session, client clients.Client) (clients.Client, error) {
|
||||
ret := _m.Called(ctx, session, client)
|
||||
// UpdateProfilePicture provides a mock function with given fields: ctx, session, user
|
||||
func (_m *Service) UpdateProfilePicture(ctx context.Context, session authn.Session, user users.User) (users.User, error) {
|
||||
ret := _m.Called(ctx, session, user)
|
||||
|
||||
if len(ret) == 0 {
|
||||
panic("no return value specified for UpdateClientRole")
|
||||
panic("no return value specified for UpdateProfilePicture")
|
||||
}
|
||||
|
||||
var r0 clients.Client
|
||||
var r0 users.User
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(0).(func(context.Context, authn.Session, clients.Client) (clients.Client, error)); ok {
|
||||
return rf(ctx, session, client)
|
||||
if rf, ok := ret.Get(0).(func(context.Context, authn.Session, users.User) (users.User, error)); ok {
|
||||
return rf(ctx, session, user)
|
||||
}
|
||||
if rf, ok := ret.Get(0).(func(context.Context, authn.Session, clients.Client) clients.Client); ok {
|
||||
r0 = rf(ctx, session, client)
|
||||
if rf, ok := ret.Get(0).(func(context.Context, authn.Session, users.User) users.User); ok {
|
||||
r0 = rf(ctx, session, user)
|
||||
} else {
|
||||
r0 = ret.Get(0).(clients.Client)
|
||||
r0 = ret.Get(0).(users.User)
|
||||
}
|
||||
|
||||
if rf, ok := ret.Get(1).(func(context.Context, authn.Session, clients.Client) error); ok {
|
||||
r1 = rf(ctx, session, client)
|
||||
if rf, ok := ret.Get(1).(func(context.Context, authn.Session, users.User) error); ok {
|
||||
r1 = rf(ctx, session, user)
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
@@ -478,23 +479,51 @@ func (_m *Service) UpdateClientRole(ctx context.Context, session authn.Session,
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// UpdateClientSecret provides a mock function with given fields: ctx, session, oldSecret, newSecret
|
||||
func (_m *Service) UpdateClientSecret(ctx context.Context, session authn.Session, oldSecret string, newSecret string) (clients.Client, error) {
|
||||
// UpdateRole provides a mock function with given fields: ctx, session, user
|
||||
func (_m *Service) UpdateRole(ctx context.Context, session authn.Session, user users.User) (users.User, error) {
|
||||
ret := _m.Called(ctx, session, user)
|
||||
|
||||
if len(ret) == 0 {
|
||||
panic("no return value specified for UpdateRole")
|
||||
}
|
||||
|
||||
var r0 users.User
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(0).(func(context.Context, authn.Session, users.User) (users.User, error)); ok {
|
||||
return rf(ctx, session, user)
|
||||
}
|
||||
if rf, ok := ret.Get(0).(func(context.Context, authn.Session, users.User) users.User); ok {
|
||||
r0 = rf(ctx, session, user)
|
||||
} else {
|
||||
r0 = ret.Get(0).(users.User)
|
||||
}
|
||||
|
||||
if rf, ok := ret.Get(1).(func(context.Context, authn.Session, users.User) error); ok {
|
||||
r1 = rf(ctx, session, user)
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// UpdateSecret provides a mock function with given fields: ctx, session, oldSecret, newSecret
|
||||
func (_m *Service) UpdateSecret(ctx context.Context, session authn.Session, oldSecret string, newSecret string) (users.User, error) {
|
||||
ret := _m.Called(ctx, session, oldSecret, newSecret)
|
||||
|
||||
if len(ret) == 0 {
|
||||
panic("no return value specified for UpdateClientSecret")
|
||||
panic("no return value specified for UpdateSecret")
|
||||
}
|
||||
|
||||
var r0 clients.Client
|
||||
var r0 users.User
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(0).(func(context.Context, authn.Session, string, string) (clients.Client, error)); ok {
|
||||
if rf, ok := ret.Get(0).(func(context.Context, authn.Session, string, string) (users.User, error)); ok {
|
||||
return rf(ctx, session, oldSecret, newSecret)
|
||||
}
|
||||
if rf, ok := ret.Get(0).(func(context.Context, authn.Session, string, string) clients.Client); ok {
|
||||
if rf, ok := ret.Get(0).(func(context.Context, authn.Session, string, string) users.User); ok {
|
||||
r0 = rf(ctx, session, oldSecret, newSecret)
|
||||
} else {
|
||||
r0 = ret.Get(0).(clients.Client)
|
||||
r0 = ret.Get(0).(users.User)
|
||||
}
|
||||
|
||||
if rf, ok := ret.Get(1).(func(context.Context, authn.Session, string, string) error); ok {
|
||||
@@ -506,27 +535,27 @@ func (_m *Service) UpdateClientSecret(ctx context.Context, session authn.Session
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// UpdateClientTags provides a mock function with given fields: ctx, session, client
|
||||
func (_m *Service) UpdateClientTags(ctx context.Context, session authn.Session, client clients.Client) (clients.Client, error) {
|
||||
ret := _m.Called(ctx, session, client)
|
||||
// UpdateTags provides a mock function with given fields: ctx, session, user
|
||||
func (_m *Service) UpdateTags(ctx context.Context, session authn.Session, user users.User) (users.User, error) {
|
||||
ret := _m.Called(ctx, session, user)
|
||||
|
||||
if len(ret) == 0 {
|
||||
panic("no return value specified for UpdateClientTags")
|
||||
panic("no return value specified for UpdateTags")
|
||||
}
|
||||
|
||||
var r0 clients.Client
|
||||
var r0 users.User
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(0).(func(context.Context, authn.Session, clients.Client) (clients.Client, error)); ok {
|
||||
return rf(ctx, session, client)
|
||||
if rf, ok := ret.Get(0).(func(context.Context, authn.Session, users.User) (users.User, error)); ok {
|
||||
return rf(ctx, session, user)
|
||||
}
|
||||
if rf, ok := ret.Get(0).(func(context.Context, authn.Session, clients.Client) clients.Client); ok {
|
||||
r0 = rf(ctx, session, client)
|
||||
if rf, ok := ret.Get(0).(func(context.Context, authn.Session, users.User) users.User); ok {
|
||||
r0 = rf(ctx, session, user)
|
||||
} else {
|
||||
r0 = ret.Get(0).(clients.Client)
|
||||
r0 = ret.Get(0).(users.User)
|
||||
}
|
||||
|
||||
if rf, ok := ret.Get(1).(func(context.Context, authn.Session, clients.Client) error); ok {
|
||||
r1 = rf(ctx, session, client)
|
||||
if rf, ok := ret.Get(1).(func(context.Context, authn.Session, users.User) error); ok {
|
||||
r1 = rf(ctx, session, user)
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
@@ -534,23 +563,51 @@ func (_m *Service) UpdateClientTags(ctx context.Context, session authn.Session,
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// ViewClient provides a mock function with given fields: ctx, session, id
|
||||
func (_m *Service) ViewClient(ctx context.Context, session authn.Session, id string) (clients.Client, error) {
|
||||
// UpdateUsername provides a mock function with given fields: ctx, session, id, username
|
||||
func (_m *Service) UpdateUsername(ctx context.Context, session authn.Session, id string, username string) (users.User, error) {
|
||||
ret := _m.Called(ctx, session, id, username)
|
||||
|
||||
if len(ret) == 0 {
|
||||
panic("no return value specified for UpdateUsername")
|
||||
}
|
||||
|
||||
var r0 users.User
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(0).(func(context.Context, authn.Session, string, string) (users.User, error)); ok {
|
||||
return rf(ctx, session, id, username)
|
||||
}
|
||||
if rf, ok := ret.Get(0).(func(context.Context, authn.Session, string, string) users.User); ok {
|
||||
r0 = rf(ctx, session, id, username)
|
||||
} else {
|
||||
r0 = ret.Get(0).(users.User)
|
||||
}
|
||||
|
||||
if rf, ok := ret.Get(1).(func(context.Context, authn.Session, string, string) error); ok {
|
||||
r1 = rf(ctx, session, id, username)
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// View provides a mock function with given fields: ctx, session, id
|
||||
func (_m *Service) View(ctx context.Context, session authn.Session, id string) (users.User, error) {
|
||||
ret := _m.Called(ctx, session, id)
|
||||
|
||||
if len(ret) == 0 {
|
||||
panic("no return value specified for ViewClient")
|
||||
panic("no return value specified for View")
|
||||
}
|
||||
|
||||
var r0 clients.Client
|
||||
var r0 users.User
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(0).(func(context.Context, authn.Session, string) (clients.Client, error)); ok {
|
||||
if rf, ok := ret.Get(0).(func(context.Context, authn.Session, string) (users.User, error)); ok {
|
||||
return rf(ctx, session, id)
|
||||
}
|
||||
if rf, ok := ret.Get(0).(func(context.Context, authn.Session, string) clients.Client); ok {
|
||||
if rf, ok := ret.Get(0).(func(context.Context, authn.Session, string) users.User); ok {
|
||||
r0 = rf(ctx, session, id)
|
||||
} else {
|
||||
r0 = ret.Get(0).(clients.Client)
|
||||
r0 = ret.Get(0).(users.User)
|
||||
}
|
||||
|
||||
if rf, ok := ret.Get(1).(func(context.Context, authn.Session, string) error); ok {
|
||||
@@ -563,22 +620,22 @@ func (_m *Service) ViewClient(ctx context.Context, session authn.Session, id str
|
||||
}
|
||||
|
||||
// ViewProfile provides a mock function with given fields: ctx, session
|
||||
func (_m *Service) ViewProfile(ctx context.Context, session authn.Session) (clients.Client, error) {
|
||||
func (_m *Service) ViewProfile(ctx context.Context, session authn.Session) (users.User, error) {
|
||||
ret := _m.Called(ctx, session)
|
||||
|
||||
if len(ret) == 0 {
|
||||
panic("no return value specified for ViewProfile")
|
||||
}
|
||||
|
||||
var r0 clients.Client
|
||||
var r0 users.User
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(0).(func(context.Context, authn.Session) (clients.Client, error)); ok {
|
||||
if rf, ok := ret.Get(0).(func(context.Context, authn.Session) (users.User, error)); ok {
|
||||
return rf(ctx, session)
|
||||
}
|
||||
if rf, ok := ret.Get(0).(func(context.Context, authn.Session) clients.Client); ok {
|
||||
if rf, ok := ret.Get(0).(func(context.Context, authn.Session) users.User); ok {
|
||||
r0 = rf(ctx, session)
|
||||
} else {
|
||||
r0 = ret.Get(0).(clients.Client)
|
||||
r0 = ret.Get(0).(users.User)
|
||||
}
|
||||
|
||||
if rf, ok := ret.Get(1).(func(context.Context, authn.Session) error); ok {
|
||||
|
||||
@@ -1,203 +0,0 @@
|
||||
// Copyright (c) Abstract Machines
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package postgres
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
mgclients "github.com/absmach/magistrala/pkg/clients"
|
||||
pgclients "github.com/absmach/magistrala/pkg/clients/postgres"
|
||||
"github.com/absmach/magistrala/pkg/errors"
|
||||
repoerr "github.com/absmach/magistrala/pkg/errors/repository"
|
||||
"github.com/absmach/magistrala/pkg/postgres"
|
||||
)
|
||||
|
||||
var _ mgclients.Repository = (*clientRepo)(nil)
|
||||
|
||||
type clientRepo struct {
|
||||
pgclients.Repository
|
||||
}
|
||||
|
||||
// Repository defines the required dependencies for Client repository.
|
||||
//
|
||||
//go:generate mockery --name Repository --output=../mocks --filename repository.go --quiet --note "Copyright (c) Abstract Machines"
|
||||
type Repository interface {
|
||||
mgclients.Repository
|
||||
|
||||
// Save persists the client account. A non-nil error is returned to indicate
|
||||
// operation failure.
|
||||
Save(ctx context.Context, client mgclients.Client) (mgclients.Client, error)
|
||||
|
||||
RetrieveByID(ctx context.Context, id string) (mgclients.Client, error)
|
||||
|
||||
UpdateRole(ctx context.Context, client mgclients.Client) (mgclients.Client, error)
|
||||
|
||||
CheckSuperAdmin(ctx context.Context, adminID string) error
|
||||
}
|
||||
|
||||
// NewRepository instantiates a PostgreSQL
|
||||
// implementation of Clients repository.
|
||||
func NewRepository(db postgres.Database) Repository {
|
||||
return &clientRepo{
|
||||
Repository: pgclients.Repository{DB: db},
|
||||
}
|
||||
}
|
||||
|
||||
func (repo clientRepo) Save(ctx context.Context, c mgclients.Client) (mgclients.Client, error) {
|
||||
q := `INSERT INTO clients (id, name, tags, identity, secret, metadata, created_at, status, role)
|
||||
VALUES (:id, :name, :tags, :identity, :secret, :metadata, :created_at, :status, :role)
|
||||
RETURNING id, name, tags, identity, metadata, status, created_at`
|
||||
dbc, err := pgclients.ToDBClient(c)
|
||||
if err != nil {
|
||||
return mgclients.Client{}, errors.Wrap(repoerr.ErrCreateEntity, err)
|
||||
}
|
||||
|
||||
row, err := repo.DB.NamedQueryContext(ctx, q, dbc)
|
||||
if err != nil {
|
||||
return mgclients.Client{}, postgres.HandleError(repoerr.ErrCreateEntity, err)
|
||||
}
|
||||
|
||||
defer row.Close()
|
||||
row.Next()
|
||||
dbc = pgclients.DBClient{}
|
||||
if err := row.StructScan(&dbc); err != nil {
|
||||
return mgclients.Client{}, errors.Wrap(repoerr.ErrFailedOpDB, err)
|
||||
}
|
||||
|
||||
client, err := pgclients.ToClient(dbc)
|
||||
if err != nil {
|
||||
return mgclients.Client{}, errors.Wrap(repoerr.ErrFailedOpDB, err)
|
||||
}
|
||||
|
||||
return client, nil
|
||||
}
|
||||
|
||||
func (repo clientRepo) CheckSuperAdmin(ctx context.Context, adminID string) error {
|
||||
q := "SELECT 1 FROM clients WHERE id = $1 AND role = $2"
|
||||
rows, err := repo.DB.QueryContext(ctx, q, adminID, mgclients.AdminRole)
|
||||
if err != nil {
|
||||
return postgres.HandleError(repoerr.ErrViewEntity, err)
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
if rows.Next() {
|
||||
if err := rows.Err(); err != nil {
|
||||
return postgres.HandleError(repoerr.ErrViewEntity, err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
return repoerr.ErrNotFound
|
||||
}
|
||||
|
||||
func (repo clientRepo) RetrieveByID(ctx context.Context, id string) (mgclients.Client, error) {
|
||||
q := `SELECT id, name, tags, identity, secret, metadata, created_at, updated_at, updated_by, status, role
|
||||
FROM clients WHERE id = :id`
|
||||
|
||||
dbc := pgclients.DBClient{
|
||||
ID: id,
|
||||
}
|
||||
|
||||
rows, err := repo.DB.NamedQueryContext(ctx, q, dbc)
|
||||
if err != nil {
|
||||
return mgclients.Client{}, postgres.HandleError(repoerr.ErrViewEntity, err)
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
dbc = pgclients.DBClient{}
|
||||
if rows.Next() {
|
||||
if err = rows.StructScan(&dbc); err != nil {
|
||||
return mgclients.Client{}, postgres.HandleError(repoerr.ErrViewEntity, err)
|
||||
}
|
||||
|
||||
client, err := pgclients.ToClient(dbc)
|
||||
if err != nil {
|
||||
return mgclients.Client{}, errors.Wrap(repoerr.ErrFailedOpDB, err)
|
||||
}
|
||||
|
||||
return client, nil
|
||||
}
|
||||
|
||||
return mgclients.Client{}, repoerr.ErrNotFound
|
||||
}
|
||||
|
||||
func (repo clientRepo) RetrieveAll(ctx context.Context, pm mgclients.Page) (mgclients.ClientsPage, error) {
|
||||
query, err := pgclients.PageQuery(pm)
|
||||
if err != nil {
|
||||
return mgclients.ClientsPage{}, errors.Wrap(repoerr.ErrViewEntity, err)
|
||||
}
|
||||
|
||||
q := fmt.Sprintf(`SELECT c.id, c.name, c.tags, c.identity, c.metadata, c.status, c.role,
|
||||
c.created_at, c.updated_at, COALESCE(c.updated_by, '') AS updated_by FROM clients c %s ORDER BY c.created_at LIMIT :limit OFFSET :offset;`, query)
|
||||
|
||||
dbPage, err := pgclients.ToDBClientsPage(pm)
|
||||
if err != nil {
|
||||
return mgclients.ClientsPage{}, errors.Wrap(repoerr.ErrFailedToRetrieveAllGroups, err)
|
||||
}
|
||||
rows, err := repo.DB.NamedQueryContext(ctx, q, dbPage)
|
||||
if err != nil {
|
||||
return mgclients.ClientsPage{}, errors.Wrap(repoerr.ErrFailedToRetrieveAllGroups, err)
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
var items []mgclients.Client
|
||||
for rows.Next() {
|
||||
dbc := pgclients.DBClient{}
|
||||
if err := rows.StructScan(&dbc); err != nil {
|
||||
return mgclients.ClientsPage{}, errors.Wrap(repoerr.ErrViewEntity, err)
|
||||
}
|
||||
|
||||
c, err := pgclients.ToClient(dbc)
|
||||
if err != nil {
|
||||
return mgclients.ClientsPage{}, err
|
||||
}
|
||||
|
||||
items = append(items, c)
|
||||
}
|
||||
cq := fmt.Sprintf(`SELECT COUNT(*) FROM clients c %s;`, query)
|
||||
|
||||
total, err := postgres.Total(ctx, repo.DB, cq, dbPage)
|
||||
if err != nil {
|
||||
return mgclients.ClientsPage{}, errors.Wrap(repoerr.ErrViewEntity, err)
|
||||
}
|
||||
|
||||
page := mgclients.ClientsPage{
|
||||
Clients: items,
|
||||
Page: mgclients.Page{
|
||||
Total: total,
|
||||
Offset: pm.Offset,
|
||||
Limit: pm.Limit,
|
||||
},
|
||||
}
|
||||
|
||||
return page, nil
|
||||
}
|
||||
|
||||
func (repo clientRepo) UpdateRole(ctx context.Context, client mgclients.Client) (mgclients.Client, error) {
|
||||
query := `UPDATE clients SET role = :role, updated_at = :updated_at, updated_by = :updated_by
|
||||
WHERE id = :id AND status = :status
|
||||
RETURNING id, name, tags, identity, metadata, status, role, created_at, updated_at, updated_by`
|
||||
|
||||
dbc, err := pgclients.ToDBClient(client)
|
||||
if err != nil {
|
||||
return mgclients.Client{}, errors.Wrap(repoerr.ErrUpdateEntity, err)
|
||||
}
|
||||
|
||||
row, err := repo.DB.NamedQueryContext(ctx, query, dbc)
|
||||
if err != nil {
|
||||
return mgclients.Client{}, postgres.HandleError(err, repoerr.ErrUpdateEntity)
|
||||
}
|
||||
|
||||
defer row.Close()
|
||||
if ok := row.Next(); !ok {
|
||||
return mgclients.Client{}, errors.Wrap(repoerr.ErrNotFound, row.Err())
|
||||
}
|
||||
dbc = pgclients.DBClient{}
|
||||
if err := row.StructScan(&dbc); err != nil {
|
||||
return mgclients.Client{}, err
|
||||
}
|
||||
|
||||
return pgclients.ToClient(dbc)
|
||||
}
|
||||
@@ -1,754 +0,0 @@
|
||||
// Copyright (c) Abstract Machines
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package postgres_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/0x6flab/namegenerator"
|
||||
"github.com/absmach/magistrala/internal/testsutil"
|
||||
mgclients "github.com/absmach/magistrala/pkg/clients"
|
||||
"github.com/absmach/magistrala/pkg/errors"
|
||||
repoerr "github.com/absmach/magistrala/pkg/errors/repository"
|
||||
cpostgres "github.com/absmach/magistrala/users/postgres"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
const maxNameSize = 254
|
||||
|
||||
var (
|
||||
invalidName = strings.Repeat("m", maxNameSize+10)
|
||||
password = "$tr0ngPassw0rd"
|
||||
namesgen = namegenerator.NewGenerator()
|
||||
)
|
||||
|
||||
func TestClientsSave(t *testing.T) {
|
||||
t.Cleanup(func() {
|
||||
_, err := db.Exec("DELETE FROM clients")
|
||||
require.Nil(t, err, fmt.Sprintf("clean clients unexpected error: %s", err))
|
||||
})
|
||||
repo := cpostgres.NewRepository(database)
|
||||
|
||||
uid := testsutil.GenerateUUID(t)
|
||||
|
||||
name := namesgen.Generate()
|
||||
clientIdentity := name + "@example.com"
|
||||
|
||||
cases := []struct {
|
||||
desc string
|
||||
client mgclients.Client
|
||||
err error
|
||||
}{
|
||||
{
|
||||
desc: "add new client successfully",
|
||||
client: mgclients.Client{
|
||||
ID: uid,
|
||||
Name: name,
|
||||
Credentials: mgclients.Credentials{
|
||||
Identity: clientIdentity,
|
||||
Secret: password,
|
||||
},
|
||||
Metadata: mgclients.Metadata{},
|
||||
Status: mgclients.EnabledStatus,
|
||||
},
|
||||
err: nil,
|
||||
},
|
||||
{
|
||||
desc: "add client with duplicate client identity",
|
||||
client: mgclients.Client{
|
||||
ID: testsutil.GenerateUUID(t),
|
||||
Name: namesgen.Generate(),
|
||||
Credentials: mgclients.Credentials{
|
||||
Identity: clientIdentity,
|
||||
Secret: password,
|
||||
},
|
||||
Metadata: mgclients.Metadata{},
|
||||
Status: mgclients.EnabledStatus,
|
||||
},
|
||||
err: repoerr.ErrConflict,
|
||||
},
|
||||
{
|
||||
desc: "add client with duplicate client name",
|
||||
client: mgclients.Client{
|
||||
ID: testsutil.GenerateUUID(t),
|
||||
Name: name,
|
||||
Credentials: mgclients.Credentials{
|
||||
Identity: clientIdentity,
|
||||
Secret: password,
|
||||
},
|
||||
Metadata: mgclients.Metadata{},
|
||||
Status: mgclients.EnabledStatus,
|
||||
},
|
||||
err: repoerr.ErrConflict,
|
||||
},
|
||||
{
|
||||
desc: "add client with invalid client id",
|
||||
client: mgclients.Client{
|
||||
ID: invalidName,
|
||||
Name: namesgen.Generate(),
|
||||
Credentials: mgclients.Credentials{
|
||||
Identity: fmt.Sprintf("%s@example.com", namesgen.Generate()),
|
||||
Secret: password,
|
||||
},
|
||||
Metadata: mgclients.Metadata{},
|
||||
Status: mgclients.EnabledStatus,
|
||||
},
|
||||
err: errors.ErrMalformedEntity,
|
||||
},
|
||||
{
|
||||
desc: "add client with invalid client name",
|
||||
client: mgclients.Client{
|
||||
ID: testsutil.GenerateUUID(t),
|
||||
Name: invalidName,
|
||||
Credentials: mgclients.Credentials{
|
||||
Identity: fmt.Sprintf("%s@example.com", namesgen.Generate()),
|
||||
Secret: password,
|
||||
},
|
||||
Metadata: mgclients.Metadata{},
|
||||
Status: mgclients.EnabledStatus,
|
||||
},
|
||||
err: errors.ErrMalformedEntity,
|
||||
},
|
||||
{
|
||||
desc: "add client with invalid client identity",
|
||||
client: mgclients.Client{
|
||||
ID: testsutil.GenerateUUID(t),
|
||||
Name: namesgen.Generate(),
|
||||
Credentials: mgclients.Credentials{
|
||||
Identity: invalidName,
|
||||
Secret: password,
|
||||
},
|
||||
Metadata: mgclients.Metadata{},
|
||||
Status: mgclients.EnabledStatus,
|
||||
},
|
||||
err: errors.ErrMalformedEntity,
|
||||
},
|
||||
{
|
||||
desc: "add client with a missing client name",
|
||||
client: mgclients.Client{
|
||||
ID: testsutil.GenerateUUID(t),
|
||||
Credentials: mgclients.Credentials{
|
||||
Identity: fmt.Sprintf("%s@example.com", namesgen.Generate()),
|
||||
Secret: password,
|
||||
},
|
||||
Metadata: mgclients.Metadata{},
|
||||
},
|
||||
err: nil,
|
||||
},
|
||||
{
|
||||
desc: "add client with a missing client identity",
|
||||
client: mgclients.Client{
|
||||
ID: testsutil.GenerateUUID(t),
|
||||
Name: namesgen.Generate(),
|
||||
Credentials: mgclients.Credentials{
|
||||
Secret: password,
|
||||
},
|
||||
Metadata: mgclients.Metadata{},
|
||||
},
|
||||
err: nil,
|
||||
},
|
||||
{
|
||||
desc: "add client with a missing client secret",
|
||||
client: mgclients.Client{
|
||||
ID: testsutil.GenerateUUID(t),
|
||||
Name: namesgen.Generate(),
|
||||
Credentials: mgclients.Credentials{
|
||||
Identity: fmt.Sprintf("%s@example.com", namesgen.Generate()),
|
||||
},
|
||||
Metadata: mgclients.Metadata{},
|
||||
},
|
||||
err: nil,
|
||||
},
|
||||
{
|
||||
desc: "add a client with invalid metadata",
|
||||
client: mgclients.Client{
|
||||
ID: testsutil.GenerateUUID(t),
|
||||
Name: namesgen.Generate(),
|
||||
Credentials: mgclients.Credentials{
|
||||
Identity: fmt.Sprintf("%s@example.com", namesgen.Generate()),
|
||||
Secret: password,
|
||||
},
|
||||
Metadata: map[string]interface{}{
|
||||
"key": make(chan int),
|
||||
},
|
||||
},
|
||||
err: errors.ErrMalformedEntity,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range cases {
|
||||
rClient, err := repo.Save(context.Background(), tc.client)
|
||||
assert.True(t, errors.Contains(err, tc.err), fmt.Sprintf("%s: expected %s got %s\n", tc.desc, tc.err, err))
|
||||
if err == nil {
|
||||
rClient.Credentials.Secret = tc.client.Credentials.Secret
|
||||
assert.Equal(t, tc.client, rClient, fmt.Sprintf("%s: expected %v got %v\n", tc.desc, tc.client, rClient))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestIsPlatformAdmin(t *testing.T) {
|
||||
t.Cleanup(func() {
|
||||
_, err := db.Exec("DELETE FROM clients")
|
||||
require.Nil(t, err, fmt.Sprintf("clean clients unexpected error: %s", err))
|
||||
})
|
||||
repo := cpostgres.NewRepository(database)
|
||||
|
||||
cases := []struct {
|
||||
desc string
|
||||
client mgclients.Client
|
||||
err error
|
||||
}{
|
||||
{
|
||||
desc: "authorize check for super user",
|
||||
client: mgclients.Client{
|
||||
ID: testsutil.GenerateUUID(t),
|
||||
Name: namesgen.Generate(),
|
||||
Credentials: mgclients.Credentials{
|
||||
Identity: fmt.Sprintf("%s@example.com", namesgen.Generate()),
|
||||
Secret: password,
|
||||
},
|
||||
Metadata: mgclients.Metadata{},
|
||||
Status: mgclients.EnabledStatus,
|
||||
Role: mgclients.AdminRole,
|
||||
},
|
||||
err: nil,
|
||||
},
|
||||
{
|
||||
desc: "unauthorize user",
|
||||
client: mgclients.Client{
|
||||
ID: testsutil.GenerateUUID(t),
|
||||
Name: namesgen.Generate(),
|
||||
Credentials: mgclients.Credentials{
|
||||
Identity: fmt.Sprintf("%s@example.com", namesgen.Generate()),
|
||||
Secret: password,
|
||||
},
|
||||
Metadata: mgclients.Metadata{},
|
||||
Status: mgclients.EnabledStatus,
|
||||
Role: mgclients.UserRole,
|
||||
},
|
||||
err: repoerr.ErrNotFound,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range cases {
|
||||
_, err := repo.Save(context.Background(), tc.client)
|
||||
require.Nil(t, err, fmt.Sprintf("%s: save client unexpected error: %s", tc.desc, err))
|
||||
err = repo.CheckSuperAdmin(context.Background(), tc.client.ID)
|
||||
assert.True(t, errors.Contains(err, tc.err), fmt.Sprintf("%s: expected %v got %v\n", tc.desc, tc.err, err))
|
||||
}
|
||||
}
|
||||
|
||||
func TestRetrieveByID(t *testing.T) {
|
||||
t.Cleanup(func() {
|
||||
_, err := db.Exec("DELETE FROM clients")
|
||||
require.Nil(t, err, fmt.Sprintf("clean clients unexpected error: %s", err))
|
||||
})
|
||||
repo := cpostgres.NewRepository(database)
|
||||
|
||||
client := mgclients.Client{
|
||||
ID: testsutil.GenerateUUID(t),
|
||||
Name: namesgen.Generate(),
|
||||
Credentials: mgclients.Credentials{
|
||||
Identity: fmt.Sprintf("%s@example.com", namesgen.Generate()),
|
||||
Secret: password,
|
||||
},
|
||||
Metadata: mgclients.Metadata{},
|
||||
Status: mgclients.EnabledStatus,
|
||||
}
|
||||
|
||||
_, err := repo.Save(context.Background(), client)
|
||||
require.Nil(t, err, fmt.Sprintf("failed to save client %s", client.ID))
|
||||
|
||||
cases := []struct {
|
||||
desc string
|
||||
clientID string
|
||||
err error
|
||||
}{
|
||||
{
|
||||
desc: "retrieve existing client",
|
||||
clientID: client.ID,
|
||||
err: nil,
|
||||
},
|
||||
{
|
||||
desc: "retrieve non-existing client",
|
||||
clientID: invalidName,
|
||||
err: repoerr.ErrNotFound,
|
||||
},
|
||||
{
|
||||
desc: "retrieve with empty client id",
|
||||
clientID: "",
|
||||
err: repoerr.ErrNotFound,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range cases {
|
||||
_, err := repo.RetrieveByID(context.Background(), tc.clientID)
|
||||
assert.True(t, errors.Contains(err, tc.err), fmt.Sprintf("%s: expected %v got %v\n", tc.desc, tc.err, err))
|
||||
}
|
||||
}
|
||||
|
||||
func TestRetrieveAll(t *testing.T) {
|
||||
t.Cleanup(func() {
|
||||
_, err := db.Exec("DELETE FROM clients")
|
||||
require.Nil(t, err, fmt.Sprintf("clean clients unexpected error: %s", err))
|
||||
})
|
||||
|
||||
repo := cpostgres.NewRepository(database)
|
||||
|
||||
num := 200
|
||||
var items, enabledClients []mgclients.Client
|
||||
for i := 0; i < num; i++ {
|
||||
client := mgclients.Client{
|
||||
ID: testsutil.GenerateUUID(t),
|
||||
Name: namesgen.Generate(),
|
||||
Credentials: mgclients.Credentials{
|
||||
Identity: fmt.Sprintf("%s@example.com", namesgen.Generate()),
|
||||
Secret: "",
|
||||
},
|
||||
Metadata: mgclients.Metadata{},
|
||||
Status: mgclients.EnabledStatus,
|
||||
Tags: []string{"tag1"},
|
||||
}
|
||||
if i%50 == 0 {
|
||||
client.Metadata = map[string]interface{}{
|
||||
"key": "value",
|
||||
}
|
||||
client.Role = mgclients.AdminRole
|
||||
client.Status = mgclients.DisabledStatus
|
||||
}
|
||||
_, err := repo.Save(context.Background(), client)
|
||||
require.Nil(t, err, fmt.Sprintf("failed to save client %s", client.ID))
|
||||
items = append(items, client)
|
||||
if client.Status == mgclients.EnabledStatus {
|
||||
enabledClients = append(enabledClients, client)
|
||||
}
|
||||
}
|
||||
|
||||
cases := []struct {
|
||||
desc string
|
||||
pageMeta mgclients.Page
|
||||
page mgclients.ClientsPage
|
||||
err error
|
||||
}{
|
||||
{
|
||||
desc: "retrieve first page of clients",
|
||||
pageMeta: mgclients.Page{
|
||||
Offset: 0,
|
||||
Limit: 50,
|
||||
Role: mgclients.AllRole,
|
||||
Status: mgclients.AllStatus,
|
||||
},
|
||||
page: mgclients.ClientsPage{
|
||||
Page: mgclients.Page{
|
||||
Total: 200,
|
||||
Offset: 0,
|
||||
Limit: 50,
|
||||
},
|
||||
Clients: items[0:50],
|
||||
},
|
||||
err: nil,
|
||||
},
|
||||
{
|
||||
desc: "retrieve second page of clients",
|
||||
pageMeta: mgclients.Page{
|
||||
Offset: 50,
|
||||
Limit: 200,
|
||||
Role: mgclients.AllRole,
|
||||
Status: mgclients.AllStatus,
|
||||
},
|
||||
page: mgclients.ClientsPage{
|
||||
Page: mgclients.Page{
|
||||
Total: 200,
|
||||
Offset: 50,
|
||||
Limit: 200,
|
||||
},
|
||||
Clients: items[50:200],
|
||||
},
|
||||
err: nil,
|
||||
},
|
||||
{
|
||||
desc: "retrieve clients with limit",
|
||||
pageMeta: mgclients.Page{
|
||||
Offset: 0,
|
||||
Limit: 50,
|
||||
Role: mgclients.AllRole,
|
||||
Status: mgclients.AllStatus,
|
||||
},
|
||||
page: mgclients.ClientsPage{
|
||||
Page: mgclients.Page{
|
||||
Total: uint64(num),
|
||||
Offset: 0,
|
||||
Limit: 50,
|
||||
},
|
||||
Clients: items[:50],
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "retrieve with offset out of range",
|
||||
pageMeta: mgclients.Page{
|
||||
Offset: 1000,
|
||||
Limit: 200,
|
||||
Role: mgclients.AllRole,
|
||||
Status: mgclients.AllStatus,
|
||||
},
|
||||
page: mgclients.ClientsPage{
|
||||
Page: mgclients.Page{
|
||||
Total: 200,
|
||||
Offset: 1000,
|
||||
Limit: 200,
|
||||
},
|
||||
Clients: []mgclients.Client{},
|
||||
},
|
||||
err: nil,
|
||||
},
|
||||
{
|
||||
desc: "retrieve with limit out of range",
|
||||
pageMeta: mgclients.Page{
|
||||
Offset: 0,
|
||||
Limit: 1000,
|
||||
Role: mgclients.AllRole,
|
||||
Status: mgclients.AllStatus,
|
||||
},
|
||||
page: mgclients.ClientsPage{
|
||||
Page: mgclients.Page{
|
||||
Total: 200,
|
||||
Offset: 0,
|
||||
Limit: 1000,
|
||||
},
|
||||
Clients: items,
|
||||
},
|
||||
err: nil,
|
||||
},
|
||||
{
|
||||
desc: "retrieve with empty page",
|
||||
pageMeta: mgclients.Page{},
|
||||
page: mgclients.ClientsPage{
|
||||
Page: mgclients.Page{
|
||||
Total: 196, // No of enabled clients.
|
||||
Offset: 0,
|
||||
Limit: 0,
|
||||
},
|
||||
Clients: []mgclients.Client{},
|
||||
},
|
||||
err: nil,
|
||||
},
|
||||
{
|
||||
desc: "retrieve with client id",
|
||||
pageMeta: mgclients.Page{
|
||||
IDs: []string{items[0].ID},
|
||||
Offset: 0,
|
||||
Limit: 3,
|
||||
Role: mgclients.AllRole,
|
||||
Status: mgclients.AllStatus,
|
||||
},
|
||||
page: mgclients.ClientsPage{
|
||||
Page: mgclients.Page{
|
||||
Total: 1,
|
||||
Offset: 0,
|
||||
Limit: 3,
|
||||
},
|
||||
Clients: []mgclients.Client{items[0]},
|
||||
},
|
||||
err: nil,
|
||||
},
|
||||
{
|
||||
desc: "retrieve with invalid client id",
|
||||
pageMeta: mgclients.Page{
|
||||
IDs: []string{invalidName},
|
||||
Offset: 0,
|
||||
Limit: 3,
|
||||
Role: mgclients.AllRole,
|
||||
Status: mgclients.AllStatus,
|
||||
},
|
||||
page: mgclients.ClientsPage{
|
||||
Page: mgclients.Page{
|
||||
Total: 0,
|
||||
Offset: 0,
|
||||
Limit: 3,
|
||||
},
|
||||
Clients: []mgclients.Client{},
|
||||
},
|
||||
err: nil,
|
||||
},
|
||||
{
|
||||
desc: "retrieve with client name",
|
||||
pageMeta: mgclients.Page{
|
||||
Name: items[0].Name,
|
||||
Offset: 0,
|
||||
Limit: 3,
|
||||
Role: mgclients.AllRole,
|
||||
Status: mgclients.AllStatus,
|
||||
},
|
||||
page: mgclients.ClientsPage{
|
||||
Page: mgclients.Page{
|
||||
Total: 1,
|
||||
Offset: 0,
|
||||
Limit: 3,
|
||||
},
|
||||
Clients: []mgclients.Client{items[0]},
|
||||
},
|
||||
err: nil,
|
||||
},
|
||||
{
|
||||
desc: "retrieve with enabled status",
|
||||
pageMeta: mgclients.Page{
|
||||
Status: mgclients.EnabledStatus,
|
||||
Offset: 0,
|
||||
Limit: 200,
|
||||
Role: mgclients.AllRole,
|
||||
},
|
||||
page: mgclients.ClientsPage{
|
||||
Page: mgclients.Page{
|
||||
Total: 196,
|
||||
Offset: 0,
|
||||
Limit: 200,
|
||||
},
|
||||
Clients: enabledClients,
|
||||
},
|
||||
err: nil,
|
||||
},
|
||||
{
|
||||
desc: "retrieve with disabled status",
|
||||
pageMeta: mgclients.Page{
|
||||
Status: mgclients.DisabledStatus,
|
||||
Offset: 0,
|
||||
Limit: 200,
|
||||
Role: mgclients.AllRole,
|
||||
},
|
||||
page: mgclients.ClientsPage{
|
||||
Page: mgclients.Page{
|
||||
Total: 4,
|
||||
Offset: 0,
|
||||
Limit: 200,
|
||||
},
|
||||
Clients: []mgclients.Client{items[0], items[50], items[100], items[150]},
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "retrieve with all status",
|
||||
pageMeta: mgclients.Page{
|
||||
Status: mgclients.AllStatus,
|
||||
Offset: 0,
|
||||
Limit: 200,
|
||||
Role: mgclients.AllRole,
|
||||
},
|
||||
page: mgclients.ClientsPage{
|
||||
Page: mgclients.Page{
|
||||
Total: 200,
|
||||
Offset: 0,
|
||||
Limit: 200,
|
||||
},
|
||||
Clients: items,
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "retrieve by tags",
|
||||
pageMeta: mgclients.Page{
|
||||
Tag: "tag1",
|
||||
Offset: 0,
|
||||
Limit: 200,
|
||||
Role: mgclients.AllRole,
|
||||
Status: mgclients.AllStatus,
|
||||
},
|
||||
page: mgclients.ClientsPage{
|
||||
Page: mgclients.Page{
|
||||
Total: 200,
|
||||
Offset: 0,
|
||||
Limit: 200,
|
||||
},
|
||||
Clients: items,
|
||||
},
|
||||
err: nil,
|
||||
},
|
||||
{
|
||||
desc: "retrieve with invalid client name",
|
||||
pageMeta: mgclients.Page{
|
||||
Name: invalidName,
|
||||
Offset: 0,
|
||||
Limit: 3,
|
||||
Role: mgclients.AllRole,
|
||||
Status: mgclients.AllStatus,
|
||||
},
|
||||
page: mgclients.ClientsPage{
|
||||
Page: mgclients.Page{
|
||||
Total: 0,
|
||||
Offset: 0,
|
||||
Limit: 3,
|
||||
},
|
||||
Clients: []mgclients.Client{},
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "retrieve with metadata",
|
||||
pageMeta: mgclients.Page{
|
||||
Metadata: map[string]interface{}{
|
||||
"key": "value",
|
||||
},
|
||||
Offset: 0,
|
||||
Limit: 200,
|
||||
Role: mgclients.AllRole,
|
||||
Status: mgclients.AllStatus,
|
||||
},
|
||||
page: mgclients.ClientsPage{
|
||||
Page: mgclients.Page{
|
||||
Total: 4,
|
||||
Offset: 0,
|
||||
Limit: 200,
|
||||
},
|
||||
Clients: []mgclients.Client{items[0], items[50], items[100], items[150]},
|
||||
},
|
||||
err: nil,
|
||||
},
|
||||
{
|
||||
desc: "retrieve with invalid metadata",
|
||||
pageMeta: mgclients.Page{
|
||||
Metadata: map[string]interface{}{
|
||||
"key": "value1",
|
||||
},
|
||||
Offset: 0,
|
||||
Limit: 200,
|
||||
Role: mgclients.AllRole,
|
||||
Status: mgclients.AllStatus,
|
||||
},
|
||||
page: mgclients.ClientsPage{
|
||||
Page: mgclients.Page{
|
||||
Total: 0,
|
||||
Offset: 0,
|
||||
Limit: 200,
|
||||
},
|
||||
Clients: []mgclients.Client{},
|
||||
},
|
||||
err: nil,
|
||||
},
|
||||
{
|
||||
desc: "retrieve with role",
|
||||
pageMeta: mgclients.Page{
|
||||
Role: mgclients.AdminRole,
|
||||
Offset: 0,
|
||||
Limit: 200,
|
||||
Status: mgclients.AllStatus,
|
||||
},
|
||||
page: mgclients.ClientsPage{
|
||||
Page: mgclients.Page{
|
||||
Total: 4,
|
||||
Offset: 0,
|
||||
Limit: 200,
|
||||
},
|
||||
Clients: []mgclients.Client{items[0], items[50], items[100], items[150]},
|
||||
},
|
||||
err: nil,
|
||||
},
|
||||
{
|
||||
desc: "retrieve with invalid role",
|
||||
pageMeta: mgclients.Page{
|
||||
Role: mgclients.AdminRole + 2,
|
||||
Offset: 0,
|
||||
Limit: 200,
|
||||
Status: mgclients.AllStatus,
|
||||
},
|
||||
page: mgclients.ClientsPage{
|
||||
Page: mgclients.Page{
|
||||
Total: 0,
|
||||
Offset: 0,
|
||||
Limit: 200,
|
||||
},
|
||||
Clients: []mgclients.Client{},
|
||||
},
|
||||
err: nil,
|
||||
},
|
||||
{
|
||||
desc: "retrieve with identity",
|
||||
pageMeta: mgclients.Page{
|
||||
Identity: items[0].Credentials.Identity,
|
||||
Offset: 0,
|
||||
Limit: 3,
|
||||
Role: mgclients.AllRole,
|
||||
Status: mgclients.AllStatus,
|
||||
},
|
||||
page: mgclients.ClientsPage{
|
||||
Page: mgclients.Page{
|
||||
Total: 1,
|
||||
Offset: 0,
|
||||
Limit: 3,
|
||||
},
|
||||
Clients: []mgclients.Client{items[0]},
|
||||
},
|
||||
err: nil,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range cases {
|
||||
page, err := repo.RetrieveAll(context.Background(), tc.pageMeta)
|
||||
assert.Equal(t, tc.page.Total, page.Total, fmt.Sprintf("%s: expected %d got %d\n", tc.desc, tc.page.Total, page.Total))
|
||||
assert.Equal(t, tc.page.Offset, page.Offset, fmt.Sprintf("%s: expected %d got %d\n", tc.desc, tc.page.Offset, page.Offset))
|
||||
assert.Equal(t, tc.page.Limit, page.Limit, fmt.Sprintf("%s: expected %d got %d\n", tc.desc, tc.page.Limit, page.Limit))
|
||||
assert.Equal(t, tc.page.Page, page.Page, fmt.Sprintf("%s: expected %v, got %v", tc.desc, tc.page, page))
|
||||
assert.ElementsMatch(t, tc.page.Clients, page.Clients, fmt.Sprintf("%s: expected %v, got %v", tc.desc, tc.page.Clients, page.Clients))
|
||||
assert.Equal(t, tc.err, err, fmt.Sprintf("%s: expected %s got %s\n", tc.desc, tc.err, err))
|
||||
}
|
||||
}
|
||||
|
||||
func TestUpdateRole(t *testing.T) {
|
||||
t.Cleanup(func() {
|
||||
_, err := db.Exec("DELETE FROM clients")
|
||||
require.Nil(t, err, fmt.Sprintf("clean clients unexpected error: %s", err))
|
||||
})
|
||||
|
||||
repo := cpostgres.NewRepository(database)
|
||||
|
||||
client := mgclients.Client{
|
||||
ID: testsutil.GenerateUUID(t),
|
||||
Name: namesgen.Generate(),
|
||||
Credentials: mgclients.Credentials{
|
||||
Identity: fmt.Sprintf("%s@example.com", namesgen.Generate()),
|
||||
Secret: password,
|
||||
},
|
||||
Metadata: mgclients.Metadata{},
|
||||
Status: mgclients.EnabledStatus,
|
||||
Role: mgclients.UserRole,
|
||||
}
|
||||
|
||||
_, err := repo.Save(context.Background(), client)
|
||||
require.Nil(t, err, fmt.Sprintf("failed to save client %s", client.ID))
|
||||
|
||||
cases := []struct {
|
||||
desc string
|
||||
client mgclients.Client
|
||||
newRole mgclients.Role
|
||||
err error
|
||||
}{
|
||||
{
|
||||
desc: "update role to admin",
|
||||
client: client,
|
||||
newRole: mgclients.AdminRole,
|
||||
err: nil,
|
||||
},
|
||||
{
|
||||
desc: "update role to user",
|
||||
client: client,
|
||||
newRole: mgclients.UserRole,
|
||||
err: nil,
|
||||
},
|
||||
{
|
||||
desc: "update role with invalid client id",
|
||||
client: mgclients.Client{ID: invalidName},
|
||||
newRole: mgclients.AdminRole,
|
||||
err: repoerr.ErrNotFound,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range cases {
|
||||
tc.client.Role = tc.newRole
|
||||
client, err := repo.UpdateRole(context.Background(), tc.client)
|
||||
if err != nil {
|
||||
assert.Equal(t, err, tc.err, fmt.Sprintf("%s: expected error %v, got %v", tc.desc, tc.err, err))
|
||||
} else {
|
||||
assert.Equal(t, tc.newRole, client.Role, fmt.Sprintf("%s: expected role %v, got %v", tc.desc, tc.newRole, client.Role))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
// Copyright (c) Abstract Machines
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Package postgres contains the database implementation of clients repository layer.
|
||||
// Package postgres contains the database implementation of users repository layer.
|
||||
package postgres
|
||||
|
||||
@@ -45,6 +45,47 @@ func Migration() *migrate.MemoryMigrationSource {
|
||||
},
|
||||
Down: []string{},
|
||||
},
|
||||
{
|
||||
Id: "clients_03",
|
||||
Up: []string{
|
||||
`ALTER TABLE clients
|
||||
ADD COLUMN username VARCHAR(254) UNIQUE,
|
||||
ADD COLUMN first_name VARCHAR(254) NOT NULL DEFAULT '',
|
||||
ADD COLUMN last_name VARCHAR(254) NOT NULL DEFAULT '',
|
||||
ADD COLUMN profile_picture TEXT`,
|
||||
`ALTER TABLE clients RENAME COLUMN identity TO email`,
|
||||
`ALTER TABLE clients DROP COLUMN name`,
|
||||
},
|
||||
Down: []string{
|
||||
`ALTER TABLE clients
|
||||
DROP COLUMN username,
|
||||
DROP COLUMN first_name,
|
||||
DROP COLUMN last_name,
|
||||
DROP COLUMN profile_picture`,
|
||||
`ALTER TABLE clients RENAME COLUMN email TO identity`,
|
||||
`ALTER TABLE clients ADD COLUMN name VARCHAR(254) NOT NULL UNIQUE`,
|
||||
},
|
||||
},
|
||||
{
|
||||
Id: "clients_04",
|
||||
Up: []string{
|
||||
`ALTER TABLE IF EXISTS clients RENAME TO users`,
|
||||
},
|
||||
Down: []string{
|
||||
`ALTER TABLE IF EXISTS users RENAME TO clients`,
|
||||
},
|
||||
},
|
||||
{
|
||||
Id: "clients_05",
|
||||
Up: []string{
|
||||
`ALTER TABLE users ALTER COLUMN first_name DROP DEFAULT`,
|
||||
`ALTER TABLE users ALTER COLUMN last_name DROP DEFAULT`,
|
||||
},
|
||||
Down: []string{
|
||||
`ALTER TABLE users ALTER COLUMN first_name SET DEFAULT ''`,
|
||||
`ALTER TABLE users ALTER COLUMN last_name SET DEFAULT ''`,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,7 +11,6 @@ import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/absmach/magistrala/pkg/postgres"
|
||||
pgclient "github.com/absmach/magistrala/pkg/postgres"
|
||||
upostgres "github.com/absmach/magistrala/users/postgres"
|
||||
"github.com/jmoiron/sqlx"
|
||||
@@ -22,7 +21,7 @@ import (
|
||||
|
||||
var (
|
||||
db *sqlx.DB
|
||||
database postgres.Database
|
||||
database pgclient.Database
|
||||
tracer = otel.Tracer("repo_tests")
|
||||
)
|
||||
|
||||
@@ -80,7 +79,7 @@ func TestMain(m *testing.M) {
|
||||
log.Fatalf("Could not setup test DB connection: %s", err)
|
||||
}
|
||||
|
||||
database = postgres.NewDatabase(db, dbConfig, tracer)
|
||||
database = pgclient.NewDatabase(db, dbConfig, tracer)
|
||||
|
||||
code := m.Run()
|
||||
|
||||
|
||||
@@ -0,0 +1,684 @@
|
||||
// Copyright (c) Abstract Machines
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package postgres
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/absmach/magistrala/internal/api"
|
||||
mgclients "github.com/absmach/magistrala/pkg/clients"
|
||||
"github.com/absmach/magistrala/pkg/errors"
|
||||
repoerr "github.com/absmach/magistrala/pkg/errors/repository"
|
||||
"github.com/absmach/magistrala/pkg/groups"
|
||||
"github.com/absmach/magistrala/pkg/postgres"
|
||||
"github.com/absmach/magistrala/users"
|
||||
"github.com/jackc/pgtype"
|
||||
)
|
||||
|
||||
type userRepo struct {
|
||||
Repository users.UserRepository
|
||||
}
|
||||
|
||||
func NewRepository(db postgres.Database) users.Repository {
|
||||
return &userRepo{
|
||||
Repository: users.UserRepository{DB: db},
|
||||
}
|
||||
}
|
||||
|
||||
func (repo *userRepo) Save(ctx context.Context, c users.User) (users.User, error) {
|
||||
q := `INSERT INTO users (id, tags, email, secret, metadata, created_at, status, role, first_name, last_name, username, profile_picture)
|
||||
VALUES (:id, :tags, :email, :secret, :metadata, :created_at, :status, :role, :first_name, :last_name, :username, :profile_picture)
|
||||
RETURNING id, tags, email, metadata, created_at, status, first_name, last_name, username, profile_picture`
|
||||
|
||||
dbu, err := toDBUser(c)
|
||||
if err != nil {
|
||||
return users.User{}, errors.Wrap(repoerr.ErrCreateEntity, err)
|
||||
}
|
||||
|
||||
row, err := repo.Repository.DB.NamedQueryContext(ctx, q, dbu)
|
||||
if err != nil {
|
||||
return users.User{}, postgres.HandleError(repoerr.ErrCreateEntity, err)
|
||||
}
|
||||
|
||||
defer row.Close()
|
||||
|
||||
row.Next()
|
||||
|
||||
dbu = DBUser{}
|
||||
if err := row.StructScan(&dbu); err != nil {
|
||||
return users.User{}, errors.Wrap(repoerr.ErrFailedOpDB, err)
|
||||
}
|
||||
|
||||
user, err := ToUser(dbu)
|
||||
if err != nil {
|
||||
return users.User{}, errors.Wrap(repoerr.ErrFailedOpDB, err)
|
||||
}
|
||||
|
||||
return user, nil
|
||||
}
|
||||
|
||||
func (repo *userRepo) CheckSuperAdmin(ctx context.Context, adminID string) error {
|
||||
q := "SELECT 1 FROM users WHERE id = $1 AND role = $2"
|
||||
rows, err := repo.Repository.DB.QueryContext(ctx, q, adminID, mgclients.AdminRole)
|
||||
if err != nil {
|
||||
return postgres.HandleError(repoerr.ErrViewEntity, err)
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
if rows.Next() {
|
||||
if err := rows.Err(); err != nil {
|
||||
return postgres.HandleError(repoerr.ErrViewEntity, err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
return repoerr.ErrNotFound
|
||||
}
|
||||
|
||||
func (repo *userRepo) RetrieveByID(ctx context.Context, id string) (users.User, error) {
|
||||
q := `SELECT id, tags, email, secret, metadata, created_at, updated_at, updated_by, status, role, first_name, last_name, username, profile_picture
|
||||
FROM users WHERE id = :id`
|
||||
|
||||
dbu := DBUser{
|
||||
ID: id,
|
||||
}
|
||||
|
||||
rows, err := repo.Repository.DB.NamedQueryContext(ctx, q, dbu)
|
||||
if err != nil {
|
||||
return users.User{}, postgres.HandleError(repoerr.ErrViewEntity, err)
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
dbu = DBUser{}
|
||||
if rows.Next() {
|
||||
if err = rows.StructScan(&dbu); err != nil {
|
||||
return users.User{}, postgres.HandleError(repoerr.ErrViewEntity, err)
|
||||
}
|
||||
|
||||
user, err := ToUser(dbu)
|
||||
if err != nil {
|
||||
return users.User{}, errors.Wrap(repoerr.ErrFailedOpDB, err)
|
||||
}
|
||||
|
||||
return user, nil
|
||||
}
|
||||
|
||||
return users.User{}, repoerr.ErrNotFound
|
||||
}
|
||||
|
||||
func (repo *userRepo) RetrieveAll(ctx context.Context, pm users.Page) (users.UsersPage, error) {
|
||||
query, err := PageQuery(pm)
|
||||
if err != nil {
|
||||
return users.UsersPage{}, errors.Wrap(repoerr.ErrViewEntity, err)
|
||||
}
|
||||
|
||||
q := fmt.Sprintf(`SELECT u.id, u.tags, u.email, u.metadata, u.status, u.role, u.first_name, u.last_name, u.username,
|
||||
u.created_at, u.updated_at, u.profile_picture, COALESCE(u.updated_by, '') AS updated_by
|
||||
FROM users u %s ORDER BY u.created_at LIMIT :limit OFFSET :offset;`, query)
|
||||
|
||||
dbPage, err := ToDBUsersPage(pm)
|
||||
if err != nil {
|
||||
return users.UsersPage{}, errors.Wrap(repoerr.ErrFailedToRetrieveAllGroups, err)
|
||||
}
|
||||
rows, err := repo.Repository.DB.NamedQueryContext(ctx, q, dbPage)
|
||||
if err != nil {
|
||||
return users.UsersPage{}, errors.Wrap(repoerr.ErrFailedToRetrieveAllGroups, err)
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
var items []users.User
|
||||
for rows.Next() {
|
||||
dbu := DBUser{}
|
||||
if err := rows.StructScan(&dbu); err != nil {
|
||||
return users.UsersPage{}, errors.Wrap(repoerr.ErrViewEntity, err)
|
||||
}
|
||||
|
||||
c, err := ToUser(dbu)
|
||||
if err != nil {
|
||||
return users.UsersPage{}, err
|
||||
}
|
||||
|
||||
items = append(items, c)
|
||||
}
|
||||
|
||||
cq := fmt.Sprintf(`SELECT COUNT(*) FROM users u %s;`, query)
|
||||
|
||||
total, err := postgres.Total(ctx, repo.Repository.DB, cq, dbPage)
|
||||
if err != nil {
|
||||
return users.UsersPage{}, errors.Wrap(repoerr.ErrViewEntity, err)
|
||||
}
|
||||
|
||||
page := users.UsersPage{
|
||||
Page: users.Page{
|
||||
Total: total,
|
||||
Offset: pm.Offset,
|
||||
Limit: pm.Limit,
|
||||
},
|
||||
Users: items,
|
||||
}
|
||||
|
||||
return page, nil
|
||||
}
|
||||
|
||||
func (repo *userRepo) UpdateUsername(ctx context.Context, user users.User) (users.User, error) {
|
||||
q := `UPDATE users SET username = :username, updated_at = :updated_at, updated_by = :updated_by
|
||||
WHERE id = :id AND status = :status
|
||||
RETURNING id, tags, metadata, status, created_at, updated_at, updated_by, first_name, last_name, username, email`
|
||||
|
||||
dbu, err := toDBUser(user)
|
||||
if err != nil {
|
||||
return users.User{}, errors.Wrap(repoerr.ErrUpdateEntity, err)
|
||||
}
|
||||
|
||||
row, err := repo.Repository.DB.NamedQueryContext(ctx, q, dbu)
|
||||
if err != nil {
|
||||
return users.User{}, postgres.HandleError(err, repoerr.ErrUpdateEntity)
|
||||
}
|
||||
|
||||
defer row.Close()
|
||||
|
||||
dbu = DBUser{
|
||||
ID: user.ID,
|
||||
Username: stringToNullString(user.Credentials.Username),
|
||||
UpdatedAt: sql.NullTime{Time: time.Now(), Valid: true},
|
||||
}
|
||||
|
||||
if ok := row.Next(); !ok {
|
||||
return users.User{}, errors.Wrap(repoerr.ErrNotFound, row.Err())
|
||||
}
|
||||
|
||||
if err := row.StructScan(&dbu); err != nil {
|
||||
return users.User{}, err
|
||||
}
|
||||
|
||||
return ToUser(dbu)
|
||||
}
|
||||
|
||||
func (repo *userRepo) Update(ctx context.Context, user users.User) (users.User, error) {
|
||||
var query []string
|
||||
var upq string
|
||||
if user.FirstName != "" {
|
||||
query = append(query, "first_name = :first_name,")
|
||||
}
|
||||
if user.LastName != "" {
|
||||
query = append(query, "last_name = :last_name,")
|
||||
}
|
||||
if user.Metadata != nil {
|
||||
query = append(query, "metadata = :metadata,")
|
||||
}
|
||||
if len(user.Tags) > 0 {
|
||||
query = append(query, "tags = :tags,")
|
||||
}
|
||||
if user.Role != users.AllRole {
|
||||
query = append(query, "role = :role,")
|
||||
}
|
||||
|
||||
if user.ProfilePicture != "" {
|
||||
query = append(query, "profile_picture = :profile_picture,")
|
||||
}
|
||||
|
||||
if user.Email != "" {
|
||||
query = append(query, "email = :email,")
|
||||
}
|
||||
|
||||
if len(query) > 0 {
|
||||
upq = strings.Join(query, " ")
|
||||
}
|
||||
|
||||
q := fmt.Sprintf(`UPDATE users SET %s updated_at = :updated_at, updated_by = :updated_by
|
||||
WHERE id = :id AND status = :status
|
||||
RETURNING id, tags, metadata, status, created_at, updated_at, updated_by, last_name, first_name, username, profile_picture, email`, upq)
|
||||
|
||||
user.Status = users.EnabledStatus
|
||||
return repo.update(ctx, user, q)
|
||||
}
|
||||
|
||||
func (repo *userRepo) update(ctx context.Context, user users.User, query string) (users.User, error) {
|
||||
dbu, err := toDBUser(user)
|
||||
if err != nil {
|
||||
return users.User{}, errors.Wrap(repoerr.ErrUpdateEntity, err)
|
||||
}
|
||||
|
||||
row, err := repo.Repository.DB.NamedQueryContext(ctx, query, dbu)
|
||||
if err != nil {
|
||||
return users.User{}, postgres.HandleError(repoerr.ErrUpdateEntity, err)
|
||||
}
|
||||
defer row.Close()
|
||||
|
||||
dbu = DBUser{}
|
||||
if row.Next() {
|
||||
if err := row.StructScan(&dbu); err != nil {
|
||||
return users.User{}, errors.Wrap(repoerr.ErrUpdateEntity, err)
|
||||
}
|
||||
|
||||
return ToUser(dbu)
|
||||
}
|
||||
|
||||
return users.User{}, repoerr.ErrNotFound
|
||||
}
|
||||
|
||||
func (repo *userRepo) UpdateSecret(ctx context.Context, user users.User) (users.User, error) {
|
||||
q := `UPDATE users SET secret = :secret, updated_at = :updated_at, updated_by = :updated_by
|
||||
WHERE id = :id AND status = :status
|
||||
RETURNING id, tags, email, metadata, status, created_at, updated_at, updated_by, first_name, last_name, username`
|
||||
user.Status = users.EnabledStatus
|
||||
return repo.update(ctx, user, q)
|
||||
}
|
||||
|
||||
func (repo *userRepo) ChangeStatus(ctx context.Context, user users.User) (users.User, error) {
|
||||
q := `UPDATE users SET status = :status, updated_at = :updated_at, updated_by = :updated_by
|
||||
WHERE id = :id
|
||||
RETURNING id, tags, email, metadata, status, created_at, updated_at, updated_by, first_name, last_name, username`
|
||||
|
||||
return repo.update(ctx, user, q)
|
||||
}
|
||||
|
||||
func (repo *userRepo) Delete(ctx context.Context, id string) error {
|
||||
q := "DELETE FROM users AS u WHERE u.id = $1 ;"
|
||||
|
||||
result, err := repo.Repository.DB.ExecContext(ctx, q, id)
|
||||
if err != nil {
|
||||
return postgres.HandleError(repoerr.ErrRemoveEntity, err)
|
||||
}
|
||||
if rows, _ := result.RowsAffected(); rows == 0 {
|
||||
return repoerr.ErrNotFound
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (repo *userRepo) SearchUsers(ctx context.Context, pm users.Page) (users.UsersPage, error) {
|
||||
query, err := PageQuery(pm)
|
||||
if err != nil {
|
||||
return users.UsersPage{}, errors.Wrap(repoerr.ErrViewEntity, err)
|
||||
}
|
||||
|
||||
tq := query
|
||||
query = applyOrdering(query, pm)
|
||||
|
||||
q := fmt.Sprintf(`SELECT u.id, u.username, u.first_name, u.last_name, u.created_at, u.updated_at FROM users u %s LIMIT :limit OFFSET :offset;`, query)
|
||||
|
||||
dbPage, err := ToDBUsersPage(pm)
|
||||
if err != nil {
|
||||
return users.UsersPage{}, errors.Wrap(repoerr.ErrFailedToRetrieveAllGroups, err)
|
||||
}
|
||||
|
||||
rows, err := repo.Repository.DB.NamedQueryContext(ctx, q, dbPage)
|
||||
if err != nil {
|
||||
return users.UsersPage{}, errors.Wrap(repoerr.ErrFailedToRetrieveAllGroups, err)
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
var items []users.User
|
||||
for rows.Next() {
|
||||
dbu := DBUser{}
|
||||
if err := rows.StructScan(&dbu); err != nil {
|
||||
return users.UsersPage{}, errors.Wrap(repoerr.ErrViewEntity, err)
|
||||
}
|
||||
|
||||
c, err := ToUser(dbu)
|
||||
if err != nil {
|
||||
return users.UsersPage{}, err
|
||||
}
|
||||
|
||||
items = append(items, c)
|
||||
}
|
||||
|
||||
cq := fmt.Sprintf(`SELECT COUNT(*) FROM users u %s;`, tq)
|
||||
|
||||
total, err := postgres.Total(ctx, repo.Repository.DB, cq, dbPage)
|
||||
if err != nil {
|
||||
return users.UsersPage{}, errors.Wrap(repoerr.ErrViewEntity, err)
|
||||
}
|
||||
|
||||
page := users.UsersPage{
|
||||
Users: items,
|
||||
Page: users.Page{
|
||||
Total: total,
|
||||
Offset: pm.Offset,
|
||||
Limit: pm.Limit,
|
||||
},
|
||||
}
|
||||
|
||||
return page, nil
|
||||
}
|
||||
|
||||
func (repo *userRepo) RetrieveAllByIDs(ctx context.Context, pm users.Page) (users.UsersPage, error) {
|
||||
if (len(pm.IDs) == 0) && (pm.Domain == "") {
|
||||
return users.UsersPage{
|
||||
Page: users.Page{Total: pm.Total, Offset: pm.Offset, Limit: pm.Limit},
|
||||
}, nil
|
||||
}
|
||||
query, err := PageQuery(pm)
|
||||
if err != nil {
|
||||
return users.UsersPage{}, errors.Wrap(repoerr.ErrViewEntity, err)
|
||||
}
|
||||
query = applyOrdering(query, pm)
|
||||
|
||||
q := fmt.Sprintf(`SELECT u.id, u.username, u.tags, u.email, u.metadata, u.status, u.role, u.first_name, u.last_name,
|
||||
u.created_at, u.updated_at, COALESCE(u.updated_by, '') AS updated_by FROM users u %s ORDER BY u.created_at LIMIT :limit OFFSET :offset;`, query)
|
||||
|
||||
dbPage, err := ToDBUsersPage(pm)
|
||||
if err != nil {
|
||||
return users.UsersPage{}, errors.Wrap(repoerr.ErrFailedToRetrieveAllGroups, err)
|
||||
}
|
||||
rows, err := repo.Repository.DB.NamedQueryContext(ctx, q, dbPage)
|
||||
if err != nil {
|
||||
return users.UsersPage{}, errors.Wrap(repoerr.ErrFailedToRetrieveAllGroups, err)
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
var items []users.User
|
||||
for rows.Next() {
|
||||
dbu := DBUser{}
|
||||
if err := rows.StructScan(&dbu); err != nil {
|
||||
return users.UsersPage{}, errors.Wrap(repoerr.ErrViewEntity, err)
|
||||
}
|
||||
|
||||
c, err := ToUser(dbu)
|
||||
if err != nil {
|
||||
return users.UsersPage{}, err
|
||||
}
|
||||
|
||||
items = append(items, c)
|
||||
}
|
||||
cq := fmt.Sprintf(`SELECT COUNT(*) FROM clients c %s;`, query)
|
||||
|
||||
total, err := postgres.Total(ctx, repo.Repository.DB, cq, dbPage)
|
||||
if err != nil {
|
||||
return users.UsersPage{}, errors.Wrap(repoerr.ErrViewEntity, err)
|
||||
}
|
||||
|
||||
page := users.UsersPage{
|
||||
Users: items,
|
||||
Page: users.Page{
|
||||
Total: total,
|
||||
Offset: pm.Offset,
|
||||
Limit: pm.Limit,
|
||||
},
|
||||
}
|
||||
|
||||
return page, nil
|
||||
}
|
||||
|
||||
func (repo *userRepo) RetrieveByEmail(ctx context.Context, email string) (users.User, error) {
|
||||
q := `SELECT id, tags, email, secret, metadata, created_at, updated_at, updated_by, status, role, first_name, last_name, username
|
||||
FROM users WHERE email = :email AND status = :status`
|
||||
|
||||
dbu := DBUser{
|
||||
Email: email,
|
||||
Status: users.EnabledStatus,
|
||||
}
|
||||
|
||||
row, err := repo.Repository.DB.NamedQueryContext(ctx, q, dbu)
|
||||
if err != nil {
|
||||
return users.User{}, postgres.HandleError(repoerr.ErrViewEntity, err)
|
||||
}
|
||||
defer row.Close()
|
||||
|
||||
dbu = DBUser{}
|
||||
if row.Next() {
|
||||
if err := row.StructScan(&dbu); err != nil {
|
||||
return users.User{}, errors.Wrap(repoerr.ErrViewEntity, err)
|
||||
}
|
||||
|
||||
return ToUser(dbu)
|
||||
}
|
||||
|
||||
return users.User{}, repoerr.ErrNotFound
|
||||
}
|
||||
|
||||
func (repo *userRepo) RetrieveByUsername(ctx context.Context, username string) (users.User, error) {
|
||||
q := `SELECT id, tags, email, secret, metadata, created_at, updated_at, updated_by, status, role, first_name, last_name, username
|
||||
FROM users WHERE username = :username AND status = :status`
|
||||
|
||||
dbu := DBUser{
|
||||
Username: sql.NullString{String: username, Valid: username != ""},
|
||||
Status: users.EnabledStatus,
|
||||
}
|
||||
|
||||
row, err := repo.Repository.DB.NamedQueryContext(ctx, q, dbu)
|
||||
if err != nil {
|
||||
return users.User{}, postgres.HandleError(repoerr.ErrViewEntity, err)
|
||||
}
|
||||
defer row.Close()
|
||||
|
||||
dbu = DBUser{}
|
||||
if row.Next() {
|
||||
if err := row.StructScan(&dbu); err != nil {
|
||||
return users.User{}, errors.Wrap(repoerr.ErrViewEntity, err)
|
||||
}
|
||||
|
||||
return ToUser(dbu)
|
||||
}
|
||||
|
||||
return users.User{}, repoerr.ErrNotFound
|
||||
}
|
||||
|
||||
type DBUser struct {
|
||||
ID string `db:"id"`
|
||||
Domain string `db:"domain_id"`
|
||||
Secret string `db:"secret"`
|
||||
Metadata []byte `db:"metadata,omitempty"`
|
||||
Tags pgtype.TextArray `db:"tags,omitempty"` // Tags
|
||||
CreatedAt time.Time `db:"created_at,omitempty"`
|
||||
UpdatedAt sql.NullTime `db:"updated_at,omitempty"`
|
||||
UpdatedBy *string `db:"updated_by,omitempty"`
|
||||
Groups []groups.Group `db:"groups,omitempty"`
|
||||
Status users.Status `db:"status,omitempty"`
|
||||
Role *users.Role `db:"role,omitempty"`
|
||||
Username sql.NullString `db:"username, omitempty"`
|
||||
FirstName sql.NullString `db:"first_name, omitempty"`
|
||||
LastName sql.NullString `db:"last_name, omitempty"`
|
||||
ProfilePicture sql.NullString `db:"profile_picture, omitempty"`
|
||||
Email string `db:"email,omitempty"`
|
||||
}
|
||||
|
||||
func toDBUser(u users.User) (DBUser, error) {
|
||||
data := []byte("{}")
|
||||
if len(u.Metadata) > 0 {
|
||||
b, err := json.Marshal(u.Metadata)
|
||||
if err != nil {
|
||||
return DBUser{}, errors.Wrap(repoerr.ErrMalformedEntity, err)
|
||||
}
|
||||
data = b
|
||||
}
|
||||
var tags pgtype.TextArray
|
||||
if err := tags.Set(u.Tags); err != nil {
|
||||
return DBUser{}, err
|
||||
}
|
||||
var updatedBy *string
|
||||
if u.UpdatedBy != "" {
|
||||
updatedBy = &u.UpdatedBy
|
||||
}
|
||||
var updatedAt sql.NullTime
|
||||
if u.UpdatedAt != (time.Time{}) {
|
||||
updatedAt = sql.NullTime{Time: u.UpdatedAt, Valid: true}
|
||||
}
|
||||
|
||||
return DBUser{
|
||||
ID: u.ID,
|
||||
Tags: tags,
|
||||
Secret: u.Credentials.Secret,
|
||||
Metadata: data,
|
||||
CreatedAt: u.CreatedAt,
|
||||
UpdatedAt: updatedAt,
|
||||
UpdatedBy: updatedBy,
|
||||
Status: u.Status,
|
||||
Role: &u.Role,
|
||||
LastName: stringToNullString(u.LastName),
|
||||
FirstName: stringToNullString(u.FirstName),
|
||||
Username: stringToNullString(u.Credentials.Username),
|
||||
ProfilePicture: stringToNullString(u.ProfilePicture),
|
||||
Email: u.Email,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func ToUser(dbu DBUser) (users.User, error) {
|
||||
var metadata users.Metadata
|
||||
if dbu.Metadata != nil {
|
||||
if err := json.Unmarshal([]byte(dbu.Metadata), &metadata); err != nil {
|
||||
return users.User{}, errors.Wrap(repoerr.ErrMalformedEntity, err)
|
||||
}
|
||||
}
|
||||
var tags []string
|
||||
for _, e := range dbu.Tags.Elements {
|
||||
tags = append(tags, e.String)
|
||||
}
|
||||
var updatedBy string
|
||||
if dbu.UpdatedBy != nil {
|
||||
updatedBy = *dbu.UpdatedBy
|
||||
}
|
||||
var updatedAt time.Time
|
||||
if dbu.UpdatedAt.Valid {
|
||||
updatedAt = dbu.UpdatedAt.Time
|
||||
}
|
||||
|
||||
user := users.User{
|
||||
ID: dbu.ID,
|
||||
FirstName: nullStringString(dbu.FirstName),
|
||||
LastName: nullStringString(dbu.LastName),
|
||||
Credentials: users.Credentials{
|
||||
Username: nullStringString(dbu.Username),
|
||||
Secret: dbu.Secret,
|
||||
},
|
||||
Email: dbu.Email,
|
||||
Metadata: metadata,
|
||||
CreatedAt: dbu.CreatedAt,
|
||||
UpdatedAt: updatedAt,
|
||||
UpdatedBy: updatedBy,
|
||||
Status: dbu.Status,
|
||||
Tags: tags,
|
||||
ProfilePicture: nullStringString(dbu.ProfilePicture),
|
||||
}
|
||||
if dbu.Role != nil {
|
||||
user.Role = *dbu.Role
|
||||
}
|
||||
return user, nil
|
||||
}
|
||||
|
||||
type DBUsersPage struct {
|
||||
Total uint64 `db:"total"`
|
||||
Limit uint64 `db:"limit"`
|
||||
Offset uint64 `db:"offset"`
|
||||
FirstName string `db:"first_name"`
|
||||
LastName string `db:"last_name"`
|
||||
Username string `db:"username"`
|
||||
Id string `db:"id"`
|
||||
Email string `db:"email"`
|
||||
Metadata []byte `db:"metadata"`
|
||||
Tag string `db:"tag"`
|
||||
GroupID string `db:"group_id"`
|
||||
Role users.Role `db:"role"`
|
||||
Status users.Status `db:"status"`
|
||||
}
|
||||
|
||||
func ToDBUsersPage(pm users.Page) (DBUsersPage, error) {
|
||||
_, data, err := postgres.CreateMetadataQuery("", pm.Metadata)
|
||||
if err != nil {
|
||||
return DBUsersPage{}, errors.Wrap(repoerr.ErrViewEntity, err)
|
||||
}
|
||||
|
||||
return DBUsersPage{
|
||||
FirstName: pm.FirstName,
|
||||
LastName: pm.LastName,
|
||||
Username: pm.Username,
|
||||
Email: pm.Email,
|
||||
Id: pm.Id,
|
||||
Metadata: data,
|
||||
Total: pm.Total,
|
||||
Offset: pm.Offset,
|
||||
Limit: pm.Limit,
|
||||
Status: pm.Status,
|
||||
Tag: pm.Tag,
|
||||
Role: pm.Role,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func PageQuery(pm users.Page) (string, error) {
|
||||
mq, _, err := postgres.CreateMetadataQuery("", pm.Metadata)
|
||||
if err != nil {
|
||||
return "", errors.Wrap(errors.ErrMalformedEntity, err)
|
||||
}
|
||||
|
||||
var query []string
|
||||
if pm.FirstName != "" {
|
||||
query = append(query, "first_name ILIKE '%' || :first_name || '%'")
|
||||
}
|
||||
if pm.LastName != "" {
|
||||
query = append(query, "last_name ILIKE '%' || :last_name || '%'")
|
||||
}
|
||||
if pm.Username != "" {
|
||||
query = append(query, "username ILIKE '%' || :username || '%'")
|
||||
}
|
||||
if pm.Email != "" {
|
||||
query = append(query, "email ILIKE '%' || :email || '%'")
|
||||
}
|
||||
if pm.Id != "" {
|
||||
query = append(query, "id ILIKE '%' || :id || '%'")
|
||||
}
|
||||
if pm.Tag != "" {
|
||||
query = append(query, "EXISTS (SELECT 1 FROM unnest(tags) AS tag WHERE tag ILIKE '%' || :tag || '%')")
|
||||
}
|
||||
if pm.Role != users.AllRole {
|
||||
query = append(query, "u.role = :role")
|
||||
}
|
||||
// If there are search params presents, use search and ignore other options.
|
||||
// Always combine role with search params, so len(query) > 1.
|
||||
if len(query) > 1 {
|
||||
return fmt.Sprintf("WHERE %s", strings.Join(query, " AND ")), nil
|
||||
}
|
||||
|
||||
if mq != "" {
|
||||
query = append(query, mq)
|
||||
}
|
||||
|
||||
if len(pm.IDs) != 0 {
|
||||
query = append(query, fmt.Sprintf("id IN ('%s')", strings.Join(pm.IDs, "','")))
|
||||
}
|
||||
if pm.Status != users.AllStatus {
|
||||
query = append(query, "u.status = :status")
|
||||
}
|
||||
|
||||
var emq string
|
||||
if len(query) > 0 {
|
||||
emq = fmt.Sprintf("WHERE %s", strings.Join(query, " AND "))
|
||||
}
|
||||
|
||||
return emq, nil
|
||||
}
|
||||
|
||||
func applyOrdering(emq string, pm users.Page) string {
|
||||
switch pm.Order {
|
||||
case "username", "first_name", "email", "last_name", "created_at", "updated_at":
|
||||
emq = fmt.Sprintf("%s ORDER BY %s", emq, pm.Order)
|
||||
if pm.Dir == api.AscDir || pm.Dir == api.DescDir {
|
||||
emq = fmt.Sprintf("%s %s", emq, pm.Dir)
|
||||
}
|
||||
}
|
||||
return emq
|
||||
}
|
||||
|
||||
func stringToNullString(s string) sql.NullString {
|
||||
if s == "" {
|
||||
return sql.NullString{}
|
||||
}
|
||||
|
||||
return sql.NullString{
|
||||
String: s,
|
||||
Valid: true,
|
||||
}
|
||||
}
|
||||
|
||||
func nullStringString(ns sql.NullString) string {
|
||||
if ns.Valid {
|
||||
return ns.String
|
||||
}
|
||||
return ""
|
||||
}
|
||||
@@ -0,0 +1,703 @@
|
||||
// Copyright (c) Abstract Machines
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package postgres_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/0x6flab/namegenerator"
|
||||
"github.com/absmach/magistrala/internal/testsutil"
|
||||
"github.com/absmach/magistrala/pkg/errors"
|
||||
repoerr "github.com/absmach/magistrala/pkg/errors/repository"
|
||||
"github.com/absmach/magistrala/users"
|
||||
cpostgres "github.com/absmach/magistrala/users/postgres"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
const maxNameSize = 254
|
||||
|
||||
var (
|
||||
invalidName = strings.Repeat("m", maxNameSize+10)
|
||||
password = "$tr0ngPassw0rd"
|
||||
namesgen = namegenerator.NewGenerator()
|
||||
)
|
||||
|
||||
func TestUsersSave(t *testing.T) {
|
||||
t.Cleanup(func() {
|
||||
_, err := db.Exec("DELETE FROM users")
|
||||
require.Nil(t, err, fmt.Sprintf("clean users unexpected error: %s", err))
|
||||
})
|
||||
|
||||
repo := cpostgres.NewRepository(database)
|
||||
|
||||
uid := testsutil.GenerateUUID(t)
|
||||
|
||||
first_name := namesgen.Generate()
|
||||
last_name := namesgen.Generate()
|
||||
username := namesgen.Generate()
|
||||
|
||||
email := first_name + "@example.com"
|
||||
|
||||
cases := []struct {
|
||||
desc string
|
||||
user users.User
|
||||
err error
|
||||
}{
|
||||
{
|
||||
desc: "add new user successfully",
|
||||
user: users.User{
|
||||
ID: uid,
|
||||
FirstName: first_name,
|
||||
LastName: last_name,
|
||||
Email: email,
|
||||
Credentials: users.Credentials{
|
||||
Username: username,
|
||||
Secret: password,
|
||||
},
|
||||
Metadata: users.Metadata{},
|
||||
Status: users.EnabledStatus,
|
||||
},
|
||||
err: nil,
|
||||
},
|
||||
{
|
||||
desc: "add user with duplicate user email",
|
||||
user: users.User{
|
||||
ID: testsutil.GenerateUUID(t),
|
||||
FirstName: first_name,
|
||||
LastName: last_name,
|
||||
Email: email,
|
||||
Credentials: users.Credentials{
|
||||
Username: namesgen.Generate(),
|
||||
Secret: password,
|
||||
},
|
||||
Metadata: users.Metadata{},
|
||||
Status: users.EnabledStatus,
|
||||
},
|
||||
err: repoerr.ErrConflict,
|
||||
},
|
||||
{
|
||||
desc: "add user with duplicate user name",
|
||||
user: users.User{
|
||||
ID: testsutil.GenerateUUID(t),
|
||||
FirstName: namesgen.Generate(),
|
||||
LastName: last_name,
|
||||
Email: namesgen.Generate() + "@example.com",
|
||||
Credentials: users.Credentials{
|
||||
Username: username,
|
||||
Secret: password,
|
||||
},
|
||||
Metadata: users.Metadata{},
|
||||
Status: users.EnabledStatus,
|
||||
},
|
||||
err: repoerr.ErrConflict,
|
||||
},
|
||||
{
|
||||
desc: "add user with invalid user id",
|
||||
user: users.User{
|
||||
ID: invalidName,
|
||||
FirstName: namesgen.Generate(),
|
||||
LastName: namesgen.Generate(),
|
||||
Email: namesgen.Generate() + "@example.com",
|
||||
Credentials: users.Credentials{
|
||||
Username: username,
|
||||
Secret: password,
|
||||
},
|
||||
Metadata: users.Metadata{},
|
||||
Status: users.EnabledStatus,
|
||||
},
|
||||
err: errors.ErrMalformedEntity,
|
||||
},
|
||||
{
|
||||
desc: "add user with invalid user name",
|
||||
user: users.User{
|
||||
ID: testsutil.GenerateUUID(t),
|
||||
FirstName: first_name,
|
||||
LastName: last_name,
|
||||
Email: namesgen.Generate() + "@example.com",
|
||||
Credentials: users.Credentials{
|
||||
Username: invalidName,
|
||||
Secret: password,
|
||||
},
|
||||
Metadata: users.Metadata{},
|
||||
Status: users.EnabledStatus,
|
||||
},
|
||||
err: errors.ErrMalformedEntity,
|
||||
},
|
||||
{
|
||||
desc: "add user with a missing username",
|
||||
user: users.User{
|
||||
ID: testsutil.GenerateUUID(t),
|
||||
FirstName: first_name,
|
||||
LastName: last_name,
|
||||
Email: namesgen.Generate() + "@example.com",
|
||||
Credentials: users.Credentials{
|
||||
Secret: password,
|
||||
},
|
||||
Metadata: users.Metadata{},
|
||||
},
|
||||
err: nil,
|
||||
},
|
||||
{
|
||||
desc: "add user with a missing user secret",
|
||||
user: users.User{
|
||||
ID: testsutil.GenerateUUID(t),
|
||||
FirstName: namesgen.Generate(),
|
||||
LastName: namesgen.Generate(),
|
||||
Email: namesgen.Generate() + "@example.com",
|
||||
Credentials: users.Credentials{
|
||||
Username: namesgen.Generate(),
|
||||
},
|
||||
Metadata: users.Metadata{},
|
||||
},
|
||||
err: nil,
|
||||
},
|
||||
{
|
||||
desc: "add a user with invalid metadata",
|
||||
user: users.User{
|
||||
ID: testsutil.GenerateUUID(t),
|
||||
FirstName: namesgen.Generate(),
|
||||
Email: namesgen.Generate() + "@example.com",
|
||||
Credentials: users.Credentials{
|
||||
Username: username,
|
||||
Secret: password,
|
||||
},
|
||||
Metadata: map[string]interface{}{
|
||||
"key": make(chan int),
|
||||
},
|
||||
},
|
||||
err: errors.ErrMalformedEntity,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range cases {
|
||||
rUser, err := repo.Save(context.Background(), tc.user)
|
||||
assert.True(t, errors.Contains(err, tc.err), fmt.Sprintf("%s: expected %s got %s\n", tc.desc, tc.err, err))
|
||||
if err == nil {
|
||||
rUser.Credentials.Secret = tc.user.Credentials.Secret
|
||||
assert.Equal(t, tc.user, rUser, fmt.Sprintf("%s: expected %v got %v\n", tc.desc, tc.user, rUser))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestIsPlatformAdmin(t *testing.T) {
|
||||
t.Cleanup(func() {
|
||||
_, err := db.Exec("DELETE FROM users")
|
||||
require.Nil(t, err, fmt.Sprintf("clean users unexpected error: %s", err))
|
||||
})
|
||||
|
||||
repo := cpostgres.NewRepository(database)
|
||||
|
||||
first_name := namesgen.Generate()
|
||||
last_name := namesgen.Generate()
|
||||
username := namesgen.Generate()
|
||||
email := first_name + "@example.com"
|
||||
|
||||
cases := []struct {
|
||||
desc string
|
||||
user users.User
|
||||
err error
|
||||
}{
|
||||
{
|
||||
desc: "authorize check for super user",
|
||||
user: users.User{
|
||||
ID: testsutil.GenerateUUID(t),
|
||||
FirstName: first_name,
|
||||
LastName: last_name,
|
||||
Email: email,
|
||||
Credentials: users.Credentials{
|
||||
Username: username,
|
||||
Secret: password,
|
||||
},
|
||||
Metadata: users.Metadata{},
|
||||
Status: users.EnabledStatus,
|
||||
Role: users.AdminRole,
|
||||
},
|
||||
err: nil,
|
||||
},
|
||||
{
|
||||
desc: "unauthorize user",
|
||||
user: users.User{
|
||||
ID: testsutil.GenerateUUID(t),
|
||||
FirstName: first_name,
|
||||
LastName: last_name,
|
||||
Email: namesgen.Generate() + "@example.com",
|
||||
Credentials: users.Credentials{
|
||||
Username: namesgen.Generate(),
|
||||
Secret: password,
|
||||
},
|
||||
Metadata: users.Metadata{},
|
||||
Status: users.EnabledStatus,
|
||||
Role: users.UserRole,
|
||||
},
|
||||
err: repoerr.ErrNotFound,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range cases {
|
||||
_, err := repo.Save(context.Background(), tc.user)
|
||||
require.Nil(t, err, fmt.Sprintf("%s: save user unexpected error: %s", tc.desc, err))
|
||||
err = repo.CheckSuperAdmin(context.Background(), tc.user.ID)
|
||||
assert.True(t, errors.Contains(err, tc.err), fmt.Sprintf("%s: expected %v got %v\n", tc.desc, tc.err, err))
|
||||
}
|
||||
}
|
||||
|
||||
func TestRetrieveByID(t *testing.T) {
|
||||
t.Cleanup(func() {
|
||||
_, err := db.Exec("DELETE FROM users")
|
||||
require.Nil(t, err, fmt.Sprintf("clean users unexpected error: %s", err))
|
||||
})
|
||||
|
||||
repo := cpostgres.NewRepository(database)
|
||||
|
||||
user := users.User{
|
||||
ID: testsutil.GenerateUUID(t),
|
||||
FirstName: namesgen.Generate(),
|
||||
LastName: namesgen.Generate(),
|
||||
Email: namesgen.Generate() + "@example.com",
|
||||
Credentials: users.Credentials{
|
||||
Username: namesgen.Generate(),
|
||||
Secret: password,
|
||||
},
|
||||
Metadata: users.Metadata{},
|
||||
Status: users.EnabledStatus,
|
||||
}
|
||||
|
||||
_, err := repo.Save(context.Background(), user)
|
||||
require.Nil(t, err, fmt.Sprintf("failed to save users %s", user.ID))
|
||||
|
||||
cases := []struct {
|
||||
desc string
|
||||
userID string
|
||||
err error
|
||||
}{
|
||||
{
|
||||
desc: "retrieve existing user",
|
||||
userID: user.ID,
|
||||
err: nil,
|
||||
},
|
||||
{
|
||||
desc: "retrieve non-existing user",
|
||||
userID: invalidName,
|
||||
err: repoerr.ErrNotFound,
|
||||
},
|
||||
{
|
||||
desc: "retrieve with empty user id",
|
||||
userID: "",
|
||||
err: repoerr.ErrNotFound,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range cases {
|
||||
_, err := repo.RetrieveByID(context.Background(), tc.userID)
|
||||
assert.True(t, errors.Contains(err, tc.err), fmt.Sprintf("%s: expected %v got %v\n", tc.desc, tc.err, err))
|
||||
}
|
||||
}
|
||||
|
||||
func TestRetrieveAll(t *testing.T) {
|
||||
t.Cleanup(func() {
|
||||
_, err := db.Exec("DELETE FROM users")
|
||||
require.Nil(t, err, fmt.Sprintf("clean users unexpected error: %s", err))
|
||||
})
|
||||
|
||||
repo := cpostgres.NewRepository(database)
|
||||
|
||||
num := 200
|
||||
var items, enabledUsers []users.User
|
||||
for i := 0; i < num; i++ {
|
||||
user := users.User{
|
||||
ID: testsutil.GenerateUUID(t),
|
||||
FirstName: namesgen.Generate(),
|
||||
LastName: namesgen.Generate(),
|
||||
Email: namesgen.Generate() + "@example.com",
|
||||
Credentials: users.Credentials{
|
||||
Username: namesgen.Generate(),
|
||||
Secret: "",
|
||||
},
|
||||
Metadata: users.Metadata{},
|
||||
Status: users.EnabledStatus,
|
||||
Tags: []string{"tag1"},
|
||||
}
|
||||
if i%50 == 0 {
|
||||
user.Metadata = map[string]interface{}{
|
||||
"key": "value",
|
||||
}
|
||||
user.Role = users.AdminRole
|
||||
user.Status = users.DisabledStatus
|
||||
}
|
||||
_, err := repo.Save(context.Background(), user)
|
||||
require.Nil(t, err, fmt.Sprintf("failed to save user %s", user.ID))
|
||||
items = append(items, user)
|
||||
if user.Status == users.EnabledStatus {
|
||||
enabledUsers = append(enabledUsers, user)
|
||||
}
|
||||
}
|
||||
|
||||
cases := []struct {
|
||||
desc string
|
||||
pageMeta users.Page
|
||||
page users.UsersPage
|
||||
err error
|
||||
}{
|
||||
{
|
||||
desc: "retrieve first page of users",
|
||||
pageMeta: users.Page{
|
||||
Offset: 0,
|
||||
Limit: 50,
|
||||
Role: users.AllRole,
|
||||
Status: users.AllStatus,
|
||||
},
|
||||
page: users.UsersPage{
|
||||
Page: users.Page{
|
||||
Total: 200,
|
||||
Offset: 0,
|
||||
Limit: 50,
|
||||
},
|
||||
Users: items[0:50],
|
||||
},
|
||||
err: nil,
|
||||
},
|
||||
{
|
||||
desc: "retrieve second page of users",
|
||||
pageMeta: users.Page{
|
||||
Offset: 50,
|
||||
Limit: 200,
|
||||
Role: users.AllRole,
|
||||
Status: users.AllStatus,
|
||||
},
|
||||
page: users.UsersPage{
|
||||
Page: users.Page{
|
||||
Total: 200,
|
||||
Offset: 50,
|
||||
Limit: 200,
|
||||
},
|
||||
Users: items[50:200],
|
||||
},
|
||||
err: nil,
|
||||
},
|
||||
{
|
||||
desc: "retrieve users with limit",
|
||||
pageMeta: users.Page{
|
||||
Offset: 0,
|
||||
Limit: 50,
|
||||
Role: users.AllRole,
|
||||
Status: users.AllStatus,
|
||||
},
|
||||
page: users.UsersPage{
|
||||
Page: users.Page{
|
||||
Total: uint64(num),
|
||||
Offset: 0,
|
||||
Limit: 50,
|
||||
},
|
||||
Users: items[:50],
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "retrieve with offset out of range",
|
||||
pageMeta: users.Page{
|
||||
Offset: 1000,
|
||||
Limit: 200,
|
||||
Role: users.AllRole,
|
||||
Status: users.AllStatus,
|
||||
},
|
||||
page: users.UsersPage{
|
||||
Page: users.Page{
|
||||
Total: 200,
|
||||
Offset: 1000,
|
||||
Limit: 200,
|
||||
},
|
||||
Users: []users.User{},
|
||||
},
|
||||
err: nil,
|
||||
},
|
||||
{
|
||||
desc: "retrieve with limit out of range",
|
||||
pageMeta: users.Page{
|
||||
Offset: 0,
|
||||
Limit: 1000,
|
||||
Role: users.AllRole,
|
||||
Status: users.AllStatus,
|
||||
},
|
||||
page: users.UsersPage{
|
||||
Page: users.Page{
|
||||
Total: 200,
|
||||
Offset: 0,
|
||||
Limit: 1000,
|
||||
},
|
||||
Users: items,
|
||||
},
|
||||
err: nil,
|
||||
},
|
||||
{
|
||||
desc: "retrieve with empty page",
|
||||
pageMeta: users.Page{},
|
||||
page: users.UsersPage{
|
||||
Page: users.Page{
|
||||
Total: 196, // number of enabled users
|
||||
Offset: 0,
|
||||
Limit: 0,
|
||||
},
|
||||
Users: []users.User{},
|
||||
},
|
||||
err: nil,
|
||||
},
|
||||
{
|
||||
desc: "retrieve with user id",
|
||||
pageMeta: users.Page{
|
||||
IDs: []string{items[0].ID},
|
||||
Offset: 0,
|
||||
Limit: 3,
|
||||
Role: users.AllRole,
|
||||
Status: users.AllStatus,
|
||||
},
|
||||
page: users.UsersPage{
|
||||
Page: users.Page{
|
||||
Total: 1,
|
||||
Offset: 0,
|
||||
Limit: 3,
|
||||
},
|
||||
Users: []users.User{items[0]},
|
||||
},
|
||||
err: nil,
|
||||
},
|
||||
{
|
||||
desc: "retrieve with invalid user id",
|
||||
pageMeta: users.Page{
|
||||
IDs: []string{invalidName},
|
||||
Offset: 0,
|
||||
Limit: 3,
|
||||
Role: users.AllRole,
|
||||
Status: users.AllStatus,
|
||||
},
|
||||
page: users.UsersPage{
|
||||
Page: users.Page{
|
||||
Total: 0,
|
||||
Offset: 0,
|
||||
Limit: 3,
|
||||
},
|
||||
Users: []users.User{},
|
||||
},
|
||||
err: nil,
|
||||
},
|
||||
{
|
||||
desc: "retrieve with first name",
|
||||
pageMeta: users.Page{
|
||||
FirstName: items[0].FirstName,
|
||||
Offset: 0,
|
||||
Limit: 3,
|
||||
Role: users.AllRole,
|
||||
Status: users.AllStatus,
|
||||
},
|
||||
page: users.UsersPage{
|
||||
Page: users.Page{
|
||||
Total: 1,
|
||||
Offset: 0,
|
||||
Limit: 3,
|
||||
},
|
||||
Users: []users.User{items[0]},
|
||||
},
|
||||
err: nil,
|
||||
},
|
||||
{
|
||||
desc: "retrieve with username",
|
||||
pageMeta: users.Page{
|
||||
Username: items[0].Credentials.Username,
|
||||
Offset: 0,
|
||||
Limit: 3,
|
||||
Role: users.AllRole,
|
||||
Status: users.AllStatus,
|
||||
},
|
||||
page: users.UsersPage{
|
||||
Page: users.Page{
|
||||
Total: 1,
|
||||
Offset: 0,
|
||||
Limit: 3,
|
||||
},
|
||||
Users: []users.User{items[0]},
|
||||
},
|
||||
err: nil,
|
||||
},
|
||||
{
|
||||
desc: "retrieve with enabled status",
|
||||
pageMeta: users.Page{
|
||||
Status: users.EnabledStatus,
|
||||
Offset: 0,
|
||||
Limit: 200,
|
||||
Role: users.AllRole,
|
||||
},
|
||||
page: users.UsersPage{
|
||||
Page: users.Page{
|
||||
Total: 196,
|
||||
Offset: 0,
|
||||
Limit: 200,
|
||||
},
|
||||
Users: enabledUsers,
|
||||
},
|
||||
err: nil,
|
||||
},
|
||||
{
|
||||
desc: "retrieve with disabled status",
|
||||
pageMeta: users.Page{
|
||||
Status: users.DisabledStatus,
|
||||
Offset: 0,
|
||||
Limit: 200,
|
||||
Role: users.AllRole,
|
||||
},
|
||||
page: users.UsersPage{
|
||||
Page: users.Page{
|
||||
Total: 4,
|
||||
Offset: 0,
|
||||
Limit: 200,
|
||||
},
|
||||
Users: []users.User{items[0], items[50], items[100], items[150]},
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "retrieve with all status",
|
||||
pageMeta: users.Page{
|
||||
Status: users.AllStatus,
|
||||
Offset: 0,
|
||||
Limit: 200,
|
||||
Role: users.AllRole,
|
||||
},
|
||||
page: users.UsersPage{
|
||||
Page: users.Page{
|
||||
Total: 200,
|
||||
Offset: 0,
|
||||
Limit: 200,
|
||||
},
|
||||
Users: items,
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "retrieve by tags",
|
||||
pageMeta: users.Page{
|
||||
Tag: "tag1",
|
||||
Offset: 0,
|
||||
Limit: 200,
|
||||
Role: users.AllRole,
|
||||
Status: users.AllStatus,
|
||||
},
|
||||
page: users.UsersPage{
|
||||
Page: users.Page{
|
||||
Total: 200,
|
||||
Offset: 0,
|
||||
Limit: 200,
|
||||
},
|
||||
Users: items,
|
||||
},
|
||||
err: nil,
|
||||
},
|
||||
{
|
||||
desc: "retrieve with invalid first name",
|
||||
pageMeta: users.Page{
|
||||
FirstName: invalidName,
|
||||
Offset: 0,
|
||||
Limit: 3,
|
||||
Role: users.AllRole,
|
||||
Status: users.AllStatus,
|
||||
},
|
||||
page: users.UsersPage{
|
||||
Page: users.Page{
|
||||
Total: 0,
|
||||
Offset: 0,
|
||||
Limit: 3,
|
||||
},
|
||||
Users: []users.User{},
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "retrieve with metadata",
|
||||
pageMeta: users.Page{
|
||||
Metadata: map[string]interface{}{
|
||||
"key": "value",
|
||||
},
|
||||
Offset: 0,
|
||||
Limit: 200,
|
||||
Role: users.AllRole,
|
||||
Status: users.AllStatus,
|
||||
},
|
||||
page: users.UsersPage{
|
||||
Page: users.Page{
|
||||
Total: 4,
|
||||
Offset: 0,
|
||||
Limit: 200,
|
||||
},
|
||||
Users: []users.User{items[0], items[50], items[100], items[150]},
|
||||
},
|
||||
err: nil,
|
||||
},
|
||||
{
|
||||
desc: "retrieve with invalid metadata",
|
||||
pageMeta: users.Page{
|
||||
Metadata: map[string]interface{}{
|
||||
"key": "value1",
|
||||
},
|
||||
Offset: 0,
|
||||
Limit: 200,
|
||||
Role: users.AllRole,
|
||||
Status: users.AllStatus,
|
||||
},
|
||||
page: users.UsersPage{
|
||||
Page: users.Page{
|
||||
Total: 0,
|
||||
Offset: 0,
|
||||
Limit: 200,
|
||||
},
|
||||
Users: []users.User{},
|
||||
},
|
||||
err: nil,
|
||||
},
|
||||
{
|
||||
desc: "retrieve with role",
|
||||
pageMeta: users.Page{
|
||||
Role: users.AdminRole,
|
||||
Offset: 0,
|
||||
Limit: 200,
|
||||
Status: users.AllStatus,
|
||||
},
|
||||
page: users.UsersPage{
|
||||
Page: users.Page{
|
||||
Total: 4,
|
||||
Offset: 0,
|
||||
Limit: 200,
|
||||
},
|
||||
Users: []users.User{items[0], items[50], items[100], items[150]},
|
||||
},
|
||||
err: nil,
|
||||
},
|
||||
{
|
||||
desc: "retrieve with invalid role",
|
||||
pageMeta: users.Page{
|
||||
Role: users.AdminRole + 2,
|
||||
Offset: 0,
|
||||
Limit: 200,
|
||||
Status: users.AllStatus,
|
||||
},
|
||||
page: users.UsersPage{
|
||||
Page: users.Page{
|
||||
Total: 0,
|
||||
Offset: 0,
|
||||
Limit: 200,
|
||||
},
|
||||
Users: []users.User{},
|
||||
},
|
||||
err: nil,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range cases {
|
||||
page, err := repo.RetrieveAll(context.Background(), tc.pageMeta)
|
||||
|
||||
assert.Equal(t, tc.page.Total, page.Total, fmt.Sprintf("%s: expected %d got %d\n", tc.desc, tc.page.Total, page.Total))
|
||||
assert.Equal(t, tc.page.Offset, page.Offset, fmt.Sprintf("%s: expected %d got %d\n", tc.desc, tc.page.Offset, page.Offset))
|
||||
assert.Equal(t, tc.page.Limit, page.Limit, fmt.Sprintf("%s: expected %d got %d\n", tc.desc, tc.page.Limit, page.Limit))
|
||||
assert.Equal(t, tc.page.Page, page.Page, fmt.Sprintf("%s: expected %v, got %v", tc.desc, tc.page, page))
|
||||
assert.ElementsMatch(t, tc.page.Users, page.Users, fmt.Sprintf("%s: expected %v, got %v", tc.desc, tc.page.Users, page.Users))
|
||||
assert.Equal(t, tc.err, err, fmt.Sprintf("%s: expected %s got %s\n", tc.desc, tc.err, err))
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
// Copyright (c) Abstract Machines
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package users
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"strings"
|
||||
|
||||
"github.com/absmach/magistrala/pkg/apiutil"
|
||||
)
|
||||
|
||||
// Role represents User role.
|
||||
type Role uint8
|
||||
|
||||
// Possible User role values.
|
||||
const (
|
||||
UserRole Role = iota
|
||||
AdminRole
|
||||
|
||||
// AllRole is used for querying purposes to list users irrespective
|
||||
// of their role - both admin and user. It is never stored in the
|
||||
// database as the actual user role and should always be the largest
|
||||
// value in this enumeration.
|
||||
AllRole
|
||||
)
|
||||
|
||||
// String representation of the possible role values.
|
||||
const (
|
||||
Admin = "admin"
|
||||
user = "user"
|
||||
)
|
||||
|
||||
// String converts user role to string literal.
|
||||
func (cs Role) String() string {
|
||||
switch cs {
|
||||
case AdminRole:
|
||||
return Admin
|
||||
case UserRole:
|
||||
return user
|
||||
case AllRole:
|
||||
return All
|
||||
default:
|
||||
return Unknown
|
||||
}
|
||||
}
|
||||
|
||||
// ToRole converts string value to a valid User role.
|
||||
func ToRole(status string) (Role, error) {
|
||||
switch status {
|
||||
case "", user:
|
||||
return UserRole, nil
|
||||
case Admin:
|
||||
return AdminRole, nil
|
||||
case All:
|
||||
return AllRole, nil
|
||||
default:
|
||||
return Role(0), apiutil.ErrInvalidRole
|
||||
}
|
||||
}
|
||||
|
||||
func (r Role) MarshalJSON() ([]byte, error) {
|
||||
return json.Marshal(r.String())
|
||||
}
|
||||
|
||||
func (r *Role) UnmarshalJSON(data []byte) error {
|
||||
str := strings.Trim(string(data), "\"")
|
||||
val, err := ToRole(str)
|
||||
*r = val
|
||||
return err
|
||||
}
|
||||
+248
-185
@@ -5,6 +5,7 @@ package users
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/mail"
|
||||
"time"
|
||||
|
||||
"github.com/absmach/magistrala"
|
||||
@@ -15,7 +16,6 @@ import (
|
||||
repoerr "github.com/absmach/magistrala/pkg/errors/repository"
|
||||
svcerr "github.com/absmach/magistrala/pkg/errors/service"
|
||||
"github.com/absmach/magistrala/pkg/policies"
|
||||
"github.com/absmach/magistrala/users/postgres"
|
||||
"golang.org/x/sync/errgroup"
|
||||
)
|
||||
|
||||
@@ -28,7 +28,7 @@ var (
|
||||
|
||||
type service struct {
|
||||
token magistrala.TokenServiceClient
|
||||
clients postgres.Repository
|
||||
users Repository
|
||||
idProvider magistrala.IDProvider
|
||||
policies policies.Service
|
||||
hasher Hasher
|
||||
@@ -36,10 +36,10 @@ type service struct {
|
||||
}
|
||||
|
||||
// NewService returns a new Users service implementation.
|
||||
func NewService(token magistrala.TokenServiceClient, crepo postgres.Repository, policyService policies.Service, emailer Emailer, hasher Hasher, idp magistrala.IDProvider) Service {
|
||||
func NewService(token magistrala.TokenServiceClient, urepo Repository, policyService policies.Service, emailer Emailer, hasher Hasher, idp magistrala.IDProvider) Service {
|
||||
return service{
|
||||
token: token,
|
||||
clients: crepo,
|
||||
users: urepo,
|
||||
policies: policyService,
|
||||
hasher: hasher,
|
||||
email: emailer,
|
||||
@@ -47,57 +47,66 @@ func NewService(token magistrala.TokenServiceClient, crepo postgres.Repository,
|
||||
}
|
||||
}
|
||||
|
||||
func (svc service) RegisterClient(ctx context.Context, session authn.Session, cli mgclients.Client, selfRegister bool) (rc mgclients.Client, err error) {
|
||||
func (svc service) Register(ctx context.Context, session authn.Session, u User, selfRegister bool) (uc User, err error) {
|
||||
if !selfRegister {
|
||||
if err := svc.checkSuperAdmin(ctx, session); err != nil {
|
||||
return mgclients.Client{}, err
|
||||
return User{}, err
|
||||
}
|
||||
}
|
||||
|
||||
clientID, err := svc.idProvider.ID()
|
||||
userID, err := svc.idProvider.ID()
|
||||
if err != nil {
|
||||
return mgclients.Client{}, err
|
||||
return User{}, err
|
||||
}
|
||||
|
||||
if cli.Credentials.Secret != "" {
|
||||
hash, err := svc.hasher.Hash(cli.Credentials.Secret)
|
||||
if u.Credentials.Secret != "" {
|
||||
hash, err := svc.hasher.Hash(u.Credentials.Secret)
|
||||
if err != nil {
|
||||
return mgclients.Client{}, errors.Wrap(svcerr.ErrMalformedEntity, err)
|
||||
return User{}, errors.Wrap(svcerr.ErrMalformedEntity, err)
|
||||
}
|
||||
cli.Credentials.Secret = hash
|
||||
u.Credentials.Secret = hash
|
||||
}
|
||||
|
||||
if cli.Status != mgclients.DisabledStatus && cli.Status != mgclients.EnabledStatus {
|
||||
return mgclients.Client{}, errors.Wrap(svcerr.ErrMalformedEntity, svcerr.ErrInvalidStatus)
|
||||
if u.Status != DisabledStatus && u.Status != EnabledStatus {
|
||||
return User{}, errors.Wrap(svcerr.ErrMalformedEntity, svcerr.ErrInvalidStatus)
|
||||
}
|
||||
if cli.Role != mgclients.UserRole && cli.Role != mgclients.AdminRole {
|
||||
return mgclients.Client{}, errors.Wrap(svcerr.ErrMalformedEntity, svcerr.ErrInvalidRole)
|
||||
if u.Role != UserRole && u.Role != AdminRole {
|
||||
return User{}, errors.Wrap(svcerr.ErrMalformedEntity, svcerr.ErrInvalidRole)
|
||||
}
|
||||
cli.ID = clientID
|
||||
cli.CreatedAt = time.Now()
|
||||
u.ID = userID
|
||||
u.CreatedAt = time.Now()
|
||||
|
||||
if err := svc.addClientPolicy(ctx, cli.ID, cli.Role); err != nil {
|
||||
return mgclients.Client{}, err
|
||||
if err := svc.addUserPolicy(ctx, u.ID, u.Role); err != nil {
|
||||
return User{}, err
|
||||
}
|
||||
defer func() {
|
||||
if err != nil {
|
||||
if errRollback := svc.addClientPolicyRollback(ctx, cli.ID, cli.Role); errRollback != nil {
|
||||
if errRollback := svc.addUserPolicyRollback(ctx, u.ID, u.Role); errRollback != nil {
|
||||
err = errors.Wrap(errors.Wrap(errors.ErrRollbackTx, errRollback), err)
|
||||
}
|
||||
}
|
||||
}()
|
||||
client, err := svc.clients.Save(ctx, cli)
|
||||
user, err := svc.users.Save(ctx, u)
|
||||
if err != nil {
|
||||
return mgclients.Client{}, errors.Wrap(svcerr.ErrCreateEntity, err)
|
||||
return User{}, errors.Wrap(svcerr.ErrCreateEntity, err)
|
||||
}
|
||||
return client, nil
|
||||
return user, nil
|
||||
}
|
||||
|
||||
func (svc service) IssueToken(ctx context.Context, identity, secret string) (*magistrala.Token, error) {
|
||||
dbUser, err := svc.clients.RetrieveByIdentity(ctx, identity)
|
||||
var dbUser User
|
||||
var err error
|
||||
|
||||
if _, parseErr := mail.ParseAddress(identity); parseErr != nil {
|
||||
dbUser, err = svc.users.RetrieveByUsername(ctx, identity)
|
||||
} else {
|
||||
dbUser, err = svc.users.RetrieveByEmail(ctx, identity)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return &magistrala.Token{}, errors.Wrap(svcerr.ErrAuthentication, err)
|
||||
}
|
||||
|
||||
if err := svc.hasher.Compare(secret, dbUser.Credentials.Secret); err != nil {
|
||||
return &magistrala.Token{}, errors.Wrap(svcerr.ErrLogin, err)
|
||||
}
|
||||
@@ -107,150 +116,178 @@ func (svc service) IssueToken(ctx context.Context, identity, secret string) (*ma
|
||||
return &magistrala.Token{}, errors.Wrap(errIssueToken, err)
|
||||
}
|
||||
|
||||
return token, err
|
||||
return token, nil
|
||||
}
|
||||
|
||||
func (svc service) RefreshToken(ctx context.Context, session authn.Session, refreshToken string) (*magistrala.Token, error) {
|
||||
dbUser, err := svc.clients.RetrieveByID(ctx, session.UserID)
|
||||
dbUser, err := svc.users.RetrieveByID(ctx, session.UserID)
|
||||
if err != nil {
|
||||
return &magistrala.Token{}, errors.Wrap(svcerr.ErrAuthentication, err)
|
||||
}
|
||||
if dbUser.Status == mgclients.DisabledStatus {
|
||||
if dbUser.Status == DisabledStatus {
|
||||
return &magistrala.Token{}, errors.Wrap(svcerr.ErrAuthentication, errLoginDisableUser)
|
||||
}
|
||||
|
||||
return svc.token.Refresh(ctx, &magistrala.RefreshReq{RefreshToken: refreshToken})
|
||||
}
|
||||
|
||||
func (svc service) ViewClient(ctx context.Context, session authn.Session, id string) (mgclients.Client, error) {
|
||||
client, err := svc.clients.RetrieveByID(ctx, id)
|
||||
func (svc service) View(ctx context.Context, session authn.Session, id string) (User, error) {
|
||||
user, err := svc.users.RetrieveByID(ctx, id)
|
||||
if err != nil {
|
||||
return mgclients.Client{}, errors.Wrap(svcerr.ErrViewEntity, err)
|
||||
return User{}, errors.Wrap(svcerr.ErrViewEntity, err)
|
||||
}
|
||||
|
||||
if session.UserID != id {
|
||||
if err := svc.checkSuperAdmin(ctx, session); err != nil {
|
||||
return mgclients.Client{Name: client.Name, ID: client.ID}, nil
|
||||
return User{
|
||||
FirstName: user.FirstName,
|
||||
LastName: user.LastName,
|
||||
ID: user.ID,
|
||||
Credentials: Credentials{Username: user.Credentials.Username},
|
||||
}, nil
|
||||
}
|
||||
}
|
||||
|
||||
client.Credentials.Secret = ""
|
||||
user.Credentials.Secret = ""
|
||||
|
||||
return client, nil
|
||||
return user, nil
|
||||
}
|
||||
|
||||
func (svc service) ViewProfile(ctx context.Context, session authn.Session) (mgclients.Client, error) {
|
||||
client, err := svc.clients.RetrieveByID(ctx, session.UserID)
|
||||
func (svc service) ViewProfile(ctx context.Context, session authn.Session) (User, error) {
|
||||
user, err := svc.users.RetrieveByID(ctx, session.UserID)
|
||||
if err != nil {
|
||||
return mgclients.Client{}, errors.Wrap(svcerr.ErrViewEntity, err)
|
||||
return User{}, errors.Wrap(svcerr.ErrViewEntity, err)
|
||||
}
|
||||
client.Credentials.Secret = ""
|
||||
user.Credentials.Secret = ""
|
||||
|
||||
return client, nil
|
||||
return user, nil
|
||||
}
|
||||
|
||||
func (svc service) ListClients(ctx context.Context, session authn.Session, pm mgclients.Page) (mgclients.ClientsPage, error) {
|
||||
func (svc service) ListUsers(ctx context.Context, session authn.Session, pm Page) (UsersPage, error) {
|
||||
if err := svc.checkSuperAdmin(ctx, session); err != nil {
|
||||
return mgclients.ClientsPage{}, err
|
||||
return UsersPage{}, err
|
||||
}
|
||||
|
||||
pm.Role = mgclients.AllRole
|
||||
pg, err := svc.clients.RetrieveAll(ctx, pm)
|
||||
pm.Role = AllRole
|
||||
pg, err := svc.users.RetrieveAll(ctx, pm)
|
||||
if err != nil {
|
||||
return mgclients.ClientsPage{}, errors.Wrap(svcerr.ErrViewEntity, err)
|
||||
return UsersPage{}, errors.Wrap(svcerr.ErrViewEntity, err)
|
||||
}
|
||||
return pg, err
|
||||
}
|
||||
|
||||
func (svc service) SearchUsers(ctx context.Context, pm mgclients.Page) (mgclients.ClientsPage, error) {
|
||||
page := mgclients.Page{
|
||||
Offset: pm.Offset,
|
||||
Limit: pm.Limit,
|
||||
Name: pm.Name,
|
||||
Id: pm.Id,
|
||||
Role: mgclients.UserRole,
|
||||
func (svc service) SearchUsers(ctx context.Context, pm Page) (UsersPage, error) {
|
||||
page := Page{
|
||||
Offset: pm.Offset,
|
||||
Limit: pm.Limit,
|
||||
FirstName: pm.FirstName,
|
||||
LastName: pm.LastName,
|
||||
Username: pm.Username,
|
||||
Id: pm.Id,
|
||||
Role: UserRole,
|
||||
}
|
||||
|
||||
cp, err := svc.clients.SearchClients(ctx, page)
|
||||
cp, err := svc.users.SearchUsers(ctx, page)
|
||||
if err != nil {
|
||||
return mgclients.ClientsPage{}, errors.Wrap(svcerr.ErrViewEntity, err)
|
||||
return UsersPage{}, errors.Wrap(svcerr.ErrViewEntity, err)
|
||||
}
|
||||
|
||||
return cp, nil
|
||||
}
|
||||
|
||||
func (svc service) UpdateClient(ctx context.Context, session authn.Session, cli mgclients.Client) (mgclients.Client, error) {
|
||||
if session.UserID != cli.ID {
|
||||
func (svc service) Update(ctx context.Context, session authn.Session, usr User) (User, error) {
|
||||
if session.UserID != usr.ID {
|
||||
if err := svc.checkSuperAdmin(ctx, session); err != nil {
|
||||
return mgclients.Client{}, err
|
||||
return User{}, err
|
||||
}
|
||||
}
|
||||
|
||||
client := mgclients.Client{
|
||||
ID: cli.ID,
|
||||
Name: cli.Name,
|
||||
Metadata: cli.Metadata,
|
||||
user := User{
|
||||
ID: usr.ID,
|
||||
FirstName: usr.FirstName,
|
||||
LastName: usr.LastName,
|
||||
Metadata: usr.Metadata,
|
||||
UpdatedAt: time.Now(),
|
||||
UpdatedBy: session.UserID,
|
||||
}
|
||||
|
||||
client, err := svc.clients.Update(ctx, client)
|
||||
user, err := svc.users.Update(ctx, user)
|
||||
if err != nil {
|
||||
return mgclients.Client{}, errors.Wrap(svcerr.ErrUpdateEntity, err)
|
||||
return User{}, errors.Wrap(svcerr.ErrUpdateEntity, err)
|
||||
}
|
||||
return client, nil
|
||||
return user, nil
|
||||
}
|
||||
|
||||
func (svc service) UpdateClientTags(ctx context.Context, session authn.Session, cli mgclients.Client) (mgclients.Client, error) {
|
||||
if session.UserID != cli.ID {
|
||||
func (svc service) UpdateTags(ctx context.Context, session authn.Session, usr User) (User, error) {
|
||||
if session.UserID != usr.ID {
|
||||
if err := svc.checkSuperAdmin(ctx, session); err != nil {
|
||||
return mgclients.Client{}, err
|
||||
return User{}, err
|
||||
}
|
||||
}
|
||||
|
||||
client := mgclients.Client{
|
||||
ID: cli.ID,
|
||||
Tags: cli.Tags,
|
||||
user := User{
|
||||
ID: usr.ID,
|
||||
Tags: usr.Tags,
|
||||
UpdatedAt: time.Now(),
|
||||
UpdatedBy: session.UserID,
|
||||
}
|
||||
client, err := svc.clients.UpdateTags(ctx, client)
|
||||
user, err := svc.users.Update(ctx, user)
|
||||
if err != nil {
|
||||
return mgclients.Client{}, errors.Wrap(svcerr.ErrUpdateEntity, err)
|
||||
return User{}, errors.Wrap(svcerr.ErrUpdateEntity, err)
|
||||
}
|
||||
|
||||
return client, nil
|
||||
return user, nil
|
||||
}
|
||||
|
||||
func (svc service) UpdateClientIdentity(ctx context.Context, session authn.Session, clientID, identity string) (mgclients.Client, error) {
|
||||
if session.UserID != clientID {
|
||||
func (svc service) UpdateProfilePicture(ctx context.Context, session authn.Session, usr User) (User, error) {
|
||||
if session.UserID != usr.ID {
|
||||
if err := svc.checkSuperAdmin(ctx, session); err != nil {
|
||||
return mgclients.Client{}, err
|
||||
return User{}, err
|
||||
}
|
||||
}
|
||||
|
||||
cli := mgclients.Client{
|
||||
ID: clientID,
|
||||
Credentials: mgclients.Credentials{
|
||||
Identity: identity,
|
||||
},
|
||||
user := User{
|
||||
ID: usr.ID,
|
||||
ProfilePicture: usr.ProfilePicture,
|
||||
UpdatedAt: time.Now(),
|
||||
UpdatedBy: session.UserID,
|
||||
}
|
||||
|
||||
user, err := svc.users.Update(ctx, user)
|
||||
if err != nil {
|
||||
return User{}, errors.Wrap(svcerr.ErrUpdateEntity, err)
|
||||
}
|
||||
|
||||
return user, nil
|
||||
}
|
||||
|
||||
func (svc service) UpdateEmail(ctx context.Context, session authn.Session, userID, email string) (User, error) {
|
||||
if session.UserID != userID {
|
||||
if err := svc.checkSuperAdmin(ctx, session); err != nil {
|
||||
return User{}, err
|
||||
}
|
||||
}
|
||||
|
||||
user := User{
|
||||
ID: userID,
|
||||
Email: email,
|
||||
UpdatedAt: time.Now(),
|
||||
UpdatedBy: session.UserID,
|
||||
}
|
||||
cli, err := svc.clients.UpdateIdentity(ctx, cli)
|
||||
user, err := svc.users.Update(ctx, user)
|
||||
if err != nil {
|
||||
return mgclients.Client{}, errors.Wrap(svcerr.ErrUpdateEntity, err)
|
||||
return User{}, errors.Wrap(svcerr.ErrUpdateEntity, err)
|
||||
}
|
||||
return cli, nil
|
||||
return user, nil
|
||||
}
|
||||
|
||||
func (svc service) GenerateResetToken(ctx context.Context, email, host string) error {
|
||||
client, err := svc.clients.RetrieveByIdentity(ctx, email)
|
||||
user, err := svc.users.RetrieveByEmail(ctx, email)
|
||||
if err != nil {
|
||||
return errors.Wrap(svcerr.ErrViewEntity, err)
|
||||
}
|
||||
issueReq := &magistrala.IssueReq{
|
||||
UserId: client.ID,
|
||||
UserId: user.ID,
|
||||
Type: uint32(mgauth.RecoveryKey),
|
||||
}
|
||||
token, err := svc.token.Issue(ctx, issueReq)
|
||||
@@ -258,11 +295,11 @@ func (svc service) GenerateResetToken(ctx context.Context, email, host string) e
|
||||
return errors.Wrap(errRecoveryToken, err)
|
||||
}
|
||||
|
||||
return svc.SendPasswordReset(ctx, host, email, client.Name, token.AccessToken)
|
||||
return svc.SendPasswordReset(ctx, host, email, user.Credentials.Username, token.AccessToken)
|
||||
}
|
||||
|
||||
func (svc service) ResetSecret(ctx context.Context, session authn.Session, secret string) error {
|
||||
c, err := svc.clients.RetrieveByID(ctx, session.UserID)
|
||||
u, err := svc.users.RetrieveByID(ctx, session.UserID)
|
||||
if err != nil {
|
||||
return errors.Wrap(svcerr.ErrViewEntity, err)
|
||||
}
|
||||
@@ -271,43 +308,65 @@ func (svc service) ResetSecret(ctx context.Context, session authn.Session, secre
|
||||
if err != nil {
|
||||
return errors.Wrap(svcerr.ErrMalformedEntity, err)
|
||||
}
|
||||
c = mgclients.Client{
|
||||
ID: c.ID,
|
||||
Credentials: mgclients.Credentials{
|
||||
Identity: c.Credentials.Identity,
|
||||
Secret: secret,
|
||||
u = User{
|
||||
ID: u.ID,
|
||||
Email: u.Email,
|
||||
Credentials: Credentials{
|
||||
Secret: secret,
|
||||
},
|
||||
UpdatedAt: time.Now(),
|
||||
UpdatedBy: session.UserID,
|
||||
}
|
||||
if _, err := svc.clients.UpdateSecret(ctx, c); err != nil {
|
||||
if _, err := svc.users.UpdateSecret(ctx, u); err != nil {
|
||||
return errors.Wrap(svcerr.ErrAuthorization, err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (svc service) UpdateClientSecret(ctx context.Context, session authn.Session, oldSecret, newSecret string) (mgclients.Client, error) {
|
||||
dbClient, err := svc.clients.RetrieveByID(ctx, session.UserID)
|
||||
func (svc service) UpdateSecret(ctx context.Context, session authn.Session, oldSecret, newSecret string) (User, error) {
|
||||
dbUser, err := svc.users.RetrieveByID(ctx, session.UserID)
|
||||
if err != nil {
|
||||
return mgclients.Client{}, errors.Wrap(svcerr.ErrViewEntity, err)
|
||||
return User{}, errors.Wrap(svcerr.ErrViewEntity, err)
|
||||
}
|
||||
if _, err := svc.IssueToken(ctx, dbClient.Credentials.Identity, oldSecret); err != nil {
|
||||
return mgclients.Client{}, err
|
||||
if _, err := svc.IssueToken(ctx, dbUser.Credentials.Username, oldSecret); err != nil {
|
||||
return User{}, err
|
||||
}
|
||||
newSecret, err = svc.hasher.Hash(newSecret)
|
||||
if err != nil {
|
||||
return mgclients.Client{}, errors.Wrap(svcerr.ErrMalformedEntity, err)
|
||||
return User{}, errors.Wrap(svcerr.ErrMalformedEntity, err)
|
||||
}
|
||||
dbClient.Credentials.Secret = newSecret
|
||||
dbClient.UpdatedAt = time.Now()
|
||||
dbClient.UpdatedBy = session.UserID
|
||||
dbUser.Credentials.Secret = newSecret
|
||||
dbUser.UpdatedAt = time.Now()
|
||||
dbUser.UpdatedBy = session.UserID
|
||||
|
||||
dbClient, err = svc.clients.UpdateSecret(ctx, dbClient)
|
||||
dbUser, err = svc.users.UpdateSecret(ctx, dbUser)
|
||||
if err != nil {
|
||||
return mgclients.Client{}, errors.Wrap(svcerr.ErrUpdateEntity, err)
|
||||
return User{}, errors.Wrap(svcerr.ErrUpdateEntity, err)
|
||||
}
|
||||
|
||||
return dbClient, nil
|
||||
return dbUser, nil
|
||||
}
|
||||
|
||||
func (svc service) UpdateUsername(ctx context.Context, session authn.Session, id, username string) (User, error) {
|
||||
if session.UserID != id {
|
||||
if err := svc.checkSuperAdmin(ctx, session); err != nil {
|
||||
return User{}, err
|
||||
}
|
||||
}
|
||||
|
||||
usr := User{
|
||||
ID: id,
|
||||
Credentials: Credentials{
|
||||
Username: username,
|
||||
},
|
||||
UpdatedAt: time.Now(),
|
||||
UpdatedBy: session.UserID,
|
||||
}
|
||||
updatedUser, err := svc.users.UpdateUsername(ctx, usr)
|
||||
if err != nil {
|
||||
return User{}, errors.Wrap(svcerr.ErrUpdateEntity, err)
|
||||
}
|
||||
return updatedUser, nil
|
||||
}
|
||||
|
||||
func (svc service) SendPasswordReset(_ context.Context, host, email, user, token string) error {
|
||||
@@ -315,97 +374,97 @@ func (svc service) SendPasswordReset(_ context.Context, host, email, user, token
|
||||
return svc.email.SendPasswordReset(to, host, user, token)
|
||||
}
|
||||
|
||||
func (svc service) UpdateClientRole(ctx context.Context, session authn.Session, cli mgclients.Client) (mgclients.Client, error) {
|
||||
func (svc service) UpdateRole(ctx context.Context, session authn.Session, usr User) (User, error) {
|
||||
if err := svc.checkSuperAdmin(ctx, session); err != nil {
|
||||
return mgclients.Client{}, err
|
||||
return User{}, err
|
||||
}
|
||||
client := mgclients.Client{
|
||||
ID: cli.ID,
|
||||
Role: cli.Role,
|
||||
user := User{
|
||||
ID: usr.ID,
|
||||
Role: usr.Role,
|
||||
UpdatedAt: time.Now(),
|
||||
UpdatedBy: session.UserID,
|
||||
}
|
||||
|
||||
if err := svc.updateClientPolicy(ctx, cli.ID, cli.Role); err != nil {
|
||||
return mgclients.Client{}, err
|
||||
if err := svc.updateUserPolicy(ctx, usr.ID, usr.Role); err != nil {
|
||||
return User{}, err
|
||||
}
|
||||
|
||||
client, err := svc.clients.UpdateRole(ctx, client)
|
||||
u, err := svc.users.Update(ctx, user)
|
||||
if err != nil {
|
||||
// If failed to update role in DB, then revert back to platform admin policies in spicedb
|
||||
if errRollback := svc.updateClientPolicy(ctx, cli.ID, mgclients.UserRole); errRollback != nil {
|
||||
return mgclients.Client{}, errors.Wrap(errRollback, err)
|
||||
if errRollback := svc.updateUserPolicy(ctx, usr.ID, UserRole); errRollback != nil {
|
||||
return User{}, errors.Wrap(errRollback, err)
|
||||
}
|
||||
return mgclients.Client{}, errors.Wrap(svcerr.ErrUpdateEntity, err)
|
||||
return User{}, errors.Wrap(svcerr.ErrUpdateEntity, err)
|
||||
}
|
||||
return client, nil
|
||||
return u, nil
|
||||
}
|
||||
|
||||
func (svc service) EnableClient(ctx context.Context, session authn.Session, id string) (mgclients.Client, error) {
|
||||
client := mgclients.Client{
|
||||
func (svc service) Enable(ctx context.Context, session authn.Session, id string) (User, error) {
|
||||
u := User{
|
||||
ID: id,
|
||||
UpdatedAt: time.Now(),
|
||||
Status: mgclients.EnabledStatus,
|
||||
Status: EnabledStatus,
|
||||
}
|
||||
client, err := svc.changeClientStatus(ctx, session, client)
|
||||
user, err := svc.changeUserStatus(ctx, session, u)
|
||||
if err != nil {
|
||||
return mgclients.Client{}, errors.Wrap(mgclients.ErrEnableClient, err)
|
||||
return User{}, errors.Wrap(mgclients.ErrEnableClient, err)
|
||||
}
|
||||
|
||||
return client, nil
|
||||
return user, nil
|
||||
}
|
||||
|
||||
func (svc service) DisableClient(ctx context.Context, session authn.Session, id string) (mgclients.Client, error) {
|
||||
client := mgclients.Client{
|
||||
func (svc service) Disable(ctx context.Context, session authn.Session, id string) (User, error) {
|
||||
user := User{
|
||||
ID: id,
|
||||
UpdatedAt: time.Now(),
|
||||
Status: mgclients.DisabledStatus,
|
||||
Status: DisabledStatus,
|
||||
}
|
||||
client, err := svc.changeClientStatus(ctx, session, client)
|
||||
user, err := svc.changeUserStatus(ctx, session, user)
|
||||
if err != nil {
|
||||
return mgclients.Client{}, err
|
||||
return User{}, err
|
||||
}
|
||||
|
||||
return client, nil
|
||||
return user, nil
|
||||
}
|
||||
|
||||
func (svc service) changeClientStatus(ctx context.Context, session authn.Session, client mgclients.Client) (mgclients.Client, error) {
|
||||
if session.UserID != client.ID {
|
||||
func (svc service) changeUserStatus(ctx context.Context, session authn.Session, user User) (User, error) {
|
||||
if session.UserID != user.ID {
|
||||
if err := svc.checkSuperAdmin(ctx, session); err != nil {
|
||||
return mgclients.Client{}, err
|
||||
return User{}, err
|
||||
}
|
||||
}
|
||||
dbClient, err := svc.clients.RetrieveByID(ctx, client.ID)
|
||||
dbu, err := svc.users.RetrieveByID(ctx, user.ID)
|
||||
if err != nil {
|
||||
return mgclients.Client{}, errors.Wrap(svcerr.ErrViewEntity, err)
|
||||
return User{}, errors.Wrap(svcerr.ErrViewEntity, err)
|
||||
}
|
||||
if dbClient.Status == client.Status {
|
||||
return mgclients.Client{}, errors.ErrStatusAlreadyAssigned
|
||||
if dbu.Status == user.Status {
|
||||
return User{}, errors.ErrStatusAlreadyAssigned
|
||||
}
|
||||
client.UpdatedBy = session.UserID
|
||||
user.UpdatedBy = session.UserID
|
||||
|
||||
client, err = svc.clients.ChangeStatus(ctx, client)
|
||||
user, err = svc.users.ChangeStatus(ctx, user)
|
||||
if err != nil {
|
||||
return mgclients.Client{}, errors.Wrap(svcerr.ErrUpdateEntity, err)
|
||||
return User{}, errors.Wrap(svcerr.ErrUpdateEntity, err)
|
||||
}
|
||||
return client, nil
|
||||
return user, nil
|
||||
}
|
||||
|
||||
func (svc service) DeleteClient(ctx context.Context, session authn.Session, id string) error {
|
||||
client := mgclients.Client{
|
||||
func (svc service) Delete(ctx context.Context, session authn.Session, id string) error {
|
||||
user := User{
|
||||
ID: id,
|
||||
UpdatedAt: time.Now(),
|
||||
Status: mgclients.DeletedStatus,
|
||||
Status: DeletedStatus,
|
||||
}
|
||||
|
||||
if _, err := svc.changeClientStatus(ctx, session, client); err != nil {
|
||||
if _, err := svc.changeUserStatus(ctx, session, user); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (svc service) ListMembers(ctx context.Context, session authn.Session, objectKind, objectID string, pm mgclients.Page) (mgclients.MembersPage, error) {
|
||||
func (svc service) ListMembers(ctx context.Context, session authn.Session, objectKind, objectID string, pm Page) (MembersPage, error) {
|
||||
var objectType string
|
||||
switch objectKind {
|
||||
case policies.ThingsKind:
|
||||
@@ -425,11 +484,11 @@ func (svc service) ListMembers(ctx context.Context, session authn.Session, objec
|
||||
ObjectType: objectType,
|
||||
})
|
||||
if err != nil {
|
||||
return mgclients.MembersPage{}, errors.Wrap(svcerr.ErrNotFound, err)
|
||||
return MembersPage{}, errors.Wrap(svcerr.ErrNotFound, err)
|
||||
}
|
||||
if len(duids.Policies) == 0 {
|
||||
return mgclients.MembersPage{
|
||||
Page: mgclients.Page{Total: 0, Offset: pm.Offset, Limit: pm.Limit},
|
||||
return MembersPage{
|
||||
Page: Page{Total: 0, Offset: pm.Offset, Limit: pm.Limit},
|
||||
}, nil
|
||||
}
|
||||
|
||||
@@ -441,50 +500,54 @@ func (svc service) ListMembers(ctx context.Context, session authn.Session, objec
|
||||
}
|
||||
pm.IDs = userIDs
|
||||
|
||||
cp, err := svc.clients.RetrieveAll(ctx, pm)
|
||||
up, err := svc.users.RetrieveAll(ctx, pm)
|
||||
if err != nil {
|
||||
return mgclients.MembersPage{}, errors.Wrap(svcerr.ErrViewEntity, err)
|
||||
return MembersPage{}, errors.Wrap(svcerr.ErrViewEntity, err)
|
||||
}
|
||||
|
||||
for i, c := range cp.Clients {
|
||||
cp.Clients[i] = mgclients.Client{
|
||||
ID: c.ID,
|
||||
Name: c.Name,
|
||||
CreatedAt: c.CreatedAt,
|
||||
UpdatedAt: c.UpdatedAt,
|
||||
Status: c.Status,
|
||||
for i, u := range up.Users {
|
||||
up.Users[i] = User{
|
||||
ID: u.ID,
|
||||
FirstName: u.FirstName,
|
||||
LastName: u.LastName,
|
||||
Credentials: Credentials{
|
||||
Username: u.Credentials.Username,
|
||||
},
|
||||
CreatedAt: u.CreatedAt,
|
||||
UpdatedAt: u.UpdatedAt,
|
||||
Status: u.Status,
|
||||
}
|
||||
}
|
||||
|
||||
if pm.ListPerms && len(cp.Clients) > 0 {
|
||||
if pm.ListPerms && len(up.Users) > 0 {
|
||||
g, ctx := errgroup.WithContext(ctx)
|
||||
|
||||
for i := range cp.Clients {
|
||||
for i := range up.Users {
|
||||
// Copying loop variable "i" to avoid "loop variable captured by func literal"
|
||||
iter := i
|
||||
g.Go(func() error {
|
||||
return svc.retrieveObjectUsersPermissions(ctx, session.DomainID, objectType, objectID, &cp.Clients[iter])
|
||||
return svc.retrieveObjectUsersPermissions(ctx, session.DomainID, objectType, objectID, &up.Users[iter])
|
||||
})
|
||||
}
|
||||
|
||||
if err := g.Wait(); err != nil {
|
||||
return mgclients.MembersPage{}, err
|
||||
return MembersPage{}, err
|
||||
}
|
||||
}
|
||||
|
||||
return mgclients.MembersPage{
|
||||
Page: cp.Page,
|
||||
Members: cp.Clients,
|
||||
return MembersPage{
|
||||
Page: up.Page,
|
||||
Members: up.Users,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (svc service) retrieveObjectUsersPermissions(ctx context.Context, domainID, objectType, objectID string, client *mgclients.Client) error {
|
||||
userID := mgauth.EncodeDomainUserID(domainID, client.ID)
|
||||
func (svc service) retrieveObjectUsersPermissions(ctx context.Context, domainID, objectType, objectID string, user *User) error {
|
||||
userID := mgauth.EncodeDomainUserID(domainID, user.ID)
|
||||
permissions, err := svc.listObjectUserPermission(ctx, userID, objectType, objectID)
|
||||
if err != nil {
|
||||
return errors.Wrap(svcerr.ErrAuthorization, err)
|
||||
}
|
||||
client.Permissions = permissions
|
||||
user.Permissions = permissions
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -503,7 +566,7 @@ func (svc service) listObjectUserPermission(ctx context.Context, userID, objectT
|
||||
|
||||
func (svc *service) checkSuperAdmin(ctx context.Context, session authn.Session) error {
|
||||
if !session.SuperAdmin {
|
||||
if err := svc.clients.CheckSuperAdmin(ctx, session.UserID); err != nil {
|
||||
if err := svc.users.CheckSuperAdmin(ctx, session.UserID); err != nil {
|
||||
return errors.Wrap(svcerr.ErrAuthorization, err)
|
||||
}
|
||||
}
|
||||
@@ -511,35 +574,35 @@ func (svc *service) checkSuperAdmin(ctx context.Context, session authn.Session)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (svc service) OAuthCallback(ctx context.Context, client mgclients.Client) (mgclients.Client, error) {
|
||||
rclient, err := svc.clients.RetrieveByIdentity(ctx, client.Credentials.Identity)
|
||||
func (svc service) OAuthCallback(ctx context.Context, user User) (User, error) {
|
||||
ruser, err := svc.users.RetrieveByEmail(ctx, user.Email)
|
||||
if err != nil {
|
||||
switch errors.Contains(err, repoerr.ErrNotFound) {
|
||||
case true:
|
||||
rclient, err = svc.RegisterClient(ctx, authn.Session{}, client, true)
|
||||
ruser, err = svc.Register(ctx, authn.Session{}, user, true)
|
||||
if err != nil {
|
||||
return mgclients.Client{}, err
|
||||
return User{}, err
|
||||
}
|
||||
default:
|
||||
return mgclients.Client{}, err
|
||||
return User{}, err
|
||||
}
|
||||
}
|
||||
|
||||
return mgclients.Client{
|
||||
ID: rclient.ID,
|
||||
Role: rclient.Role,
|
||||
return User{
|
||||
ID: ruser.ID,
|
||||
Role: ruser.Role,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (svc service) OAuthAddClientPolicy(ctx context.Context, client mgclients.Client) error {
|
||||
return svc.addClientPolicy(ctx, client.ID, client.Role)
|
||||
func (svc service) OAuthAddUserPolicy(ctx context.Context, user User) error {
|
||||
return svc.addUserPolicy(ctx, user.ID, user.Role)
|
||||
}
|
||||
|
||||
func (svc service) Identify(ctx context.Context, session authn.Session) (string, error) {
|
||||
return session.UserID, nil
|
||||
}
|
||||
|
||||
func (svc service) addClientPolicy(ctx context.Context, userID string, role mgclients.Role) error {
|
||||
func (svc service) addUserPolicy(ctx context.Context, userID string, role Role) error {
|
||||
policyList := []policies.Policy{}
|
||||
|
||||
policyList = append(policyList, policies.Policy{
|
||||
@@ -550,7 +613,7 @@ func (svc service) addClientPolicy(ctx context.Context, userID string, role mgcl
|
||||
Object: policies.MagistralaObject,
|
||||
})
|
||||
|
||||
if role == mgclients.AdminRole {
|
||||
if role == AdminRole {
|
||||
policyList = append(policyList, policies.Policy{
|
||||
SubjectType: policies.UserType,
|
||||
Subject: userID,
|
||||
@@ -567,7 +630,7 @@ func (svc service) addClientPolicy(ctx context.Context, userID string, role mgcl
|
||||
return nil
|
||||
}
|
||||
|
||||
func (svc service) addClientPolicyRollback(ctx context.Context, userID string, role mgclients.Role) error {
|
||||
func (svc service) addUserPolicyRollback(ctx context.Context, userID string, role Role) error {
|
||||
policyList := []policies.Policy{}
|
||||
|
||||
policyList = append(policyList, policies.Policy{
|
||||
@@ -578,7 +641,7 @@ func (svc service) addClientPolicyRollback(ctx context.Context, userID string, r
|
||||
Object: policies.MagistralaObject,
|
||||
})
|
||||
|
||||
if role == mgclients.AdminRole {
|
||||
if role == AdminRole {
|
||||
policyList = append(policyList, policies.Policy{
|
||||
SubjectType: policies.UserType,
|
||||
Subject: userID,
|
||||
@@ -595,9 +658,9 @@ func (svc service) addClientPolicyRollback(ctx context.Context, userID string, r
|
||||
return nil
|
||||
}
|
||||
|
||||
func (svc service) updateClientPolicy(ctx context.Context, userID string, role mgclients.Role) error {
|
||||
func (svc service) updateUserPolicy(ctx context.Context, userID string, role Role) error {
|
||||
switch role {
|
||||
case mgclients.AdminRole:
|
||||
case AdminRole:
|
||||
err := svc.policies.AddPolicy(ctx, policies.Policy{
|
||||
SubjectType: policies.UserType,
|
||||
Subject: userID,
|
||||
@@ -610,7 +673,7 @@ func (svc service) updateClientPolicy(ctx context.Context, userID string, role m
|
||||
}
|
||||
|
||||
return nil
|
||||
case mgclients.UserRole:
|
||||
case UserRole:
|
||||
fallthrough
|
||||
default:
|
||||
err := svc.policies.DeletePolicyFilter(ctx, policies.Policy{
|
||||
|
||||
+680
-690
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,83 @@
|
||||
// Copyright (c) Abstract Machines
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package users
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"strings"
|
||||
|
||||
svcerr "github.com/absmach/magistrala/pkg/errors/service"
|
||||
)
|
||||
|
||||
// Status represents User status.
|
||||
type Status uint8
|
||||
|
||||
// Possible User status values.
|
||||
const (
|
||||
// EnabledStatus represents enabled User.
|
||||
EnabledStatus Status = iota
|
||||
// DisabledStatus represents disabled User.
|
||||
DisabledStatus
|
||||
// DeletedStatus represents a user that will be deleted.
|
||||
DeletedStatus
|
||||
|
||||
// AllStatus is used for querying purposes to list users irrespective
|
||||
// of their status - both enabled and disabled. It is never stored in the
|
||||
// database as the actual User status and should always be the largest
|
||||
// value in this enumeration.
|
||||
AllStatus
|
||||
)
|
||||
|
||||
// String representation of the possible status values.
|
||||
const (
|
||||
Disabled = "disabled"
|
||||
Enabled = "enabled"
|
||||
Deleted = "deleted"
|
||||
All = "all"
|
||||
Unknown = "unknown"
|
||||
)
|
||||
|
||||
// String converts user/group status to string literal.
|
||||
func (s Status) String() string {
|
||||
switch s {
|
||||
case DisabledStatus:
|
||||
return Disabled
|
||||
case EnabledStatus:
|
||||
return Enabled
|
||||
case DeletedStatus:
|
||||
return Deleted
|
||||
case AllStatus:
|
||||
return All
|
||||
default:
|
||||
return Unknown
|
||||
}
|
||||
}
|
||||
|
||||
// ToStatus converts string value to a valid User/Group status.
|
||||
func ToStatus(status string) (Status, error) {
|
||||
switch status {
|
||||
case "", Enabled:
|
||||
return EnabledStatus, nil
|
||||
case Disabled:
|
||||
return DisabledStatus, nil
|
||||
case Deleted:
|
||||
return DeletedStatus, nil
|
||||
case All:
|
||||
return AllStatus, nil
|
||||
}
|
||||
return Status(0), svcerr.ErrInvalidStatus
|
||||
}
|
||||
|
||||
// Custom Marshaller for Uesr/Groups.
|
||||
func (s Status) MarshalJSON() ([]byte, error) {
|
||||
return json.Marshal(s.String())
|
||||
}
|
||||
|
||||
// Custom Unmarshaler for User/Groups.
|
||||
func (s *Status) UnmarshalJSON(data []byte) error {
|
||||
str := strings.Trim(string(data), "\"")
|
||||
val, err := ToStatus(str)
|
||||
*s = val
|
||||
return err
|
||||
}
|
||||
+92
-73
@@ -8,8 +8,7 @@ import (
|
||||
|
||||
"github.com/absmach/magistrala"
|
||||
"github.com/absmach/magistrala/pkg/authn"
|
||||
mgclients "github.com/absmach/magistrala/pkg/clients"
|
||||
"github.com/absmach/magistrala/users"
|
||||
users "github.com/absmach/magistrala/users"
|
||||
"go.opentelemetry.io/otel/attribute"
|
||||
"go.opentelemetry.io/otel/trace"
|
||||
)
|
||||
@@ -26,23 +25,23 @@ func New(svc users.Service, tracer trace.Tracer) users.Service {
|
||||
return &tracingMiddleware{tracer, svc}
|
||||
}
|
||||
|
||||
// RegisterClient traces the "RegisterClient" operation of the wrapped clients.Service.
|
||||
func (tm *tracingMiddleware) RegisterClient(ctx context.Context, session authn.Session, client mgclients.Client, selfRegister bool) (mgclients.Client, error) {
|
||||
ctx, span := tm.tracer.Start(ctx, "svc_register_client", trace.WithAttributes(attribute.String("identity", client.Credentials.Identity)))
|
||||
// Register traces the "Register" operation of the wrapped users.Service.
|
||||
func (tm *tracingMiddleware) Register(ctx context.Context, session authn.Session, user users.User, selfRegister bool) (users.User, error) {
|
||||
ctx, span := tm.tracer.Start(ctx, "svc_register_user", trace.WithAttributes(attribute.String("email", user.Email)))
|
||||
defer span.End()
|
||||
|
||||
return tm.svc.RegisterClient(ctx, session, client, selfRegister)
|
||||
return tm.svc.Register(ctx, session, user, selfRegister)
|
||||
}
|
||||
|
||||
// IssueToken traces the "IssueToken" operation of the wrapped clients.Service.
|
||||
func (tm *tracingMiddleware) IssueToken(ctx context.Context, identity, secret string) (*magistrala.Token, error) {
|
||||
ctx, span := tm.tracer.Start(ctx, "svc_issue_token", trace.WithAttributes(attribute.String("identity", identity)))
|
||||
// IssueToken traces the "IssueToken" operation of the wrapped users.Service.
|
||||
func (tm *tracingMiddleware) IssueToken(ctx context.Context, username, secret string) (*magistrala.Token, error) {
|
||||
ctx, span := tm.tracer.Start(ctx, "svc_issue_token", trace.WithAttributes(attribute.String("username", username)))
|
||||
defer span.End()
|
||||
|
||||
return tm.svc.IssueToken(ctx, identity, secret)
|
||||
return tm.svc.IssueToken(ctx, username, secret)
|
||||
}
|
||||
|
||||
// RefreshToken traces the "RefreshToken" operation of the wrapped clients.Service.
|
||||
// RefreshToken traces the "RefreshToken" operation of the wrapped users.Service.
|
||||
func (tm *tracingMiddleware) RefreshToken(ctx context.Context, session authn.Session, refreshToken string) (*magistrala.Token, error) {
|
||||
ctx, span := tm.tracer.Start(ctx, "svc_refresh_token", trace.WithAttributes(attribute.String("refresh_token", refreshToken)))
|
||||
defer span.End()
|
||||
@@ -50,17 +49,17 @@ func (tm *tracingMiddleware) RefreshToken(ctx context.Context, session authn.Ses
|
||||
return tm.svc.RefreshToken(ctx, session, refreshToken)
|
||||
}
|
||||
|
||||
// ViewClient traces the "ViewClient" operation of the wrapped clients.Service.
|
||||
func (tm *tracingMiddleware) ViewClient(ctx context.Context, session authn.Session, id string) (mgclients.Client, error) {
|
||||
ctx, span := tm.tracer.Start(ctx, "svc_view_client", trace.WithAttributes(attribute.String("id", id)))
|
||||
// View traces the "View" operation of the wrapped users.Service.
|
||||
func (tm *tracingMiddleware) View(ctx context.Context, session authn.Session, id string) (users.User, error) {
|
||||
ctx, span := tm.tracer.Start(ctx, "svc_view_user", trace.WithAttributes(attribute.String("id", id)))
|
||||
defer span.End()
|
||||
|
||||
return tm.svc.ViewClient(ctx, session, id)
|
||||
return tm.svc.View(ctx, session, id)
|
||||
}
|
||||
|
||||
// ListClients traces the "ListClients" operation of the wrapped clients.Service.
|
||||
func (tm *tracingMiddleware) ListClients(ctx context.Context, session authn.Session, pm mgclients.Page) (mgclients.ClientsPage, error) {
|
||||
ctx, span := tm.tracer.Start(ctx, "svc_list_clients", trace.WithAttributes(
|
||||
// ListUsers traces the "ListUsers" operation of the wrapped users.Service.
|
||||
func (tm *tracingMiddleware) ListUsers(ctx context.Context, session authn.Session, pm users.Page) (users.UsersPage, error) {
|
||||
ctx, span := tm.tracer.Start(ctx, "svc_list_users", trace.WithAttributes(
|
||||
attribute.Int64("offset", int64(pm.Offset)),
|
||||
attribute.Int64("limit", int64(pm.Limit)),
|
||||
attribute.String("direction", pm.Dir),
|
||||
@@ -69,12 +68,12 @@ func (tm *tracingMiddleware) ListClients(ctx context.Context, session authn.Sess
|
||||
|
||||
defer span.End()
|
||||
|
||||
return tm.svc.ListClients(ctx, session, pm)
|
||||
return tm.svc.ListUsers(ctx, session, pm)
|
||||
}
|
||||
|
||||
// SearchUsers traces the "SearchUsers" operation of the wrapped clients.Service.
|
||||
func (tm *tracingMiddleware) SearchUsers(ctx context.Context, pm mgclients.Page) (mgclients.ClientsPage, error) {
|
||||
ctx, span := tm.tracer.Start(ctx, "svc_search_clients", trace.WithAttributes(
|
||||
// SearchUsers traces the "SearchUsers" operation of the wrapped users.Service.
|
||||
func (tm *tracingMiddleware) SearchUsers(ctx context.Context, pm users.Page) (users.UsersPage, error) {
|
||||
ctx, span := tm.tracer.Start(ctx, "svc_search_users", trace.WithAttributes(
|
||||
attribute.Int64("offset", int64(pm.Offset)),
|
||||
attribute.Int64("limit", int64(pm.Limit)),
|
||||
attribute.String("direction", pm.Dir),
|
||||
@@ -85,48 +84,68 @@ func (tm *tracingMiddleware) SearchUsers(ctx context.Context, pm mgclients.Page)
|
||||
return tm.svc.SearchUsers(ctx, pm)
|
||||
}
|
||||
|
||||
// UpdateClient traces the "UpdateClient" operation of the wrapped clients.Service.
|
||||
func (tm *tracingMiddleware) UpdateClient(ctx context.Context, session authn.Session, cli mgclients.Client) (mgclients.Client, error) {
|
||||
ctx, span := tm.tracer.Start(ctx, "svc_update_client_name_and_metadata", trace.WithAttributes(
|
||||
// Update traces the "Update" operation of the wrapped users.Service.
|
||||
func (tm *tracingMiddleware) Update(ctx context.Context, session authn.Session, cli users.User) (users.User, error) {
|
||||
ctx, span := tm.tracer.Start(ctx, "svc_update_user", trace.WithAttributes(
|
||||
attribute.String("id", cli.ID),
|
||||
attribute.String("name", cli.Name),
|
||||
attribute.String("first_name", cli.FirstName),
|
||||
attribute.String("last_name", cli.LastName),
|
||||
))
|
||||
defer span.End()
|
||||
|
||||
return tm.svc.UpdateClient(ctx, session, cli)
|
||||
return tm.svc.Update(ctx, session, cli)
|
||||
}
|
||||
|
||||
// UpdateClientTags traces the "UpdateClientTags" operation of the wrapped clients.Service.
|
||||
func (tm *tracingMiddleware) UpdateClientTags(ctx context.Context, session authn.Session, cli mgclients.Client) (mgclients.Client, error) {
|
||||
ctx, span := tm.tracer.Start(ctx, "svc_update_client_tags", trace.WithAttributes(
|
||||
// UpdateTags traces the "UpdateTags" operation of the wrapped users.Service.
|
||||
func (tm *tracingMiddleware) UpdateTags(ctx context.Context, session authn.Session, cli users.User) (users.User, error) {
|
||||
ctx, span := tm.tracer.Start(ctx, "svc_update_user_tags", trace.WithAttributes(
|
||||
attribute.String("id", cli.ID),
|
||||
attribute.StringSlice("tags", cli.Tags),
|
||||
))
|
||||
defer span.End()
|
||||
|
||||
return tm.svc.UpdateClientTags(ctx, session, cli)
|
||||
return tm.svc.UpdateTags(ctx, session, cli)
|
||||
}
|
||||
|
||||
// UpdateClientIdentity traces the "UpdateClientIdentity" operation of the wrapped clients.Service.
|
||||
func (tm *tracingMiddleware) UpdateClientIdentity(ctx context.Context, session authn.Session, id, identity string) (mgclients.Client, error) {
|
||||
ctx, span := tm.tracer.Start(ctx, "svc_update_client_identity", trace.WithAttributes(
|
||||
// UpdateEmail traces the "UpdateEmail" operation of the wrapped users.Service.
|
||||
func (tm *tracingMiddleware) UpdateEmail(ctx context.Context, session authn.Session, id, email string) (users.User, error) {
|
||||
ctx, span := tm.tracer.Start(ctx, "svc_update_user_email", trace.WithAttributes(
|
||||
attribute.String("id", id),
|
||||
attribute.String("identity", identity),
|
||||
attribute.String("email", email),
|
||||
))
|
||||
defer span.End()
|
||||
|
||||
return tm.svc.UpdateClientIdentity(ctx, session, id, identity)
|
||||
return tm.svc.UpdateEmail(ctx, session, id, email)
|
||||
}
|
||||
|
||||
// UpdateClientSecret traces the "UpdateClientSecret" operation of the wrapped clients.Service.
|
||||
func (tm *tracingMiddleware) UpdateClientSecret(ctx context.Context, session authn.Session, oldSecret, newSecret string) (mgclients.Client, error) {
|
||||
ctx, span := tm.tracer.Start(ctx, "svc_update_client_secret")
|
||||
// UpdateSecret traces the "UpdateSecret" operation of the wrapped users.Service.
|
||||
func (tm *tracingMiddleware) UpdateSecret(ctx context.Context, session authn.Session, oldSecret, newSecret string) (users.User, error) {
|
||||
ctx, span := tm.tracer.Start(ctx, "svc_update_user_secret")
|
||||
defer span.End()
|
||||
|
||||
return tm.svc.UpdateClientSecret(ctx, session, oldSecret, newSecret)
|
||||
return tm.svc.UpdateSecret(ctx, session, oldSecret, newSecret)
|
||||
}
|
||||
|
||||
// GenerateResetToken traces the "GenerateResetToken" operation of the wrapped clients.Service.
|
||||
// UpdateUsername traces the "UpdateUsername" operation of the wrapped users.Service.
|
||||
func (tm *tracingMiddleware) UpdateUsername(ctx context.Context, session authn.Session, id, username string) (users.User, error) {
|
||||
ctx, span := tm.tracer.Start(ctx, "svc_update_usernames", trace.WithAttributes(
|
||||
attribute.String("id", id),
|
||||
attribute.String("username", username),
|
||||
))
|
||||
defer span.End()
|
||||
|
||||
return tm.svc.UpdateUsername(ctx, session, id, username)
|
||||
}
|
||||
|
||||
// UpdateProfilePicture traces the "UpdateProfilePicture" operation of the wrapped users.Service.
|
||||
func (tm *tracingMiddleware) UpdateProfilePicture(ctx context.Context, session authn.Session, usr users.User) (users.User, error) {
|
||||
ctx, span := tm.tracer.Start(ctx, "svc_update_profile_picture", trace.WithAttributes(attribute.String("id", usr.ID)))
|
||||
defer span.End()
|
||||
|
||||
return tm.svc.Update(ctx, session, usr)
|
||||
}
|
||||
|
||||
// GenerateResetToken traces the "GenerateResetToken" operation of the wrapped users.Service.
|
||||
func (tm *tracingMiddleware) GenerateResetToken(ctx context.Context, email, host string) error {
|
||||
ctx, span := tm.tracer.Start(ctx, "svc_generate_reset_token", trace.WithAttributes(
|
||||
attribute.String("email", email),
|
||||
@@ -137,7 +156,7 @@ func (tm *tracingMiddleware) GenerateResetToken(ctx context.Context, email, host
|
||||
return tm.svc.GenerateResetToken(ctx, email, host)
|
||||
}
|
||||
|
||||
// ResetSecret traces the "ResetSecret" operation of the wrapped clients.Service.
|
||||
// ResetSecret traces the "ResetSecret" operation of the wrapped users.Service.
|
||||
func (tm *tracingMiddleware) ResetSecret(ctx context.Context, session authn.Session, secret string) error {
|
||||
ctx, span := tm.tracer.Start(ctx, "svc_reset_secret")
|
||||
defer span.End()
|
||||
@@ -145,7 +164,7 @@ func (tm *tracingMiddleware) ResetSecret(ctx context.Context, session authn.Sess
|
||||
return tm.svc.ResetSecret(ctx, session, secret)
|
||||
}
|
||||
|
||||
// SendPasswordReset traces the "SendPasswordReset" operation of the wrapped clients.Service.
|
||||
// SendPasswordReset traces the "SendPasswordReset" operation of the wrapped users.Service.
|
||||
func (tm *tracingMiddleware) SendPasswordReset(ctx context.Context, host, email, user, token string) error {
|
||||
ctx, span := tm.tracer.Start(ctx, "svc_send_password_reset", trace.WithAttributes(
|
||||
attribute.String("email", email),
|
||||
@@ -156,50 +175,50 @@ func (tm *tracingMiddleware) SendPasswordReset(ctx context.Context, host, email,
|
||||
return tm.svc.SendPasswordReset(ctx, host, email, user, token)
|
||||
}
|
||||
|
||||
// ViewProfile traces the "ViewProfile" operation of the wrapped clients.Service.
|
||||
func (tm *tracingMiddleware) ViewProfile(ctx context.Context, session authn.Session) (mgclients.Client, error) {
|
||||
// ViewProfile traces the "ViewProfile" operation of the wrapped users.Service.
|
||||
func (tm *tracingMiddleware) ViewProfile(ctx context.Context, session authn.Session) (users.User, error) {
|
||||
ctx, span := tm.tracer.Start(ctx, "svc_view_profile")
|
||||
defer span.End()
|
||||
|
||||
return tm.svc.ViewProfile(ctx, session)
|
||||
}
|
||||
|
||||
// UpdateClientRole traces the "UpdateClientRole" operation of the wrapped clients.Service.
|
||||
func (tm *tracingMiddleware) UpdateClientRole(ctx context.Context, session authn.Session, cli mgclients.Client) (mgclients.Client, error) {
|
||||
ctx, span := tm.tracer.Start(ctx, "svc_update_client_role", trace.WithAttributes(
|
||||
// UpdateRole traces the "UpdateRole" operation of the wrapped users.Service.
|
||||
func (tm *tracingMiddleware) UpdateRole(ctx context.Context, session authn.Session, cli users.User) (users.User, error) {
|
||||
ctx, span := tm.tracer.Start(ctx, "svc_update_user_role", trace.WithAttributes(
|
||||
attribute.String("id", cli.ID),
|
||||
attribute.StringSlice("tags", cli.Tags),
|
||||
))
|
||||
defer span.End()
|
||||
|
||||
return tm.svc.UpdateClientRole(ctx, session, cli)
|
||||
return tm.svc.UpdateRole(ctx, session, cli)
|
||||
}
|
||||
|
||||
// EnableClient traces the "EnableClient" operation of the wrapped clients.Service.
|
||||
func (tm *tracingMiddleware) EnableClient(ctx context.Context, session authn.Session, id string) (mgclients.Client, error) {
|
||||
ctx, span := tm.tracer.Start(ctx, "svc_enable_client", trace.WithAttributes(attribute.String("id", id)))
|
||||
// Enable traces the "Enable" operation of the wrapped users.Service.
|
||||
func (tm *tracingMiddleware) Enable(ctx context.Context, session authn.Session, id string) (users.User, error) {
|
||||
ctx, span := tm.tracer.Start(ctx, "svc_enable_user", trace.WithAttributes(attribute.String("id", id)))
|
||||
defer span.End()
|
||||
|
||||
return tm.svc.EnableClient(ctx, session, id)
|
||||
return tm.svc.Enable(ctx, session, id)
|
||||
}
|
||||
|
||||
// DisableClient traces the "DisableClient" operation of the wrapped clients.Service.
|
||||
func (tm *tracingMiddleware) DisableClient(ctx context.Context, session authn.Session, id string) (mgclients.Client, error) {
|
||||
ctx, span := tm.tracer.Start(ctx, "svc_disable_client", trace.WithAttributes(attribute.String("id", id)))
|
||||
// Disable traces the "Disable" operation of the wrapped users.Service.
|
||||
func (tm *tracingMiddleware) Disable(ctx context.Context, session authn.Session, id string) (users.User, error) {
|
||||
ctx, span := tm.tracer.Start(ctx, "svc_disable_user", trace.WithAttributes(attribute.String("id", id)))
|
||||
defer span.End()
|
||||
|
||||
return tm.svc.DisableClient(ctx, session, id)
|
||||
return tm.svc.Disable(ctx, session, id)
|
||||
}
|
||||
|
||||
// ListMembers traces the "ListMembers" operation of the wrapped clients.Service.
|
||||
func (tm *tracingMiddleware) ListMembers(ctx context.Context, session authn.Session, objectKind, objectID string, pm mgclients.Page) (mgclients.MembersPage, error) {
|
||||
// ListMembers traces the "ListMembers" operation of the wrapped users.Service.
|
||||
func (tm *tracingMiddleware) ListMembers(ctx context.Context, session authn.Session, objectKind, objectID string, pm users.Page) (users.MembersPage, error) {
|
||||
ctx, span := tm.tracer.Start(ctx, "svc_list_members", trace.WithAttributes(attribute.String("object_kind", objectKind)), trace.WithAttributes(attribute.String("object_id", objectID)))
|
||||
defer span.End()
|
||||
|
||||
return tm.svc.ListMembers(ctx, session, objectKind, objectID, pm)
|
||||
}
|
||||
|
||||
// Identify traces the "Identify" operation of the wrapped clients.Service.
|
||||
// Identify traces the "Identify" operation of the wrapped users.Service.
|
||||
func (tm *tracingMiddleware) Identify(ctx context.Context, session authn.Session) (string, error) {
|
||||
ctx, span := tm.tracer.Start(ctx, "svc_identify", trace.WithAttributes(attribute.String("user_id", session.UserID)))
|
||||
defer span.End()
|
||||
@@ -207,30 +226,30 @@ func (tm *tracingMiddleware) Identify(ctx context.Context, session authn.Session
|
||||
return tm.svc.Identify(ctx, session)
|
||||
}
|
||||
|
||||
// OAuthCallback traces the "OAuthCallback" operation of the wrapped clients.Service.
|
||||
func (tm *tracingMiddleware) OAuthCallback(ctx context.Context, client mgclients.Client) (mgclients.Client, error) {
|
||||
// OAuthCallback traces the "OAuthCallback" operation of the wrapped users.Service.
|
||||
func (tm *tracingMiddleware) OAuthCallback(ctx context.Context, user users.User) (users.User, error) {
|
||||
ctx, span := tm.tracer.Start(ctx, "svc_oauth_callback", trace.WithAttributes(
|
||||
attribute.String("client_id", client.ID),
|
||||
attribute.String("user_id", user.ID),
|
||||
))
|
||||
defer span.End()
|
||||
|
||||
return tm.svc.OAuthCallback(ctx, client)
|
||||
return tm.svc.OAuthCallback(ctx, user)
|
||||
}
|
||||
|
||||
// DeleteClient traces the "DeleteClient" operation of the wrapped clients.Service.
|
||||
func (tm *tracingMiddleware) DeleteClient(ctx context.Context, session authn.Session, id string) error {
|
||||
ctx, span := tm.tracer.Start(ctx, "svc_delete_client", trace.WithAttributes(attribute.String("id", id)))
|
||||
// Delete traces the "Delete" operation of the wrapped users.Service.
|
||||
func (tm *tracingMiddleware) Delete(ctx context.Context, session authn.Session, id string) error {
|
||||
ctx, span := tm.tracer.Start(ctx, "svc_delete_user", trace.WithAttributes(attribute.String("id", id)))
|
||||
defer span.End()
|
||||
|
||||
return tm.svc.DeleteClient(ctx, session, id)
|
||||
return tm.svc.Delete(ctx, session, id)
|
||||
}
|
||||
|
||||
// OAuthAddClientPolicy traces the "OAuthAddClientPolicy" operation of the wrapped clients.Service.
|
||||
func (tm *tracingMiddleware) OAuthAddClientPolicy(ctx context.Context, client mgclients.Client) error {
|
||||
ctx, span := tm.tracer.Start(ctx, "svc_add_client_policy", trace.WithAttributes(
|
||||
attribute.String("id", client.ID),
|
||||
// OAuthAddUserPolicy traces the "OAuthAddUserPolicy" operation of the wrapped users.Service.
|
||||
func (tm *tracingMiddleware) OAuthAddUserPolicy(ctx context.Context, user users.User) error {
|
||||
ctx, span := tm.tracer.Start(ctx, "svc_add_user_policy", trace.WithAttributes(
|
||||
attribute.String("id", user.ID),
|
||||
))
|
||||
defer span.End()
|
||||
|
||||
return tm.svc.OAuthAddClientPolicy(ctx, client)
|
||||
return tm.svc.OAuthAddUserPolicy(ctx, user)
|
||||
}
|
||||
|
||||
+218
@@ -0,0 +1,218 @@
|
||||
// Copyright (c) Abstract Machines
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package users
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/mail"
|
||||
"time"
|
||||
|
||||
"github.com/absmach/magistrala"
|
||||
"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"`
|
||||
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"`
|
||||
}
|
||||
|
||||
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]interface{}
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
//go:generate mockery --name Repository --output=./mocks --filename repository.go --quiet --note "Copyright (c) Abstract Machines"
|
||||
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, user User) (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)
|
||||
|
||||
// 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)
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
// Page contains page metadata that helps navigation.
|
||||
type Page struct {
|
||||
Total uint64 `json:"total"`
|
||||
Offset uint64 `json:"offset"`
|
||||
Limit uint64 `json:"limit"`
|
||||
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"`
|
||||
Tag string `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"`
|
||||
}
|
||||
|
||||
// Service specifies an API that must be fullfiled by the domain service
|
||||
// implementation, and all of its decorators (e.g. logging & metrics).
|
||||
//
|
||||
//go:generate mockery --name Service --output=./mocks --filename service.go --quiet --note "Copyright (c) Abstract Machines"
|
||||
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)
|
||||
|
||||
// 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)
|
||||
|
||||
// ListMembers retrieves everything that is assigned to a group/thing identified by objectID.
|
||||
ListMembers(ctx context.Context, session authn.Session, objectKind, objectID string, pm Page) (MembersPage, 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, user User) (User, error)
|
||||
|
||||
// UpdateTags updates the user's tags.
|
||||
UpdateTags(ctx context.Context, session authn.Session, user User) (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)
|
||||
|
||||
// UpdateProfile updates the user's profile picture.
|
||||
UpdateProfilePicture(ctx context.Context, session authn.Session, user User) (User, error)
|
||||
|
||||
// GenerateResetToken email where mail will be sent.
|
||||
// host is used for generating reset link.
|
||||
GenerateResetToken(ctx context.Context, email, host 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
|
||||
|
||||
// SendPasswordReset sends reset password link to email.
|
||||
SendPasswordReset(ctx context.Context, host, email, user, token 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 string) (*magistrala.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) (*magistrala.Token, 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
|
||||
}
|
||||
Reference in New Issue
Block a user