diff --git a/.github/workflows/api-tests.yml b/.github/workflows/api-tests.yml new file mode 100644 index 000000000..98a313d6c --- /dev/null +++ b/.github/workflows/api-tests.yml @@ -0,0 +1,54 @@ +# Copyright (c) Abstract Machines +# SPDX-License-Identifier: Apache-2.0 + +name: Property Based Tests + +on: + push: + branches: + - main + pull_request: + branches: + - main + +env: + TOKEN_URL: http://localhost:9002/users/tokens/issue + USER_IDENTITY: admin@example.com + USER_SECRET: 12345678 + +jobs: + api-test: + runs-on: ubuntu-latest + steps: + - name: Checkout Code + uses: actions/checkout@v4 + + - name: Install Go + uses: actions/setup-go@v4 + with: + go-version: 1.21.x + cache-dependency-path: "go.sum" + + - name: Build images + run: make all -j $(nproc) && make dockers_dev -j $(nproc) + + - name: Start containers + run: make run up args="-d" && sleep 10 + + - name: Set access token + run: | + export USER_TOKEN=$(curl -sSX POST $TOKEN_URL -H "Content-Type: application/json" -d "{\"identity\": \"$USER_IDENTITY\",\"secret\": \"$USER_SECRET\"}" | jq -r .access_token) + echo "USER_TOKEN=$USER_TOKEN" >> $GITHUB_ENV + + - name: Run Users API tests + uses: schemathesis/action@v1 + with: + schema: api/openapi/users.yml + base-url: http://localhost:9002 + checks: all + report: false + args: '--header "Authorization: Bearer ${{ env.USER_TOKEN }}" --contrib-unique-data --contrib-openapi-formats-uuid --hypothesis-suppress-health-check=filter_too_much --stateful=links' + + - name: Stop containers + if: always() + run: make run down args="-v" diff --git a/.gitignore b/.gitignore index 8d9ae5778..60286b531 100644 --- a/.gitignore +++ b/.gitignore @@ -12,3 +12,6 @@ tools/provision/mgconn.toml # coverage files coverage + +# Schemathesis +.hypothesis diff --git a/Makefile b/Makefile index d95a67906..38c1aae9a 100644 --- a/Makefile +++ b/Makefile @@ -104,7 +104,7 @@ FILTERED_SERVICES = $(filter-out $(RUN_ADDON_ARGS), $(SERVICES)) all: $(SERVICES) -.PHONY: all $(SERVICES) dockers dockers_dev latest release run run_addons grpc_mtls_certs check_mtls check_certs +.PHONY: all $(SERVICES) dockers dockers_dev latest release run run_addons grpc_mtls_certs check_mtls check_certs test_api clean: rm -rf ${BUILD_DIR} @@ -136,6 +136,16 @@ test: mocks done go test -v --race -count 1 -tags test -coverprofile=coverage/coverage.out $$(go list ./... | grep -v 'consumers\|readers\|postgres\|internal\|opcua\|cmd') +test_api: + @which st > /dev/null || (echo "schemathesis not found, please install it from https://github.com/schemathesis/schemathesis#getting-started" && exit 1) + st run api/openapi/users.yml \ + --checks all \ + --base-url http://localhost:9002 \ + --header "Authorization: Bearer $(USER_TOKEN)" \ + --contrib-unique-data --contrib-openapi-formats-uuid \ + --hypothesis-suppress-health-check=filter_too_much \ + --stateful=links + proto: protoc -I. --go_out=. --go_opt=paths=source_relative pkg/messaging/*.proto protoc -I. --go_out=. --go_opt=paths=source_relative --go-grpc_out=. --go-grpc_opt=paths=source_relative ./*.proto diff --git a/api/openapi/users.yml b/api/openapi/users.yml index 6cb178605..a0a25af04 100644 --- a/api/openapi/users.yml +++ b/api/openapi/users.yml @@ -34,6 +34,7 @@ tags: paths: /users: post: + operationId: createUser tags: - Users summary: Registers user account @@ -49,14 +50,19 @@ paths: description: Failed due to malformed JSON. "401": description: Missing or invalid access token provided. + "403": + description: Failed to perform authorization over the entity. "409": description: Failed due to using an existing identity. "415": description: Missing or invalid content type. + "422": + description: Database can't process request. "500": $ref: "#/components/responses/ServiceError" get: + operationId: listUsers tags: - Users summary: List users @@ -95,6 +101,7 @@ paths: /users/profile: get: + operationId: getProfile summary: Gets info on currently logged in user. description: | Gets info on currently logged in user. Info is obtained using @@ -115,6 +122,7 @@ paths: /users/{userID}: get: + operationId: getUser summary: Retrieves a user description: | Retrieves a specific user that is identifier by the user ID. @@ -131,6 +139,8 @@ paths: description: Failed due to malformed query parameters. "401": description: Missing or invalid access token provided. + "403": + description: Failed to perform authorization over the entity. "404": description: A non-existent entity request. "422": @@ -139,6 +149,7 @@ paths: $ref: "#/components/responses/ServiceError" patch: + operationId: updateUser summary: Updates name and metadata of the user. description: | Updates name and metadata of the user with provided ID. Name and metadata @@ -156,15 +167,24 @@ paths: $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 identity. + "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 summary: Updates tags the user. description: | Updates tags of the user with provided ID. Tags is updated using @@ -182,15 +202,22 @@ paths: $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}/identity: patch: + operationId: updateUserIdentity summary: Updates Identity of the user. description: | Updates identity of the user with provided ID. Identity is @@ -208,15 +235,24 @@ paths: $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 identity. + "415": + description: Missing or invalid content type. + "422": + description: Database can't process request. "500": $ref: "#/components/responses/ServiceError" /users/{userID}/role: patch: + operationId: updateUserRole summary: Updates the user role. description: | Updates role for the user with provided ID. @@ -233,15 +269,22 @@ paths: $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}/disable: post: + operationId: disableUser summary: Disables a user description: | Disables a specific user that is identifier by the user ID. @@ -258,8 +301,14 @@ paths: description: Failed due to malformed query parameters. "401": description: Missing or invalid access token provided. + "403": + description: Failed to perform authorization over the entity. "404": description: A non-existent entity request. + "409": + description: Failed due to already disabled user. + "415": + description: Missing or invalid content type. "422": description: Database can't process request. "500": @@ -267,6 +316,7 @@ paths: /users/{userID}/enable: post: + operationId: enableUser summary: Enables a user description: | Enables a specific user that is identifier by the user ID. @@ -283,8 +333,14 @@ paths: description: Failed due to malformed query parameters. "401": description: Missing or invalid access token provided. + "403": + description: Failed to perform authorization over the entity. "404": description: A non-existent entity request. + "409": + description: Failed due to already enabled user. + "415": + description: Missing or invalid content type. "422": description: Database can't process request. "500": @@ -292,6 +348,7 @@ paths: /users/secret: patch: + operationId: updateUserSecret summary: Updates Secret of currently logged in user. description: | Updates secret of currently logged in user. Secret is updated using @@ -307,15 +364,20 @@ paths: $ref: "#/components/responses/UserRes" "400": description: Failed due to malformed JSON. - "404": - description: Failed due to non existing user. "401": description: Missing or invalid access token provided. + "404": + description: Failed due to non existing user. + "415": + description: Missing or invalid content type. + "422": + description: Database can't process request. "500": $ref: "#/components/responses/ServiceError" /password/reset-request: post: + operationId: requestPasswordReset summary: User password reset request description: | Generates a reset token and sends and @@ -333,11 +395,14 @@ paths: description: Failed due to malformed JSON. "415": description: Missing or invalid content type. + "422": + description: Database can't process request. "500": $ref: "#/components/responses/ServiceError" /password/reset: put: + operationId: resetPassword summary: User password reset endpoint description: | When user gets reset token, after he submitted @@ -352,13 +417,18 @@ paths: description: User link . "400": description: Failed due to malformed JSON. + "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" /groups/{groupID}/users: get: + operationId: listUsersInGroup tags: - Users summary: List users in a group @@ -386,6 +456,8 @@ paths: description: | Missing or invalid access token provided. This endpoint is available only for administrators. + "403": + description: Failed to perform authorization over the entity. "404": description: A non-existent entity request. "422": @@ -395,6 +467,7 @@ paths: /channels/{channelID}/users: get: + operationId: listUsersInChannel tags: - Users summary: List users in a channel @@ -422,6 +495,8 @@ paths: description: | Missing or invalid access token provided. This endpoint is available only for administrators. + "403": + description: Failed to perform authorization over the entity. "404": description: A non-existent entity request. "422": @@ -431,6 +506,7 @@ paths: /users/tokens/issue: post: + operationId: issueToken summary: Issue Token description: | Issue Access and Refresh Token used for authenticating into the system. @@ -441,8 +517,12 @@ paths: responses: "200": $ref: "#/components/responses/TokenRes" + "400": + description: Failed due to malformed JSON. "404": description: A non-existent entity request. + "415": + description: Missing or invalid content type. "422": description: Database can't process request. "500": @@ -450,6 +530,7 @@ paths: /users/tokens/refresh: post: + operationId: refreshToken summary: Refresh Token description: | Refreshes Access and Refresh Token used for authenticating into the system. @@ -460,8 +541,12 @@ paths: responses: "200": $ref: "#/components/responses/TokenRes" + "400": + description: Failed due to malformed JSON. "404": description: A non-existent entity request. + "415": + description: Missing or invalid content type. "422": description: Database can't process request. "500": @@ -469,6 +554,7 @@ paths: /groups: post: + operationId: createGroup tags: - Groups summary: Creates new group @@ -486,14 +572,19 @@ paths: description: Failed due to malformed JSON. "401": description: Missing or invalid access token provided. + "403": + description: Failed to perform authorization over the entity. "409": description: Failed due to using an existing identity. "415": description: Missing or invalid content type. + "422": + description: Database can't process request. "500": $ref: "#/components/responses/ServiceError" get: + operationId: listGroups summary: Lists groups. description: | Lists groups up to a max level of hierarchy that can be fetched in one @@ -520,13 +611,18 @@ paths: description: Failed due to malformed query parameters. "401": description: Missing or invalid access token provided. + "403": + description: Failed to perform authorization over the entity. "404": description: Group does not exist. + "422": + description: Database can't process request. "500": $ref: "#/components/responses/ServiceError" /groups/{groupID}: get: + operationId: getGroup summary: Gets group info. description: | Gets info on a group specified by id. @@ -543,12 +639,17 @@ paths: description: Failed due to malformed query parameters. "401": description: Missing or invalid access token provided. + "403": + description: Failed to perform authorization over the entity. "404": description: Group does not exist. + "422": + description: Database can't process request. "500": $ref: "#/components/responses/ServiceError" put: + operationId: updateGroup summary: Updates group data. description: | Updates Name, Description or Metadata of a group. @@ -567,8 +668,16 @@ paths: description: Failed due to malformed query parameters. "401": description: Missing or invalid access token provided. + "403": + description: Failed to perform authorization over the entity. "404": description: Group does not exist. + "409": + description: Failed due to using an existing identity. + "415": + description: Missing or invalid content type. + "422": + description: Database can't process request. "500": $ref: "#/components/responses/ServiceError" delete: @@ -594,6 +703,7 @@ paths: /groups/{groupID}/children: get: + operationId: listChildren summary: List children of a certain group description: | Lists groups up to a max level of hierarchy that can be fetched in one @@ -621,13 +731,18 @@ paths: description: Failed due to malformed query parameters. "401": description: Missing or invalid access token provided. + "403": + description: Failed to perform authorization over the entity. "404": description: Group does not exist. + "422": + description: Database can't process request. "500": $ref: "#/components/responses/ServiceError" /groups/{groupID}/parents: get: + operationId: listParents summary: List parents of a certain group description: | Lists groups up to a max level of hierarchy that can be fetched in one @@ -655,13 +770,18 @@ paths: description: Failed due to malformed query parameters. "401": description: Missing or invalid access token provided. + "403": + description: Failed to perform authorization over the entity. "404": description: Group does not exist. + "422": + description: Database can't process request. "500": $ref: "#/components/responses/ServiceError" /groups/{groupID}/enable: post: + operationId: enableGroup summary: Enables a group description: | Enables a specific group that is identifier by the group ID. @@ -678,8 +798,14 @@ paths: description: Failed due to malformed query parameters. "401": description: Missing or invalid access token provided. + "403": + description: Failed to perform authorization over the entity. "404": description: A non-existent entity request. + "409": + description: Failed due to already enabled group. + "415": + description: Missing or invalid content type. "422": description: Database can't process request. "500": @@ -687,6 +813,7 @@ paths: /groups/{groupID}/disable: post: + operationId: disableGroup summary: Disables a group description: | Disables a specific group that is identifier by the group ID. @@ -703,8 +830,14 @@ paths: description: Failed due to malformed query parameters. "401": description: Missing or invalid access token provided. + "403": + description: Failed to perform authorization over the entity. "404": description: A non-existent entity request. + "409": + description: Failed due to already disabled group. + "415": + description: Missing or invalid content type. "422": description: Database can't process request. "500": @@ -712,6 +845,7 @@ paths: /groups/{groupID}/users/assign: post: + operationId: assignUser summary: Assigns a user to a group description: | Assigns a specific user to a group that is identifier by the group ID. @@ -730,8 +864,12 @@ paths: description: Failed due to malformed group's ID. "401": description: Missing or invalid access token provided. + "403": + description: Failed to perform authorization over the entity. "404": description: A non-existent entity request. + "415": + description: Missing or invalid content type. "422": description: Database can't process request. "500": @@ -739,6 +877,7 @@ paths: /groups/{groupID}/users/unassign: post: + operationId: unassignUser summary: Unassigns a user to a group description: | Unassigns a specific user to a group that is identifier by the group ID. @@ -757,8 +896,12 @@ paths: description: Failed due to malformed group's ID. "401": description: Missing or invalid access token provided. + "403": + description: Failed to perform authorization over the entity. "404": description: A non-existent entity request. + "415": + description: Missing or invalid content type. "422": description: Database can't process request. "500": @@ -766,6 +909,7 @@ paths: /channels/{memberID}/groups: get: + operationId: listGroupsInChannel summary: Get group associated with the member description: | Gets groups associated with the channel member specified by id. @@ -787,13 +931,18 @@ paths: description: Failed due to malformed query parameters. "401": description: Missing or invalid access token provided. + "403": + description: Failed to perform authorization over the entity. "404": description: Group does not exist. + "422": + description: Database can't process request. "500": $ref: "#/components/responses/ServiceError" /users/{memberID}/groups: get: + operationId: listGroupsByUser summary: Get group associated with the member description: | Gets groups associated with the user member specified by id. @@ -815,8 +964,12 @@ paths: description: Failed due to malformed query parameters. "401": description: Missing or invalid access token provided. + "403": + description: Failed to perform authorization over the entity. "404": description: Group does not exist. + "422": + description: Database can't process request. "500": $ref: "#/components/responses/ServiceError" /domains/{domainID}/users: @@ -852,6 +1005,7 @@ paths: $ref: "#/components/responses/ServiceError" /health: get: + operationId: health summary: Retrieves service health check info. tags: - health @@ -1207,7 +1361,7 @@ components: required: - groups - total - - level + - offset MembershipsPage: type: object @@ -1316,7 +1470,7 @@ components: properties: role: type: string - enum: ["admin","user"] + enum: ["admin", "user"] example: user description: User role example. required: @@ -1455,6 +1609,9 @@ components: schema: type: string format: uuid + minLength: 36 + maxLength: 36 + pattern: "^[a-f0-9]{8}-[a-f0-9]{4}-[1-5][a-f0-9]{3}-[89ab][a-f0-9]{3}-[a-f0-9]{12}$" required: true example: bb7edb32-2eac-4aad-aebe-ed96fe073879 @@ -1473,6 +1630,7 @@ components: in: query schema: type: string + pattern: "^[^\u0000-\u001F]*$" required: false example: "admin@example.com" @@ -1553,6 +1711,9 @@ components: schema: type: string format: uuid + minLength: 36 + maxLength: 36 + pattern: "^[a-f0-9]{8}-[a-f0-9]{4}-[1-5][a-f0-9]{3}-[89ab][a-f0-9]{3}-[a-f0-9]{12}$" required: true example: bb7edb32-2eac-4aad-aebe-ed96fe073879 @@ -1563,6 +1724,9 @@ components: schema: type: string format: uuid + minLength: 36 + maxLength: 36 + pattern: "^[a-f0-9]{8}-[a-f0-9]{4}-[1-5][a-f0-9]{3}-[89ab][a-f0-9]{3}-[a-f0-9]{12}$" required: true example: bb7edb32-2eac-4aad-aebe-ed96fe073879 @@ -1573,6 +1737,9 @@ components: schema: type: string format: uuid + minLength: 36 + maxLength: 36 + pattern: "^[a-f0-9]{8}-[a-f0-9]{4}-[1-5][a-f0-9]{3}-[89ab][a-f0-9]{3}-[a-f0-9]{12}$" required: true example: bb7edb32-2eac-4aad-aebe-ed96fe073879 @@ -1801,6 +1968,43 @@ components: application/json: schema: $ref: "#/components/schemas/User" + links: + get: + operationId: getUser + parameters: + userID: $response.body#/id + get_groups: + operationId: listUsersInGroup + parameters: + groupID: $response.body#/id + get_channels: + operationId: listUsersInChannel + parameters: + channelID: $response.body#/id + update: + operationId: updateUser + parameters: + userID: $response.body#/id + update_tags: + operationId: updateUserTags + parameters: + userID: $response.body#/id + update_identity: + operationId: updateUserIdentity + parameters: + userID: $response.body#/id + update_role: + operationId: updateUserRole + parameters: + userID: $response.body#/id + disable: + operationId: disableUser + parameters: + userID: $response.body#/id + enable: + operationId: enableUser + parameters: + userID: $response.body#/id UserRes: description: Data retrieved. @@ -1808,6 +2012,15 @@ components: application/json: schema: $ref: "#/components/schemas/User" + links: + get_groups: + operationId: listUsersInGroup + parameters: + groupID: $response.body#/id + get_channels: + operationId: listUsersInChannel + parameters: + channelID: $response.body#/id UserPageRes: description: Data retrieved. @@ -1835,6 +2048,47 @@ components: application/json: schema: $ref: "#/components/schemas/Group" + links: + get: + operationId: getGroup + parameters: + groupID: $response.body#/id + get_children: + operationId: listChildren + parameters: + groupID: $response.body#/id + get_parent: + operationId: listParents + parameters: + groupID: $response.body#/id + get_channels: + operationId: listGroupsInChannel + parameters: + memberID: $response.body#/id + get_users: + operationId: listGroupsByUser + parameters: + memberID: $response.body#/id + update: + operationId: updateGroup + parameters: + groupID: $response.body#/id + disable: + operationId: disableGroup + parameters: + groupID: $response.body#/id + enable: + operationId: enableGroup + parameters: + groupID: $response.body#/id + assign: + operationId: assignUser + parameters: + groupID: $response.body#/id + unassign: + operationId: unassignUser + parameters: + groupID: $response.body#/id GroupRes: description: Data retrieved. @@ -1842,6 +2096,31 @@ components: application/json: schema: $ref: "#/components/schemas/Group" + links: + get_children: + operationId: listChildren + parameters: + groupID: $response.body#/id + get_parent: + operationId: listParents + parameters: + groupID: $response.body#/id + get_channels: + operationId: listGroupsInChannel + parameters: + memberID: $response.body#/id + get_users: + operationId: listGroupsByUser + parameters: + memberID: $response.body#/id + assign: + operationId: assignUser + parameters: + groupID: $response.body#/id + unassign: + operationId: unassignUser + parameters: + groupID: $response.body#/id GroupPageRes: description: Data retrieved. diff --git a/auth/postgres/domains.go b/auth/postgres/domains.go index b0e555652..d8fcad61f 100644 --- a/auth/postgres/domains.go +++ b/auth/postgres/domains.go @@ -103,13 +103,13 @@ func (repo domainRepo) RetrievePermissions(ctx context.Context, subject, id stri rows, err := repo.db.QueryxContext(ctx, q, id, subject) if err != nil { - return []string{}, errors.Wrap(postgres.ErrFailedToRetrieveAll, err) + return []string{}, errors.Wrap(repoerr.ErrFailedToRetrieveAllGroups, err) } defer rows.Close() domains, err := repo.processRows(rows) if err != nil { - return []string{}, errors.Wrap(postgres.ErrFailedToRetrieveAll, err) + return []string{}, errors.Wrap(repoerr.ErrFailedToRetrieveAllGroups, err) } permissions := []string{} @@ -141,18 +141,18 @@ func (repo domainRepo) RetrieveAllByIDs(ctx context.Context, pm auth.Page) (auth dbPage, err := toDBClientsPage(pm) if err != nil { - return auth.DomainsPage{}, errors.Wrap(postgres.ErrFailedToRetrieveAll, err) + return auth.DomainsPage{}, errors.Wrap(repoerr.ErrFailedToRetrieveAllGroups, err) } rows, err := repo.db.NamedQueryContext(ctx, q, dbPage) if err != nil { - return auth.DomainsPage{}, errors.Wrap(postgres.ErrFailedToRetrieveAll, err) + return auth.DomainsPage{}, errors.Wrap(repoerr.ErrFailedToRetrieveAllGroups, err) } defer rows.Close() domains, err := repo.processRows(rows) if err != nil { - return auth.DomainsPage{}, errors.Wrap(postgres.ErrFailedToRetrieveAll, err) + return auth.DomainsPage{}, errors.Wrap(repoerr.ErrFailedToRetrieveAllGroups, err) } cq := "SELECT COUNT(*) FROM domains d" @@ -162,7 +162,7 @@ func (repo domainRepo) RetrieveAllByIDs(ctx context.Context, pm auth.Page) (auth total, err := postgres.Total(ctx, repo.db, cq, dbPage) if err != nil { - return auth.DomainsPage{}, errors.Wrap(postgres.ErrFailedToRetrieveAll, err) + return auth.DomainsPage{}, errors.Wrap(repoerr.ErrFailedToRetrieveAllGroups, err) } pm.Total = total @@ -201,18 +201,18 @@ func (repo domainRepo) ListDomains(ctx context.Context, pm auth.Page) (auth.Doma dbPage, err := toDBClientsPage(pm) if err != nil { - return auth.DomainsPage{}, errors.Wrap(postgres.ErrFailedToRetrieveAll, err) + return auth.DomainsPage{}, errors.Wrap(repoerr.ErrFailedToRetrieveAllGroups, err) } rows, err := repo.db.NamedQueryContext(ctx, q, dbPage) if err != nil { - return auth.DomainsPage{}, errors.Wrap(postgres.ErrFailedToRetrieveAll, err) + return auth.DomainsPage{}, errors.Wrap(repoerr.ErrFailedToRetrieveAllGroups, err) } defer rows.Close() domains, err := repo.processRows(rows) if err != nil { - return auth.DomainsPage{}, errors.Wrap(postgres.ErrFailedToRetrieveAll, err) + return auth.DomainsPage{}, errors.Wrap(repoerr.ErrFailedToRetrieveAllGroups, err) } cq := "SELECT COUNT(*) FROM domains d JOIN policies pc ON pc.object_id = d.id" @@ -222,7 +222,7 @@ func (repo domainRepo) ListDomains(ctx context.Context, pm auth.Page) (auth.Doma total, err := postgres.Total(ctx, repo.db, cq, dbPage) if err != nil { - return auth.DomainsPage{}, errors.Wrap(postgres.ErrFailedToRetrieveAll, err) + return auth.DomainsPage{}, errors.Wrap(repoerr.ErrFailedToRetrieveAllGroups, err) } pm.Total = total diff --git a/auth/service.go b/auth/service.go index a277a0ebe..075c60d93 100644 --- a/auth/service.go +++ b/auth/service.go @@ -30,15 +30,6 @@ var ( // ErrFailedToRetrieveMembership failed to retrieve memberships. ErrFailedToRetrieveMembership = errors.New("failed to retrieve memberships") - // ErrFailedToRetrieveAll failed to retrieve groups. - ErrFailedToRetrieveAll = errors.New("failed to retrieve all groups") - - // ErrFailedToRetrieveParents failed to retrieve groups. - ErrFailedToRetrieveParents = errors.New("failed to retrieve all groups") - - // ErrFailedToRetrieveChildren failed to retrieve groups. - ErrFailedToRetrieveChildren = errors.New("failed to retrieve all groups") - errIssueUser = errors.New("failed to issue new login key") errIssueTmp = errors.New("failed to issue new temporary key") errRevoke = errors.New("failed to remove key") @@ -353,7 +344,12 @@ func (svc service) CountSubjects(ctx context.Context, pr PolicyReq) (int, error) } func (svc service) ListPermissions(ctx context.Context, pr PolicyReq, filterPermisions []string) (Permissions, error) { - return svc.agent.RetrievePermissions(ctx, pr, filterPermisions) + pers, err := svc.agent.RetrievePermissions(ctx, pr, filterPermisions) + if err != nil { + return []string{}, errors.Wrap(svcerr.ErrViewEntity, err) + } + + return pers, nil } func (svc service) tmpKey(duration time.Duration, key Key) (Token, error) { diff --git a/internal/api/common.go b/internal/api/common.go index 0f5193f16..a15cf2523 100644 --- a/internal/api/common.go +++ b/internal/api/common.go @@ -10,7 +10,6 @@ import ( "github.com/absmach/magistrala" "github.com/absmach/magistrala/internal/apiutil" - "github.com/absmach/magistrala/internal/postgres" mgclients "github.com/absmach/magistrala/pkg/clients" "github.com/absmach/magistrala/pkg/errors" svcerr "github.com/absmach/magistrala/pkg/errors/service" @@ -116,22 +115,34 @@ func EncodeError(_ context.Context, err error, w http.ResponseWriter) { errors.Contains(err, apiutil.ErrLimitSize), errors.Contains(err, apiutil.ErrBearerKey), errors.Contains(err, apiutil.ErrNameSize), - errors.Contains(err, svcerr.ErrInvalidStatus), errors.Contains(err, apiutil.ErrInvalidIDFormat), - errors.Contains(err, apiutil.ErrInvalidQueryParams), errors.Contains(err, apiutil.ErrInvalidStatus), + errors.Contains(err, svcerr.ErrInvalidStatus), + errors.Contains(err, apiutil.ErrInvitationState), + errors.Contains(err, apiutil.ErrInvalidRole), + errors.Contains(err, apiutil.ErrMissingEmail), + errors.Contains(err, apiutil.ErrMissingHost), + errors.Contains(err, apiutil.ErrMissingIdentity), + errors.Contains(err, apiutil.ErrMissingSecret), + errors.Contains(err, apiutil.ErrMissingPass), + errors.Contains(err, apiutil.ErrMissingConfPass), + errors.Contains(err, apiutil.ErrInvalidResetPass), errors.Contains(err, apiutil.ErrMissingRelation), + errors.Contains(err, errors.ErrPasswordFormat), + errors.Contains(err, apiutil.ErrInvalidLevel), + errors.Contains(err, apiutil.ErrInvalidQueryParams), errors.Contains(err, apiutil.ErrValidation): w.WriteHeader(http.StatusBadRequest) case errors.Contains(err, svcerr.ErrAuthentication), errors.Contains(err, errors.ErrAuthentication), + errors.Contains(err, errors.ErrLogin), errors.Contains(err, apiutil.ErrBearerToken): w.WriteHeader(http.StatusUnauthorized) case errors.Contains(err, svcerr.ErrNotFound): w.WriteHeader(http.StatusNotFound) case errors.Contains(err, svcerr.ErrConflict), - errors.Contains(err, postgres.ErrMemberAlreadyAssigned), - errors.Contains(err, errors.ErrConflict): + errors.Contains(err, errors.ErrConflict), + errors.Contains(err, errors.ErrStatusAlreadyAssigned): w.WriteHeader(http.StatusConflict) case errors.Contains(err, svcerr.ErrAuthorization), errors.Contains(err, errors.ErrAuthorization), @@ -141,9 +152,12 @@ func EncodeError(_ context.Context, err error, w http.ResponseWriter) { w.WriteHeader(http.StatusUnsupportedMediaType) case errors.Contains(err, svcerr.ErrCreateEntity), errors.Contains(err, svcerr.ErrUpdateEntity), + errors.Contains(err, svcerr.ErrFailedOwnerUpdate), errors.Contains(err, svcerr.ErrViewEntity), + errors.Contains(err, svcerr.ErrAddPolicies), + errors.Contains(err, svcerr.ErrDeletePolicies), errors.Contains(err, svcerr.ErrRemoveEntity): - w.WriteHeader(http.StatusInternalServerError) + w.WriteHeader(http.StatusUnprocessableEntity) default: w.WriteHeader(http.StatusInternalServerError) } diff --git a/internal/api/common_test.go b/internal/api/common_test.go index a29bba99c..abf97d119 100644 --- a/internal/api/common_test.go +++ b/internal/api/common_test.go @@ -294,14 +294,14 @@ func TestEncodeError(t *testing.T) { code: http.StatusUnsupportedMediaType, }, { - desc: "InternalServerError", + desc: "StatusUnprocessableEntity", errs: []error{ svcerr.ErrCreateEntity, svcerr.ErrUpdateEntity, svcerr.ErrViewEntity, svcerr.ErrRemoveEntity, }, - code: http.StatusInternalServerError, + code: http.StatusUnprocessableEntity, }, { desc: "InternalServerError", diff --git a/internal/apiutil/errors.go b/internal/apiutil/errors.go index bca1c710e..e2346edd2 100644 --- a/internal/apiutil/errors.go +++ b/internal/apiutil/errors.go @@ -114,12 +114,12 @@ var ( // ErrMissingRelation indicates missing relation. ErrMissingRelation = errors.New("missing relation") + // ErrInvalidRelation indicates an invalid relation. + ErrInvalidRelation = errors.New("invalid relation") + // ErrInvalidAPIKey indicates an invalid API key type. ErrInvalidAPIKey = errors.New("invalid api key type") - // ErrMaxLevelExceeded indicates an invalid group level. - ErrMaxLevelExceeded = errors.New("invalid group level (should be lower than 5)") - // ErrBootstrapState indicates an invalid bootstrap state. ErrBootstrapState = errors.New("invalid bootstrap state") diff --git a/internal/groups/api/responses.go b/internal/groups/api/responses.go index 08b4b314c..a9fbcc5ef 100644 --- a/internal/groups/api/responses.go +++ b/internal/groups/api/responses.go @@ -86,9 +86,9 @@ type groupPageRes struct { } type pageRes struct { - Limit uint64 `json:"limit"` + Limit uint64 `json:"limit,omitempty"` Offset uint64 `json:"offset"` - Total uint64 `json:"total,omitempty"` + Total uint64 `json:"total"` Level uint64 `json:"level,omitempty"` } diff --git a/internal/groups/postgres/groups.go b/internal/groups/postgres/groups.go index 4077bf593..b10258ac6 100644 --- a/internal/groups/postgres/groups.go +++ b/internal/groups/postgres/groups.go @@ -160,17 +160,17 @@ func (repo groupRepository) RetrieveAll(ctx context.Context, gm mggroups.Page) ( dbPage, err := toDBGroupPage(gm) if err != nil { - return mggroups.Page{}, errors.Wrap(postgres.ErrFailedToRetrieveAll, err) + return mggroups.Page{}, errors.Wrap(repoerr.ErrFailedToRetrieveAllGroups, err) } rows, err := repo.db.NamedQueryContext(ctx, q, dbPage) if err != nil { - return mggroups.Page{}, errors.Wrap(postgres.ErrFailedToRetrieveAll, err) + return mggroups.Page{}, errors.Wrap(repoerr.ErrFailedToRetrieveAllGroups, err) } defer rows.Close() items, err := repo.processRows(rows) if err != nil { - return mggroups.Page{}, errors.Wrap(postgres.ErrFailedToRetrieveAll, err) + return mggroups.Page{}, errors.Wrap(repoerr.ErrFailedToRetrieveAllGroups, err) } cq := "SELECT COUNT(*) FROM groups g" @@ -180,7 +180,7 @@ func (repo groupRepository) RetrieveAll(ctx context.Context, gm mggroups.Page) ( total, err := postgres.Total(ctx, repo.db, cq, dbPage) if err != nil { - return mggroups.Page{}, errors.Wrap(postgres.ErrFailedToRetrieveAll, err) + return mggroups.Page{}, errors.Wrap(repoerr.ErrFailedToRetrieveAllGroups, err) } page := gm @@ -208,17 +208,17 @@ func (repo groupRepository) RetrieveByIDs(ctx context.Context, gm mggroups.Page, dbPage, err := toDBGroupPage(gm) if err != nil { - return mggroups.Page{}, errors.Wrap(postgres.ErrFailedToRetrieveAll, err) + return mggroups.Page{}, errors.Wrap(repoerr.ErrFailedToRetrieveAllGroups, err) } rows, err := repo.db.NamedQueryContext(ctx, q, dbPage) if err != nil { - return mggroups.Page{}, errors.Wrap(postgres.ErrFailedToRetrieveAll, err) + return mggroups.Page{}, errors.Wrap(repoerr.ErrFailedToRetrieveAllGroups, err) } defer rows.Close() items, err := repo.processRows(rows) if err != nil { - return mggroups.Page{}, errors.Wrap(postgres.ErrFailedToRetrieveAll, err) + return mggroups.Page{}, errors.Wrap(repoerr.ErrFailedToRetrieveAllGroups, err) } cq := "SELECT COUNT(*) FROM groups g" @@ -228,7 +228,7 @@ func (repo groupRepository) RetrieveByIDs(ctx context.Context, gm mggroups.Page, total, err := postgres.Total(ctx, repo.db, cq, dbPage) if err != nil { - return mggroups.Page{}, errors.Wrap(postgres.ErrFailedToRetrieveAll, err) + return mggroups.Page{}, errors.Wrap(repoerr.ErrFailedToRetrieveAllGroups, err) } page := gm diff --git a/internal/groups/service.go b/internal/groups/service.go index 44297b73b..1a59a85de 100644 --- a/internal/groups/service.go +++ b/internal/groups/service.go @@ -13,6 +13,7 @@ import ( "github.com/absmach/magistrala/internal/apiutil" 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/pkg/groups" "golang.org/x/sync/errgroup" ) @@ -20,8 +21,6 @@ import ( var ( errParentUnAuthz = errors.New("failed to authorize parent group") errMemberKind = errors.New("invalid member kind") - errAddPolicies = errors.New("failed to add policies") - errDeletePolicies = errors.New("failed to delete policies") errRetrieveGroups = errors.New("failed to retrieve groups") errGroupIDs = errors.New("invalid group ids") ) @@ -284,10 +283,10 @@ func (svc service) checkSuperAdmin(ctx context.Context, userID string) error { Object: auth.MagistralaObject, }) if err != nil { - return err + return errors.Wrap(svcerr.ErrAuthorization, err) } if !res.Authorized { - return errors.ErrAuthorization + return svcerr.ErrAuthorization } return nil } @@ -445,7 +444,7 @@ func (svc service) Assign(ctx context.Context, token, groupID, relation, memberK } if _, err := svc.auth.AddPolicies(ctx, &policies); err != nil { - return errors.Wrap(errAddPolicies, err) + return errors.Wrap(svcerr.ErrAddPolicies, err) } return nil @@ -596,7 +595,7 @@ func (svc service) Unassign(ctx context.Context, token, groupID, relation, membe } if _, err := svc.auth.DeletePolicies(ctx, &policies); err != nil { - return errors.Wrap(errDeletePolicies, err) + return errors.Wrap(svcerr.ErrDeletePolicies, err) } return nil } @@ -694,7 +693,7 @@ func (svc service) changeGroupStatus(ctx context.Context, token string, group gr return groups.Group{}, err } if dbGroup.Status == group.Status { - return groups.Group{}, mgclients.ErrStatusAlreadyAssigned + return groups.Group{}, errors.ErrStatusAlreadyAssigned } group.UpdatedBy = id @@ -723,10 +722,10 @@ func (svc service) authorizeToken(ctx context.Context, subjectType, subject, per } res, err := svc.auth.Authorize(ctx, req) if err != nil { - return "", err + return "", errors.Wrap(svcerr.ErrAuthorization, err) } if !res.GetAuthorized() { - return "", errors.ErrAuthorization + return "", svcerr.ErrAuthorization } return res.GetId(), nil } @@ -743,10 +742,10 @@ func (svc service) authorizeKind(ctx context.Context, domainID, subjectType, sub } res, err := svc.auth.Authorize(ctx, req) if err != nil { - return "", err + return "", errors.Wrap(svcerr.ErrAuthorization, err) } if !res.GetAuthorized() { - return "", errors.ErrAuthorization + return "", svcerr.ErrAuthorization } return res.GetId(), nil } diff --git a/internal/groups/service_test.go b/internal/groups/service_test.go index 481482b5f..39e3807bf 100644 --- a/internal/groups/service_test.go +++ b/internal/groups/service_test.go @@ -17,7 +17,6 @@ import ( "github.com/absmach/magistrala/internal/groups" "github.com/absmach/magistrala/internal/testsutil" "github.com/absmach/magistrala/pkg/clients" - mgclients "github.com/absmach/magistrala/pkg/clients" "github.com/absmach/magistrala/pkg/errors" mggroups "github.com/absmach/magistrala/pkg/groups" "github.com/absmach/magistrala/pkg/groups/mocks" @@ -585,7 +584,7 @@ func TestEnableGroup(t *testing.T) { retrieveResp: mggroups.Group{ Status: clients.Status(groups.EnabledStatus), }, - err: mgclients.ErrStatusAlreadyAssigned, + err: errors.ErrStatusAlreadyAssigned, }, { desc: "with retrieve error", @@ -685,7 +684,7 @@ func TestDisableGroup(t *testing.T) { retrieveResp: mggroups.Group{ Status: clients.Status(groups.DisabledStatus), }, - err: mgclients.ErrStatusAlreadyAssigned, + err: errors.ErrStatusAlreadyAssigned, }, { desc: "with retrieve error", diff --git a/internal/postgres/common.go b/internal/postgres/common.go index 1c5b18371..c34ac9fb4 100644 --- a/internal/postgres/common.go +++ b/internal/postgres/common.go @@ -6,42 +6,9 @@ package postgres import ( "context" "encoding/json" - "errors" "fmt" ) -var ( - // ErrAssignToGroup indicates failure to assign member to a group. - ErrAssignToGroup = errors.New("failed to assign member to a group") - - // ErrUnassignFromGroup indicates failure to unassign member from a group. - ErrUnassignFromGroup = errors.New("failed to unassign member from a group") - - // ErrMissingParent indicates that parent can't be found. - ErrMissingParent = errors.New("failed to retrieve parent") - - // ErrGroupNotEmpty indicates group is not empty, can't be deleted. - ErrGroupNotEmpty = errors.New("group is not empty") - - // ErrMemberAlreadyAssigned indicates that members is already assigned. - ErrMemberAlreadyAssigned = errors.New("member is already assigned") - - // ErrFailedToRetrieveMembers failed to retrieve group members. - ErrFailedToRetrieveMembers = errors.New("failed to retrieve group members") - - // ErrFailedToRetrieveMembership failed to retrieve memberships. - ErrFailedToRetrieveMembership = errors.New("failed to retrieve memberships") - - // ErrFailedToRetrieveAll failed to retrieve groups. - ErrFailedToRetrieveAll = errors.New("failed to retrieve all groups") - - // ErrFailedToRetrieveParents failed to retrieve groups. - ErrFailedToRetrieveParents = errors.New("failed to retrieve all groups") - - // ErrFailedToRetrieveChildren failed to retrieve groups. - ErrFailedToRetrieveChildren = errors.New("failed to retrieve all groups") -) - func CreateMetadataQuery(entity string, um map[string]interface{}) (string, []byte, error) { if len(um) == 0 { return "", nil, nil diff --git a/invitations/api/endpoint_test.go b/invitations/api/endpoint_test.go index fbf0cd4d0..bf48b965e 100644 --- a/invitations/api/endpoint_test.go +++ b/invitations/api/endpoint_test.go @@ -266,7 +266,7 @@ func TestListInvitation(t *testing.T) { desc: "with invalid state", token: validToken, query: "state=invalid", - status: http.StatusInternalServerError, + status: http.StatusBadRequest, contentType: validContenType, svcErr: nil, }, diff --git a/invitations/api/requests_test.go b/invitations/api/requests_test.go index 1f1192a92..a2e22660a 100644 --- a/invitations/api/requests_test.go +++ b/invitations/api/requests_test.go @@ -4,7 +4,6 @@ package api import ( - "errors" "fmt" "testing" @@ -14,11 +13,7 @@ import ( "github.com/stretchr/testify/assert" ) -var ( - errMissingRelation = errors.New("missing relation") - errInvalidRelation = errors.New("invalid relation") - valid = "valid" -) +var valid = "valid" func TestSendInvitationReqValidation(t *testing.T) { cases := []struct { @@ -79,7 +74,7 @@ func TestSendInvitationReqValidation(t *testing.T) { Relation: "", Resend: true, }, - err: errMissingRelation, + err: apiutil.ErrMissingRelation, }, { desc: "invalid relation", @@ -90,7 +85,7 @@ func TestSendInvitationReqValidation(t *testing.T) { Relation: "invalid", Resend: true, }, - err: errInvalidRelation, + err: apiutil.ErrInvalidRelation, }, } diff --git a/invitations/invitations.go b/invitations/invitations.go index 354dd3108..0cf6c5a2a 100644 --- a/invitations/invitations.go +++ b/invitations/invitations.go @@ -6,15 +6,10 @@ package invitations import ( "context" "encoding/json" - "errors" "time" "github.com/absmach/magistrala/auth" -) - -var ( - errMissingRelation = errors.New("missing relation") - errInvalidRelation = errors.New("invalid relation") + "github.com/absmach/magistrala/internal/apiutil" ) // Invitation is an invitation to join a domain. @@ -122,7 +117,7 @@ type Repository interface { // It returns an error if the relation is empty or invalid. func CheckRelation(relation string) error { if relation == "" { - return errMissingRelation + return apiutil.ErrMissingRelation } if relation != auth.AdministratorRelation && relation != auth.EditorRelation && @@ -133,7 +128,7 @@ func CheckRelation(relation string) error { relation != auth.RoleGroupRelation && relation != auth.GroupRelation && relation != auth.PlatformRelation { - return errInvalidRelation + return apiutil.ErrInvalidRelation } return nil diff --git a/invitations/invitations_test.go b/invitations/invitations_test.go index 942eaec76..2c367d7a7 100644 --- a/invitations/invitations_test.go +++ b/invitations/invitations_test.go @@ -4,19 +4,14 @@ package invitations_test import ( - "errors" "fmt" "testing" + "github.com/absmach/magistrala/internal/apiutil" "github.com/absmach/magistrala/invitations" "github.com/stretchr/testify/assert" ) -var ( - errMissingRelation = errors.New("missing relation") - errInvalidRelation = errors.New("invalid relation") -) - func TestInvitation_MarshalJSON(t *testing.T) { cases := []struct { desc string @@ -60,8 +55,8 @@ func TestCheckRelation(t *testing.T) { relation string err error }{ - {"", errMissingRelation}, - {"admin", errInvalidRelation}, + {"", apiutil.ErrMissingRelation}, + {"admin", apiutil.ErrInvalidRelation}, {"editor", nil}, {"viewer", nil}, {"member", nil}, diff --git a/invitations/service_test.go b/invitations/service_test.go index c1c1da521..ee6c3215d 100644 --- a/invitations/service_test.go +++ b/invitations/service_test.go @@ -5,7 +5,6 @@ package invitations_test import ( "context" - "errors" "math/rand" "testing" "time" @@ -13,6 +12,7 @@ import ( "github.com/absmach/magistrala" "github.com/absmach/magistrala/auth" authmocks "github.com/absmach/magistrala/auth/mocks" + "github.com/absmach/magistrala/internal/apiutil" "github.com/absmach/magistrala/internal/testsutil" "github.com/absmach/magistrala/invitations" "github.com/absmach/magistrala/invitations/mocks" @@ -79,7 +79,7 @@ func TestSendInvitation(t *testing.T) { token: validToken, tokenUserID: testsutil.GenerateUUID(t), req: invitations.Invitation{Relation: "invalid"}, - err: errors.New("invalid relation"), + err: apiutil.ErrInvalidRelation, authNErr: nil, domainErr: nil, adminErr: nil, diff --git a/pkg/clients/postgres/clients.go b/pkg/clients/postgres/clients.go index 59144c0e1..5417fda87 100644 --- a/pkg/clients/postgres/clients.go +++ b/pkg/clients/postgres/clients.go @@ -157,11 +157,11 @@ func (repo *Repository) RetrieveAll(ctx context.Context, pm clients.Page) (clien dbPage, err := ToDBClientsPage(pm) if err != nil { - return clients.ClientsPage{}, errors.Wrap(postgres.ErrFailedToRetrieveAll, err) + return clients.ClientsPage{}, errors.Wrap(repoerr.ErrFailedToRetrieveAllGroups, err) } rows, err := repo.DB.NamedQueryContext(ctx, q, dbPage) if err != nil { - return clients.ClientsPage{}, errors.Wrap(postgres.ErrFailedToRetrieveAll, err) + return clients.ClientsPage{}, errors.Wrap(repoerr.ErrFailedToRetrieveAllGroups, err) } defer rows.Close() @@ -205,12 +205,12 @@ func (repo *Repository) RetrieveAllBasicInfo(ctx context.Context, pm clients.Pag dbPage, err := ToDBClientsPage(pm) if err != nil { - return clients.ClientsPage{}, errors.Wrap(postgres.ErrFailedToRetrieveAll, err) + return clients.ClientsPage{}, errors.Wrap(repoerr.ErrFailedToRetrieveAllGroups, err) } rows, err := repo.DB.NamedQueryContext(ctx, q, dbPage) if err != nil { - return clients.ClientsPage{}, errors.Wrap(postgres.ErrFailedToRetrieveAll, err) + return clients.ClientsPage{}, errors.Wrap(repoerr.ErrFailedToRetrieveAllGroups, err) } defer rows.Close() @@ -263,11 +263,11 @@ func (repo *Repository) RetrieveAllByIDs(ctx context.Context, pm clients.Page) ( dbPage, err := ToDBClientsPage(pm) if err != nil { - return clients.ClientsPage{}, errors.Wrap(postgres.ErrFailedToRetrieveAll, err) + return clients.ClientsPage{}, errors.Wrap(repoerr.ErrFailedToRetrieveAllGroups, err) } rows, err := repo.DB.NamedQueryContext(ctx, q, dbPage) if err != nil { - return clients.ClientsPage{}, errors.Wrap(postgres.ErrFailedToRetrieveAll, err) + return clients.ClientsPage{}, errors.Wrap(repoerr.ErrFailedToRetrieveAllGroups, err) } defer rows.Close() diff --git a/pkg/clients/status.go b/pkg/clients/status.go index 9d4c59e2f..82a716d51 100644 --- a/pkg/clients/status.go +++ b/pkg/clients/status.go @@ -5,7 +5,6 @@ package clients import ( "encoding/json" - "errors" "strings" "github.com/absmach/magistrala/internal/apiutil" @@ -36,9 +35,6 @@ const ( Unknown = "unknown" ) -// ErrStatusAlreadyAssigned indicated that the client or group has already been assigned the status. -var ErrStatusAlreadyAssigned = errors.New("status already assigned") - // String converts client/group status to string literal. func (s Status) String() string { switch s { diff --git a/pkg/errors/repository/types.go b/pkg/errors/repository/types.go index 2637d79b3..c27124343 100644 --- a/pkg/errors/repository/types.go +++ b/pkg/errors/repository/types.go @@ -48,4 +48,7 @@ var ( // ErrInvalidSecret indicates invalid secret. ErrInvalidSecret = errors.New("missing secret") + + // ErrFailedToRetrieveAllGroups failed to retrieve groups. + ErrFailedToRetrieveAllGroups = errors.New("failed to retrieve all groups") ) diff --git a/pkg/errors/service/types.go b/pkg/errors/service/types.go index a0a3049f2..720f19838 100644 --- a/pkg/errors/service/types.go +++ b/pkg/errors/service/types.go @@ -45,4 +45,19 @@ var ( // ErrInvalidPolicy indicates that an invalid policy. ErrInvalidPolicy = errors.New("invalid policy") + + // ErrRecoveryToken indicates error in generating password recovery token. + ErrRecoveryToken = errors.New("failed to generate password recovery token") + + // ErrFailedPolicyUpdate indicates a failure to update user policy. + ErrFailedPolicyUpdate = errors.New("failed to update user policy") + + // ErrFailedOwnerUpdate indicates a failure to update user policy. + ErrFailedOwnerUpdate = errors.New("failed to update user owner") + + // ErrAddPolicies indicates failed to add policies. + ErrAddPolicies = errors.New("failed to add policies") + + // ErrDeletePolicies indicates failed to delete policies. + ErrDeletePolicies = errors.New("failed to delete policies") ) diff --git a/pkg/errors/types.go b/pkg/errors/types.go index 684c4dec6..f49e71a79 100644 --- a/pkg/errors/types.go +++ b/pkg/errors/types.go @@ -45,6 +45,9 @@ var ( // ErrLogin indicates wrong login credentials. ErrLogin = New("invalid user id or secret") + // ErrPasswordFormat indicates weak password. + ErrPasswordFormat = errors.New("password does not meet the requirements") + // ErrUnsupportedContentType indicates invalid content type. ErrUnsupportedContentType = errors.New("invalid content type") @@ -53,4 +56,7 @@ var ( // ErrEmptyPath indicates empty file path. ErrEmptyPath = errors.New("empty file path") + + // ErrStatusAlreadyAssigned indicated that the client or group has already been assigned the status. + ErrStatusAlreadyAssigned = errors.New("status already assigned") ) diff --git a/pkg/groups/errors.go b/pkg/groups/errors.go index 24b1a29d9..b6665fa0b 100644 --- a/pkg/groups/errors.go +++ b/pkg/groups/errors.go @@ -14,7 +14,4 @@ var ( // ErrDisableGroup indicates error in disabling group. ErrDisableGroup = errors.New("failed to disable group") - - // ErrStatusAlreadyAssigned indicated that the group has already been assigned the status. - ErrStatusAlreadyAssigned = errors.New("status already assigned") ) diff --git a/pkg/sdk/go/channels_test.go b/pkg/sdk/go/channels_test.go index 7cc2bad9f..a90ca2c9a 100644 --- a/pkg/sdk/go/channels_test.go +++ b/pkg/sdk/go/channels_test.go @@ -109,7 +109,7 @@ func TestCreateChannel(t *testing.T) { Status: mgclients.EnabledStatus.String(), }, token: token, - err: errors.NewSDKErrorWithStatus(errors.ErrCreateEntity, http.StatusInternalServerError), + err: errors.NewSDKErrorWithStatus(errors.ErrCreateEntity, http.StatusUnprocessableEntity), }, { desc: "create channel with invalid owner", @@ -119,7 +119,7 @@ func TestCreateChannel(t *testing.T) { Status: mgclients.EnabledStatus.String(), }, token: token, - err: errors.NewSDKErrorWithStatus(sdk.ErrFailedCreation, http.StatusInternalServerError), + err: errors.NewSDKErrorWithStatus(sdk.ErrFailedCreation, http.StatusUnprocessableEntity), }, { desc: "create channel with missing name", @@ -475,7 +475,7 @@ func TestUpdateChannel(t *testing.T) { }, response: sdk.Channel{}, token: invalidToken, - err: errors.NewSDKErrorWithStatus(svcerr.ErrAuthorization, http.StatusForbidden), + err: errors.NewSDKErrorWithStatus(errors.Wrap(svcerr.ErrAuthorization, svcerr.ErrAuthorization), http.StatusForbidden), }, { desc: "update channel description with invalid token", @@ -485,7 +485,7 @@ func TestUpdateChannel(t *testing.T) { }, response: sdk.Channel{}, token: invalidToken, - err: errors.NewSDKErrorWithStatus(svcerr.ErrAuthorization, http.StatusForbidden), + err: errors.NewSDKErrorWithStatus(errors.Wrap(svcerr.ErrAuthorization, svcerr.ErrAuthorization), http.StatusForbidden), }, { desc: "update channel metadata with invalid token", @@ -497,7 +497,7 @@ func TestUpdateChannel(t *testing.T) { }, response: sdk.Channel{}, token: invalidToken, - err: errors.NewSDKErrorWithStatus(svcerr.ErrAuthorization, http.StatusForbidden), + err: errors.NewSDKErrorWithStatus(errors.Wrap(svcerr.ErrAuthorization, svcerr.ErrAuthorization), http.StatusForbidden), }, { desc: "update channel that can't be marshalled", diff --git a/pkg/sdk/go/groups_test.go b/pkg/sdk/go/groups_test.go index 62760e1d8..3d7e77e7a 100644 --- a/pkg/sdk/go/groups_test.go +++ b/pkg/sdk/go/groups_test.go @@ -94,7 +94,7 @@ func TestCreateGroup(t *testing.T) { ParentID: wrongID, Status: clients.EnabledStatus.String(), }, - err: errors.NewSDKErrorWithStatus(svcerr.ErrCreateEntity, http.StatusInternalServerError), + err: errors.NewSDKErrorWithStatus(svcerr.ErrCreateEntity, http.StatusUnprocessableEntity), }, { desc: "create group with invalid owner", @@ -104,7 +104,7 @@ func TestCreateGroup(t *testing.T) { OwnerID: wrongID, Status: clients.EnabledStatus.String(), }, - err: errors.NewSDKErrorWithStatus(sdk.ErrFailedCreation, http.StatusInternalServerError), + err: errors.NewSDKErrorWithStatus(sdk.ErrFailedCreation, http.StatusUnprocessableEntity), }, { desc: "create group with missing name", @@ -731,7 +731,7 @@ func TestUpdateGroup(t *testing.T) { }, response: sdk.Group{}, token: invalidToken, - err: errors.NewSDKErrorWithStatus(svcerr.ErrAuthorization, http.StatusForbidden), + err: errors.NewSDKErrorWithStatus(errors.Wrap(svcerr.ErrAuthorization, svcerr.ErrAuthorization), http.StatusForbidden), }, { desc: "update group description with invalid token", @@ -741,7 +741,7 @@ func TestUpdateGroup(t *testing.T) { }, response: sdk.Group{}, token: invalidToken, - err: errors.NewSDKErrorWithStatus(svcerr.ErrAuthorization, http.StatusForbidden), + err: errors.NewSDKErrorWithStatus(errors.Wrap(svcerr.ErrAuthorization, svcerr.ErrAuthorization), http.StatusForbidden), }, { desc: "update group metadata with invalid token", @@ -753,7 +753,7 @@ func TestUpdateGroup(t *testing.T) { }, response: sdk.Group{}, token: invalidToken, - err: errors.NewSDKErrorWithStatus(svcerr.ErrAuthorization, http.StatusForbidden), + err: errors.NewSDKErrorWithStatus(errors.Wrap(svcerr.ErrAuthorization, svcerr.ErrAuthorization), http.StatusForbidden), }, { desc: "update a group that can't be marshalled", diff --git a/pkg/sdk/go/things_test.go b/pkg/sdk/go/things_test.go index f0f64fec4..e3c16998d 100644 --- a/pkg/sdk/go/things_test.go +++ b/pkg/sdk/go/things_test.go @@ -97,7 +97,7 @@ func TestCreateThing(t *testing.T) { response: sdk.Thing{}, token: token, repoErr: sdk.ErrFailedCreation, - err: errors.NewSDKErrorWithStatus(errors.Wrap(sdk.ErrFailedCreation, repoerr.ErrCreateEntity), http.StatusInternalServerError), + err: errors.NewSDKErrorWithStatus(errors.Wrap(sdk.ErrFailedCreation, repoerr.ErrCreateEntity), http.StatusUnprocessableEntity), }, { desc: "register empty thing", @@ -249,7 +249,7 @@ func TestCreateThings(t *testing.T) { things: thingsList, response: []sdk.Thing{}, token: token, - err: errors.NewSDKErrorWithStatus(errors.Wrap(sdk.ErrFailedCreation, sdk.ErrFailedCreation), http.StatusInternalServerError), + err: errors.NewSDKErrorWithStatus(errors.Wrap(sdk.ErrFailedCreation, sdk.ErrFailedCreation), http.StatusUnprocessableEntity), }, { desc: "register empty things", @@ -755,7 +755,7 @@ func TestUpdateThing(t *testing.T) { thing: thing2, response: sdk.Thing{}, token: validToken, - err: errors.NewSDKErrorWithStatus(errors.Wrap(svcerr.ErrUpdateEntity, svcerr.ErrUpdateEntity), http.StatusInternalServerError), + err: errors.NewSDKErrorWithStatus(errors.Wrap(svcerr.ErrUpdateEntity, svcerr.ErrUpdateEntity), http.StatusUnprocessableEntity), }, { desc: "update thing that can't be marshalled", @@ -840,7 +840,7 @@ func TestUpdateThingTags(t *testing.T) { thing: thing2, response: sdk.Thing{}, token: validToken, - err: errors.NewSDKErrorWithStatus(errors.Wrap(svcerr.ErrUpdateEntity, svcerr.ErrUpdateEntity), http.StatusInternalServerError), + err: errors.NewSDKErrorWithStatus(errors.Wrap(svcerr.ErrUpdateEntity, svcerr.ErrUpdateEntity), http.StatusUnprocessableEntity), }, { desc: "update thing that can't be marshalled", diff --git a/pkg/sdk/go/tokens_test.go b/pkg/sdk/go/tokens_test.go index ebf6cfbd5..ad5f07a95 100644 --- a/pkg/sdk/go/tokens_test.go +++ b/pkg/sdk/go/tokens_test.go @@ -62,7 +62,7 @@ func TestIssueToken(t *testing.T) { desc: "issue token for an empty user", login: sdk.Login{}, token: &magistrala.Token{}, - err: errors.NewSDKErrorWithStatus(errors.Wrap(apiutil.ErrValidation, apiutil.ErrMissingIdentity), http.StatusInternalServerError), + err: errors.NewSDKErrorWithStatus(errors.Wrap(apiutil.ErrValidation, apiutil.ErrMissingIdentity), http.StatusBadRequest), }, { desc: "issue token for invalid identity", diff --git a/pkg/sdk/go/users_test.go b/pkg/sdk/go/users_test.go index f65dc037f..7d588a52c 100644 --- a/pkg/sdk/go/users_test.go +++ b/pkg/sdk/go/users_test.go @@ -86,7 +86,7 @@ func TestCreateClient(t *testing.T) { client: user, response: sdk.User{}, token: token, - err: errors.NewSDKErrorWithStatus(errors.Wrap(sdk.ErrFailedCreation, sdk.ErrFailedCreation), http.StatusInternalServerError), + err: errors.NewSDKErrorWithStatus(errors.Wrap(sdk.ErrFailedCreation, sdk.ErrFailedCreation), http.StatusUnprocessableEntity), }, { desc: "register empty user", @@ -367,7 +367,6 @@ func TestListClients(t *testing.T) { for _, tc := range cases { pm := sdk.PageMetadata{ Status: tc.status, - Total: total, Offset: tc.offset, Limit: tc.limit, Name: tc.name, @@ -570,7 +569,7 @@ func TestUpdateClient(t *testing.T) { client: client2, response: sdk.User{}, token: validToken, - err: errors.NewSDKErrorWithStatus(errors.Wrap(svcerr.ErrUpdateEntity, svcerr.ErrUpdateEntity), http.StatusInternalServerError), + err: errors.NewSDKErrorWithStatus(errors.Wrap(svcerr.ErrUpdateEntity, svcerr.ErrUpdateEntity), http.StatusUnprocessableEntity), }, { desc: "update a user that can't be marshalled", @@ -661,7 +660,7 @@ func TestUpdateClientTags(t *testing.T) { client: client2, response: sdk.User{}, token: validToken, - err: errors.NewSDKErrorWithStatus(errors.Wrap(svcerr.ErrUpdateEntity, svcerr.ErrUpdateEntity), http.StatusInternalServerError), + err: errors.NewSDKErrorWithStatus(errors.Wrap(svcerr.ErrUpdateEntity, svcerr.ErrUpdateEntity), http.StatusUnprocessableEntity), }, { desc: "update a user that can't be marshalled", @@ -751,7 +750,7 @@ func TestUpdateClientIdentity(t *testing.T) { client: client2, response: sdk.User{}, token: validToken, - err: errors.NewSDKErrorWithStatus(errors.Wrap(svcerr.ErrUpdateEntity, svcerr.ErrUpdateEntity), http.StatusInternalServerError), + err: errors.NewSDKErrorWithStatus(errors.Wrap(svcerr.ErrUpdateEntity, svcerr.ErrUpdateEntity), http.StatusUnprocessableEntity), }, { desc: "update a user that can't be marshalled", @@ -767,7 +766,7 @@ func TestUpdateClientIdentity(t *testing.T) { }, response: sdk.User{}, token: validToken, - err: errors.NewSDKErrorWithStatus(errors.Wrap(svcerr.ErrUpdateEntity, svcerr.ErrUpdateEntity), http.StatusInternalServerError), + err: errors.NewSDKErrorWithStatus(errors.Wrap(svcerr.ErrUpdateEntity, svcerr.ErrUpdateEntity), http.StatusUnprocessableEntity), }, } @@ -920,7 +919,7 @@ func TestUpdateClientRole(t *testing.T) { client: client2, response: sdk.User{}, token: validToken, - err: errors.NewSDKErrorWithStatus(errors.Wrap(users.ErrFailedOwnerUpdate, users.ErrFailedOwnerUpdate), http.StatusInternalServerError), + err: errors.NewSDKErrorWithStatus(errors.Wrap(svcerr.ErrFailedOwnerUpdate, svcerr.ErrFailedOwnerUpdate), http.StatusUnprocessableEntity), }, { desc: "update a user that can't be marshalled", diff --git a/things/service.go b/things/service.go index 1a5c3b5d3..2d391d88a 100644 --- a/things/service.go +++ b/things/service.go @@ -507,7 +507,7 @@ func (svc service) changeClientStatus(ctx context.Context, token string, client return mgclients.Client{}, errors.Wrap(repoerr.ErrNotFound, err) } if dbClient.Status == client.Status { - return mgclients.Client{}, mgclients.ErrStatusAlreadyAssigned + return mgclients.Client{}, errors.ErrStatusAlreadyAssigned } client.UpdatedBy = userID diff --git a/things/service_test.go b/things/service_test.go index 8e92c29c4..cf62d62d4 100644 --- a/things/service_test.go +++ b/things/service_test.go @@ -1080,8 +1080,8 @@ func TestEnableClient(t *testing.T) { changeStatusResponse: enabledClient1, retrieveByIDResponse: enabledClient1, authorizeResponse: &magistrala.AuthorizeRes{Authorized: true}, - changeStatusErr: mgclients.ErrStatusAlreadyAssigned, - err: mgclients.ErrStatusAlreadyAssigned, + changeStatusErr: errors.ErrStatusAlreadyAssigned, + err: errors.ErrStatusAlreadyAssigned, }, { desc: "enable non-existing client", @@ -1237,8 +1237,8 @@ func TestDisableClient(t *testing.T) { changeStatusResponse: mgclients.Client{}, retrieveByIDResponse: disabledClient1, authorizeResponse: &magistrala.AuthorizeRes{Authorized: true}, - changeStatusErr: mgclients.ErrStatusAlreadyAssigned, - err: mgclients.ErrStatusAlreadyAssigned, + changeStatusErr: errors.ErrStatusAlreadyAssigned, + err: errors.ErrStatusAlreadyAssigned, }, { desc: "disable non-existing client", diff --git a/users/api/clients.go b/users/api/clients.go index 0f9075c3e..c7176cd85 100644 --- a/users/api/clients.go +++ b/users/api/clients.go @@ -26,6 +26,7 @@ func clientsHandler(svc users.Service, r *chi.Mux, logger *slog.Logger) http.Han opts := []kithttp.ServerOption{ kithttp.ServerErrorEncoder(apiutil.LoggingErrorEncoder(logger, api.EncodeError)), } + r.Route("/users", func(r chi.Router) { r.Post("/", otelhttp.NewHandler(kithttp.NewServer( registrationEndpoint(svc), @@ -83,20 +84,6 @@ func clientsHandler(svc users.Service, r *chi.Mux, logger *slog.Logger) http.Han opts..., ), "update_client_identity").ServeHTTP) - r.Post("/password/reset-request", otelhttp.NewHandler(kithttp.NewServer( - passwordResetRequestEndpoint(svc), - decodePasswordResetRequest, - api.EncodeResponse, - opts..., - ), "password_reset_req").ServeHTTP) - - r.Put("/password/reset", otelhttp.NewHandler(kithttp.NewServer( - passwordResetEndpoint(svc), - decodePasswordReset, - api.EncodeResponse, - opts..., - ), "password_reset").ServeHTTP) - r.Patch("/{id}/role", otelhttp.NewHandler(kithttp.NewServer( updateClientRoleEndpoint(svc), decodeUpdateClientRole, @@ -133,6 +120,22 @@ func clientsHandler(svc users.Service, r *chi.Mux, logger *slog.Logger) http.Han ), "disable_client").ServeHTTP) }) + r.Route("/password", func(r chi.Router) { + r.Post("/reset-request", otelhttp.NewHandler(kithttp.NewServer( + passwordResetRequestEndpoint(svc), + decodePasswordResetRequest, + api.EncodeResponse, + opts..., + ), "password_reset_req").ServeHTTP) + + r.Put("/reset", otelhttp.NewHandler(kithttp.NewServer( + passwordResetEndpoint(svc), + decodePasswordReset, + api.EncodeResponse, + opts..., + ), "password_reset").ServeHTTP) + }) + // Ideal location: users service, groups endpoint. // Reason for placing here : // SpiceDB provides list of user ids in given user_group_id @@ -219,7 +222,7 @@ func decodeListClients(_ context.Context, r *http.Request) (interface{}, error) } oid, err := apiutil.ReadStringQuery(r, api.OwnerKey, "") if err != nil { - return nil, err + return nil, errors.Wrap(apiutil.ErrValidation, err) } order, err := apiutil.ReadStringQuery(r, api.OrderKey, api.DefOrder) if err != nil { diff --git a/users/api/responses.go b/users/api/responses.go index 716f5a912..fb05de493 100644 --- a/users/api/responses.go +++ b/users/api/responses.go @@ -25,8 +25,8 @@ var ( type pageRes struct { Limit uint64 `json:"limit,omitempty"` - Offset uint64 `json:"offset,omitempty"` - Total uint64 `json:"total,omitempty"` + Offset uint64 `json:"offset"` + Total uint64 `json:"total"` } type createClientRes struct { diff --git a/users/postgres/clients.go b/users/postgres/clients.go index dd1abfae9..9d8b4c1d6 100644 --- a/users/postgres/clients.go +++ b/users/postgres/clients.go @@ -133,11 +133,11 @@ func (repo clientRepo) RetrieveAll(ctx context.Context, pm mgclients.Page) (mgcl dbPage, err := pgclients.ToDBClientsPage(pm) if err != nil { - return mgclients.ClientsPage{}, errors.Wrap(postgres.ErrFailedToRetrieveAll, err) + return mgclients.ClientsPage{}, errors.Wrap(repoerr.ErrFailedToRetrieveAllGroups, err) } rows, err := repo.DB.NamedQueryContext(ctx, q, dbPage) if err != nil { - return mgclients.ClientsPage{}, errors.Wrap(postgres.ErrFailedToRetrieveAll, err) + return mgclients.ClientsPage{}, errors.Wrap(repoerr.ErrFailedToRetrieveAllGroups, err) } defer rows.Close() diff --git a/users/service.go b/users/service.go index 7f01083e9..efe77636b 100644 --- a/users/service.go +++ b/users/service.go @@ -19,17 +19,6 @@ import ( ) var ( - // ErrRecoveryToken indicates error in generating password recovery token. - ErrRecoveryToken = errors.New("failed to generate password recovery token") - - // ErrPasswordFormat indicates weak password. - ErrPasswordFormat = errors.New("password does not meet the requirements") - - // ErrFailedPolicyUpdate indicates a failure to update user policy. - ErrFailedPolicyUpdate = errors.New("failed to update user policy") - - // ErrFailedOwnerUpdate indicates a failure to update user policy. - ErrFailedOwnerUpdate = errors.New("failed to update user owner") // ErrAddPolicies indictaed a failre to add policies. errAddPolicies = errors.New("failed to add policies") @@ -292,7 +281,7 @@ func (svc service) GenerateResetToken(ctx context.Context, email, host string) e } token, err := svc.auth.Issue(ctx, issueReq) if err != nil { - return errors.Wrap(ErrRecoveryToken, err) + return errors.Wrap(svcerr.ErrRecoveryToken, err) } return svc.SendPasswordReset(ctx, host, email, client.Name, token.AccessToken) @@ -311,7 +300,7 @@ func (svc service) ResetSecret(ctx context.Context, resetToken, secret string) e return errors.ErrNotFound } if !svc.passRegex.MatchString(secret) { - return ErrPasswordFormat + return errors.ErrPasswordFormat } secret, err = svc.hasher.Hash(secret) if err != nil { @@ -337,7 +326,7 @@ func (svc service) UpdateClientSecret(ctx context.Context, token, oldSecret, new return mgclients.Client{}, err } if !svc.passRegex.MatchString(newSecret) { - return mgclients.Client{}, ErrPasswordFormat + return mgclients.Client{}, errors.ErrPasswordFormat } dbClient, err := svc.clients.RetrieveByID(ctx, id) if err != nil { @@ -384,7 +373,7 @@ func (svc service) UpdateClientRole(ctx context.Context, token string, cli mgcli } if err := svc.updateClientPolicy(ctx, cli.ID, cli.Role); err != nil { - return mgclients.Client{}, errors.Wrap(ErrFailedPolicyUpdate, err) + return mgclients.Client{}, errors.Wrap(svcerr.ErrFailedPolicyUpdate, err) } client, err = svc.clients.UpdateRole(ctx, client) if err != nil { @@ -392,7 +381,7 @@ func (svc service) UpdateClientRole(ctx context.Context, token string, cli mgcli if errRollback := svc.updateClientPolicy(ctx, cli.ID, mgclients.UserRole); errRollback != nil { return mgclients.Client{}, errors.Wrap(err, errors.Wrap(repoerr.ErrRollbackTx, errRollback)) } - return mgclients.Client{}, errors.Wrap(ErrFailedOwnerUpdate, err) + return mgclients.Client{}, errors.Wrap(svcerr.ErrFailedOwnerUpdate, err) } return client, nil } @@ -438,7 +427,7 @@ func (svc service) changeClientStatus(ctx context.Context, token string, client return mgclients.Client{}, errors.Wrap(repoerr.ErrNotFound, err) } if dbClient.Status == client.Status { - return mgclients.Client{}, mgclients.ErrStatusAlreadyAssigned + return mgclients.Client{}, errors.ErrStatusAlreadyAssigned } client.UpdatedBy = tokenUserID @@ -580,7 +569,7 @@ func (svc *service) authorize(ctx context.Context, subjType, subjKind, subj, per } if !res.GetAuthorized() { - return "", errors.Wrap(svcerr.ErrAuthorization, err) + return "", svcerr.ErrAuthorization } return res.GetId(), nil } diff --git a/users/service_test.go b/users/service_test.go index 93be53385..cc8141818 100644 --- a/users/service_test.go +++ b/users/service_test.go @@ -1185,7 +1185,7 @@ func TestUpdateClientSecret(t *testing.T) { newSecret: "weak", token: validToken, identifyResponse: &magistrala.IdentityRes{UserId: client.ID}, - err: users.ErrPasswordFormat, + err: errors.ErrPasswordFormat, }, { desc: "update client secret with failed to retrieve client by ID", @@ -1351,7 +1351,7 @@ func TestEnableClient(t *testing.T) { identifyResponse: &magistrala.IdentityRes{UserId: enabledClient1.ID}, authorizeResponse: &magistrala.AuthorizeRes{Authorized: true}, retrieveByIDResponse: enabledClient1, - err: mgclients.ErrStatusAlreadyAssigned, + err: errors.ErrStatusAlreadyAssigned, }, { desc: "enable disabled client with failed to change status", @@ -1538,7 +1538,7 @@ func TestDisableClient(t *testing.T) { identifyResponse: &magistrala.IdentityRes{UserId: disabledClient1.ID}, authorizeResponse: &magistrala.AuthorizeRes{Authorized: true}, retrieveByIDResponse: disabledClient1, - err: mgclients.ErrStatusAlreadyAssigned, + err: errors.ErrStatusAlreadyAssigned, }, { desc: "disable enabled client with failed to change status", @@ -2280,7 +2280,7 @@ func TestGenerateResetToken(t *testing.T) { retrieveByIdentityResponse: client, issueResponse: &magistrala.Token{}, issueErr: svcerr.ErrAuthorization, - err: users.ErrRecoveryToken, + err: svcerr.ErrRecoveryToken, }, } @@ -2369,7 +2369,7 @@ func TestResetSecret(t *testing.T) { newSecret: "weak", identifyResponse: &magistrala.IdentityRes{UserId: client.ID}, retrieveByIDResponse: client, - err: users.ErrPasswordFormat, + err: errors.ErrPasswordFormat, }, { desc: "reset secret with failed to update secret",