MG-2456 - Refactor architecture (#2494)

Signed-off-by: Felix Gateru <felix.gateru@gmail.com>
Signed-off-by: Arvindh <arvindh91@gmail.com>
Signed-off-by: Dusan Borovcanin <borovcanindusan1@gmail.com>
Co-authored-by: Arvindh <30824765+arvindh123@users.noreply.github.com>
Co-authored-by: Felix Gateru <felix.gateru@gmail.com>
This commit is contained in:
Dušan Borovčanin
2024-12-02 12:42:38 +01:00
committed by Dusan Borovcanin
parent e29b54c54d
commit 243ccade0b
535 changed files with 65453 additions and 39551 deletions
+12 -12
View File
@@ -19,7 +19,7 @@ on:
- "journal/api/**"
- "provision/api/**"
- "readers/api/**"
- "things/api/**"
- "clients/api/**"
- "users/api/**"
env:
@@ -29,7 +29,7 @@ env:
USER_SECRET: 12345678
DOMAIN_NAME: demo-test
USERS_URL: http://localhost:9002
THINGS_URL: http://localhost:9000
CLIENTS_URL: http://localhost:9000
HTTP_ADAPTER_URL: http://localhost:8008
INVITATIONS_URL: http://localhost:9020
AUTH_URL: http://localhost:8189
@@ -65,8 +65,8 @@ jobs:
export DOMAIN_ID=$(curl -sSX POST $DOMAINS_URL -H "Content-Type: application/json" -H "Authorization: Bearer $USER_TOKEN" -d "{\"name\":\"$DOMAIN_NAME\",\"alias\":\"$DOMAIN_NAME\"}" | jq -r .id)
export USER_TOKEN=$(curl -sSX POST $TOKENS_URL -H "Content-Type: application/json" -d "{\"identity\": \"$USER_IDENTITY\",\"secret\": \"$USER_SECRET\",\"domain_id\": \"$DOMAIN_ID\"}" | jq -r .access_token)
echo "USER_TOKEN=$USER_TOKEN" >> $GITHUB_ENV
export THING_SECRET=$(magistrala-cli provision test | /usr/bin/grep -Eo '"secret": "[^"]+"' | awk 'NR % 2 == 0' | sed 's/"secret": "\(.*\)"/\1/')
echo "THING_SECRET=$THING_SECRET" >> $GITHUB_ENV
export CLIENT_SECRET=$(magistrala-cli provision test | /usr/bin/grep -Eo '"secret": "[^"]+"' | awk 'NR % 2 == 0' | sed 's/"secret": "\(.*\)"/\1/')
echo "CLIENT_SECRET=$CLIENT_SECRET" >> $GITHUB_ENV
- name: Check for changes in specific paths
uses: dorny/paths-filter@v3
@@ -113,10 +113,10 @@ jobs:
- "api/openapi/readers.yml"
- "readers/api/**"
things:
clients:
- ".github/workflows/api-tests.yml"
- "api/openapi/things.yml"
- "things/api/**"
- "api/openapi/clients.yml"
- "clients/api/**"
users:
- ".github/workflows/api-tests.yml"
@@ -133,12 +133,12 @@ jobs:
report: false
args: '--header "Authorization: Bearer ${{ env.USER_TOKEN }}" --contrib-openapi-formats-uuid --hypothesis-suppress-health-check=filter_too_much --stateful=links'
- name: Run Things API tests
if: steps.changes.outputs.things == 'true'
- name: Run Clients API tests
if: steps.changes.outputs.clients == 'true'
uses: schemathesis/action@v1
with:
schema: api/openapi/things.yml
base-url: ${{ env.THINGS_URL }}
schema: api/openapi/clients.yml
base-url: ${{ env.CLIENTS_URL }}
checks: all
report: false
args: '--header "Authorization: Bearer ${{ env.USER_TOKEN }}" --contrib-openapi-formats-uuid --hypothesis-suppress-health-check=filter_too_much --stateful=links'
@@ -151,7 +151,7 @@ jobs:
base-url: ${{ env.HTTP_ADAPTER_URL }}
checks: all
report: false
args: '--header "Authorization: Thing ${{ env.THING_SECRET }}" --contrib-openapi-formats-uuid --hypothesis-suppress-health-check=filter_too_much --stateful=links'
args: '--header "Authorization: Client ${{ env.CLIENT_SECRET }}" --contrib-openapi-formats-uuid --hypothesis-suppress-health-check=filter_too_much --stateful=links'
- name: Run Invitations API tests
if: steps.changes.outputs.invitations == 'true'
+97 -69
View File
@@ -49,8 +49,8 @@ jobs:
- "users/clients.go"
- "pkg/clients/clients.go"
- "pkg/messaging/pubsub.go"
- "things/postgres/clients.go"
- "things/things.go"
- "clients/postgres/clients.go"
- "clients/clients.go"
- "pkg/authz.go"
- "pkg/authn.go"
- "auth/domains.go"
@@ -79,9 +79,9 @@ jobs:
- name: Set up protoc
if: steps.changes.outputs.proto == 'true'
run: |
PROTOC_VERSION=27.1
PROTOC_GEN_VERSION=v1.34.2
PROTOC_GRPC_VERSION=v1.4.0
PROTOC_VERSION=28.3
PROTOC_GEN_VERSION=v1.35.2
PROTOC_GRPC_VERSION=v1.5.1
# Export the variables so they are available in future steps
echo "PROTOC_VERSION=$PROTOC_VERSION" >> $GITHUB_ENV
@@ -128,42 +128,56 @@ jobs:
MOCKERY_VERSION=v2.43.2
go install github.com/vektra/mockery/v2@$MOCKERY_VERSION
mv ./pkg/sdk/mocks/sdk.go ./pkg/sdk/mocks/sdk.go.tmp
mv ./users/mocks/repository.go ./users/mocks/repository.go.tmp
mv ./users/mocks/service.go ./users/mocks/service.go.tmp
mv ./pkg/messaging/mocks/pubsub.go ./pkg/messaging/mocks/pubsub.go.tmp
mv ./things/mocks/repository.go ./things/mocks/repository.go.tmp
mv ./things/mocks/service.go ./things/mocks/service.go.tmp
mv ./things/mocks/cache.go ./things/mocks/cache.go.tmp
mv ./invitations/mocks/repository.go ./invitations/mocks/repository.go.tmp
mv ./invitations/mocks/service.go ./invitations/mocks/service.go.tmp
mv ./auth/mocks/token_client.go ./auth/mocks/token_client.go.tmp
mv ./auth/mocks/authz.go ./auth/mocks/authz.go.tmp
mv ./auth/mocks/domains.go ./auth/mocks/domains.go.tmp
mv ./auth/mocks/keys.go ./auth/mocks/keys.go.tmp
mv ./auth/mocks/service.go ./auth/mocks/service.go.tmp
mv ./auth/mocks/token_client.go ./auth/mocks/token_client.go.tmp
mv ./pkg/events/mocks/publisher.go ./pkg/events/mocks/publisher.go.tmp
mv ./pkg/events/mocks/subscriber.go ./pkg/events/mocks/subscriber.go.tmp
mv ./provision/mocks/service.go ./provision/mocks/service.go.tmp
mv ./pkg/groups/mocks/repository.go ./pkg/groups/mocks/repository.go.tmp
mv ./pkg/groups/mocks/service.go ./pkg/groups/mocks/service.go.tmp
mv ./bootstrap/mocks/service.go ./bootstrap/mocks/service.go.tmp
mv ./bootstrap/mocks/configs.go ./bootstrap/mocks/configs.go.tmp
mv ./invitations/mocks/service.go ./invitations/mocks/service.go.tmp
mv ./invitations/mocks/repository.go ./invitations/mocks/repository.go.tmp
mv ./users/mocks/emailer.go ./users/mocks/emailer.go.tmp
mv ./bootstrap/mocks/config_reader.go ./bootstrap/mocks/config_reader.go.tmp
mv ./bootstrap/mocks/service.go ./bootstrap/mocks/service.go.tmp
mv ./domains/mocks/domains_client.go ./domains/mocks/domains_client.go.tmp
mv ./domains/mocks/repository.go ./domains/mocks/repository.go.tmp
mv ./domains/mocks/service.go ./domains/mocks/service.go.tmp
mv ./channels/mocks/repository.go ./channels/mocks/repository.go.tmp
mv ./channels/mocks/channels_client.go ./channels/mocks/channels_client.go.tmp
mv ./channels/mocks/service.go ./channels/mocks/service.go.tmp
mv ./groups/private/mocks/service.go ./groups/private/mocks/service.go.tmp
mv ./groups/mocks/repository.go ./groups/mocks/repository.go.tmp
mv ./groups/mocks/groups_client.go ./groups/mocks/groups_client.go.tmp
mv ./groups/mocks/service.go ./groups/mocks/service.go.tmp
mv ./users/mocks/hasher.go ./users/mocks/hasher.go.tmp
mv ./mqtt/mocks/events.go ./mqtt/mocks/events.go.tmp
mv ./readers/mocks/messages.go ./readers/mocks/messages.go.tmp
mv ./consumers/notifiers/mocks/notifier.go ./consumers/notifiers/mocks/notifier.go.tmp
mv ./consumers/notifiers/mocks/service.go ./consumers/notifiers/mocks/service.go.tmp
mv ./consumers/notifiers/mocks/repository.go ./consumers/notifiers/mocks/repository.go.tmp
mv ./certs/mocks/pki.go ./certs/mocks/pki.go.tmp
mv ./certs/mocks/service.go ./certs/mocks/service.go.tmp
mv ./users/mocks/emailer.go ./users/mocks/emailer.go.tmp
mv ./users/mocks/repository.go ./users/mocks/repository.go.tmp
mv ./users/mocks/service.go ./users/mocks/service.go.tmp
mv ./journal/mocks/repository.go ./journal/mocks/repository.go.tmp
mv ./journal/mocks/service.go ./journal/mocks/service.go.tmp
mv ./auth/mocks/domains_client.go ./auth/mocks/domains_client.go.tmp
mv ./things/mocks/things_client.go ./things/mocks/things_client.go.tmp
mv ./pkg/authz/mocks/authz.go ./pkg/authz/mocks/authz.go.tmp
mv ./consumers/notifiers/mocks/notifier.go ./consumers/notifiers/mocks/notifier.go.tmp
mv ./consumers/notifiers/mocks/repository.go ./consumers/notifiers/mocks/repository.go.tmp
mv ./consumers/notifiers/mocks/service.go ./consumers/notifiers/mocks/service.go.tmp
mv ./certs/mocks/pki.go ./certs/mocks/pki.go.tmp
mv ./certs/mocks/service.go ./certs/mocks/service.go.tmp
mv ./provision/mocks/service.go ./provision/mocks/service.go.tmp
mv ./clients/private/mocks/service.go ./clients/private/mocks/service.go.tmp
mv ./clients/mocks/repository.go ./clients/mocks/repository.go.tmp
mv ./clients/mocks/clients_client.go ./clients/mocks/clients_client.go.tmp
mv ./clients/mocks/cache.go ./clients/mocks/cache.go.tmp
mv ./clients/mocks/service.go ./clients/mocks/service.go.tmp
mv ./mqtt/mocks/events.go ./mqtt/mocks/events.go.tmp
mv ./readers/mocks/messages.go ./readers/mocks/messages.go.tmp
mv ./pkg/sdk/mocks/sdk.go ./pkg/sdk/mocks/sdk.go.tmp
mv ./pkg/messaging/mocks/pubsub.go ./pkg/messaging/mocks/pubsub.go.tmp
mv ./pkg/authn/mocks/authn.go ./pkg/authn/mocks/authn.go.tmp
mv ./pkg/roles/mocks/rolesRepo.go ./pkg/roles/mocks/rolesRepo.go.tmp
mv ./pkg/roles/mocks/provisioner.go ./pkg/roles/mocks/provisioner.go.tmp
mv ./pkg/roles/mocks/rolemanager.go ./pkg/roles/mocks/rolemanager.go.tmp
mv ./pkg/oauth2/mocks/provider.go ./pkg/oauth2/mocks/provider.go.tmp
mv ./pkg/authz/mocks/authz.go ./pkg/authz/mocks/authz.go.tmp
mv ./pkg/events/mocks/subscriber.go ./pkg/events/mocks/subscriber.go.tmp
mv ./pkg/events/mocks/publisher.go ./pkg/events/mocks/publisher.go.tmp
mv ./pkg/policies/mocks/evaluator.go ./pkg/policies/mocks/evaluator.go.tmp
mv ./pkg/policies/mocks/service.go ./pkg/policies/mocks/service.go.tmp
make mocks
@@ -179,39 +193,53 @@ jobs:
fi
}
check_mock_changes ./pkg/sdk/mocks/sdk.go "SDK ./pkg/sdk/mocks/sdk.go"
check_mock_changes ./users/mocks/repository.go "Users Repository ./users/mocks/repository.go"
check_mock_changes ./users/mocks/service.go "Users Service ./users/mocks/service.go"
check_mock_changes ./pkg/messaging/mocks/pubsub.go "PubSub ./pkg/messaging/mocks/pubsub.go"
check_mock_changes ./things/mocks/repository.go "Things Repository ./things/mocks/repository.go"
check_mock_changes ./things/mocks/service.go "Things Service ./things/mocks/service.go"
check_mock_changes ./things/mocks/cache.go "Things Cache ./things/mocks/cache.go"
check_mock_changes ./auth/mocks/authz.go "Auth Authz ./auth/mocks/authz.go"
check_mock_changes ./auth/mocks/domains.go "Auth Domains ./auth/mocks/domains.go"
check_mock_changes ./auth/mocks/keys.go "Auth Keys ./auth/mocks/keys.go"
check_mock_changes ./auth/mocks/service.go "Auth Service ./auth/mocks/service.go"
check_mock_changes ./pkg/authn/mocks/authn.go "Authn Service Client .pkg/authn/mocks/authn.go"
check_mock_changes ./pkg/authz/mocks/authz.go "Authz Service Client .pkg/authz/mocks/authz.go"
check_mock_changes ./pkg/events/mocks/publisher.go "ES Publisher ./pkg/events/mocks/publisher.go"
check_mock_changes ./pkg/events/mocks/subscriber.go "EE Subscriber ./pkg/events/mocks/subscriber.go"
check_mock_changes ./provision/mocks/service.go "Provision Service ./provision/mocks/service.go"
check_mock_changes ./pkg/groups/mocks/repository.go "Groups Repository ./pkg/groups/mocks/repository.go"
check_mock_changes ./pkg/groups/mocks/service.go "Groups Service ./pkg/groups/mocks/service.go"
check_mock_changes ./bootstrap/mocks/service.go "Bootstrap Service ./bootstrap/mocks/service.go"
check_mock_changes ./bootstrap/mocks/configs.go "Bootstrap Repository ./bootstrap/mocks/configs.go"
check_mock_changes ./invitations/mocks/service.go "Invitations Service ./invitations/mocks/service.go"
check_mock_changes ./invitations/mocks/repository.go "Invitations Repository ./invitations/mocks/repository.go"
check_mock_changes ./users/mocks/emailer.go "Users Emailer ./users/mocks/emailer.go"
check_mock_changes ./users/mocks/hasher.go "Users Hasher ./users/mocks/hasher.go"
check_mock_changes ./mqtt/mocks/events.go "MQTT Events Store ./mqtt/mocks/events.go"
check_mock_changes ./readers/mocks/messages.go "Message Readers ./readers/mocks/messages.go"
check_mock_changes ./consumers/notifiers/mocks/notifier.go "Notifiers Notifier ./consumers/notifiers/mocks/notifier.go"
check_mock_changes ./consumers/notifiers/mocks/service.go "Notifiers Service ./consumers/notifiers/mocks/service.go"
check_mock_changes ./consumers/notifiers/mocks/repository.go "Notifiers Repository ./consumers/notifiers/mocks/repository.go"
check_mock_changes ./certs/mocks/pki.go "PKI ./certs/mocks/pki.go"
check_mock_changes ./certs/mocks/service.go "Certs Service ./certs/mocks/service.go"
check_mock_changes ./journal/mocks/repository.go "Journal Repository ./journal/mocks/repository.go"
check_mock_changes ./journal/mocks/service.go "Journal Service ./journal/mocks/service.go"
check_mock_changes ./auth/mocks/domains_client.go "Domains Service Client ./auth/mocks/domains_client.go"
check_mock_changes ./auth/mocks/token_client.go "Token Service Client ./auth/mocks/token_client.go"
check_mock_changes ./things/mocks/things_client.go "Things Service Client things/mocks/things_client.go"
check_mock_changes ./invitations/mocks/repository.go " ./invitations/mocks/repository.go"
check_mock_changes ./invitations/mocks/service.go " ./invitations/mocks/service.go"
check_mock_changes ./auth/mocks/token_client.go " ./auth/mocks/token_client.go"
check_mock_changes ./auth/mocks/authz.go " ./auth/mocks/authz.go"
check_mock_changes ./auth/mocks/keys.go " ./auth/mocks/keys.go"
check_mock_changes ./auth/mocks/service.go " ./auth/mocks/service.go"
check_mock_changes ./bootstrap/mocks/configs.go " ./bootstrap/mocks/configs.go"
check_mock_changes ./bootstrap/mocks/config_reader.go " ./bootstrap/mocks/config_reader.go"
check_mock_changes ./bootstrap/mocks/service.go " ./bootstrap/mocks/service.go"
check_mock_changes ./domains/mocks/domains_client.go " ./domains/mocks/domains_client.go"
check_mock_changes ./domains/mocks/repository.go " ./domains/mocks/repository.go"
check_mock_changes ./domains/mocks/service.go " ./domains/mocks/service.go"
check_mock_changes ./channels/mocks/repository.go " ./channels/mocks/repository.go"
check_mock_changes ./channels/mocks/channels_client.go " ./channels/mocks/channels_client.go"
check_mock_changes ./channels/mocks/service.go " ./channels/mocks/service.go"
check_mock_changes ./groups/private/mocks/service.go " ./groups/private/mocks/service.go"
check_mock_changes ./groups/mocks/repository.go " ./groups/mocks/repository.go"
check_mock_changes ./groups/mocks/groups_client.go " ./groups/mocks/groups_client.go"
check_mock_changes ./groups/mocks/service.go " ./groups/mocks/service.go"
check_mock_changes ./users/mocks/hasher.go " ./users/mocks/hasher.go"
check_mock_changes ./users/mocks/emailer.go " ./users/mocks/emailer.go"
check_mock_changes ./users/mocks/repository.go " ./users/mocks/repository.go"
check_mock_changes ./users/mocks/service.go " ./users/mocks/service.go"
check_mock_changes ./journal/mocks/repository.go " ./journal/mocks/repository.go"
check_mock_changes ./journal/mocks/service.go " ./journal/mocks/service.go"
check_mock_changes ./consumers/notifiers/mocks/notifier.go " ./consumers/notifiers/mocks/notifier.go"
check_mock_changes ./consumers/notifiers/mocks/repository.go " ./consumers/notifiers/mocks/repository.go"
check_mock_changes ./consumers/notifiers/mocks/service.go " ./consumers/notifiers/mocks/service.go"
check_mock_changes ./certs/mocks/pki.go " ./certs/mocks/pki.go"
check_mock_changes ./certs/mocks/service.go " ./certs/mocks/service.go"
check_mock_changes ./provision/mocks/service.go " ./provision/mocks/service.go"
check_mock_changes ./clients/private/mocks/service.go " ./clients/private/mocks/service.go"
check_mock_changes ./clients/mocks/repository.go " ./clients/mocks/repository.go"
check_mock_changes ./clients/mocks/clients_client.go " ./clients/mocks/clients_client.go"
check_mock_changes ./clients/mocks/cache.go " ./clients/mocks/cache.go"
check_mock_changes ./clients/mocks/service.go " ./clients/mocks/service.go"
check_mock_changes ./mqtt/mocks/events.go " ./mqtt/mocks/events.go"
check_mock_changes ./readers/mocks/messages.go " ./readers/mocks/messages.go"
check_mock_changes ./pkg/sdk/mocks/sdk.go " ./pkg/sdk/mocks/sdk.go"
check_mock_changes ./pkg/messaging/mocks/pubsub.go " ./pkg/messaging/mocks/pubsub.go"
check_mock_changes ./pkg/authn/mocks/authn.go " ./pkg/authn/mocks/authn.go"
check_mock_changes ./pkg/roles/mocks/rolesRepo.go " ./pkg/roles/mocks/rolesRepo.go"
check_mock_changes ./pkg/roles/mocks/provisioner.go " ./pkg/roles/mocks/provisioner.go"
check_mock_changes ./pkg/roles/mocks/rolemanager.go " ./pkg/roles/mocks/rolemanager.go"
check_mock_changes ./pkg/oauth2/mocks/provider.go " ./pkg/oauth2/mocks/provider.go"
check_mock_changes ./pkg/authz/mocks/authz.go " ./pkg/authz/mocks/authz.go"
check_mock_changes ./pkg/events/mocks/subscriber.go " ./pkg/events/mocks/subscriber.go"
check_mock_changes ./pkg/events/mocks/publisher.go " ./pkg/events/mocks/publisher.go"
check_mock_changes ./pkg/policies/mocks/evaluator.go " ./pkg/policies/mocks/evaluator.go"
check_mock_changes ./pkg/policies/mocks/service.go " ./pkg/policies/mocks/service.go"
+12 -12
View File
@@ -111,7 +111,7 @@ jobs:
- "cmd/coap/**"
- "auth.pb.go"
- "auth_grpc.pb.go"
- "things/**"
- "clients/**"
- "pkg/messaging/**"
consumers:
@@ -140,7 +140,7 @@ jobs:
- "cmd/http/**"
- "auth.pb.go"
- "auth_grpc.pb.go"
- "things/**"
- "clients/**"
- "pkg/messaging/**"
- "logger/**"
@@ -163,7 +163,7 @@ jobs:
- "cmd/mqtt/**"
- "auth.pb.go"
- "auth_grpc.pb.go"
- "things/**"
- "clients/**"
- "pkg/messaging/**"
- "logger/**"
- "pkg/events/**"
@@ -197,7 +197,7 @@ jobs:
- "invitations/**"
- "provision/**"
- "readers/**"
- "things/**"
- "clients/**"
- "users/**"
pkg-transformers:
@@ -221,12 +221,12 @@ jobs:
- "cmd/timescale-reader/**"
- "auth.pb.go"
- "auth_grpc.pb.go"
- "things/**"
- "clients/**"
- "auth/**"
things:
- "things/**"
- "cmd/things/**"
clients:
- "clients/**"
- "cmd/clients/**"
- "auth.pb.go"
- "auth_grpc.pb.go"
- "auth/**"
@@ -249,7 +249,7 @@ jobs:
- "cmd/ws/**"
- "auth.pb.go"
- "auth_grpc.pb.go"
- "things/**"
- "clients/**"
- "pkg/messaging/**"
- name: Create coverage directory
@@ -366,10 +366,10 @@ jobs:
run: |
go test --race -v -count=1 -coverprofile=coverage/readers.out ./readers/...
- name: Run things tests
if: steps.changes.outputs.things == 'true' || steps.changes.outputs.workflow == 'true'
- name: Run clients tests
if: steps.changes.outputs.clients == 'true' || steps.changes.outputs.workflow == 'true'
run: |
go test --race -v -count=1 -coverprofile=coverage/things.out ./things/...
go test --race -v -count=1 -coverprofile=coverage/clients.out ./clients/...
- name: Run users tests
if: steps.changes.outputs.users == 'true' || steps.changes.outputs.workflow == 'true'
+23 -15
View File
@@ -1,11 +1,11 @@
# Copyright (c) Abstract Machines
# SPDX-License-Identifier: Apache-2.0
MG_DOCKER_IMAGE_NAME_PREFIX ?= magistrala
BUILD_DIR = build
SERVICES = auth users things http coap ws postgres-writer postgres-reader timescale-writer \
MG_DOCKER_IMAGE_NAME_PREFIX ?= supermq
BUILD_DIR ?= build
SERVICES = auth users clients groups channels domains http coap ws postgres-writer postgres-reader timescale-writer \
timescale-reader cli bootstrap mqtt provision certs invitations journal
TEST_API_SERVICES = journal auth bootstrap certs http invitations notifiers provision readers things users
TEST_API_SERVICES = journal auth bootstrap certs http invitations notifiers provision readers clients users
TEST_API = $(addprefix test_api_,$(TEST_API_SERVICES))
DOCKERS = $(addprefix docker_,$(SERVICES))
DOCKERS_DEV = $(addprefix docker_dev_,$(SERVICES))
@@ -19,10 +19,14 @@ empty:=
space:= $(empty) $(empty)
# Docker compose project name should follow this guidelines: https://docs.docker.com/compose/reference/#use--p-to-specify-a-project-name
DOCKER_PROJECT ?= $(shell echo $(subst $(space),,$(USER_REPO)) | tr -c -s '[:alnum:][=-=]' '_' | tr '[:upper:]' '[:lower:]')
DOCKER_COMPOSE_COMMANDS_SUPPORTED := up down config
DOCKER_COMPOSE_COMMANDS_SUPPORTED := up down config restart
DEFAULT_DOCKER_COMPOSE_COMMAND := up
GRPC_MTLS_CERT_FILES_EXISTS = 0
MOCKERY_VERSION=v2.43.2
INTERNAL_PROTO_GEN_OUT_DIR=internal/grpc
INTERNAL_PROTO_DIR=internal/proto
INTERNAL_PROTO_FILES := $(shell find $(INTERNAL_PROTO_DIR) -name "*.proto" | sed 's|$(INTERNAL_PROTO_DIR)/||')
ifneq ($(MG_MESSAGE_BROKER_TYPE),)
MG_MESSAGE_BROKER_TYPE := $(MG_MESSAGE_BROKER_TYPE)
else
@@ -38,9 +42,9 @@ endif
define compile_service
CGO_ENABLED=$(CGO_ENABLED) GOOS=$(GOOS) GOARCH=$(GOARCH) GOARM=$(GOARM) \
go build -tags $(MG_MESSAGE_BROKER_TYPE) --tags $(MG_ES_TYPE) -ldflags "-s -w \
-X 'github.com/absmach/magistrala.BuildTime=$(TIME)' \
-X 'github.com/absmach/magistrala.Version=$(VERSION)' \
-X 'github.com/absmach/magistrala.Commit=$(COMMIT)'" \
-X 'github.com/absmach/supermq.BuildTime=$(TIME)' \
-X 'github.com/absmach/supermq.Version=$(VERSION)' \
-X 'github.com/absmach/supermq.Commit=$(COMMIT)'" \
-o ${BUILD_DIR}/$(1) cmd/$(1)/main.go
endef
@@ -138,8 +142,8 @@ define test_api_service
exit 1; \
fi
@if [ "$(svc)" = "http" ] && [ -z "$(THING_SECRET)" ]; then \
echo "THING_SECRET is not set"; \
@if [ "$(svc)" = "http" ] && [ -z "$(CLIENT_SECRET)" ]; then \
echo "CLIENT_SECRET is not set"; \
echo "Please set it to a valid secret"; \
exit 1; \
fi
@@ -148,7 +152,7 @@ define test_api_service
st run api/openapi/$(svc).yml \
--checks all \
--base-url $(2) \
--header "Authorization: Thing $(THING_SECRET)" \
--header "Authorization: Client $(CLIENT_SECRET)" \
--contrib-openapi-formats-uuid \
--hypothesis-suppress-health-check=filter_too_much \
--stateful=links; \
@@ -164,7 +168,7 @@ define test_api_service
endef
test_api_users: TEST_API_URL := http://localhost:9002
test_api_things: TEST_API_URL := http://localhost:9000
test_api_clients: TEST_API_URL := http://localhost:9000
test_api_http: TEST_API_URL := http://localhost:8008
test_api_invitations: TEST_API_URL := http://localhost:9020
test_api_auth: TEST_API_URL := http://localhost:8189
@@ -179,7 +183,8 @@ $(TEST_API):
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
mkdir -p $(INTERNAL_PROTO_GEN_OUT_DIR)
protoc -I $(INTERNAL_PROTO_DIR) --go_out=$(INTERNAL_PROTO_GEN_OUT_DIR) --go_opt=paths=source_relative --go-grpc_out=$(INTERNAL_PROTO_GEN_OUT_DIR) --go-grpc_opt=paths=source_relative $(INTERNAL_PROTO_FILES)
$(FILTERED_SERVICES):
$(call compile_service,$(@))
@@ -218,7 +223,7 @@ rundev:
cd scripts && ./run.sh
grpc_mtls_certs:
$(MAKE) -C docker/ssl auth_grpc_certs things_grpc_certs
$(MAKE) -C docker/ssl auth_grpc_certs clients_grpc_certs
check_tls:
ifeq ($(GRPC_TLS),true)
@@ -244,7 +249,7 @@ check_certs: check_mtls check_tls
ifeq ($(GRPC_MTLS_CERT_FILES_EXISTS),0)
ifeq ($(filter true,$(GRPC_MTLS) $(GRPC_TLS)),true)
ifeq ($(filter $(DEFAULT_DOCKER_COMPOSE_COMMAND),$(DOCKER_COMPOSE_COMMAND)),$(DEFAULT_DOCKER_COMPOSE_COMMAND))
$(MAKE) -C docker/ssl auth_grpc_certs things_grpc_certs
$(MAKE) -C docker/ssl auth_grpc_certs clients_grpc_certs
endif
endif
endif
@@ -257,3 +262,6 @@ run_addons: check_certs
@for SVC in $(RUN_ADDON_ARGS); do \
MG_ADDONS_CERTS_PATH_PREFIX="../." docker compose -f docker/addons/$$SVC/docker-compose.yml -p $(DOCKER_PROJECT) --env-file ./docker/.env $(DOCKER_COMPOSE_COMMAND) $(args) & \
done
run_live: check_certs
GOPATH=$(go env GOPATH) docker compose -f docker/docker-compose.yml -f docker/docker-compose-live.yaml --env-file docker/.env -p $(DOCKER_PROJECT) $(DOCKER_COMPOSE_COMMAND) $(args)
+3 -1
View File
@@ -137,7 +137,8 @@ You like SuperMQ and you would like to make it your day job? We're always lookin
[Apache-2.0](LICENSE)
[![FOSSA Status](https://app.fossa.com/api/projects/git%2Bgithub.com%2Fabsmach%2Fmagistrala.svg?type=large&issueType=license)](https://app.fossa.com/projects/git%2Bgithub.com%2Fabsmach%2Fmagistrala?ref=badge_large&issueType=license)
[![FOSSA Status][FOSSA]][FOSSA]
## Data Collection for SuperMQ
SuperMQ is committed to continuously improving its services and ensuring a seamless experience for its users. To achieve this, we collect certain data from your deployments. Rest assured, this data is collected solely for the purpose of enhancing SuperMQ and is not used with any malicious intent. The deployment summary can be found on our [website][callhome].
@@ -189,3 +190,4 @@ By utilizing SuperMQ, you actively contribute to its improvement. Together, we c
[rodneyosodo]: https://github.com/rodneyosodo
[callhome]: https://deployments.magistrala.abstractmachines.fr/
[contrib]: https://www.github.com/absmach/mg-contrib
[FOSSA]: https://app.fossa.com/api/projects/git%2Bgithub.com%2Fabsmach%2Fsupermq.svg?type=small
+8 -8
View File
@@ -17,8 +17,8 @@ info:
license:
name: Apache 2.0
url: 'https://github.com/absmach/magistrala/blob/main/LICENSE'
defaultContentType: application/json
servers:
@@ -33,7 +33,7 @@ servers:
enum:
- '1883'
- '8883'
security:
security:
- user-password: []
channels:
@@ -45,7 +45,7 @@ channels:
required: true
subtopic:
$ref: '#/components/parameters/subtopic'
in: path
in: path
required: false
publish:
@@ -88,7 +88,7 @@ components:
parameters:
channelID:
description: Channel ID connected to the Thing ID defined in the username.
description: Channel ID connected to the Client ID defined in the username.
schema:
type: string
format: uuid
@@ -97,13 +97,13 @@ components:
schema:
type: string
default: ''
securitySchemes:
user-password:
type: userPassword
description: |
username is thing ID connected to the channel defined in the mqtt topic and
password is thing key corresponding to the thing ID
username is client ID connected to the channel defined in the mqtt topic and
password is client secret corresponding to the client ID
operationTraits:
mqtt:
+2 -2
View File
@@ -126,7 +126,7 @@ components:
```
parameters:
channelID:
description: Channel ID connected to the Thing ID defined in the username.
description: Channel ID connected to the Client ID defined in the username.
schema:
type: string
format: uuid
@@ -141,4 +141,4 @@ components:
scheme: bearer
bearerFormat: uuid
description: |
* Thing access: "Authorization: Thing <thing_key>"
* Client access: "Authorization: Client <client_key>"
+2 -2
View File
@@ -31,7 +31,7 @@ tags:
externalDocs:
description: Find out more about domains
url: https://docs.magistrala.abstractmachines.fr/
- name: Health
description: Service health check endpoint.
externalDocs:
@@ -549,7 +549,7 @@ components:
metadata:
type: object
example: { "domain": "example.com" }
description: Arbitrary, object-encoded thing's data.
description: Arbitrary, object-encoded client's data.
alias:
type: string
example: domain alias
+38 -38
View File
@@ -5,7 +5,7 @@ openapi: 3.0.1
info:
title: Magistrala Bootstrap service
description: |
HTTP API for managing platform things configuration.
HTTP API for managing platform clients configuration.
Some useful links:
- [The Magistrala repository](https://github.com/absmach/magistrala)
contact:
@@ -27,7 +27,7 @@ tags:
url: https://docs.magistrala.abstractmachines.fr/
paths:
/{domainID}/things/configs:
/{domainID}/clients/configs:
post:
operationId: createConfig
summary: Adds new config
@@ -60,7 +60,7 @@ paths:
"500":
$ref: "#/components/responses/ServiceError"
"503":
description: Failed to receive response from the things service.
description: Failed to receive response from the clients service.
get:
operationId: getConfigs
summary: Retrieves managed configs
@@ -88,7 +88,7 @@ paths:
description: Database can't process request.
"500":
$ref: "#/components/responses/ServiceError"
/{domainID}/things/configs/{configId}:
/{domainID}/clients/configs/{configId}:
get:
operationId: getConfig
summary: Retrieves config info (with channels).
@@ -118,7 +118,7 @@ paths:
description: |
Update is performed by replacing the current resource data with values
provided in a request payload. Note that the owner, ID, external ID,
external key, Magistrala Thing ID and key cannot be changed.
external key, Magistrala Client ID and key cannot be changed.
tags:
- configs
parameters:
@@ -167,7 +167,7 @@ paths:
description: Database can't process request.
"500":
$ref: "#/components/responses/ServiceError"
/{domainID}/things/configs/certs/{configId}:
/{domainID}/clients/configs/certs/{configId}:
patch:
operationId: updateConfigCerts
summary: Updates certs
@@ -199,13 +199,13 @@ paths:
description: Database can't process request.
"500":
$ref: "#/components/responses/ServiceError"
/{domainID}/things/configs/connections/{configId}:
/{domainID}/clients/configs/connections/{configId}:
put:
operationId: updateConfigConnections
summary: Updates channels the thing is connected to
summary: Updates channels the client is connected to
description: |
Update connections performs update of the channel list corresponding
Thing is connected to.
Client is connected to.
tags:
- configs
parameters:
@@ -230,7 +230,7 @@ paths:
description: Database can't process request.
"500":
$ref: "#/components/responses/ServiceError"
/things/bootstrap/{externalId}:
/clients/bootstrap/{externalId}:
get:
operationId: getBootstrapConfig
summary: Retrieves configuration.
@@ -255,7 +255,7 @@ paths:
description: Database can't process request.
"500":
$ref: "#/components/responses/ServiceError"
/things/bootstrap/secure/{externalId}:
/clients/bootstrap/secure/{externalId}:
get:
operationId: getSecureBootstrapConfig
summary: Retrieves configuration.
@@ -281,13 +281,13 @@ paths:
description: Database can't process request.
"500":
$ref: "#/components/responses/ServiceError"
/{domainID}/things/state/{configId}:
/{domainID}/clients/state/{configId}:
put:
operationId: updateConfigState
summary: Updates Config state.
description: |
Updating state represents enabling/disabling Config, i.e. connecting
and disconnecting corresponding Magistrala Thing to the list of Channels.
and disconnecting corresponding Magistrala Client to the list of Channels.
tags:
- configs
parameters:
@@ -330,14 +330,14 @@ components:
Config:
type: object
properties:
thing_id:
client_id:
type: string
format: uuid
description: Corresponding Magistrala Thing ID.
magistrala_key:
description: Corresponding Magistrala Client ID.
magistrala_secret:
type: string
format: uuid
description: Corresponding Magistrala Thing key.
description: Corresponding Magistrala Client key.
channels:
type: array
minItems: 0
@@ -402,14 +402,14 @@ components:
BootstrapConfig:
type: object
properties:
thing_id:
client_id:
type: string
format: uuid
description: Corresponding Magistrala Thing ID.
thing_key:
description: Corresponding Magistrala Client ID.
client_key:
type: string
format: uuid
description: Corresponding Magistrala Thing key.
description: Corresponding Magistrala Client key.
channels:
type: array
minItems: 0
@@ -428,17 +428,17 @@ components:
type: string
description: Issuing CA certificate.
required:
- thing_id
- thing_key
- client_id
- client_key
- channels
- content
ConfigUpdateCerts:
type: object
properties:
thing_id:
client_id:
type: string
format: uuid
description: Corresponding Magistrala Thing ID.
description: Corresponding Magistrala Client ID.
client_cert:
type: string
description: Client certificate.
@@ -449,15 +449,15 @@ components:
type: string
description: Issuing CA certificate.
required:
- thing_id
- thing_key
- client_id
- client_key
- channels
- content
parameters:
ConfigId:
name: configId
description: Unique Config identifier. It's the ID of the corresponding Thing.
description: Unique Config identifier. It's the ID of the corresponding Client.
in: path
schema:
type: string
@@ -519,10 +519,10 @@ components:
external_key:
type: string
description: External key.
thing_id:
client_id:
type: string
format: uuid
description: ID of the corresponding Magistrala Thing.
description: ID of the corresponding Magistrala Client.
channels:
type: array
minItems: 0
@@ -535,17 +535,17 @@ components:
type: string
client_cert:
type: string
description: Thing Certificate.
description: Client Certificate.
client_key:
type: string
description: Thing Private Key.
description: Client Private Key.
ca_cert:
type: string
required:
- external_id
- external_key
ConfigUpdateReq:
description: JSON-formatted document describing the updated thing.
description: JSON-formatted document describing the updated client.
content:
application/json:
schema:
@@ -559,7 +559,7 @@ components:
- content
- name
ConfigCertUpdateReq:
description: JSON-formatted document describing the updated thing.
description: JSON-formatted document describing the updated client.
content:
application/json:
schema:
@@ -572,7 +572,7 @@ components:
ca_cert:
type: string
ConfigConnUpdateReq:
description: Array if IDs the thing is be connected to.
description: Array if IDs the client is be connected to.
content:
application/json:
schema:
@@ -603,7 +603,7 @@ components:
text/plain:
schema:
type: string
description: Created configuration's relative URL (i.e. /things/configs/{configId}).
description: Created configuration's relative URL (i.e. /clients/configs/{configId}).
ConfigListRes:
description: Data retrieved. Configs from this list don't contain channels.
content:
@@ -673,14 +673,14 @@ components:
scheme: bearer
bearerFormat: string
description: |
* Things access: "Authorization: Thing <external_key>"
* Clients access: "Authorization: Client <external_key>"
bootstrapEncAuth:
type: http
scheme: bearer
bearerFormat: aes-sha256-uuid
description: |
* Things access: "Authorization: Thing <external_enc_key>"
* Clients access: "Authorization: Client <external_enc_key>"
Hex-encoded configuration external key encrypted using
the AES algorithm and SHA256 sum of the external key
itself as an encryption key.
+15 -15
View File
@@ -30,8 +30,8 @@ paths:
/{domainID}/certs:
post:
operationId: createCert
summary: Creates a certificate for thing
description: Creates a certificate for thing
summary: Creates a certificate for client
description: Creates a certificate for client
tags:
- certs
parameters:
@@ -106,17 +106,17 @@ paths:
description: Database can't process request.
"500":
$ref: "#/components/responses/ServiceError"
/{domainID}/serials/{thingID}:
/{domainID}/serials/{clientID}:
get:
operationId: getSerials
summary: Retrieves certificates' serial IDs
description: |
Retrieves a list of certificates' serial IDs for a given thing ID.
Retrieves a list of certificates' serial IDs for a given client ID.
tags:
- certs
parameters:
- $ref: "auth.yml#/components/parameters/DomainID"
- $ref: "#/components/parameters/ThingID"
- $ref: "#/components/parameters/ClientID"
responses:
"200":
$ref: "#/components/responses/SerialsPageRes"
@@ -147,9 +147,9 @@ paths:
components:
parameters:
ThingID:
name: thingID
description: Thing ID
ClientID:
name: clientID
description: Client ID
in: path
schema:
type: string
@@ -168,10 +168,10 @@ components:
Cert:
type: object
properties:
thing_id:
client_id:
type: string
format: uuid
description: Corresponding Magistrala Thing ID.
description: Corresponding Magistrala Client ID.
client_cert:
type: string
description: Client Certificate.
@@ -240,18 +240,18 @@ components:
requestBodies:
CertReq:
description: |
Issues a certificate that is required for mTLS. To create a certificate for a thing
provide a thing id, data identifying particular thing will be embedded into the Certificate.
Issues a certificate that is required for mTLS. To create a certificate for a client
provide a client id, data identifying particular client will be embedded into the Certificate.
x509 and ECC certificates are supported when using when Vault is used as PKI.
content:
application/json:
schema:
type: object
required:
- thing_id
- client_id
- ttl
properties:
thing_id:
client_id:
type: string
format: uuid
ttl:
@@ -271,7 +271,7 @@ components:
serial:
operationId: getSerials
parameters:
thingID: $response.body#/thing_id
clientID: $response.body#/client_id
delete:
operationId: revokeCert
parameters:
File diff suppressed because it is too large Load Diff
+2 -2
View File
@@ -169,13 +169,13 @@ components:
scheme: bearer
bearerFormat: uuid
description: |
* Thing access: "Authorization: Thing <thing_key>"
* Client access: "Authorization: Client <client_key>"
basicAuth:
type: http
scheme: basic
description: |
* Things access: "Authorization: Basic <base64-encoded_credentials>"
* Clients access: "Authorization: Basic <base64-encoded_credentials>"
security:
- bearerAuth: []
+2 -2
View File
@@ -197,13 +197,13 @@ components:
entity_type:
name: entityType
description: Type of entity, e.g. user, group, thing, etc.entityType
description: Type of entity, e.g. user, group, client, etc.entityType
in: path
schema:
type: string
enum:
- group
- thing
- client
- channel
required: true
example: group
+4 -4
View File
@@ -179,7 +179,7 @@ components:
required: false
Publisher:
name: Publisher
description: Unique thing identifier.
description: Unique client identifier.
in: query
schema:
type: string
@@ -302,13 +302,13 @@ components:
description: |
* Users access: "Authorization: Bearer <user_token>"
thingAuth:
clientAuth:
type: http
scheme: bearer
bearerFormat: uuid
description: |
* Things access: "Authorization: Thing <thing_key>"
* Clients access: "Authorization: Client <client_key>"
security:
- bearerAuth: []
- thingAuth: []
- clientAuth: []
-993
View File
@@ -1,993 +0,0 @@
// Copyright (c) Abstract Machines
// SPDX-License-Identifier: Apache-2.0
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.34.2
// protoc v5.27.1
// source: auth.proto
package magistrala
import (
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
reflect "reflect"
sync "sync"
)
const (
// Verify that this generated code is sufficiently up-to-date.
_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
// Verify that runtime/protoimpl is sufficiently up-to-date.
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
)
// If a token is not carrying any information itself, the type
// field can be used to determine how to validate the token.
// Also, different tokens can be encoded in different ways.
type Token struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
AccessToken string `protobuf:"bytes,1,opt,name=access_token,json=accessToken,proto3" json:"access_token,omitempty"`
RefreshToken *string `protobuf:"bytes,2,opt,name=refresh_token,json=refreshToken,proto3,oneof" json:"refresh_token,omitempty"`
AccessType string `protobuf:"bytes,3,opt,name=access_type,json=accessType,proto3" json:"access_type,omitempty"`
}
func (x *Token) Reset() {
*x = Token{}
if protoimpl.UnsafeEnabled {
mi := &file_auth_proto_msgTypes[0]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *Token) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*Token) ProtoMessage() {}
func (x *Token) ProtoReflect() protoreflect.Message {
mi := &file_auth_proto_msgTypes[0]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use Token.ProtoReflect.Descriptor instead.
func (*Token) Descriptor() ([]byte, []int) {
return file_auth_proto_rawDescGZIP(), []int{0}
}
func (x *Token) GetAccessToken() string {
if x != nil {
return x.AccessToken
}
return ""
}
func (x *Token) GetRefreshToken() string {
if x != nil && x.RefreshToken != nil {
return *x.RefreshToken
}
return ""
}
func (x *Token) GetAccessType() string {
if x != nil {
return x.AccessType
}
return ""
}
type AuthNReq struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Token string `protobuf:"bytes,1,opt,name=token,proto3" json:"token,omitempty"`
}
func (x *AuthNReq) Reset() {
*x = AuthNReq{}
if protoimpl.UnsafeEnabled {
mi := &file_auth_proto_msgTypes[1]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *AuthNReq) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*AuthNReq) ProtoMessage() {}
func (x *AuthNReq) ProtoReflect() protoreflect.Message {
mi := &file_auth_proto_msgTypes[1]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use AuthNReq.ProtoReflect.Descriptor instead.
func (*AuthNReq) Descriptor() ([]byte, []int) {
return file_auth_proto_rawDescGZIP(), []int{1}
}
func (x *AuthNReq) GetToken() string {
if x != nil {
return x.Token
}
return ""
}
type AuthNRes struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` // change "id" to "subject", sub in jwt = user + domain id
UserId string `protobuf:"bytes,2,opt,name=user_id,json=userId,proto3" json:"user_id,omitempty"` // user id
DomainId string `protobuf:"bytes,3,opt,name=domain_id,json=domainId,proto3" json:"domain_id,omitempty"` // domain id
}
func (x *AuthNRes) Reset() {
*x = AuthNRes{}
if protoimpl.UnsafeEnabled {
mi := &file_auth_proto_msgTypes[2]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *AuthNRes) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*AuthNRes) ProtoMessage() {}
func (x *AuthNRes) ProtoReflect() protoreflect.Message {
mi := &file_auth_proto_msgTypes[2]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use AuthNRes.ProtoReflect.Descriptor instead.
func (*AuthNRes) Descriptor() ([]byte, []int) {
return file_auth_proto_rawDescGZIP(), []int{2}
}
func (x *AuthNRes) GetId() string {
if x != nil {
return x.Id
}
return ""
}
func (x *AuthNRes) GetUserId() string {
if x != nil {
return x.UserId
}
return ""
}
func (x *AuthNRes) GetDomainId() string {
if x != nil {
return x.DomainId
}
return ""
}
type IssueReq struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
UserId string `protobuf:"bytes,1,opt,name=user_id,json=userId,proto3" json:"user_id,omitempty"`
Type uint32 `protobuf:"varint,2,opt,name=type,proto3" json:"type,omitempty"`
}
func (x *IssueReq) Reset() {
*x = IssueReq{}
if protoimpl.UnsafeEnabled {
mi := &file_auth_proto_msgTypes[3]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *IssueReq) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*IssueReq) ProtoMessage() {}
func (x *IssueReq) ProtoReflect() protoreflect.Message {
mi := &file_auth_proto_msgTypes[3]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use IssueReq.ProtoReflect.Descriptor instead.
func (*IssueReq) Descriptor() ([]byte, []int) {
return file_auth_proto_rawDescGZIP(), []int{3}
}
func (x *IssueReq) GetUserId() string {
if x != nil {
return x.UserId
}
return ""
}
func (x *IssueReq) GetType() uint32 {
if x != nil {
return x.Type
}
return 0
}
type RefreshReq struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
RefreshToken string `protobuf:"bytes,1,opt,name=refresh_token,json=refreshToken,proto3" json:"refresh_token,omitempty"`
}
func (x *RefreshReq) Reset() {
*x = RefreshReq{}
if protoimpl.UnsafeEnabled {
mi := &file_auth_proto_msgTypes[4]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *RefreshReq) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*RefreshReq) ProtoMessage() {}
func (x *RefreshReq) ProtoReflect() protoreflect.Message {
mi := &file_auth_proto_msgTypes[4]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use RefreshReq.ProtoReflect.Descriptor instead.
func (*RefreshReq) Descriptor() ([]byte, []int) {
return file_auth_proto_rawDescGZIP(), []int{4}
}
func (x *RefreshReq) GetRefreshToken() string {
if x != nil {
return x.RefreshToken
}
return ""
}
type AuthZReq struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Domain string `protobuf:"bytes,1,opt,name=domain,proto3" json:"domain,omitempty"` // Domain
SubjectType string `protobuf:"bytes,2,opt,name=subject_type,json=subjectType,proto3" json:"subject_type,omitempty"` // Thing or User
SubjectKind string `protobuf:"bytes,3,opt,name=subject_kind,json=subjectKind,proto3" json:"subject_kind,omitempty"` // ID or Token
SubjectRelation string `protobuf:"bytes,4,opt,name=subject_relation,json=subjectRelation,proto3" json:"subject_relation,omitempty"` // Subject relation
Subject string `protobuf:"bytes,5,opt,name=subject,proto3" json:"subject,omitempty"` // Subject value (id or token, depending on kind)
Relation string `protobuf:"bytes,6,opt,name=relation,proto3" json:"relation,omitempty"` // Relation to filter
Permission string `protobuf:"bytes,7,opt,name=permission,proto3" json:"permission,omitempty"` // Action
Object string `protobuf:"bytes,8,opt,name=object,proto3" json:"object,omitempty"` // Object ID
ObjectType string `protobuf:"bytes,9,opt,name=object_type,json=objectType,proto3" json:"object_type,omitempty"` // Thing, User, Group
}
func (x *AuthZReq) Reset() {
*x = AuthZReq{}
if protoimpl.UnsafeEnabled {
mi := &file_auth_proto_msgTypes[5]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *AuthZReq) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*AuthZReq) ProtoMessage() {}
func (x *AuthZReq) ProtoReflect() protoreflect.Message {
mi := &file_auth_proto_msgTypes[5]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use AuthZReq.ProtoReflect.Descriptor instead.
func (*AuthZReq) Descriptor() ([]byte, []int) {
return file_auth_proto_rawDescGZIP(), []int{5}
}
func (x *AuthZReq) GetDomain() string {
if x != nil {
return x.Domain
}
return ""
}
func (x *AuthZReq) GetSubjectType() string {
if x != nil {
return x.SubjectType
}
return ""
}
func (x *AuthZReq) GetSubjectKind() string {
if x != nil {
return x.SubjectKind
}
return ""
}
func (x *AuthZReq) GetSubjectRelation() string {
if x != nil {
return x.SubjectRelation
}
return ""
}
func (x *AuthZReq) GetSubject() string {
if x != nil {
return x.Subject
}
return ""
}
func (x *AuthZReq) GetRelation() string {
if x != nil {
return x.Relation
}
return ""
}
func (x *AuthZReq) GetPermission() string {
if x != nil {
return x.Permission
}
return ""
}
func (x *AuthZReq) GetObject() string {
if x != nil {
return x.Object
}
return ""
}
func (x *AuthZReq) GetObjectType() string {
if x != nil {
return x.ObjectType
}
return ""
}
type AuthZRes struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Authorized bool `protobuf:"varint,1,opt,name=authorized,proto3" json:"authorized,omitempty"`
Id string `protobuf:"bytes,2,opt,name=id,proto3" json:"id,omitempty"`
}
func (x *AuthZRes) Reset() {
*x = AuthZRes{}
if protoimpl.UnsafeEnabled {
mi := &file_auth_proto_msgTypes[6]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *AuthZRes) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*AuthZRes) ProtoMessage() {}
func (x *AuthZRes) ProtoReflect() protoreflect.Message {
mi := &file_auth_proto_msgTypes[6]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use AuthZRes.ProtoReflect.Descriptor instead.
func (*AuthZRes) Descriptor() ([]byte, []int) {
return file_auth_proto_rawDescGZIP(), []int{6}
}
func (x *AuthZRes) GetAuthorized() bool {
if x != nil {
return x.Authorized
}
return false
}
func (x *AuthZRes) GetId() string {
if x != nil {
return x.Id
}
return ""
}
type DeleteUserRes struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Deleted bool `protobuf:"varint,1,opt,name=deleted,proto3" json:"deleted,omitempty"`
}
func (x *DeleteUserRes) Reset() {
*x = DeleteUserRes{}
if protoimpl.UnsafeEnabled {
mi := &file_auth_proto_msgTypes[7]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *DeleteUserRes) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*DeleteUserRes) ProtoMessage() {}
func (x *DeleteUserRes) ProtoReflect() protoreflect.Message {
mi := &file_auth_proto_msgTypes[7]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use DeleteUserRes.ProtoReflect.Descriptor instead.
func (*DeleteUserRes) Descriptor() ([]byte, []int) {
return file_auth_proto_rawDescGZIP(), []int{7}
}
func (x *DeleteUserRes) GetDeleted() bool {
if x != nil {
return x.Deleted
}
return false
}
type DeleteUserReq struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"`
}
func (x *DeleteUserReq) Reset() {
*x = DeleteUserReq{}
if protoimpl.UnsafeEnabled {
mi := &file_auth_proto_msgTypes[8]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *DeleteUserReq) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*DeleteUserReq) ProtoMessage() {}
func (x *DeleteUserReq) ProtoReflect() protoreflect.Message {
mi := &file_auth_proto_msgTypes[8]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use DeleteUserReq.ProtoReflect.Descriptor instead.
func (*DeleteUserReq) Descriptor() ([]byte, []int) {
return file_auth_proto_rawDescGZIP(), []int{8}
}
func (x *DeleteUserReq) GetId() string {
if x != nil {
return x.Id
}
return ""
}
type ThingsAuthzReq struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
ChannelId string `protobuf:"bytes,1,opt,name=channel_id,json=channelId,proto3" json:"channel_id,omitempty"`
ThingId string `protobuf:"bytes,2,opt,name=thing_id,json=thingId,proto3" json:"thing_id,omitempty"`
ThingKey string `protobuf:"bytes,3,opt,name=thing_key,json=thingKey,proto3" json:"thing_key,omitempty"`
Permission string `protobuf:"bytes,4,opt,name=permission,proto3" json:"permission,omitempty"`
}
func (x *ThingsAuthzReq) Reset() {
*x = ThingsAuthzReq{}
if protoimpl.UnsafeEnabled {
mi := &file_auth_proto_msgTypes[9]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *ThingsAuthzReq) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*ThingsAuthzReq) ProtoMessage() {}
func (x *ThingsAuthzReq) ProtoReflect() protoreflect.Message {
mi := &file_auth_proto_msgTypes[9]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use ThingsAuthzReq.ProtoReflect.Descriptor instead.
func (*ThingsAuthzReq) Descriptor() ([]byte, []int) {
return file_auth_proto_rawDescGZIP(), []int{9}
}
func (x *ThingsAuthzReq) GetChannelId() string {
if x != nil {
return x.ChannelId
}
return ""
}
func (x *ThingsAuthzReq) GetThingId() string {
if x != nil {
return x.ThingId
}
return ""
}
func (x *ThingsAuthzReq) GetThingKey() string {
if x != nil {
return x.ThingKey
}
return ""
}
func (x *ThingsAuthzReq) GetPermission() string {
if x != nil {
return x.Permission
}
return ""
}
type ThingsAuthzRes struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Authorized bool `protobuf:"varint,1,opt,name=authorized,proto3" json:"authorized,omitempty"`
Id string `protobuf:"bytes,2,opt,name=id,proto3" json:"id,omitempty"`
}
func (x *ThingsAuthzRes) Reset() {
*x = ThingsAuthzRes{}
if protoimpl.UnsafeEnabled {
mi := &file_auth_proto_msgTypes[10]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *ThingsAuthzRes) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*ThingsAuthzRes) ProtoMessage() {}
func (x *ThingsAuthzRes) ProtoReflect() protoreflect.Message {
mi := &file_auth_proto_msgTypes[10]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use ThingsAuthzRes.ProtoReflect.Descriptor instead.
func (*ThingsAuthzRes) Descriptor() ([]byte, []int) {
return file_auth_proto_rawDescGZIP(), []int{10}
}
func (x *ThingsAuthzRes) GetAuthorized() bool {
if x != nil {
return x.Authorized
}
return false
}
func (x *ThingsAuthzRes) GetId() string {
if x != nil {
return x.Id
}
return ""
}
var File_auth_proto protoreflect.FileDescriptor
var file_auth_proto_rawDesc = []byte{
0x0a, 0x0a, 0x61, 0x75, 0x74, 0x68, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x0a, 0x6d, 0x61,
0x67, 0x69, 0x73, 0x74, 0x72, 0x61, 0x6c, 0x61, 0x22, 0x87, 0x01, 0x0a, 0x05, 0x54, 0x6f, 0x6b,
0x65, 0x6e, 0x12, 0x21, 0x0a, 0x0c, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x5f, 0x74, 0x6f, 0x6b,
0x65, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73,
0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x12, 0x28, 0x0a, 0x0d, 0x72, 0x65, 0x66, 0x72, 0x65, 0x73, 0x68,
0x5f, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x0c,
0x72, 0x65, 0x66, 0x72, 0x65, 0x73, 0x68, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x88, 0x01, 0x01, 0x12,
0x1f, 0x0a, 0x0b, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x03,
0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x54, 0x79, 0x70, 0x65,
0x42, 0x10, 0x0a, 0x0e, 0x5f, 0x72, 0x65, 0x66, 0x72, 0x65, 0x73, 0x68, 0x5f, 0x74, 0x6f, 0x6b,
0x65, 0x6e, 0x22, 0x20, 0x0a, 0x08, 0x41, 0x75, 0x74, 0x68, 0x4e, 0x52, 0x65, 0x71, 0x12, 0x14,
0x0a, 0x05, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x74,
0x6f, 0x6b, 0x65, 0x6e, 0x22, 0x50, 0x0a, 0x08, 0x41, 0x75, 0x74, 0x68, 0x4e, 0x52, 0x65, 0x73,
0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64,
0x12, 0x17, 0x0a, 0x07, 0x75, 0x73, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28,
0x09, 0x52, 0x06, 0x75, 0x73, 0x65, 0x72, 0x49, 0x64, 0x12, 0x1b, 0x0a, 0x09, 0x64, 0x6f, 0x6d,
0x61, 0x69, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x64, 0x6f,
0x6d, 0x61, 0x69, 0x6e, 0x49, 0x64, 0x22, 0x37, 0x0a, 0x08, 0x49, 0x73, 0x73, 0x75, 0x65, 0x52,
0x65, 0x71, 0x12, 0x17, 0x0a, 0x07, 0x75, 0x73, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20,
0x01, 0x28, 0x09, 0x52, 0x06, 0x75, 0x73, 0x65, 0x72, 0x49, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x74,
0x79, 0x70, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x22,
0x31, 0x0a, 0x0a, 0x52, 0x65, 0x66, 0x72, 0x65, 0x73, 0x68, 0x52, 0x65, 0x71, 0x12, 0x23, 0x0a,
0x0d, 0x72, 0x65, 0x66, 0x72, 0x65, 0x73, 0x68, 0x5f, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x18, 0x01,
0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x72, 0x65, 0x66, 0x72, 0x65, 0x73, 0x68, 0x54, 0x6f, 0x6b,
0x65, 0x6e, 0x22, 0xa2, 0x02, 0x0a, 0x08, 0x41, 0x75, 0x74, 0x68, 0x5a, 0x52, 0x65, 0x71, 0x12,
0x16, 0x0a, 0x06, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52,
0x06, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x12, 0x21, 0x0a, 0x0c, 0x73, 0x75, 0x62, 0x6a, 0x65,
0x63, 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x73,
0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x54, 0x79, 0x70, 0x65, 0x12, 0x21, 0x0a, 0x0c, 0x73, 0x75,
0x62, 0x6a, 0x65, 0x63, 0x74, 0x5f, 0x6b, 0x69, 0x6e, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09,
0x52, 0x0b, 0x73, 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x4b, 0x69, 0x6e, 0x64, 0x12, 0x29, 0x0a,
0x10, 0x73, 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x5f, 0x72, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f,
0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x73, 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74,
0x52, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x18, 0x0a, 0x07, 0x73, 0x75, 0x62, 0x6a,
0x65, 0x63, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x73, 0x75, 0x62, 0x6a, 0x65,
0x63, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x72, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x06,
0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x72, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x1e,
0x0a, 0x0a, 0x70, 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x07, 0x20, 0x01,
0x28, 0x09, 0x52, 0x0a, 0x70, 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x16,
0x0a, 0x06, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06,
0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x12, 0x1f, 0x0a, 0x0b, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74,
0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x09, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x6f, 0x62, 0x6a,
0x65, 0x63, 0x74, 0x54, 0x79, 0x70, 0x65, 0x22, 0x3a, 0x0a, 0x08, 0x41, 0x75, 0x74, 0x68, 0x5a,
0x52, 0x65, 0x73, 0x12, 0x1e, 0x0a, 0x0a, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x65,
0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0a, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69,
0x7a, 0x65, 0x64, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52,
0x02, 0x69, 0x64, 0x22, 0x29, 0x0a, 0x0d, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x55, 0x73, 0x65,
0x72, 0x52, 0x65, 0x73, 0x12, 0x18, 0x0a, 0x07, 0x64, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x64, 0x18,
0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x64, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x64, 0x22, 0x1f,
0x0a, 0x0d, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x55, 0x73, 0x65, 0x72, 0x52, 0x65, 0x71, 0x12,
0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x22,
0x87, 0x01, 0x0a, 0x0e, 0x54, 0x68, 0x69, 0x6e, 0x67, 0x73, 0x41, 0x75, 0x74, 0x68, 0x7a, 0x52,
0x65, 0x71, 0x12, 0x1d, 0x0a, 0x0a, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x5f, 0x69, 0x64,
0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x49,
0x64, 0x12, 0x19, 0x0a, 0x08, 0x74, 0x68, 0x69, 0x6e, 0x67, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20,
0x01, 0x28, 0x09, 0x52, 0x07, 0x74, 0x68, 0x69, 0x6e, 0x67, 0x49, 0x64, 0x12, 0x1b, 0x0a, 0x09,
0x74, 0x68, 0x69, 0x6e, 0x67, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52,
0x08, 0x74, 0x68, 0x69, 0x6e, 0x67, 0x4b, 0x65, 0x79, 0x12, 0x1e, 0x0a, 0x0a, 0x70, 0x65, 0x72,
0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x70,
0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x22, 0x40, 0x0a, 0x0e, 0x54, 0x68, 0x69,
0x6e, 0x67, 0x73, 0x41, 0x75, 0x74, 0x68, 0x7a, 0x52, 0x65, 0x73, 0x12, 0x1e, 0x0a, 0x0a, 0x61,
0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x65, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52,
0x0a, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x65, 0x64, 0x12, 0x0e, 0x0a, 0x02, 0x69,
0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x32, 0x56, 0x0a, 0x0d, 0x54,
0x68, 0x69, 0x6e, 0x67, 0x73, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x45, 0x0a, 0x09,
0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x65, 0x12, 0x1a, 0x2e, 0x6d, 0x61, 0x67, 0x69,
0x73, 0x74, 0x72, 0x61, 0x6c, 0x61, 0x2e, 0x54, 0x68, 0x69, 0x6e, 0x67, 0x73, 0x41, 0x75, 0x74,
0x68, 0x7a, 0x52, 0x65, 0x71, 0x1a, 0x1a, 0x2e, 0x6d, 0x61, 0x67, 0x69, 0x73, 0x74, 0x72, 0x61,
0x6c, 0x61, 0x2e, 0x54, 0x68, 0x69, 0x6e, 0x67, 0x73, 0x41, 0x75, 0x74, 0x68, 0x7a, 0x52, 0x65,
0x73, 0x22, 0x00, 0x32, 0x7a, 0x0a, 0x0c, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x53, 0x65, 0x72, 0x76,
0x69, 0x63, 0x65, 0x12, 0x32, 0x0a, 0x05, 0x49, 0x73, 0x73, 0x75, 0x65, 0x12, 0x14, 0x2e, 0x6d,
0x61, 0x67, 0x69, 0x73, 0x74, 0x72, 0x61, 0x6c, 0x61, 0x2e, 0x49, 0x73, 0x73, 0x75, 0x65, 0x52,
0x65, 0x71, 0x1a, 0x11, 0x2e, 0x6d, 0x61, 0x67, 0x69, 0x73, 0x74, 0x72, 0x61, 0x6c, 0x61, 0x2e,
0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x22, 0x00, 0x12, 0x36, 0x0a, 0x07, 0x52, 0x65, 0x66, 0x72, 0x65,
0x73, 0x68, 0x12, 0x16, 0x2e, 0x6d, 0x61, 0x67, 0x69, 0x73, 0x74, 0x72, 0x61, 0x6c, 0x61, 0x2e,
0x52, 0x65, 0x66, 0x72, 0x65, 0x73, 0x68, 0x52, 0x65, 0x71, 0x1a, 0x11, 0x2e, 0x6d, 0x61, 0x67,
0x69, 0x73, 0x74, 0x72, 0x61, 0x6c, 0x61, 0x2e, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x22, 0x00, 0x32,
0x86, 0x01, 0x0a, 0x0b, 0x41, 0x75, 0x74, 0x68, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12,
0x39, 0x0a, 0x09, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x65, 0x12, 0x14, 0x2e, 0x6d,
0x61, 0x67, 0x69, 0x73, 0x74, 0x72, 0x61, 0x6c, 0x61, 0x2e, 0x41, 0x75, 0x74, 0x68, 0x5a, 0x52,
0x65, 0x71, 0x1a, 0x14, 0x2e, 0x6d, 0x61, 0x67, 0x69, 0x73, 0x74, 0x72, 0x61, 0x6c, 0x61, 0x2e,
0x41, 0x75, 0x74, 0x68, 0x5a, 0x52, 0x65, 0x73, 0x22, 0x00, 0x12, 0x3c, 0x0a, 0x0c, 0x41, 0x75,
0x74, 0x68, 0x65, 0x6e, 0x74, 0x69, 0x63, 0x61, 0x74, 0x65, 0x12, 0x14, 0x2e, 0x6d, 0x61, 0x67,
0x69, 0x73, 0x74, 0x72, 0x61, 0x6c, 0x61, 0x2e, 0x41, 0x75, 0x74, 0x68, 0x4e, 0x52, 0x65, 0x71,
0x1a, 0x14, 0x2e, 0x6d, 0x61, 0x67, 0x69, 0x73, 0x74, 0x72, 0x61, 0x6c, 0x61, 0x2e, 0x41, 0x75,
0x74, 0x68, 0x4e, 0x52, 0x65, 0x73, 0x22, 0x00, 0x32, 0x61, 0x0a, 0x0e, 0x44, 0x6f, 0x6d, 0x61,
0x69, 0x6e, 0x73, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x4f, 0x0a, 0x15, 0x44, 0x65,
0x6c, 0x65, 0x74, 0x65, 0x55, 0x73, 0x65, 0x72, 0x46, 0x72, 0x6f, 0x6d, 0x44, 0x6f, 0x6d, 0x61,
0x69, 0x6e, 0x73, 0x12, 0x19, 0x2e, 0x6d, 0x61, 0x67, 0x69, 0x73, 0x74, 0x72, 0x61, 0x6c, 0x61,
0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x55, 0x73, 0x65, 0x72, 0x52, 0x65, 0x71, 0x1a, 0x19,
0x2e, 0x6d, 0x61, 0x67, 0x69, 0x73, 0x74, 0x72, 0x61, 0x6c, 0x61, 0x2e, 0x44, 0x65, 0x6c, 0x65,
0x74, 0x65, 0x55, 0x73, 0x65, 0x72, 0x52, 0x65, 0x73, 0x22, 0x00, 0x42, 0x0e, 0x5a, 0x0c, 0x2e,
0x2f, 0x6d, 0x61, 0x67, 0x69, 0x73, 0x74, 0x72, 0x61, 0x6c, 0x61, 0x62, 0x06, 0x70, 0x72, 0x6f,
0x74, 0x6f, 0x33,
}
var (
file_auth_proto_rawDescOnce sync.Once
file_auth_proto_rawDescData = file_auth_proto_rawDesc
)
func file_auth_proto_rawDescGZIP() []byte {
file_auth_proto_rawDescOnce.Do(func() {
file_auth_proto_rawDescData = protoimpl.X.CompressGZIP(file_auth_proto_rawDescData)
})
return file_auth_proto_rawDescData
}
var file_auth_proto_msgTypes = make([]protoimpl.MessageInfo, 11)
var file_auth_proto_goTypes = []any{
(*Token)(nil), // 0: magistrala.Token
(*AuthNReq)(nil), // 1: magistrala.AuthNReq
(*AuthNRes)(nil), // 2: magistrala.AuthNRes
(*IssueReq)(nil), // 3: magistrala.IssueReq
(*RefreshReq)(nil), // 4: magistrala.RefreshReq
(*AuthZReq)(nil), // 5: magistrala.AuthZReq
(*AuthZRes)(nil), // 6: magistrala.AuthZRes
(*DeleteUserRes)(nil), // 7: magistrala.DeleteUserRes
(*DeleteUserReq)(nil), // 8: magistrala.DeleteUserReq
(*ThingsAuthzReq)(nil), // 9: magistrala.ThingsAuthzReq
(*ThingsAuthzRes)(nil), // 10: magistrala.ThingsAuthzRes
}
var file_auth_proto_depIdxs = []int32{
9, // 0: magistrala.ThingsService.Authorize:input_type -> magistrala.ThingsAuthzReq
3, // 1: magistrala.TokenService.Issue:input_type -> magistrala.IssueReq
4, // 2: magistrala.TokenService.Refresh:input_type -> magistrala.RefreshReq
5, // 3: magistrala.AuthService.Authorize:input_type -> magistrala.AuthZReq
1, // 4: magistrala.AuthService.Authenticate:input_type -> magistrala.AuthNReq
8, // 5: magistrala.DomainsService.DeleteUserFromDomains:input_type -> magistrala.DeleteUserReq
10, // 6: magistrala.ThingsService.Authorize:output_type -> magistrala.ThingsAuthzRes
0, // 7: magistrala.TokenService.Issue:output_type -> magistrala.Token
0, // 8: magistrala.TokenService.Refresh:output_type -> magistrala.Token
6, // 9: magistrala.AuthService.Authorize:output_type -> magistrala.AuthZRes
2, // 10: magistrala.AuthService.Authenticate:output_type -> magistrala.AuthNRes
7, // 11: magistrala.DomainsService.DeleteUserFromDomains:output_type -> magistrala.DeleteUserRes
6, // [6:12] is the sub-list for method output_type
0, // [0:6] is the sub-list for method input_type
0, // [0:0] is the sub-list for extension type_name
0, // [0:0] is the sub-list for extension extendee
0, // [0:0] is the sub-list for field type_name
}
func init() { file_auth_proto_init() }
func file_auth_proto_init() {
if File_auth_proto != nil {
return
}
if !protoimpl.UnsafeEnabled {
file_auth_proto_msgTypes[0].Exporter = func(v any, i int) any {
switch v := v.(*Token); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
file_auth_proto_msgTypes[1].Exporter = func(v any, i int) any {
switch v := v.(*AuthNReq); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
file_auth_proto_msgTypes[2].Exporter = func(v any, i int) any {
switch v := v.(*AuthNRes); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
file_auth_proto_msgTypes[3].Exporter = func(v any, i int) any {
switch v := v.(*IssueReq); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
file_auth_proto_msgTypes[4].Exporter = func(v any, i int) any {
switch v := v.(*RefreshReq); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
file_auth_proto_msgTypes[5].Exporter = func(v any, i int) any {
switch v := v.(*AuthZReq); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
file_auth_proto_msgTypes[6].Exporter = func(v any, i int) any {
switch v := v.(*AuthZRes); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
file_auth_proto_msgTypes[7].Exporter = func(v any, i int) any {
switch v := v.(*DeleteUserRes); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
file_auth_proto_msgTypes[8].Exporter = func(v any, i int) any {
switch v := v.(*DeleteUserReq); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
file_auth_proto_msgTypes[9].Exporter = func(v any, i int) any {
switch v := v.(*ThingsAuthzReq); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
file_auth_proto_msgTypes[10].Exporter = func(v any, i int) any {
switch v := v.(*ThingsAuthzRes); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
}
file_auth_proto_msgTypes[0].OneofWrappers = []any{}
type x struct{}
out := protoimpl.TypeBuilder{
File: protoimpl.DescBuilder{
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: file_auth_proto_rawDesc,
NumEnums: 0,
NumMessages: 11,
NumExtensions: 0,
NumServices: 4,
},
GoTypes: file_auth_proto_goTypes,
DependencyIndexes: file_auth_proto_depIdxs,
MessageInfos: file_auth_proto_msgTypes,
}.Build()
File_auth_proto = out.File
file_auth_proto_rawDesc = nil
file_auth_proto_goTypes = nil
file_auth_proto_depIdxs = nil
}
-98
View File
@@ -1,98 +0,0 @@
// Copyright (c) Abstract Machines
// SPDX-License-Identifier: Apache-2.0
syntax = "proto3";
package magistrala;
option go_package = "./magistrala";
// ThingsService is a service that provides things authorization functionalities
// for magistrala services.
service ThingsService {
// Authorize checks if the thing is authorized to perform
// the action on the channel.
rpc Authorize(ThingsAuthzReq) returns (ThingsAuthzRes) {}
}
service TokenService {
rpc Issue(IssueReq) returns (Token) {}
rpc Refresh(RefreshReq) returns (Token) {}
}
// AuthService is a service that provides authentication and authorization
// functionalities for magistrala services.
service AuthService {
rpc Authorize(AuthZReq) returns (AuthZRes) {}
rpc Authenticate(AuthNReq) returns (AuthNRes) {}
}
// DomainsService is a service that provides access to domains
// functionalities for magistrala services.
service DomainsService {
rpc DeleteUserFromDomains(DeleteUserReq) returns (DeleteUserRes) {}
}
// If a token is not carrying any information itself, the type
// field can be used to determine how to validate the token.
// Also, different tokens can be encoded in different ways.
message Token {
string access_token = 1;
optional string refresh_token = 2;
string access_type = 3;
}
message AuthNReq {
string token = 1;
}
message AuthNRes {
string id = 1; // change "id" to "subject", sub in jwt = user + domain id
string user_id = 2; // user id
string domain_id = 3; // domain id
}
message IssueReq {
string user_id = 1;
uint32 type = 2;
}
message RefreshReq {
string refresh_token = 1;
}
message AuthZReq {
string domain = 1; // Domain
string subject_type = 2; // Thing or User
string subject_kind = 3; // ID or Token
string subject_relation = 4; // Subject relation
string subject = 5; // Subject value (id or token, depending on kind)
string relation = 6; // Relation to filter
string permission = 7; // Action
string object = 8; // Object ID
string object_type = 9; // Thing, User, Group
}
message AuthZRes {
bool authorized = 1;
string id = 2;
}
message DeleteUserRes {
bool deleted = 1;
}
message DeleteUserReq {
string id = 1;
}
message ThingsAuthzReq {
string channel_id = 1;
string thing_id = 2;
string thing_key = 3;
string permission = 4;
}
message ThingsAuthzRes {
bool authorized = 1;
string id = 2;
}
+3 -3
View File
@@ -1,6 +1,6 @@
# Auth - Authentication and Authorization service
Auth service provides authentication features as an API for managing authentication keys as well as administering groups of entities - `things` and `users`.
Auth service provides authentication features as an API for managing authentication keys as well as administering groups of entities - `clients` and `users`.
## Authentication
@@ -25,7 +25,7 @@ Authentication keys are represented and distributed by the corresponding [JWT](j
User keys are issued when user logs in. Each user request (other than `registration` and `login`) contains user key that is used to authenticate the user.
API keys are similar to the User keys. The main difference is that API keys have configurable expiration time. If no time is set, the key will never expire. For that reason, API keys are _the only key type that can be revoked_. This also means that, despite being used as a JWT, it requires a query to the database to validate the API key. The user with API key can perform all the same actions as the user with login key (can act on behalf of the user for Thing, Channel, or user profile management), _except issuing new API keys_.
API keys are similar to the User keys. The main difference is that API keys have configurable expiration time. If no time is set, the key will never expire. For that reason, API keys are _the only key type that can be revoked_. This also means that, despite being used as a JWT, it requires a query to the database to validate the API key. The user with API key can perform all the same actions as the user with login key (can act on behalf of the user for Client, Channel, or user profile management), _except issuing new API keys_.
Recovery key is the password recovery key. It's short-lived token used for password recovery process.
@@ -40,7 +40,7 @@ The following actions are supported:
## Domains
Domains are used to group users and things. Each domain has a unique alias that is used to identify the domain. Domains are used to group users and their entities.
Domains are used to group users and clients. Each domain has a unique alias that is used to identify the domain. Domains are used to group users and their entities.
Domain consists of the following fields:
+16 -16
View File
@@ -7,14 +7,14 @@ import (
"context"
"time"
"github.com/absmach/magistrala"
grpcapi "github.com/absmach/magistrala/auth/api/grpc"
grpcAuthV1 "github.com/absmach/magistrala/internal/grpc/auth/v1"
"github.com/go-kit/kit/endpoint"
kitgrpc "github.com/go-kit/kit/transport/grpc"
"google.golang.org/grpc"
)
const authSvcName = "magistrala.AuthService"
const authSvcName = "auth.v1.AuthService"
type authGrpcClient struct {
authenticate endpoint.Endpoint
@@ -22,10 +22,10 @@ type authGrpcClient struct {
timeout time.Duration
}
var _ magistrala.AuthServiceClient = (*authGrpcClient)(nil)
var _ grpcAuthV1.AuthServiceClient = (*authGrpcClient)(nil)
// NewAuthClient returns new auth gRPC client instance.
func NewAuthClient(conn *grpc.ClientConn, timeout time.Duration) magistrala.AuthServiceClient {
func NewAuthClient(conn *grpc.ClientConn, timeout time.Duration) grpcAuthV1.AuthServiceClient {
return &authGrpcClient{
authenticate: kitgrpc.NewClient(
conn,
@@ -33,7 +33,7 @@ func NewAuthClient(conn *grpc.ClientConn, timeout time.Duration) magistrala.Auth
"Authenticate",
encodeIdentifyRequest,
decodeIdentifyResponse,
magistrala.AuthNRes{},
grpcAuthV1.AuthNRes{},
).Endpoint(),
authorize: kitgrpc.NewClient(
conn,
@@ -41,35 +41,35 @@ func NewAuthClient(conn *grpc.ClientConn, timeout time.Duration) magistrala.Auth
"Authorize",
encodeAuthorizeRequest,
decodeAuthorizeResponse,
magistrala.AuthZRes{},
grpcAuthV1.AuthZRes{},
).Endpoint(),
timeout: timeout,
}
}
func (client authGrpcClient) Authenticate(ctx context.Context, token *magistrala.AuthNReq, _ ...grpc.CallOption) (*magistrala.AuthNRes, error) {
func (client authGrpcClient) Authenticate(ctx context.Context, token *grpcAuthV1.AuthNReq, _ ...grpc.CallOption) (*grpcAuthV1.AuthNRes, error) {
ctx, cancel := context.WithTimeout(ctx, client.timeout)
defer cancel()
res, err := client.authenticate(ctx, authenticateReq{token: token.GetToken()})
if err != nil {
return &magistrala.AuthNRes{}, grpcapi.DecodeError(err)
return &grpcAuthV1.AuthNRes{}, grpcapi.DecodeError(err)
}
ir := res.(authenticateRes)
return &magistrala.AuthNRes{Id: ir.id, UserId: ir.userID, DomainId: ir.domainID}, nil
return &grpcAuthV1.AuthNRes{Id: ir.id, UserId: ir.userID, DomainId: ir.domainID}, nil
}
func encodeIdentifyRequest(_ context.Context, grpcReq interface{}) (interface{}, error) {
req := grpcReq.(authenticateReq)
return &magistrala.AuthNReq{Token: req.token}, nil
return &grpcAuthV1.AuthNReq{Token: req.token}, nil
}
func decodeIdentifyResponse(_ context.Context, grpcRes interface{}) (interface{}, error) {
res := grpcRes.(*magistrala.AuthNRes)
res := grpcRes.(*grpcAuthV1.AuthNRes)
return authenticateRes{id: res.GetId(), userID: res.GetUserId(), domainID: res.GetDomainId()}, nil
}
func (client authGrpcClient) Authorize(ctx context.Context, req *magistrala.AuthZReq, _ ...grpc.CallOption) (r *magistrala.AuthZRes, err error) {
func (client authGrpcClient) Authorize(ctx context.Context, req *grpcAuthV1.AuthZReq, _ ...grpc.CallOption) (r *grpcAuthV1.AuthZRes, err error) {
ctx, cancel := context.WithTimeout(ctx, client.timeout)
defer cancel()
@@ -84,21 +84,21 @@ func (client authGrpcClient) Authorize(ctx context.Context, req *magistrala.Auth
Object: req.GetObject(),
})
if err != nil {
return &magistrala.AuthZRes{}, grpcapi.DecodeError(err)
return &grpcAuthV1.AuthZRes{}, grpcapi.DecodeError(err)
}
ar := res.(authorizeRes)
return &magistrala.AuthZRes{Authorized: ar.authorized, Id: ar.id}, nil
return &grpcAuthV1.AuthZRes{Authorized: ar.authorized, Id: ar.id}, nil
}
func decodeAuthorizeResponse(_ context.Context, grpcRes interface{}) (interface{}, error) {
res := grpcRes.(*magistrala.AuthZRes)
res := grpcRes.(*grpcAuthV1.AuthZRes)
return authorizeRes{authorized: res.Authorized, id: res.Id}, nil
}
func encodeAuthorizeRequest(_ context.Context, grpcReq interface{}) (interface{}, error) {
req := grpcReq.(authReq)
return &magistrala.AuthZReq{
return &grpcAuthV1.AuthZReq{
Domain: req.Domain,
SubjectType: req.SubjectType,
Subject: req.Subject,
+26 -24
View File
@@ -10,9 +10,9 @@ import (
"testing"
"time"
"github.com/absmach/magistrala"
"github.com/absmach/magistrala/auth"
grpcapi "github.com/absmach/magistrala/auth/api/grpc/auth"
grpcAuthV1 "github.com/absmach/magistrala/internal/grpc/auth/v1"
"github.com/absmach/magistrala/internal/testsutil"
"github.com/absmach/magistrala/pkg/apiutil"
"github.com/absmach/magistrala/pkg/errors"
@@ -28,7 +28,7 @@ const (
secret = "secret"
email = "test@example.com"
id = "testID"
thingsType = "things"
clientsType = "clients"
usersType = "users"
description = "Description"
groupName = "mgx"
@@ -52,7 +52,7 @@ var (
func startGRPCServer(svc auth.Service, port int) *grpc.Server {
listener, _ := net.Listen("tcp", fmt.Sprintf(":%d", port))
server := grpc.NewServer()
magistrala.RegisterAuthServiceServer(server, grpcapi.NewAuthServer(svc))
grpcAuthV1.RegisterAuthServiceServer(server, grpcapi.NewAuthServer(svc))
go func() {
err := server.Serve(listener)
assert.Nil(&testing.T{}, err, fmt.Sprintf(`"Unexpected error creating auth server %s"`, err))
@@ -63,40 +63,41 @@ func startGRPCServer(svc auth.Service, port int) *grpc.Server {
func TestIdentify(t *testing.T) {
conn, err := grpc.NewClient(authAddr, grpc.WithTransportCredentials(insecure.NewCredentials()))
defer conn.Close()
assert.Nil(t, err, fmt.Sprintf("Unexpected error creating client connection %s", err))
grpcClient := grpcapi.NewAuthClient(conn, time.Second)
cases := []struct {
desc string
token string
idt *magistrala.AuthNRes
idt *grpcAuthV1.AuthNRes
svcErr error
err error
}{
{
desc: "authenticate user with valid user token",
token: validToken,
idt: &magistrala.AuthNRes{Id: id, UserId: email, DomainId: domainID},
idt: &grpcAuthV1.AuthNRes{Id: id, UserId: email, DomainId: domainID},
err: nil,
},
{
desc: "authenticate user with invalid user token",
token: "invalid",
idt: &magistrala.AuthNRes{},
idt: &grpcAuthV1.AuthNRes{},
svcErr: svcerr.ErrAuthentication,
err: svcerr.ErrAuthentication,
},
{
desc: "authenticate user with empty token",
token: "",
idt: &magistrala.AuthNRes{},
idt: &grpcAuthV1.AuthNRes{},
err: apiutil.ErrBearerToken,
},
}
for _, tc := range cases {
svcCall := svc.On("Identify", mock.Anything, mock.Anything, mock.Anything).Return(auth.Key{Subject: id, User: email, Domain: domainID}, tc.svcErr)
idt, err := grpcClient.Authenticate(context.Background(), &magistrala.AuthNReq{Token: tc.token})
idt, err := grpcClient.Authenticate(context.Background(), &grpcAuthV1.AuthNReq{Token: tc.token})
if idt != nil {
assert.Equal(t, tc.idt, idt, fmt.Sprintf("%s: expected %v got %v", tc.desc, tc.idt, idt))
}
@@ -107,20 +108,21 @@ func TestIdentify(t *testing.T) {
func TestAuthorize(t *testing.T) {
conn, err := grpc.NewClient(authAddr, grpc.WithTransportCredentials(insecure.NewCredentials()))
defer conn.Close()
assert.Nil(t, err, fmt.Sprintf("Unexpected error creating client connection %s", err))
grpcClient := grpcapi.NewAuthClient(conn, time.Second)
cases := []struct {
desc string
token string
authRequest *magistrala.AuthZReq
authResponse *magistrala.AuthZRes
authRequest *grpcAuthV1.AuthZReq
authResponse *grpcAuthV1.AuthZRes
err error
}{
{
desc: "authorize user with authorized token",
token: validToken,
authRequest: &magistrala.AuthZReq{
authRequest: &grpcAuthV1.AuthZReq{
Subject: id,
SubjectType: usersType,
Object: authoritiesObj,
@@ -128,13 +130,13 @@ func TestAuthorize(t *testing.T) {
Relation: memberRelation,
Permission: adminpermission,
},
authResponse: &magistrala.AuthZRes{Authorized: true},
authResponse: &grpcAuthV1.AuthZRes{Authorized: true},
err: nil,
},
{
desc: "authorize user with unauthorized token",
token: inValidToken,
authRequest: &magistrala.AuthZReq{
authRequest: &grpcAuthV1.AuthZReq{
Subject: id,
SubjectType: usersType,
Object: authoritiesObj,
@@ -142,13 +144,13 @@ func TestAuthorize(t *testing.T) {
Relation: memberRelation,
Permission: adminpermission,
},
authResponse: &magistrala.AuthZRes{Authorized: false},
authResponse: &grpcAuthV1.AuthZRes{Authorized: false},
err: svcerr.ErrAuthorization,
},
{
desc: "authorize user with empty subject",
token: validToken,
authRequest: &magistrala.AuthZReq{
authRequest: &grpcAuthV1.AuthZReq{
Subject: "",
SubjectType: usersType,
Object: authoritiesObj,
@@ -156,13 +158,13 @@ func TestAuthorize(t *testing.T) {
Relation: memberRelation,
Permission: adminpermission,
},
authResponse: &magistrala.AuthZRes{Authorized: false},
authResponse: &grpcAuthV1.AuthZRes{Authorized: false},
err: apiutil.ErrMissingPolicySub,
},
{
desc: "authorize user with empty subject type",
token: validToken,
authRequest: &magistrala.AuthZReq{
authRequest: &grpcAuthV1.AuthZReq{
Subject: id,
SubjectType: "",
Object: authoritiesObj,
@@ -170,13 +172,13 @@ func TestAuthorize(t *testing.T) {
Relation: memberRelation,
Permission: adminpermission,
},
authResponse: &magistrala.AuthZRes{Authorized: false},
authResponse: &grpcAuthV1.AuthZRes{Authorized: false},
err: apiutil.ErrMissingPolicySub,
},
{
desc: "authorize user with empty object",
token: validToken,
authRequest: &magistrala.AuthZReq{
authRequest: &grpcAuthV1.AuthZReq{
Subject: id,
SubjectType: usersType,
Object: "",
@@ -184,13 +186,13 @@ func TestAuthorize(t *testing.T) {
Relation: memberRelation,
Permission: adminpermission,
},
authResponse: &magistrala.AuthZRes{Authorized: false},
authResponse: &grpcAuthV1.AuthZRes{Authorized: false},
err: apiutil.ErrMissingPolicyObj,
},
{
desc: "authorize user with empty object type",
token: validToken,
authRequest: &magistrala.AuthZReq{
authRequest: &grpcAuthV1.AuthZReq{
Subject: id,
SubjectType: usersType,
Object: authoritiesObj,
@@ -198,13 +200,13 @@ func TestAuthorize(t *testing.T) {
Relation: memberRelation,
Permission: adminpermission,
},
authResponse: &magistrala.AuthZRes{Authorized: false},
authResponse: &grpcAuthV1.AuthZRes{Authorized: false},
err: apiutil.ErrMissingPolicyObj,
},
{
desc: "authorize user with empty permission",
token: validToken,
authRequest: &magistrala.AuthZReq{
authRequest: &grpcAuthV1.AuthZReq{
Subject: id,
SubjectType: usersType,
Object: authoritiesObj,
@@ -212,7 +214,7 @@ func TestAuthorize(t *testing.T) {
Relation: memberRelation,
Permission: "",
},
authResponse: &magistrala.AuthZRes{Authorized: false},
authResponse: &grpcAuthV1.AuthZRes{Authorized: false},
err: apiutil.ErrMalformedPolicyPer,
},
}
+12 -12
View File
@@ -6,22 +6,22 @@ package auth
import (
"context"
"github.com/absmach/magistrala"
"github.com/absmach/magistrala/auth"
grpcapi "github.com/absmach/magistrala/auth/api/grpc"
grpcAuthV1 "github.com/absmach/magistrala/internal/grpc/auth/v1"
kitgrpc "github.com/go-kit/kit/transport/grpc"
)
var _ magistrala.AuthServiceServer = (*authGrpcServer)(nil)
var _ grpcAuthV1.AuthServiceServer = (*authGrpcServer)(nil)
type authGrpcServer struct {
magistrala.UnimplementedAuthServiceServer
grpcAuthV1.UnimplementedAuthServiceServer
authorize kitgrpc.Handler
authenticate kitgrpc.Handler
}
// NewAuthServer returns new AuthnServiceServer instance.
func NewAuthServer(svc auth.Service) magistrala.AuthServiceServer {
func NewAuthServer(svc auth.Service) grpcAuthV1.AuthServiceServer {
return &authGrpcServer{
authorize: kitgrpc.NewServer(
(authorizeEndpoint(svc)),
@@ -37,34 +37,34 @@ func NewAuthServer(svc auth.Service) magistrala.AuthServiceServer {
}
}
func (s *authGrpcServer) Authenticate(ctx context.Context, req *magistrala.AuthNReq) (*magistrala.AuthNRes, error) {
func (s *authGrpcServer) Authenticate(ctx context.Context, req *grpcAuthV1.AuthNReq) (*grpcAuthV1.AuthNRes, error) {
_, res, err := s.authenticate.ServeGRPC(ctx, req)
if err != nil {
return nil, grpcapi.EncodeError(err)
}
return res.(*magistrala.AuthNRes), nil
return res.(*grpcAuthV1.AuthNRes), nil
}
func (s *authGrpcServer) Authorize(ctx context.Context, req *magistrala.AuthZReq) (*magistrala.AuthZRes, error) {
func (s *authGrpcServer) Authorize(ctx context.Context, req *grpcAuthV1.AuthZReq) (*grpcAuthV1.AuthZRes, error) {
_, res, err := s.authorize.ServeGRPC(ctx, req)
if err != nil {
return nil, grpcapi.EncodeError(err)
}
return res.(*magistrala.AuthZRes), nil
return res.(*grpcAuthV1.AuthZRes), nil
}
func decodeAuthenticateRequest(_ context.Context, grpcReq interface{}) (interface{}, error) {
req := grpcReq.(*magistrala.AuthNReq)
req := grpcReq.(*grpcAuthV1.AuthNReq)
return authenticateReq{token: req.GetToken()}, nil
}
func encodeAuthenticateResponse(_ context.Context, grpcRes interface{}) (interface{}, error) {
res := grpcRes.(authenticateRes)
return &magistrala.AuthNRes{Id: res.id, UserId: res.userID, DomainId: res.domainID}, nil
return &grpcAuthV1.AuthNRes{Id: res.id, UserId: res.userID, DomainId: res.domainID}, nil
}
func decodeAuthorizeRequest(_ context.Context, grpcReq interface{}) (interface{}, error) {
req := grpcReq.(*magistrala.AuthZReq)
req := grpcReq.(*grpcAuthV1.AuthZReq)
return authReq{
Domain: req.GetDomain(),
SubjectType: req.GetSubjectType(),
@@ -79,5 +79,5 @@ func decodeAuthorizeRequest(_ context.Context, grpcReq interface{}) (interface{}
func encodeAuthorizeResponse(_ context.Context, grpcRes interface{}) (interface{}, error) {
res := grpcRes.(authorizeRes)
return &magistrala.AuthZRes{Authorized: res.authorized, Id: res.id}, nil
return &grpcAuthV1.AuthZRes{Authorized: res.authorized, Id: res.id}, nil
}
+14 -14
View File
@@ -7,15 +7,15 @@ import (
"context"
"time"
"github.com/absmach/magistrala"
"github.com/absmach/magistrala/auth"
grpcapi "github.com/absmach/magistrala/auth/api/grpc"
grpcTokenV1 "github.com/absmach/magistrala/internal/grpc/token/v1"
"github.com/go-kit/kit/endpoint"
kitgrpc "github.com/go-kit/kit/transport/grpc"
"google.golang.org/grpc"
)
const tokenSvcName = "magistrala.TokenService"
const tokenSvcName = "token.v1.TokenService"
type tokenGrpcClient struct {
issue endpoint.Endpoint
@@ -23,10 +23,10 @@ type tokenGrpcClient struct {
timeout time.Duration
}
var _ magistrala.TokenServiceClient = (*tokenGrpcClient)(nil)
var _ grpcTokenV1.TokenServiceClient = (*tokenGrpcClient)(nil)
// NewAuthClient returns new auth gRPC client instance.
func NewTokenClient(conn *grpc.ClientConn, timeout time.Duration) magistrala.TokenServiceClient {
func NewTokenClient(conn *grpc.ClientConn, timeout time.Duration) grpcTokenV1.TokenServiceClient {
return &tokenGrpcClient{
issue: kitgrpc.NewClient(
conn,
@@ -34,7 +34,7 @@ func NewTokenClient(conn *grpc.ClientConn, timeout time.Duration) magistrala.Tok
"Issue",
encodeIssueRequest,
decodeIssueResponse,
magistrala.Token{},
grpcTokenV1.Token{},
).Endpoint(),
refresh: kitgrpc.NewClient(
conn,
@@ -42,13 +42,13 @@ func NewTokenClient(conn *grpc.ClientConn, timeout time.Duration) magistrala.Tok
"Refresh",
encodeRefreshRequest,
decodeRefreshResponse,
magistrala.Token{},
grpcTokenV1.Token{},
).Endpoint(),
timeout: timeout,
}
}
func (client tokenGrpcClient) Issue(ctx context.Context, req *magistrala.IssueReq, _ ...grpc.CallOption) (*magistrala.Token, error) {
func (client tokenGrpcClient) Issue(ctx context.Context, req *grpcTokenV1.IssueReq, _ ...grpc.CallOption) (*grpcTokenV1.Token, error) {
ctx, cancel := context.WithTimeout(ctx, client.timeout)
defer cancel()
@@ -57,14 +57,14 @@ func (client tokenGrpcClient) Issue(ctx context.Context, req *magistrala.IssueRe
keyType: auth.KeyType(req.GetType()),
})
if err != nil {
return &magistrala.Token{}, grpcapi.DecodeError(err)
return &grpcTokenV1.Token{}, grpcapi.DecodeError(err)
}
return res.(*magistrala.Token), nil
return res.(*grpcTokenV1.Token), nil
}
func encodeIssueRequest(_ context.Context, grpcReq interface{}) (interface{}, error) {
req := grpcReq.(issueReq)
return &magistrala.IssueReq{
return &grpcTokenV1.IssueReq{
UserId: req.userID,
Type: uint32(req.keyType),
}, nil
@@ -74,20 +74,20 @@ func decodeIssueResponse(_ context.Context, grpcRes interface{}) (interface{}, e
return grpcRes, nil
}
func (client tokenGrpcClient) Refresh(ctx context.Context, req *magistrala.RefreshReq, _ ...grpc.CallOption) (*magistrala.Token, error) {
func (client tokenGrpcClient) Refresh(ctx context.Context, req *grpcTokenV1.RefreshReq, _ ...grpc.CallOption) (*grpcTokenV1.Token, error) {
ctx, cancel := context.WithTimeout(ctx, client.timeout)
defer cancel()
res, err := client.refresh(ctx, refreshReq{refreshToken: req.GetRefreshToken()})
if err != nil {
return &magistrala.Token{}, grpcapi.DecodeError(err)
return &grpcTokenV1.Token{}, grpcapi.DecodeError(err)
}
return res.(*magistrala.Token), nil
return res.(*grpcTokenV1.Token), nil
}
func encodeRefreshRequest(_ context.Context, grpcReq interface{}) (interface{}, error) {
req := grpcReq.(refreshReq)
return &magistrala.RefreshReq{RefreshToken: req.refreshToken}, nil
return &grpcTokenV1.RefreshReq{RefreshToken: req.refreshToken}, nil
}
func decodeRefreshResponse(_ context.Context, grpcRes interface{}) (interface{}, error) {
+14 -16
View File
@@ -10,9 +10,9 @@ import (
"testing"
"time"
"github.com/absmach/magistrala"
"github.com/absmach/magistrala/auth"
grpcapi "github.com/absmach/magistrala/auth/api/grpc/token"
grpcTokenV1 "github.com/absmach/magistrala/internal/grpc/token/v1"
"github.com/absmach/magistrala/internal/testsutil"
"github.com/absmach/magistrala/pkg/apiutil"
"github.com/absmach/magistrala/pkg/errors"
@@ -24,11 +24,11 @@ import (
)
const (
port = 8081
port = 8082
secret = "secret"
email = "test@example.com"
id = "testID"
thingsType = "things"
clientsType = "clients"
usersType = "users"
description = "Description"
groupName = "mgx"
@@ -52,7 +52,7 @@ var (
func startGRPCServer(svc auth.Service, port int) *grpc.Server {
listener, _ := net.Listen("tcp", fmt.Sprintf(":%d", port))
server := grpc.NewServer()
magistrala.RegisterTokenServiceServer(server, grpcapi.NewTokenServer(svc))
grpcTokenV1.RegisterTokenServiceServer(server, grpcapi.NewTokenServer(svc))
go func() {
err := server.Serve(listener)
assert.Nil(&testing.T{}, err, fmt.Sprintf(`"Unexpected error creating auth server %s"`, err))
@@ -63,6 +63,7 @@ func startGRPCServer(svc auth.Service, port int) *grpc.Server {
func TestIssue(t *testing.T) {
conn, err := grpc.NewClient(authAddr, grpc.WithTransportCredentials(insecure.NewCredentials()))
defer conn.Close()
assert.Nil(t, err, fmt.Sprintf("Unexpected error creating client connection %s", err))
grpcClient := grpcapi.NewTokenClient(conn, time.Second)
@@ -117,17 +118,16 @@ func TestIssue(t *testing.T) {
}
for _, tc := range cases {
t.Run(tc.desc, func(t *testing.T) {
svcCall := svc.On("Issue", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(tc.issueResponse, tc.err)
_, err := grpcClient.Issue(context.Background(), &magistrala.IssueReq{UserId: tc.userId, Type: uint32(tc.kind)})
assert.True(t, errors.Contains(err, tc.err), fmt.Sprintf("%s: expected %s got %s\n", tc.desc, tc.err, err))
svcCall.Unset()
})
svcCall := svc.On("Issue", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(tc.issueResponse, tc.err)
_, err := grpcClient.Issue(context.Background(), &grpcTokenV1.IssueReq{UserId: tc.userId, Type: uint32(tc.kind)})
assert.True(t, errors.Contains(err, tc.err), fmt.Sprintf("%s: expected %s got %s\n", tc.desc, tc.err, err))
svcCall.Unset()
}
}
func TestRefresh(t *testing.T) {
conn, err := grpc.NewClient(authAddr, grpc.WithTransportCredentials(insecure.NewCredentials()))
defer conn.Close()
assert.Nil(t, err, fmt.Sprintf("Unexpected error creating client connection %s", err))
grpcClient := grpcapi.NewTokenClient(conn, time.Second)
@@ -161,11 +161,9 @@ func TestRefresh(t *testing.T) {
}
for _, tc := range cases {
t.Run(tc.desc, func(t *testing.T) {
svcCall := svc.On("Issue", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(tc.issueResponse, tc.err)
_, err := grpcClient.Refresh(context.Background(), &magistrala.RefreshReq{RefreshToken: tc.token})
assert.True(t, errors.Contains(err, tc.err), fmt.Sprintf("%s: expected %s got %s\n", tc.desc, tc.err, err))
svcCall.Unset()
})
svcCall := svc.On("Issue", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(tc.issueResponse, tc.err)
_, err := grpcClient.Refresh(context.Background(), &grpcTokenV1.RefreshReq{RefreshToken: tc.token})
assert.True(t, errors.Contains(err, tc.err), fmt.Sprintf("%s: expected %s got %s\n", tc.desc, tc.err, err))
svcCall.Unset()
}
}
+11 -11
View File
@@ -6,22 +6,22 @@ package token
import (
"context"
"github.com/absmach/magistrala"
"github.com/absmach/magistrala/auth"
grpcapi "github.com/absmach/magistrala/auth/api/grpc"
grpcTokenV1 "github.com/absmach/magistrala/internal/grpc/token/v1"
kitgrpc "github.com/go-kit/kit/transport/grpc"
)
var _ magistrala.TokenServiceServer = (*tokenGrpcServer)(nil)
var _ grpcTokenV1.TokenServiceServer = (*tokenGrpcServer)(nil)
type tokenGrpcServer struct {
magistrala.UnimplementedTokenServiceServer
grpcTokenV1.UnimplementedTokenServiceServer
issue kitgrpc.Handler
refresh kitgrpc.Handler
}
// NewAuthServer returns new AuthnServiceServer instance.
func NewTokenServer(svc auth.Service) magistrala.TokenServiceServer {
func NewTokenServer(svc auth.Service) grpcTokenV1.TokenServiceServer {
return &tokenGrpcServer{
issue: kitgrpc.NewServer(
(issueEndpoint(svc)),
@@ -36,24 +36,24 @@ func NewTokenServer(svc auth.Service) magistrala.TokenServiceServer {
}
}
func (s *tokenGrpcServer) Issue(ctx context.Context, req *magistrala.IssueReq) (*magistrala.Token, error) {
func (s *tokenGrpcServer) Issue(ctx context.Context, req *grpcTokenV1.IssueReq) (*grpcTokenV1.Token, error) {
_, res, err := s.issue.ServeGRPC(ctx, req)
if err != nil {
return nil, grpcapi.EncodeError(err)
}
return res.(*magistrala.Token), nil
return res.(*grpcTokenV1.Token), nil
}
func (s *tokenGrpcServer) Refresh(ctx context.Context, req *magistrala.RefreshReq) (*magistrala.Token, error) {
func (s *tokenGrpcServer) Refresh(ctx context.Context, req *grpcTokenV1.RefreshReq) (*grpcTokenV1.Token, error) {
_, res, err := s.refresh.ServeGRPC(ctx, req)
if err != nil {
return nil, grpcapi.EncodeError(err)
}
return res.(*magistrala.Token), nil
return res.(*grpcTokenV1.Token), nil
}
func decodeIssueRequest(_ context.Context, grpcReq interface{}) (interface{}, error) {
req := grpcReq.(*magistrala.IssueReq)
req := grpcReq.(*grpcTokenV1.IssueReq)
return issueReq{
userID: req.GetUserId(),
keyType: auth.KeyType(req.GetType()),
@@ -61,14 +61,14 @@ func decodeIssueRequest(_ context.Context, grpcReq interface{}) (interface{}, er
}
func decodeRefreshRequest(_ context.Context, grpcReq interface{}) (interface{}, error) {
req := grpcReq.(*magistrala.RefreshReq)
req := grpcReq.(*grpcTokenV1.RefreshReq)
return refreshReq{refreshToken: req.GetRefreshToken()}, nil
}
func encodeIssueResponse(_ context.Context, grpcRes interface{}) (interface{}, error) {
res := grpcRes.(issueRes)
return &magistrala.Token{
return &grpcTokenV1.Token{
AccessToken: res.accessToken,
RefreshToken: &res.refreshToken,
AccessType: res.accessType,
-225
View File
@@ -1,225 +0,0 @@
// Copyright (c) Abstract Machines
// SPDX-License-Identifier: Apache-2.0
package domains
import (
"context"
"github.com/absmach/magistrala/auth"
"github.com/absmach/magistrala/pkg/apiutil"
"github.com/absmach/magistrala/pkg/errors"
"github.com/go-kit/kit/endpoint"
)
func createDomainEndpoint(svc auth.Service) endpoint.Endpoint {
return func(ctx context.Context, request interface{}) (interface{}, error) {
req := request.(createDomainReq)
if err := req.validate(); err != nil {
return nil, err
}
d := auth.Domain{
Name: req.Name,
Metadata: req.Metadata,
Tags: req.Tags,
Alias: req.Alias,
}
domain, err := svc.CreateDomain(ctx, req.token, d)
if err != nil {
return nil, err
}
return createDomainRes{domain}, nil
}
}
func retrieveDomainEndpoint(svc auth.Service) endpoint.Endpoint {
return func(ctx context.Context, request interface{}) (interface{}, error) {
req := request.(retrieveDomainRequest)
if err := req.validate(); err != nil {
return nil, err
}
domain, err := svc.RetrieveDomain(ctx, req.token, req.domainID)
if err != nil {
return nil, err
}
return retrieveDomainRes{domain}, nil
}
}
func retrieveDomainPermissionsEndpoint(svc auth.Service) endpoint.Endpoint {
return func(ctx context.Context, request interface{}) (interface{}, error) {
req := request.(retrieveDomainPermissionsRequest)
if err := req.validate(); err != nil {
return nil, err
}
permissions, err := svc.RetrieveDomainPermissions(ctx, req.token, req.domainID)
if err != nil {
return nil, err
}
return retrieveDomainPermissionsRes{Permissions: permissions}, nil
}
}
func updateDomainEndpoint(svc auth.Service) endpoint.Endpoint {
return func(ctx context.Context, request interface{}) (interface{}, error) {
req := request.(updateDomainReq)
if err := req.validate(); err != nil {
return nil, err
}
var metadata auth.Metadata
if req.Metadata != nil {
metadata = *req.Metadata
}
d := auth.DomainReq{
Name: req.Name,
Metadata: &metadata,
Tags: req.Tags,
Alias: req.Alias,
}
domain, err := svc.UpdateDomain(ctx, req.token, req.domainID, d)
if err != nil {
return nil, err
}
return updateDomainRes{domain}, nil
}
}
func listDomainsEndpoint(svc auth.Service) endpoint.Endpoint {
return func(ctx context.Context, request interface{}) (interface{}, error) {
req := request.(listDomainsReq)
if err := req.validate(); err != nil {
return nil, errors.Wrap(apiutil.ErrValidation, err)
}
page := auth.Page{
Offset: req.offset,
Limit: req.limit,
Name: req.name,
Metadata: req.metadata,
Order: req.order,
Dir: req.dir,
Tag: req.tag,
Permission: req.permission,
Status: req.status,
}
dp, err := svc.ListDomains(ctx, req.token, page)
if err != nil {
return nil, err
}
return listDomainsRes{dp}, nil
}
}
func enableDomainEndpoint(svc auth.Service) endpoint.Endpoint {
return func(ctx context.Context, request interface{}) (interface{}, error) {
req := request.(enableDomainReq)
if err := req.validate(); err != nil {
return nil, err
}
enable := auth.EnabledStatus
d := auth.DomainReq{
Status: &enable,
}
if _, err := svc.ChangeDomainStatus(ctx, req.token, req.domainID, d); err != nil {
return nil, err
}
return enableDomainRes{}, nil
}
}
func disableDomainEndpoint(svc auth.Service) endpoint.Endpoint {
return func(ctx context.Context, request interface{}) (interface{}, error) {
req := request.(disableDomainReq)
if err := req.validate(); err != nil {
return nil, err
}
disable := auth.DisabledStatus
d := auth.DomainReq{
Status: &disable,
}
if _, err := svc.ChangeDomainStatus(ctx, req.token, req.domainID, d); err != nil {
return nil, err
}
return disableDomainRes{}, nil
}
}
func freezeDomainEndpoint(svc auth.Service) endpoint.Endpoint {
return func(ctx context.Context, request interface{}) (interface{}, error) {
req := request.(freezeDomainReq)
if err := req.validate(); err != nil {
return nil, err
}
freeze := auth.FreezeStatus
d := auth.DomainReq{
Status: &freeze,
}
if _, err := svc.ChangeDomainStatus(ctx, req.token, req.domainID, d); err != nil {
return nil, err
}
return freezeDomainRes{}, nil
}
}
func assignDomainUsersEndpoint(svc auth.Service) endpoint.Endpoint {
return func(ctx context.Context, request interface{}) (interface{}, error) {
req := request.(assignUsersReq)
if err := req.validate(); err != nil {
return nil, err
}
if err := svc.AssignUsers(ctx, req.token, req.domainID, req.UserIDs, req.Relation); err != nil {
return nil, err
}
return assignUsersRes{}, nil
}
}
func unassignDomainUserEndpoint(svc auth.Service) endpoint.Endpoint {
return func(ctx context.Context, request interface{}) (interface{}, error) {
req := request.(unassignUserReq)
if err := req.validate(); err != nil {
return nil, err
}
if err := svc.UnassignUser(ctx, req.token, req.domainID, req.UserID); err != nil {
return nil, err
}
return unassignUsersRes{}, nil
}
}
func listUserDomainsEndpoint(svc auth.Service) endpoint.Endpoint {
return func(ctx context.Context, request interface{}) (interface{}, error) {
req := request.(listUserDomainsReq)
if err := req.validate(); err != nil {
return nil, err
}
page := auth.Page{
Offset: req.offset,
Limit: req.limit,
Name: req.name,
Metadata: req.metadata,
Order: req.order,
Dir: req.dir,
Tag: req.tag,
Permission: req.permission,
Status: req.status,
}
dp, err := svc.ListUserDomains(ctx, req.token, req.userID, page)
if err != nil {
return nil, err
}
return listUserDomainsRes{dp}, nil
}
}
File diff suppressed because it is too large Load Diff
+1 -3
View File
@@ -69,14 +69,12 @@ func (tr testRequest) make() (*http.Response, error) {
func newService() (auth.Service, *mocks.KeyRepository) {
krepo := new(mocks.KeyRepository)
drepo := new(mocks.DomainsRepository)
idProvider := uuid.NewMock()
pService := new(policymocks.Service)
pEvaluator := new(policymocks.Evaluator)
t := jwt.New([]byte(secret))
return auth.New(krepo, drepo, idProvider, t, pEvaluator, pService, loginDuration, refreshDuration, invalidDuration), krepo
return auth.New(krepo, idProvider, t, pEvaluator, pService, loginDuration, refreshDuration, invalidDuration), krepo
}
func newServer(svc auth.Service) *httptest.Server {
-2
View File
@@ -8,7 +8,6 @@ import (
"github.com/absmach/magistrala"
"github.com/absmach/magistrala/auth"
"github.com/absmach/magistrala/auth/api/http/domains"
"github.com/absmach/magistrala/auth/api/http/keys"
"github.com/go-chi/chi/v5"
"github.com/prometheus/client_golang/prometheus/promhttp"
@@ -19,7 +18,6 @@ func MakeHandler(svc auth.Service, logger *slog.Logger, instanceID string) http.
mux := chi.NewRouter()
mux = keys.MakeHandler(svc, mux, logger)
mux = domains.MakeHandler(svc, mux, logger)
mux.Get("/health", magistrala.Health("auth", instanceID))
mux.Handle("/metrics", promhttp.Handler())
-177
View File
@@ -124,180 +124,3 @@ func (lm *loggingMiddleware) Authorize(ctx context.Context, pr policies.Policy)
}(time.Now())
return lm.svc.Authorize(ctx, pr)
}
func (lm *loggingMiddleware) CreateDomain(ctx context.Context, token string, d auth.Domain) (do auth.Domain, err error) {
defer func(begin time.Time) {
args := []any{
slog.String("duration", time.Since(begin).String()),
slog.Group("domain",
slog.String("id", d.ID),
slog.String("name", d.Name),
),
}
if err != nil {
args := append(args, slog.String("error", err.Error()))
lm.logger.Warn("Create domain failed", args...)
return
}
lm.logger.Info("Create domain completed successfully", args...)
}(time.Now())
return lm.svc.CreateDomain(ctx, token, d)
}
func (lm *loggingMiddleware) RetrieveDomain(ctx context.Context, token, id string) (do auth.Domain, err error) {
defer func(begin time.Time) {
args := []any{
slog.String("duration", time.Since(begin).String()),
slog.String("domain_id", id),
}
if err != nil {
args = append(args, slog.Any("error", err))
lm.logger.Warn("Retrieve domain failed", args...)
return
}
lm.logger.Info("Retrieve domain completed successfully", args...)
}(time.Now())
return lm.svc.RetrieveDomain(ctx, token, id)
}
func (lm *loggingMiddleware) RetrieveDomainPermissions(ctx context.Context, token, id string) (permissions policies.Permissions, err error) {
defer func(begin time.Time) {
args := []any{
slog.String("duration", time.Since(begin).String()),
slog.String("domain_id", id),
}
if err != nil {
args = append(args, slog.Any("error", err))
lm.logger.Warn("Retrieve domain permissions failed", args...)
return
}
lm.logger.Info("Retrieve domain permissions completed successfully", args...)
}(time.Now())
return lm.svc.RetrieveDomainPermissions(ctx, token, id)
}
func (lm *loggingMiddleware) UpdateDomain(ctx context.Context, token, id string, d auth.DomainReq) (do auth.Domain, err error) {
defer func(begin time.Time) {
args := []any{
slog.String("duration", time.Since(begin).String()),
slog.Group("domain",
slog.String("id", id),
slog.Any("name", d.Name),
),
}
if err != nil {
args = append(args, slog.Any("error", err))
lm.logger.Warn("Update domain failed", args...)
return
}
lm.logger.Info("Update domain completed successfully", args...)
}(time.Now())
return lm.svc.UpdateDomain(ctx, token, id, d)
}
func (lm *loggingMiddleware) ChangeDomainStatus(ctx context.Context, token, id string, d auth.DomainReq) (do auth.Domain, err error) {
defer func(begin time.Time) {
args := []any{
slog.String("duration", time.Since(begin).String()),
slog.Group("domain",
slog.String("id", id),
slog.String("name", do.Name),
slog.Any("status", d.Status),
),
}
if err != nil {
args = append(args, slog.Any("error", err))
lm.logger.Warn("Change domain status failed", args...)
return
}
lm.logger.Info("Change domain status completed successfully", args...)
}(time.Now())
return lm.svc.ChangeDomainStatus(ctx, token, id, d)
}
func (lm *loggingMiddleware) ListDomains(ctx context.Context, token string, page auth.Page) (do auth.DomainsPage, err error) {
defer func(begin time.Time) {
args := []any{
slog.String("duration", time.Since(begin).String()),
slog.Group("page",
slog.Uint64("limit", page.Limit),
slog.Uint64("offset", page.Offset),
slog.Uint64("total", page.Total),
),
}
if err != nil {
args = append(args, slog.Any("error", err))
lm.logger.Warn("List domains failed", args...)
return
}
lm.logger.Info("List domains completed successfully", args...)
}(time.Now())
return lm.svc.ListDomains(ctx, token, page)
}
func (lm *loggingMiddleware) AssignUsers(ctx context.Context, token, id string, userIds []string, relation string) (err error) {
defer func(begin time.Time) {
args := []any{
slog.String("duration", time.Since(begin).String()),
slog.String("domain_id", id),
slog.String("relation", relation),
slog.Any("user_ids", userIds),
}
if err != nil {
args = append(args, slog.Any("error", err))
lm.logger.Warn("Assign users to domain failed", args...)
return
}
lm.logger.Info("Assign users to domain completed successfully", args...)
}(time.Now())
return lm.svc.AssignUsers(ctx, token, id, userIds, relation)
}
func (lm *loggingMiddleware) UnassignUser(ctx context.Context, token, id, userID string) (err error) {
defer func(begin time.Time) {
args := []any{
slog.String("duration", time.Since(begin).String()),
slog.String("domain_id", id),
slog.Any("user_id", userID),
}
if err != nil {
args = append(args, slog.Any("error", err))
lm.logger.Warn("Unassign user from domain failed", args...)
return
}
lm.logger.Info("Unassign user from domain completed successfully", args...)
}(time.Now())
return lm.svc.UnassignUser(ctx, token, id, userID)
}
func (lm *loggingMiddleware) ListUserDomains(ctx context.Context, token, userID string, page auth.Page) (do auth.DomainsPage, err error) {
defer func(begin time.Time) {
args := []any{
slog.String("duration", time.Since(begin).String()),
slog.String("user_id", userID),
}
if err != nil {
args = append(args, slog.Any("error", err))
lm.logger.Warn("List user domains failed", args...)
return
}
lm.logger.Info("List user domains completed successfully", args...)
}(time.Now())
return lm.svc.ListUserDomains(ctx, token, userID, page)
}
func (lm *loggingMiddleware) DeleteUserFromDomains(ctx context.Context, id string) (err error) {
defer func(begin time.Time) {
args := []any{
slog.String("duration", time.Since(begin).String()),
slog.String("id", id),
}
if err != nil {
args = append(args, slog.Any("error", err))
lm.logger.Warn("Delete entity policies failed to complete successfully", args...)
return
}
lm.logger.Info("Delete entity policies completed successfully", args...)
}(time.Now())
return lm.svc.DeleteUserFromDomains(ctx, id)
}
-80
View File
@@ -74,83 +74,3 @@ func (ms *metricsMiddleware) Authorize(ctx context.Context, pr policies.Policy)
}(time.Now())
return ms.svc.Authorize(ctx, pr)
}
func (ms *metricsMiddleware) CreateDomain(ctx context.Context, token string, d auth.Domain) (auth.Domain, error) {
defer func(begin time.Time) {
ms.counter.With("method", "create_domain").Add(1)
ms.latency.With("method", "create_domain").Observe(time.Since(begin).Seconds())
}(time.Now())
return ms.svc.CreateDomain(ctx, token, d)
}
func (ms *metricsMiddleware) RetrieveDomain(ctx context.Context, token, id string) (auth.Domain, error) {
defer func(begin time.Time) {
ms.counter.With("method", "retrieve_domain").Add(1)
ms.latency.With("method", "retrieve_domain").Observe(time.Since(begin).Seconds())
}(time.Now())
return ms.svc.RetrieveDomain(ctx, token, id)
}
func (ms *metricsMiddleware) RetrieveDomainPermissions(ctx context.Context, token, id string) (policies.Permissions, error) {
defer func(begin time.Time) {
ms.counter.With("method", "retrieve_domain_permissions").Add(1)
ms.latency.With("method", "retrieve_domain_permissions").Observe(time.Since(begin).Seconds())
}(time.Now())
return ms.svc.RetrieveDomainPermissions(ctx, token, id)
}
func (ms *metricsMiddleware) UpdateDomain(ctx context.Context, token, id string, d auth.DomainReq) (auth.Domain, error) {
defer func(begin time.Time) {
ms.counter.With("method", "update_domain").Add(1)
ms.latency.With("method", "update_domain").Observe(time.Since(begin).Seconds())
}(time.Now())
return ms.svc.UpdateDomain(ctx, token, id, d)
}
func (ms *metricsMiddleware) ChangeDomainStatus(ctx context.Context, token, id string, d auth.DomainReq) (auth.Domain, error) {
defer func(begin time.Time) {
ms.counter.With("method", "change_domain_status").Add(1)
ms.latency.With("method", "change_domain_status").Observe(time.Since(begin).Seconds())
}(time.Now())
return ms.svc.ChangeDomainStatus(ctx, token, id, d)
}
func (ms *metricsMiddleware) ListDomains(ctx context.Context, token string, page auth.Page) (auth.DomainsPage, error) {
defer func(begin time.Time) {
ms.counter.With("method", "list_domains").Add(1)
ms.latency.With("method", "list_domains").Observe(time.Since(begin).Seconds())
}(time.Now())
return ms.svc.ListDomains(ctx, token, page)
}
func (ms *metricsMiddleware) AssignUsers(ctx context.Context, token, id string, userIds []string, relation string) error {
defer func(begin time.Time) {
ms.counter.With("method", "assign_users").Add(1)
ms.latency.With("method", "assign_users").Observe(time.Since(begin).Seconds())
}(time.Now())
return ms.svc.AssignUsers(ctx, token, id, userIds, relation)
}
func (ms *metricsMiddleware) UnassignUser(ctx context.Context, token, id, userID string) error {
defer func(begin time.Time) {
ms.counter.With("method", "unassign_users").Add(1)
ms.latency.With("method", "unassign_users").Observe(time.Since(begin).Seconds())
}(time.Now())
return ms.svc.UnassignUser(ctx, token, id, userID)
}
func (ms *metricsMiddleware) ListUserDomains(ctx context.Context, token, userID string, page auth.Page) (auth.DomainsPage, error) {
defer func(begin time.Time) {
ms.counter.With("method", "list_user_domains").Add(1)
ms.latency.With("method", "list_user_domains").Observe(time.Since(begin).Seconds())
}(time.Now())
return ms.svc.ListUserDomains(ctx, token, userID, page)
}
func (ms *metricsMiddleware) DeleteUserFromDomains(ctx context.Context, id string) error {
defer func(begin time.Time) {
ms.counter.With("method", "delete_user_from_domains").Add(1)
ms.latency.With("method", "delete_user_from_domains").Observe(time.Since(begin).Seconds())
}(time.Now())
return ms.svc.DeleteUserFromDomains(ctx, id)
}
-221
View File
@@ -1,221 +0,0 @@
// Copyright (c) Abstract Machines
// SPDX-License-Identifier: Apache-2.0
package events
import (
"context"
"github.com/absmach/magistrala/auth"
"github.com/absmach/magistrala/pkg/events"
"github.com/absmach/magistrala/pkg/events/store"
"github.com/absmach/magistrala/pkg/policies"
)
const streamID = "magistrala.auth"
var _ auth.Service = (*eventStore)(nil)
type eventStore struct {
events.Publisher
svc auth.Service
}
// NewEventStoreMiddleware returns wrapper around auth service that sends
// events to event store.
func NewEventStoreMiddleware(ctx context.Context, svc auth.Service, url string) (auth.Service, error) {
publisher, err := store.NewPublisher(ctx, url, streamID)
if err != nil {
return nil, err
}
return &eventStore{
svc: svc,
Publisher: publisher,
}, nil
}
func (es *eventStore) CreateDomain(ctx context.Context, token string, domain auth.Domain) (auth.Domain, error) {
domain, err := es.svc.CreateDomain(ctx, token, domain)
if err != nil {
return domain, err
}
event := createDomainEvent{
domain,
}
if err := es.Publish(ctx, event); err != nil {
return domain, err
}
return domain, nil
}
func (es *eventStore) RetrieveDomain(ctx context.Context, token, id string) (auth.Domain, error) {
domain, err := es.svc.RetrieveDomain(ctx, token, id)
if err != nil {
return domain, err
}
event := retrieveDomainEvent{
domain,
}
if err := es.Publish(ctx, event); err != nil {
return domain, err
}
return domain, nil
}
func (es *eventStore) RetrieveDomainPermissions(ctx context.Context, token, id string) (policies.Permissions, error) {
permissions, err := es.svc.RetrieveDomainPermissions(ctx, token, id)
if err != nil {
return permissions, err
}
event := retrieveDomainPermissionsEvent{
domainID: id,
permissions: permissions,
}
if err := es.Publish(ctx, event); err != nil {
return permissions, err
}
return permissions, nil
}
func (es *eventStore) UpdateDomain(ctx context.Context, token, id string, d auth.DomainReq) (auth.Domain, error) {
domain, err := es.svc.UpdateDomain(ctx, token, id, d)
if err != nil {
return domain, err
}
event := updateDomainEvent{
domain,
}
if err := es.Publish(ctx, event); err != nil {
return domain, err
}
return domain, nil
}
func (es *eventStore) ChangeDomainStatus(ctx context.Context, token, id string, d auth.DomainReq) (auth.Domain, error) {
domain, err := es.svc.ChangeDomainStatus(ctx, token, id, d)
if err != nil {
return domain, err
}
event := changeDomainStatusEvent{
domainID: id,
status: domain.Status,
updatedAt: domain.UpdatedAt,
updatedBy: domain.UpdatedBy,
}
if err := es.Publish(ctx, event); err != nil {
return domain, err
}
return domain, nil
}
func (es *eventStore) ListDomains(ctx context.Context, token string, p auth.Page) (auth.DomainsPage, error) {
dp, err := es.svc.ListDomains(ctx, token, p)
if err != nil {
return dp, err
}
event := listDomainsEvent{
p, dp.Total,
}
if err := es.Publish(ctx, event); err != nil {
return dp, err
}
return dp, nil
}
func (es *eventStore) AssignUsers(ctx context.Context, token, id string, userIds []string, relation string) error {
err := es.svc.AssignUsers(ctx, token, id, userIds, relation)
if err != nil {
return err
}
event := assignUsersEvent{
domainID: id,
userIDs: userIds,
relation: relation,
}
if err := es.Publish(ctx, event); err != nil {
return err
}
return nil
}
func (es *eventStore) UnassignUser(ctx context.Context, token, id, userID string) error {
err := es.svc.UnassignUser(ctx, token, id, userID)
if err != nil {
return err
}
event := unassignUsersEvent{
domainID: id,
userID: userID,
}
if err := es.Publish(ctx, event); err != nil {
return err
}
return nil
}
func (es *eventStore) ListUserDomains(ctx context.Context, token, userID string, p auth.Page) (auth.DomainsPage, error) {
dp, err := es.svc.ListUserDomains(ctx, token, userID, p)
if err != nil {
return dp, err
}
event := listUserDomainsEvent{
Page: p,
userID: userID,
}
if err := es.Publish(ctx, event); err != nil {
return dp, err
}
return dp, nil
}
func (es *eventStore) Issue(ctx context.Context, token string, key auth.Key) (auth.Token, error) {
return es.svc.Issue(ctx, token, key)
}
func (es *eventStore) Revoke(ctx context.Context, token, id string) error {
return es.svc.Revoke(ctx, token, id)
}
func (es *eventStore) RetrieveKey(ctx context.Context, token, id string) (auth.Key, error) {
return es.svc.RetrieveKey(ctx, token, id)
}
func (es *eventStore) Identify(ctx context.Context, token string) (auth.Key, error) {
return es.svc.Identify(ctx, token)
}
func (es *eventStore) Authorize(ctx context.Context, pr policies.Policy) error {
return es.svc.Authorize(ctx, pr)
}
func (es *eventStore) DeleteUserFromDomains(ctx context.Context, id string) error {
return es.svc.DeleteUserFromDomains(ctx, id)
}
-306
View File
@@ -1,306 +0,0 @@
// Code generated by mockery v2.43.2. DO NOT EDIT.
// Copyright (c) Abstract Machines
package mocks
import (
context "context"
auth "github.com/absmach/magistrala/auth"
mock "github.com/stretchr/testify/mock"
)
// DomainsRepository is an autogenerated mock type for the DomainsRepository type
type DomainsRepository struct {
mock.Mock
}
// CheckPolicy provides a mock function with given fields: ctx, pc
func (_m *DomainsRepository) CheckPolicy(ctx context.Context, pc auth.Policy) error {
ret := _m.Called(ctx, pc)
if len(ret) == 0 {
panic("no return value specified for CheckPolicy")
}
var r0 error
if rf, ok := ret.Get(0).(func(context.Context, auth.Policy) error); ok {
r0 = rf(ctx, pc)
} else {
r0 = ret.Error(0)
}
return r0
}
// Delete provides a mock function with given fields: ctx, id
func (_m *DomainsRepository) Delete(ctx context.Context, id string) error {
ret := _m.Called(ctx, id)
if len(ret) == 0 {
panic("no return value specified for Delete")
}
var r0 error
if rf, ok := ret.Get(0).(func(context.Context, string) error); ok {
r0 = rf(ctx, id)
} else {
r0 = ret.Error(0)
}
return r0
}
// DeletePolicies provides a mock function with given fields: ctx, pcs
func (_m *DomainsRepository) DeletePolicies(ctx context.Context, pcs ...auth.Policy) error {
_va := make([]interface{}, len(pcs))
for _i := range pcs {
_va[_i] = pcs[_i]
}
var _ca []interface{}
_ca = append(_ca, ctx)
_ca = append(_ca, _va...)
ret := _m.Called(_ca...)
if len(ret) == 0 {
panic("no return value specified for DeletePolicies")
}
var r0 error
if rf, ok := ret.Get(0).(func(context.Context, ...auth.Policy) error); ok {
r0 = rf(ctx, pcs...)
} else {
r0 = ret.Error(0)
}
return r0
}
// DeleteUserPolicies provides a mock function with given fields: ctx, id
func (_m *DomainsRepository) DeleteUserPolicies(ctx context.Context, id string) error {
ret := _m.Called(ctx, id)
if len(ret) == 0 {
panic("no return value specified for DeleteUserPolicies")
}
var r0 error
if rf, ok := ret.Get(0).(func(context.Context, string) error); ok {
r0 = rf(ctx, id)
} else {
r0 = ret.Error(0)
}
return r0
}
// ListDomains provides a mock function with given fields: ctx, pm
func (_m *DomainsRepository) ListDomains(ctx context.Context, pm auth.Page) (auth.DomainsPage, error) {
ret := _m.Called(ctx, pm)
if len(ret) == 0 {
panic("no return value specified for ListDomains")
}
var r0 auth.DomainsPage
var r1 error
if rf, ok := ret.Get(0).(func(context.Context, auth.Page) (auth.DomainsPage, error)); ok {
return rf(ctx, pm)
}
if rf, ok := ret.Get(0).(func(context.Context, auth.Page) auth.DomainsPage); ok {
r0 = rf(ctx, pm)
} else {
r0 = ret.Get(0).(auth.DomainsPage)
}
if rf, ok := ret.Get(1).(func(context.Context, auth.Page) error); ok {
r1 = rf(ctx, pm)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// RetrieveAllByIDs provides a mock function with given fields: ctx, pm
func (_m *DomainsRepository) RetrieveAllByIDs(ctx context.Context, pm auth.Page) (auth.DomainsPage, error) {
ret := _m.Called(ctx, pm)
if len(ret) == 0 {
panic("no return value specified for RetrieveAllByIDs")
}
var r0 auth.DomainsPage
var r1 error
if rf, ok := ret.Get(0).(func(context.Context, auth.Page) (auth.DomainsPage, error)); ok {
return rf(ctx, pm)
}
if rf, ok := ret.Get(0).(func(context.Context, auth.Page) auth.DomainsPage); ok {
r0 = rf(ctx, pm)
} else {
r0 = ret.Get(0).(auth.DomainsPage)
}
if rf, ok := ret.Get(1).(func(context.Context, auth.Page) error); ok {
r1 = rf(ctx, pm)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// RetrieveByID provides a mock function with given fields: ctx, id
func (_m *DomainsRepository) RetrieveByID(ctx context.Context, id string) (auth.Domain, error) {
ret := _m.Called(ctx, id)
if len(ret) == 0 {
panic("no return value specified for RetrieveByID")
}
var r0 auth.Domain
var r1 error
if rf, ok := ret.Get(0).(func(context.Context, string) (auth.Domain, error)); ok {
return rf(ctx, id)
}
if rf, ok := ret.Get(0).(func(context.Context, string) auth.Domain); ok {
r0 = rf(ctx, id)
} else {
r0 = ret.Get(0).(auth.Domain)
}
if rf, ok := ret.Get(1).(func(context.Context, string) error); ok {
r1 = rf(ctx, id)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// RetrievePermissions provides a mock function with given fields: ctx, subject, id
func (_m *DomainsRepository) RetrievePermissions(ctx context.Context, subject string, id string) ([]string, error) {
ret := _m.Called(ctx, subject, id)
if len(ret) == 0 {
panic("no return value specified for RetrievePermissions")
}
var r0 []string
var r1 error
if rf, ok := ret.Get(0).(func(context.Context, string, string) ([]string, error)); ok {
return rf(ctx, subject, id)
}
if rf, ok := ret.Get(0).(func(context.Context, string, string) []string); ok {
r0 = rf(ctx, subject, id)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).([]string)
}
}
if rf, ok := ret.Get(1).(func(context.Context, string, string) error); ok {
r1 = rf(ctx, subject, id)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// Save provides a mock function with given fields: ctx, d
func (_m *DomainsRepository) Save(ctx context.Context, d auth.Domain) (auth.Domain, error) {
ret := _m.Called(ctx, d)
if len(ret) == 0 {
panic("no return value specified for Save")
}
var r0 auth.Domain
var r1 error
if rf, ok := ret.Get(0).(func(context.Context, auth.Domain) (auth.Domain, error)); ok {
return rf(ctx, d)
}
if rf, ok := ret.Get(0).(func(context.Context, auth.Domain) auth.Domain); ok {
r0 = rf(ctx, d)
} else {
r0 = ret.Get(0).(auth.Domain)
}
if rf, ok := ret.Get(1).(func(context.Context, auth.Domain) error); ok {
r1 = rf(ctx, d)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// SavePolicies provides a mock function with given fields: ctx, pcs
func (_m *DomainsRepository) SavePolicies(ctx context.Context, pcs ...auth.Policy) error {
_va := make([]interface{}, len(pcs))
for _i := range pcs {
_va[_i] = pcs[_i]
}
var _ca []interface{}
_ca = append(_ca, ctx)
_ca = append(_ca, _va...)
ret := _m.Called(_ca...)
if len(ret) == 0 {
panic("no return value specified for SavePolicies")
}
var r0 error
if rf, ok := ret.Get(0).(func(context.Context, ...auth.Policy) error); ok {
r0 = rf(ctx, pcs...)
} else {
r0 = ret.Error(0)
}
return r0
}
// Update provides a mock function with given fields: ctx, id, userID, d
func (_m *DomainsRepository) Update(ctx context.Context, id string, userID string, d auth.DomainReq) (auth.Domain, error) {
ret := _m.Called(ctx, id, userID, d)
if len(ret) == 0 {
panic("no return value specified for Update")
}
var r0 auth.Domain
var r1 error
if rf, ok := ret.Get(0).(func(context.Context, string, string, auth.DomainReq) (auth.Domain, error)); ok {
return rf(ctx, id, userID, d)
}
if rf, ok := ret.Get(0).(func(context.Context, string, string, auth.DomainReq) auth.Domain); ok {
r0 = rf(ctx, id, userID, d)
} else {
r0 = ret.Get(0).(auth.Domain)
}
if rf, ok := ret.Get(1).(func(context.Context, string, string, auth.DomainReq) error); ok {
r1 = rf(ctx, id, userID, d)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// NewDomainsRepository creates a new instance of DomainsRepository. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
// The first argument is typically a *testing.T value.
func NewDomainsRepository(t interface {
mock.TestingT
Cleanup(func())
}) *DomainsRepository {
mock := &DomainsRepository{}
mock.Mock.Test(t)
t.Cleanup(func() { mock.AssertExpectations(t) })
return mock
}
-252
View File
@@ -19,24 +19,6 @@ type Service struct {
mock.Mock
}
// AssignUsers provides a mock function with given fields: ctx, token, id, userIds, relation
func (_m *Service) AssignUsers(ctx context.Context, token string, id string, userIds []string, relation string) error {
ret := _m.Called(ctx, token, id, userIds, relation)
if len(ret) == 0 {
panic("no return value specified for AssignUsers")
}
var r0 error
if rf, ok := ret.Get(0).(func(context.Context, string, string, []string, string) error); ok {
r0 = rf(ctx, token, id, userIds, relation)
} else {
r0 = ret.Error(0)
}
return r0
}
// Authorize provides a mock function with given fields: ctx, pr
func (_m *Service) Authorize(ctx context.Context, pr policies.Policy) error {
ret := _m.Called(ctx, pr)
@@ -55,80 +37,6 @@ func (_m *Service) Authorize(ctx context.Context, pr policies.Policy) error {
return r0
}
// ChangeDomainStatus provides a mock function with given fields: ctx, token, id, d
func (_m *Service) ChangeDomainStatus(ctx context.Context, token string, id string, d auth.DomainReq) (auth.Domain, error) {
ret := _m.Called(ctx, token, id, d)
if len(ret) == 0 {
panic("no return value specified for ChangeDomainStatus")
}
var r0 auth.Domain
var r1 error
if rf, ok := ret.Get(0).(func(context.Context, string, string, auth.DomainReq) (auth.Domain, error)); ok {
return rf(ctx, token, id, d)
}
if rf, ok := ret.Get(0).(func(context.Context, string, string, auth.DomainReq) auth.Domain); ok {
r0 = rf(ctx, token, id, d)
} else {
r0 = ret.Get(0).(auth.Domain)
}
if rf, ok := ret.Get(1).(func(context.Context, string, string, auth.DomainReq) error); ok {
r1 = rf(ctx, token, id, d)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// CreateDomain provides a mock function with given fields: ctx, token, d
func (_m *Service) CreateDomain(ctx context.Context, token string, d auth.Domain) (auth.Domain, error) {
ret := _m.Called(ctx, token, d)
if len(ret) == 0 {
panic("no return value specified for CreateDomain")
}
var r0 auth.Domain
var r1 error
if rf, ok := ret.Get(0).(func(context.Context, string, auth.Domain) (auth.Domain, error)); ok {
return rf(ctx, token, d)
}
if rf, ok := ret.Get(0).(func(context.Context, string, auth.Domain) auth.Domain); ok {
r0 = rf(ctx, token, d)
} else {
r0 = ret.Get(0).(auth.Domain)
}
if rf, ok := ret.Get(1).(func(context.Context, string, auth.Domain) error); ok {
r1 = rf(ctx, token, d)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// DeleteUserFromDomains provides a mock function with given fields: ctx, id
func (_m *Service) DeleteUserFromDomains(ctx context.Context, id string) error {
ret := _m.Called(ctx, id)
if len(ret) == 0 {
panic("no return value specified for DeleteUserFromDomains")
}
var r0 error
if rf, ok := ret.Get(0).(func(context.Context, string) error); ok {
r0 = rf(ctx, id)
} else {
r0 = ret.Error(0)
}
return r0
}
// Identify provides a mock function with given fields: ctx, token
func (_m *Service) Identify(ctx context.Context, token string) (auth.Key, error) {
ret := _m.Called(ctx, token)
@@ -185,120 +93,6 @@ func (_m *Service) Issue(ctx context.Context, token string, key auth.Key) (auth.
return r0, r1
}
// ListDomains provides a mock function with given fields: ctx, token, page
func (_m *Service) ListDomains(ctx context.Context, token string, page auth.Page) (auth.DomainsPage, error) {
ret := _m.Called(ctx, token, page)
if len(ret) == 0 {
panic("no return value specified for ListDomains")
}
var r0 auth.DomainsPage
var r1 error
if rf, ok := ret.Get(0).(func(context.Context, string, auth.Page) (auth.DomainsPage, error)); ok {
return rf(ctx, token, page)
}
if rf, ok := ret.Get(0).(func(context.Context, string, auth.Page) auth.DomainsPage); ok {
r0 = rf(ctx, token, page)
} else {
r0 = ret.Get(0).(auth.DomainsPage)
}
if rf, ok := ret.Get(1).(func(context.Context, string, auth.Page) error); ok {
r1 = rf(ctx, token, page)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// ListUserDomains provides a mock function with given fields: ctx, token, userID, page
func (_m *Service) ListUserDomains(ctx context.Context, token string, userID string, page auth.Page) (auth.DomainsPage, error) {
ret := _m.Called(ctx, token, userID, page)
if len(ret) == 0 {
panic("no return value specified for ListUserDomains")
}
var r0 auth.DomainsPage
var r1 error
if rf, ok := ret.Get(0).(func(context.Context, string, string, auth.Page) (auth.DomainsPage, error)); ok {
return rf(ctx, token, userID, page)
}
if rf, ok := ret.Get(0).(func(context.Context, string, string, auth.Page) auth.DomainsPage); ok {
r0 = rf(ctx, token, userID, page)
} else {
r0 = ret.Get(0).(auth.DomainsPage)
}
if rf, ok := ret.Get(1).(func(context.Context, string, string, auth.Page) error); ok {
r1 = rf(ctx, token, userID, page)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// RetrieveDomain provides a mock function with given fields: ctx, token, id
func (_m *Service) RetrieveDomain(ctx context.Context, token string, id string) (auth.Domain, error) {
ret := _m.Called(ctx, token, id)
if len(ret) == 0 {
panic("no return value specified for RetrieveDomain")
}
var r0 auth.Domain
var r1 error
if rf, ok := ret.Get(0).(func(context.Context, string, string) (auth.Domain, error)); ok {
return rf(ctx, token, id)
}
if rf, ok := ret.Get(0).(func(context.Context, string, string) auth.Domain); ok {
r0 = rf(ctx, token, id)
} else {
r0 = ret.Get(0).(auth.Domain)
}
if rf, ok := ret.Get(1).(func(context.Context, string, string) error); ok {
r1 = rf(ctx, token, id)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// RetrieveDomainPermissions provides a mock function with given fields: ctx, token, id
func (_m *Service) RetrieveDomainPermissions(ctx context.Context, token string, id string) (policies.Permissions, error) {
ret := _m.Called(ctx, token, id)
if len(ret) == 0 {
panic("no return value specified for RetrieveDomainPermissions")
}
var r0 policies.Permissions
var r1 error
if rf, ok := ret.Get(0).(func(context.Context, string, string) (policies.Permissions, error)); ok {
return rf(ctx, token, id)
}
if rf, ok := ret.Get(0).(func(context.Context, string, string) policies.Permissions); ok {
r0 = rf(ctx, token, id)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(policies.Permissions)
}
}
if rf, ok := ret.Get(1).(func(context.Context, string, string) error); ok {
r1 = rf(ctx, token, id)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// RetrieveKey provides a mock function with given fields: ctx, token, id
func (_m *Service) RetrieveKey(ctx context.Context, token string, id string) (auth.Key, error) {
ret := _m.Called(ctx, token, id)
@@ -345,52 +139,6 @@ func (_m *Service) Revoke(ctx context.Context, token string, id string) error {
return r0
}
// UnassignUser provides a mock function with given fields: ctx, token, id, userID
func (_m *Service) UnassignUser(ctx context.Context, token string, id string, userID string) error {
ret := _m.Called(ctx, token, id, userID)
if len(ret) == 0 {
panic("no return value specified for UnassignUser")
}
var r0 error
if rf, ok := ret.Get(0).(func(context.Context, string, string, string) error); ok {
r0 = rf(ctx, token, id, userID)
} else {
r0 = ret.Error(0)
}
return r0
}
// UpdateDomain provides a mock function with given fields: ctx, token, id, d
func (_m *Service) UpdateDomain(ctx context.Context, token string, id string, d auth.DomainReq) (auth.Domain, error) {
ret := _m.Called(ctx, token, id, d)
if len(ret) == 0 {
panic("no return value specified for UpdateDomain")
}
var r0 auth.Domain
var r1 error
if rf, ok := ret.Get(0).(func(context.Context, string, string, auth.DomainReq) (auth.Domain, error)); ok {
return rf(ctx, token, id, d)
}
if rf, ok := ret.Get(0).(func(context.Context, string, string, auth.DomainReq) auth.Domain); ok {
r0 = rf(ctx, token, id, d)
} else {
r0 = ret.Get(0).(auth.Domain)
}
if rf, ok := ret.Get(1).(func(context.Context, string, string, auth.DomainReq) error); ok {
r1 = rf(ctx, token, id, d)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// NewService creates a new instance of Service. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
// The first argument is typically a *testing.T value.
func NewService(t interface {
+24 -24
View File
@@ -11,9 +11,9 @@ import (
grpc "google.golang.org/grpc"
magistrala "github.com/absmach/magistrala"
mock "github.com/stretchr/testify/mock"
v1 "github.com/absmach/magistrala/internal/grpc/token/v1"
)
// TokenServiceClient is an autogenerated mock type for the TokenServiceClient type
@@ -30,7 +30,7 @@ func (_m *TokenServiceClient) EXPECT() *TokenServiceClient_Expecter {
}
// Issue provides a mock function with given fields: ctx, in, opts
func (_m *TokenServiceClient) Issue(ctx context.Context, in *magistrala.IssueReq, opts ...grpc.CallOption) (*magistrala.Token, error) {
func (_m *TokenServiceClient) Issue(ctx context.Context, in *v1.IssueReq, opts ...grpc.CallOption) (*v1.Token, error) {
_va := make([]interface{}, len(opts))
for _i := range opts {
_va[_i] = opts[_i]
@@ -44,20 +44,20 @@ func (_m *TokenServiceClient) Issue(ctx context.Context, in *magistrala.IssueReq
panic("no return value specified for Issue")
}
var r0 *magistrala.Token
var r0 *v1.Token
var r1 error
if rf, ok := ret.Get(0).(func(context.Context, *magistrala.IssueReq, ...grpc.CallOption) (*magistrala.Token, error)); ok {
if rf, ok := ret.Get(0).(func(context.Context, *v1.IssueReq, ...grpc.CallOption) (*v1.Token, error)); ok {
return rf(ctx, in, opts...)
}
if rf, ok := ret.Get(0).(func(context.Context, *magistrala.IssueReq, ...grpc.CallOption) *magistrala.Token); ok {
if rf, ok := ret.Get(0).(func(context.Context, *v1.IssueReq, ...grpc.CallOption) *v1.Token); ok {
r0 = rf(ctx, in, opts...)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*magistrala.Token)
r0 = ret.Get(0).(*v1.Token)
}
}
if rf, ok := ret.Get(1).(func(context.Context, *magistrala.IssueReq, ...grpc.CallOption) error); ok {
if rf, ok := ret.Get(1).(func(context.Context, *v1.IssueReq, ...grpc.CallOption) error); ok {
r1 = rf(ctx, in, opts...)
} else {
r1 = ret.Error(1)
@@ -73,14 +73,14 @@ type TokenServiceClient_Issue_Call struct {
// Issue is a helper method to define mock.On call
// - ctx context.Context
// - in *magistrala.IssueReq
// - in *v1.IssueReq
// - opts ...grpc.CallOption
func (_e *TokenServiceClient_Expecter) Issue(ctx interface{}, in interface{}, opts ...interface{}) *TokenServiceClient_Issue_Call {
return &TokenServiceClient_Issue_Call{Call: _e.mock.On("Issue",
append([]interface{}{ctx, in}, opts...)...)}
}
func (_c *TokenServiceClient_Issue_Call) Run(run func(ctx context.Context, in *magistrala.IssueReq, opts ...grpc.CallOption)) *TokenServiceClient_Issue_Call {
func (_c *TokenServiceClient_Issue_Call) Run(run func(ctx context.Context, in *v1.IssueReq, opts ...grpc.CallOption)) *TokenServiceClient_Issue_Call {
_c.Call.Run(func(args mock.Arguments) {
variadicArgs := make([]grpc.CallOption, len(args)-2)
for i, a := range args[2:] {
@@ -88,23 +88,23 @@ func (_c *TokenServiceClient_Issue_Call) Run(run func(ctx context.Context, in *m
variadicArgs[i] = a.(grpc.CallOption)
}
}
run(args[0].(context.Context), args[1].(*magistrala.IssueReq), variadicArgs...)
run(args[0].(context.Context), args[1].(*v1.IssueReq), variadicArgs...)
})
return _c
}
func (_c *TokenServiceClient_Issue_Call) Return(_a0 *magistrala.Token, _a1 error) *TokenServiceClient_Issue_Call {
func (_c *TokenServiceClient_Issue_Call) Return(_a0 *v1.Token, _a1 error) *TokenServiceClient_Issue_Call {
_c.Call.Return(_a0, _a1)
return _c
}
func (_c *TokenServiceClient_Issue_Call) RunAndReturn(run func(context.Context, *magistrala.IssueReq, ...grpc.CallOption) (*magistrala.Token, error)) *TokenServiceClient_Issue_Call {
func (_c *TokenServiceClient_Issue_Call) RunAndReturn(run func(context.Context, *v1.IssueReq, ...grpc.CallOption) (*v1.Token, error)) *TokenServiceClient_Issue_Call {
_c.Call.Return(run)
return _c
}
// Refresh provides a mock function with given fields: ctx, in, opts
func (_m *TokenServiceClient) Refresh(ctx context.Context, in *magistrala.RefreshReq, opts ...grpc.CallOption) (*magistrala.Token, error) {
func (_m *TokenServiceClient) Refresh(ctx context.Context, in *v1.RefreshReq, opts ...grpc.CallOption) (*v1.Token, error) {
_va := make([]interface{}, len(opts))
for _i := range opts {
_va[_i] = opts[_i]
@@ -118,20 +118,20 @@ func (_m *TokenServiceClient) Refresh(ctx context.Context, in *magistrala.Refres
panic("no return value specified for Refresh")
}
var r0 *magistrala.Token
var r0 *v1.Token
var r1 error
if rf, ok := ret.Get(0).(func(context.Context, *magistrala.RefreshReq, ...grpc.CallOption) (*magistrala.Token, error)); ok {
if rf, ok := ret.Get(0).(func(context.Context, *v1.RefreshReq, ...grpc.CallOption) (*v1.Token, error)); ok {
return rf(ctx, in, opts...)
}
if rf, ok := ret.Get(0).(func(context.Context, *magistrala.RefreshReq, ...grpc.CallOption) *magistrala.Token); ok {
if rf, ok := ret.Get(0).(func(context.Context, *v1.RefreshReq, ...grpc.CallOption) *v1.Token); ok {
r0 = rf(ctx, in, opts...)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*magistrala.Token)
r0 = ret.Get(0).(*v1.Token)
}
}
if rf, ok := ret.Get(1).(func(context.Context, *magistrala.RefreshReq, ...grpc.CallOption) error); ok {
if rf, ok := ret.Get(1).(func(context.Context, *v1.RefreshReq, ...grpc.CallOption) error); ok {
r1 = rf(ctx, in, opts...)
} else {
r1 = ret.Error(1)
@@ -147,14 +147,14 @@ type TokenServiceClient_Refresh_Call struct {
// Refresh is a helper method to define mock.On call
// - ctx context.Context
// - in *magistrala.RefreshReq
// - in *v1.RefreshReq
// - opts ...grpc.CallOption
func (_e *TokenServiceClient_Expecter) Refresh(ctx interface{}, in interface{}, opts ...interface{}) *TokenServiceClient_Refresh_Call {
return &TokenServiceClient_Refresh_Call{Call: _e.mock.On("Refresh",
append([]interface{}{ctx, in}, opts...)...)}
}
func (_c *TokenServiceClient_Refresh_Call) Run(run func(ctx context.Context, in *magistrala.RefreshReq, opts ...grpc.CallOption)) *TokenServiceClient_Refresh_Call {
func (_c *TokenServiceClient_Refresh_Call) Run(run func(ctx context.Context, in *v1.RefreshReq, opts ...grpc.CallOption)) *TokenServiceClient_Refresh_Call {
_c.Call.Run(func(args mock.Arguments) {
variadicArgs := make([]grpc.CallOption, len(args)-2)
for i, a := range args[2:] {
@@ -162,17 +162,17 @@ func (_c *TokenServiceClient_Refresh_Call) Run(run func(ctx context.Context, in
variadicArgs[i] = a.(grpc.CallOption)
}
}
run(args[0].(context.Context), args[1].(*magistrala.RefreshReq), variadicArgs...)
run(args[0].(context.Context), args[1].(*v1.RefreshReq), variadicArgs...)
})
return _c
}
func (_c *TokenServiceClient_Refresh_Call) Return(_a0 *magistrala.Token, _a1 error) *TokenServiceClient_Refresh_Call {
func (_c *TokenServiceClient_Refresh_Call) Return(_a0 *v1.Token, _a1 error) *TokenServiceClient_Refresh_Call {
_c.Call.Return(_a0, _a1)
return _c
}
func (_c *TokenServiceClient_Refresh_Call) RunAndReturn(run func(context.Context, *magistrala.RefreshReq, ...grpc.CallOption) (*magistrala.Token, error)) *TokenServiceClient_Refresh_Call {
func (_c *TokenServiceClient_Refresh_Call) RunAndReturn(run func(context.Context, *v1.RefreshReq, ...grpc.CallOption) (*v1.Token, error)) *TokenServiceClient_Refresh_Call {
_c.Call.Return(run)
return _c
}
File diff suppressed because it is too large Load Diff
+8
View File
@@ -57,6 +57,14 @@ func Migration() *migrate.MemoryMigrationSource {
`ALTER TABLE domains ALTER COLUMN alias SET NOT NULL`,
},
},
{
Id: "auth_3",
Up: []string{
`DROP TABLE IF EXISTS policies;
DROP TABLE IF EXISTS domains;
`,
},
},
},
}
}
+10 -498
View File
@@ -5,7 +5,6 @@ package auth
import (
"context"
"fmt"
"strings"
"time"
@@ -24,18 +23,12 @@ var (
// ErrExpiry indicates that the token is expired.
ErrExpiry = errors.New("token is expired")
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")
errRetrieve = errors.New("failed to retrieve key data")
errIdentify = errors.New("failed to validate token")
errPlatform = errors.New("invalid platform id")
errCreateDomainPolicy = errors.New("failed to create domain policy")
errAddPolicies = errors.New("failed to add policies")
errRemovePolicies = errors.New("failed to remove the policies")
errRollbackPolicy = errors.New("failed to rollback policy")
errRemoveLocalPolicy = errors.New("failed to remove from local policy copy")
errRemovePolicyEngine = errors.New("failed to remove from policy engine")
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")
errRetrieve = errors.New("failed to retrieve key data")
errIdentify = errors.New("failed to validate token")
errPlatform = errors.New("invalid platform id")
)
// Authz represents a authorization service. It exposes
@@ -82,14 +75,12 @@ type Authn interface {
type Service interface {
Authn
Authz
Domains
}
var _ Service = (*service)(nil)
type service struct {
keys KeyRepository
domains DomainsRepository
idProvider magistrala.IDProvider
evaluator policies.Evaluator
policysvc policies.Service
@@ -100,10 +91,9 @@ type service struct {
}
// New instantiates the auth service implementation.
func New(keys KeyRepository, domains DomainsRepository, idp magistrala.IDProvider, tokenizer Tokenizer, policyEvaluator policies.Evaluator, policyService policies.Service, loginDuration, refreshDuration, invitationDuration time.Duration) Service {
func New(keys KeyRepository, idp magistrala.IDProvider, tokenizer Tokenizer, policyEvaluator policies.Evaluator, policyService policies.Service, loginDuration, refreshDuration, invitationDuration time.Duration) Service {
return &service{
tokenizer: tokenizer,
domains: domains,
keys: keys,
idProvider: idp,
evaluator: policyEvaluator,
@@ -188,7 +178,7 @@ func (svc service) Authorize(ctx context.Context, pr policies.Policy) error {
return errors.Wrap(svcerr.ErrAuthentication, err)
}
if key.Subject == "" {
if pr.ObjectType == policies.GroupType || pr.ObjectType == policies.ThingType || pr.ObjectType == policies.DomainType {
if pr.ObjectType == policies.GroupType || pr.ObjectType == policies.ClientType || pr.ObjectType == policies.DomainType {
return svcerr.ErrDomainAuthorization
}
return svcerr.ErrAuthentication
@@ -203,8 +193,8 @@ func (svc service) Authorize(ctx context.Context, pr policies.Policy) error {
}
func (svc service) checkPolicy(ctx context.Context, pr policies.Policy) error {
// Domain status is required for if user sent authorization request on things, channels, groups and domains
if pr.SubjectType == policies.UserType && (pr.ObjectType == policies.GroupType || pr.ObjectType == policies.ThingType || pr.ObjectType == policies.DomainType) {
// Domain status is required for if user sent authorization request on clients, channels, groups and domains
if pr.SubjectType == policies.UserType && (pr.ObjectType == policies.GroupType || pr.ObjectType == policies.ClientType || pr.ObjectType == policies.DomainType) {
domainID := pr.Domain
if domainID == "" {
if pr.ObjectType != policies.DomainType {
@@ -233,37 +223,6 @@ func (svc service) checkDomain(ctx context.Context, subjectType, subject, domain
return svcerr.ErrDomainAuthorization
}
d, err := svc.domains.RetrieveByID(ctx, domainID)
if err != nil {
return errors.Wrap(svcerr.ErrViewEntity, err)
}
switch d.Status {
case EnabledStatus:
case DisabledStatus:
if err := svc.evaluator.CheckPolicy(ctx, policies.Policy{
Subject: subject,
SubjectType: subjectType,
Permission: policies.AdminPermission,
Object: domainID,
ObjectType: policies.DomainType,
}); err != nil {
return svcerr.ErrDomainAuthorization
}
case FreezeStatus:
if err := svc.evaluator.CheckPolicy(ctx, policies.Policy{
Subject: subject,
SubjectType: subjectType,
Permission: policies.AdminPermission,
Object: policies.MagistralaObject,
ObjectType: policies.PlatformType,
}); err != nil {
return svcerr.ErrDomainAuthorization
}
default:
return svcerr.ErrDomainAuthorization
}
return nil
}
@@ -451,405 +410,6 @@ func SwitchToPermission(relation string) string {
}
}
func (svc service) CreateDomain(ctx context.Context, token string, d Domain) (do Domain, err error) {
key, err := svc.Identify(ctx, token)
if err != nil {
return Domain{}, errors.Wrap(svcerr.ErrAuthentication, err)
}
d.CreatedBy = key.User
domainID, err := svc.idProvider.ID()
if err != nil {
return Domain{}, errors.Wrap(svcerr.ErrCreateEntity, err)
}
d.ID = domainID
if d.Status != DisabledStatus && d.Status != EnabledStatus {
return Domain{}, svcerr.ErrInvalidStatus
}
d.CreatedAt = time.Now()
if err := svc.createDomainPolicy(ctx, key.User, domainID, policies.AdministratorRelation); err != nil {
return Domain{}, errors.Wrap(errCreateDomainPolicy, err)
}
defer func() {
if err != nil {
if errRollBack := svc.createDomainPolicyRollback(ctx, key.User, domainID, policies.AdministratorRelation); errRollBack != nil {
err = errors.Wrap(err, errors.Wrap(errRollbackPolicy, errRollBack))
}
}
}()
dom, err := svc.domains.Save(ctx, d)
if err != nil {
return Domain{}, errors.Wrap(svcerr.ErrCreateEntity, err)
}
return dom, nil
}
func (svc service) RetrieveDomain(ctx context.Context, token, id string) (Domain, error) {
res, err := svc.Identify(ctx, token)
if err != nil {
return Domain{}, errors.Wrap(svcerr.ErrAuthentication, err)
}
domain, err := svc.domains.RetrieveByID(ctx, id)
if err != nil {
return Domain{}, errors.Wrap(svcerr.ErrViewEntity, err)
}
if err := svc.checkSuperAdmin(ctx, res.User); err != nil {
if err = svc.Authorize(ctx, policies.Policy{
Subject: EncodeDomainUserID(id, res.User),
SubjectType: policies.UserType,
SubjectKind: policies.UsersKind,
Object: id,
ObjectType: policies.DomainType,
Permission: policies.MembershipPermission,
}); err != nil {
return Domain{ID: domain.ID, Name: domain.Name, Alias: domain.Alias}, nil
}
}
return domain, nil
}
func (svc service) RetrieveDomainPermissions(ctx context.Context, token, id string) (policies.Permissions, error) {
res, err := svc.Identify(ctx, token)
if err != nil {
return []string{}, err
}
subject := res.User
if err := svc.checkSuperAdmin(ctx, res.User); err != nil {
domainUserSubject := EncodeDomainUserID(id, res.User)
if err := svc.Authorize(ctx, policies.Policy{
Subject: domainUserSubject,
SubjectType: policies.UserType,
SubjectKind: policies.UsersKind,
Object: id,
ObjectType: policies.DomainType,
Permission: policies.MembershipPermission,
}); err != nil {
return []string{}, err
}
subject = domainUserSubject
}
lp, err := svc.policysvc.ListPermissions(ctx, policies.Policy{
SubjectType: policies.UserType,
Subject: subject,
Object: id,
ObjectType: policies.DomainType,
}, []string{policies.AdminPermission, policies.EditPermission, policies.ViewPermission, policies.MembershipPermission, policies.CreatePermission})
if err != nil {
return []string{}, errors.Wrap(svcerr.ErrViewEntity, err)
}
return lp, nil
}
func (svc service) UpdateDomain(ctx context.Context, token, id string, d DomainReq) (Domain, error) {
key, err := svc.Identify(ctx, token)
if err != nil {
return Domain{}, err
}
if err := svc.checkSuperAdmin(ctx, key.User); err != nil {
if err := svc.Authorize(ctx, policies.Policy{
Subject: EncodeDomainUserID(id, key.User),
SubjectType: policies.UserType,
SubjectKind: policies.UsersKind,
Object: id,
ObjectType: policies.DomainType,
Permission: policies.EditPermission,
}); err != nil {
return Domain{}, err
}
}
dom, err := svc.domains.Update(ctx, id, key.User, d)
if err != nil {
return Domain{}, errors.Wrap(svcerr.ErrUpdateEntity, err)
}
return dom, nil
}
func (svc service) ChangeDomainStatus(ctx context.Context, token, id string, d DomainReq) (Domain, error) {
key, err := svc.Identify(ctx, token)
if err != nil {
return Domain{}, errors.Wrap(svcerr.ErrAuthentication, err)
}
if err := svc.checkSuperAdmin(ctx, key.User); err != nil {
if err := svc.Authorize(ctx, policies.Policy{
Subject: EncodeDomainUserID(id, key.User),
SubjectType: policies.UserType,
SubjectKind: policies.UsersKind,
Object: id,
ObjectType: policies.DomainType,
Permission: policies.AdminPermission,
}); err != nil {
return Domain{}, err
}
}
dom, err := svc.domains.Update(ctx, id, key.User, d)
if err != nil {
return Domain{}, errors.Wrap(svcerr.ErrUpdateEntity, err)
}
return dom, nil
}
func (svc service) ListDomains(ctx context.Context, token string, p Page) (DomainsPage, error) {
key, err := svc.Identify(ctx, token)
if err != nil {
return DomainsPage{}, errors.Wrap(svcerr.ErrAuthentication, err)
}
p.SubjectID = key.User
if err := svc.checkSuperAdmin(ctx, key.User); err == nil {
p.SubjectID = ""
}
dp, err := svc.domains.ListDomains(ctx, p)
if err != nil {
return DomainsPage{}, errors.Wrap(svcerr.ErrViewEntity, err)
}
if p.SubjectID == "" {
for i := range dp.Domains {
dp.Domains[i].Permission = policies.AdministratorRelation
}
}
return dp, nil
}
func (svc service) AssignUsers(ctx context.Context, token, id string, userIds []string, relation string) error {
res, err := svc.Identify(ctx, token)
if err != nil {
return errors.Wrap(svcerr.ErrAuthentication, err)
}
if err := svc.checkSuperAdmin(ctx, res.User); err != nil {
domainUserID := EncodeDomainUserID(id, res.User)
if err := svc.Authorize(ctx, policies.Policy{
Subject: domainUserID,
SubjectType: policies.UserType,
SubjectKind: policies.UsersKind,
Object: id,
ObjectType: policies.DomainType,
Permission: policies.SharePermission,
}); err != nil {
return err
}
if err := svc.Authorize(ctx, policies.Policy{
Subject: domainUserID,
SubjectType: policies.UserType,
SubjectKind: policies.UsersKind,
Object: id,
ObjectType: policies.DomainType,
Permission: SwitchToPermission(relation),
}); err != nil {
return err
}
}
for _, userID := range userIds {
if err := svc.Authorize(ctx, policies.Policy{
Subject: userID,
SubjectType: policies.UserType,
Permission: policies.MembershipPermission,
Object: policies.MagistralaObject,
ObjectType: policies.PlatformType,
}); err != nil {
return errors.Wrap(svcerr.ErrMalformedEntity, fmt.Errorf("invalid user id : %s ", userID))
}
}
return svc.addDomainPolicies(ctx, id, relation, userIds...)
}
func (svc service) UnassignUser(ctx context.Context, token, id, userID string) error {
res, err := svc.Identify(ctx, token)
if err != nil {
return errors.Wrap(svcerr.ErrAuthentication, err)
}
if err := svc.checkSuperAdmin(ctx, res.User); err != nil {
domainUserID := EncodeDomainUserID(id, res.User)
pr := policies.Policy{
Subject: domainUserID,
SubjectType: policies.UserType,
SubjectKind: policies.UsersKind,
Object: id,
ObjectType: policies.DomainType,
Permission: policies.SharePermission,
}
if err := svc.Authorize(ctx, pr); err != nil {
return err
}
pr.Permission = policies.AdminPermission
if err := svc.Authorize(ctx, pr); err != nil {
pr.SubjectKind = policies.UsersKind
// User is not admin.
pr.Subject = userID
if err := svc.Authorize(ctx, pr); err == nil {
// Non admin attempts to remove admin.
return errors.Wrap(svcerr.ErrAuthorization, err)
}
}
}
if err := svc.policysvc.DeletePolicyFilter(ctx, policies.Policy{
Subject: EncodeDomainUserID(id, userID),
SubjectType: policies.UserType,
}); err != nil {
return errors.Wrap(errRemovePolicies, err)
}
pc := Policy{
SubjectType: policies.UserType,
SubjectID: userID,
ObjectType: policies.DomainType,
ObjectID: id,
}
if err := svc.domains.DeletePolicies(ctx, pc); err != nil {
return errors.Wrap(errRemovePolicies, err)
}
return nil
}
// IMPROVEMENT NOTE: Take decision: Only Patform admin or both Patform and domain admins can see others users domain.
func (svc service) ListUserDomains(ctx context.Context, token, userID string, p Page) (DomainsPage, error) {
res, err := svc.Identify(ctx, token)
if err != nil {
return DomainsPage{}, errors.Wrap(svcerr.ErrAuthentication, err)
}
if err := svc.checkSuperAdmin(ctx, res.User); err != nil {
return DomainsPage{}, errors.Wrap(svcerr.ErrAuthorization, err)
}
if userID != "" && res.User != userID {
p.SubjectID = userID
} else {
p.SubjectID = res.User
}
dp, err := svc.domains.ListDomains(ctx, p)
if err != nil {
return DomainsPage{}, errors.Wrap(svcerr.ErrViewEntity, err)
}
return dp, nil
}
func (svc service) addDomainPolicies(ctx context.Context, domainID, relation string, userIDs ...string) (err error) {
var prs []policies.Policy
var pcs []Policy
for _, userID := range userIDs {
prs = append(prs, policies.Policy{
Subject: EncodeDomainUserID(domainID, userID),
SubjectType: policies.UserType,
SubjectKind: policies.UsersKind,
Relation: relation,
Object: domainID,
ObjectType: policies.DomainType,
})
pcs = append(pcs, Policy{
SubjectType: policies.UserType,
SubjectID: userID,
Relation: relation,
ObjectType: policies.DomainType,
ObjectID: domainID,
})
}
if err := svc.policysvc.AddPolicies(ctx, prs); err != nil {
return errors.Wrap(errAddPolicies, err)
}
defer func() {
if err != nil {
if errDel := svc.policysvc.DeletePolicies(ctx, prs); errDel != nil {
err = errors.Wrap(err, errors.Wrap(errRollbackPolicy, errDel))
}
}
}()
if err = svc.domains.SavePolicies(ctx, pcs...); err != nil {
return errors.Wrap(errAddPolicies, err)
}
return nil
}
func (svc service) createDomainPolicy(ctx context.Context, userID, domainID, relation string) (err error) {
prs := []policies.Policy{
{
Subject: EncodeDomainUserID(domainID, userID),
SubjectType: policies.UserType,
SubjectKind: policies.UsersKind,
Relation: relation,
Object: domainID,
ObjectType: policies.DomainType,
},
{
Subject: policies.MagistralaObject,
SubjectType: policies.PlatformType,
Relation: policies.PlatformRelation,
Object: domainID,
ObjectType: policies.DomainType,
},
}
if err := svc.policysvc.AddPolicies(ctx, prs); err != nil {
return err
}
defer func() {
if err != nil {
if errDel := svc.policysvc.DeletePolicies(ctx, prs); errDel != nil {
err = errors.Wrap(err, errors.Wrap(errRollbackPolicy, errDel))
}
}
}()
err = svc.domains.SavePolicies(ctx, Policy{
SubjectType: policies.UserType,
SubjectID: userID,
Relation: relation,
ObjectType: policies.DomainType,
ObjectID: domainID,
})
if err != nil {
return errors.Wrap(errCreateDomainPolicy, err)
}
return err
}
func (svc service) createDomainPolicyRollback(ctx context.Context, userID, domainID, relation string) error {
var err error
prs := []policies.Policy{
{
Subject: EncodeDomainUserID(domainID, userID),
SubjectType: policies.UserType,
SubjectKind: policies.UsersKind,
Relation: relation,
Object: domainID,
ObjectType: policies.DomainType,
},
{
Subject: policies.MagistralaObject,
SubjectType: policies.PlatformType,
Relation: policies.PlatformRelation,
Object: domainID,
ObjectType: policies.DomainType,
},
}
if errPolicy := svc.policysvc.DeletePolicies(ctx, prs); errPolicy != nil {
err = errors.Wrap(errRemovePolicyEngine, errPolicy)
}
errPolicyCopy := svc.domains.DeletePolicies(ctx, Policy{
SubjectType: policies.UserType,
SubjectID: userID,
Relation: relation,
ObjectType: policies.DomainType,
ObjectID: domainID,
})
if errPolicyCopy != nil {
err = errors.Wrap(err, errors.Wrap(errRemoveLocalPolicy, errPolicyCopy))
}
return err
}
func EncodeDomainUserID(domainID, userID string) string {
if domainID == "" || userID == "" {
return ""
@@ -874,51 +434,3 @@ func DecodeDomainUserID(domainUserID string) (string, string) {
return "", ""
}
}
func (svc service) DeleteUserFromDomains(ctx context.Context, id string) (err error) {
domainsPage, err := svc.domains.ListDomains(ctx, Page{SubjectID: id, Limit: defLimit})
if err != nil {
return err
}
if domainsPage.Total > defLimit {
for i := defLimit; i < int(domainsPage.Total); i += defLimit {
page := Page{SubjectID: id, Offset: uint64(i), Limit: defLimit}
dp, err := svc.domains.ListDomains(ctx, page)
if err != nil {
return err
}
domainsPage.Domains = append(domainsPage.Domains, dp.Domains...)
}
}
for _, domain := range domainsPage.Domains {
req := policies.Policy{
Subject: EncodeDomainUserID(domain.ID, id),
SubjectType: policies.UserType,
}
if err := svc.policysvc.DeletePolicyFilter(ctx, req); err != nil {
return err
}
}
if err := svc.domains.DeleteUserPolicies(ctx, id); err != nil {
return err
}
return nil
}
func (svc service) checkSuperAdmin(ctx context.Context, userID string) error {
if err := svc.evaluator.CheckPolicy(ctx, policies.Policy{
Subject: userID,
SubjectType: policies.UserType,
Permission: policies.AdminPermission,
Object: policies.MagistralaObject,
ObjectType: policies.PlatformType,
}); err != nil {
return svcerr.ErrAuthorization
}
return nil
}
+342 -1798
View File
File diff suppressed because it is too large Load Diff
-81
View File
@@ -74,84 +74,3 @@ func (tm *tracingMiddleware) Authorize(ctx context.Context, pr policies.Policy)
return tm.svc.Authorize(ctx, pr)
}
func (tm *tracingMiddleware) CreateDomain(ctx context.Context, token string, d auth.Domain) (auth.Domain, error) {
ctx, span := tm.tracer.Start(ctx, "create_domain", trace.WithAttributes(
attribute.String("name", d.Name),
))
defer span.End()
return tm.svc.CreateDomain(ctx, token, d)
}
func (tm *tracingMiddleware) RetrieveDomain(ctx context.Context, token, id string) (auth.Domain, error) {
ctx, span := tm.tracer.Start(ctx, "view_domain", trace.WithAttributes(
attribute.String("id", id),
))
defer span.End()
return tm.svc.RetrieveDomain(ctx, token, id)
}
func (tm *tracingMiddleware) RetrieveDomainPermissions(ctx context.Context, token, id string) (policies.Permissions, error) {
ctx, span := tm.tracer.Start(ctx, "view_domain_permissions", trace.WithAttributes(
attribute.String("id", id),
))
defer span.End()
return tm.svc.RetrieveDomainPermissions(ctx, token, id)
}
func (tm *tracingMiddleware) UpdateDomain(ctx context.Context, token, id string, d auth.DomainReq) (auth.Domain, error) {
ctx, span := tm.tracer.Start(ctx, "update_domain", trace.WithAttributes(
attribute.String("id", id),
))
defer span.End()
return tm.svc.UpdateDomain(ctx, token, id, d)
}
func (tm *tracingMiddleware) ChangeDomainStatus(ctx context.Context, token, id string, d auth.DomainReq) (auth.Domain, error) {
ctx, span := tm.tracer.Start(ctx, "change_domain_status", trace.WithAttributes(
attribute.String("id", id),
))
defer span.End()
return tm.svc.ChangeDomainStatus(ctx, token, id, d)
}
func (tm *tracingMiddleware) ListDomains(ctx context.Context, token string, p auth.Page) (auth.DomainsPage, error) {
ctx, span := tm.tracer.Start(ctx, "list_domains")
defer span.End()
return tm.svc.ListDomains(ctx, token, p)
}
func (tm *tracingMiddleware) AssignUsers(ctx context.Context, token, id string, userIds []string, relation string) error {
ctx, span := tm.tracer.Start(ctx, "assign_users", trace.WithAttributes(
attribute.String("id", id),
attribute.StringSlice("user_ids", userIds),
attribute.String("relation", relation),
))
defer span.End()
return tm.svc.AssignUsers(ctx, token, id, userIds, relation)
}
func (tm *tracingMiddleware) UnassignUser(ctx context.Context, token, id, userID string) error {
ctx, span := tm.tracer.Start(ctx, "unassign_user", trace.WithAttributes(
attribute.String("id", id),
attribute.String("user_id", userID),
))
defer span.End()
return tm.svc.UnassignUser(ctx, token, id, userID)
}
func (tm *tracingMiddleware) ListUserDomains(ctx context.Context, token, userID string, p auth.Page) (auth.DomainsPage, error) {
ctx, span := tm.tracer.Start(ctx, "list_user_domains", trace.WithAttributes(
attribute.String("user_id", userID),
))
defer span.End()
return tm.svc.ListUserDomains(ctx, token, userID, p)
}
func (tm *tracingMiddleware) DeleteUserFromDomains(ctx context.Context, id string) error {
ctx, span := tm.tracer.Start(ctx, "delete_user_from_domains", trace.WithAttributes(
attribute.String("id", id),
))
defer span.End()
return tm.svc.DeleteUserFromDomains(ctx, id)
}
-484
View File
@@ -1,484 +0,0 @@
// Copyright (c) Abstract Machines
// SPDX-License-Identifier: Apache-2.0
// Code generated by protoc-gen-go-grpc. DO NOT EDIT.
// versions:
// - protoc-gen-go-grpc v1.4.0
// - protoc v5.27.1
// source: auth.proto
package magistrala
import (
context "context"
grpc "google.golang.org/grpc"
codes "google.golang.org/grpc/codes"
status "google.golang.org/grpc/status"
)
// This is a compile-time assertion to ensure that this generated file
// is compatible with the grpc package it is being compiled against.
// Requires gRPC-Go v1.62.0 or later.
const _ = grpc.SupportPackageIsVersion8
const (
ThingsService_Authorize_FullMethodName = "/magistrala.ThingsService/Authorize"
)
// ThingsServiceClient is the client API for ThingsService service.
//
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream.
//
// ThingsService is a service that provides things authorization functionalities
// for magistrala services.
type ThingsServiceClient interface {
// Authorize checks if the thing is authorized to perform
// the action on the channel.
Authorize(ctx context.Context, in *ThingsAuthzReq, opts ...grpc.CallOption) (*ThingsAuthzRes, error)
}
type thingsServiceClient struct {
cc grpc.ClientConnInterface
}
func NewThingsServiceClient(cc grpc.ClientConnInterface) ThingsServiceClient {
return &thingsServiceClient{cc}
}
func (c *thingsServiceClient) Authorize(ctx context.Context, in *ThingsAuthzReq, opts ...grpc.CallOption) (*ThingsAuthzRes, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(ThingsAuthzRes)
err := c.cc.Invoke(ctx, ThingsService_Authorize_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
return out, nil
}
// ThingsServiceServer is the server API for ThingsService service.
// All implementations must embed UnimplementedThingsServiceServer
// for forward compatibility
//
// ThingsService is a service that provides things authorization functionalities
// for magistrala services.
type ThingsServiceServer interface {
// Authorize checks if the thing is authorized to perform
// the action on the channel.
Authorize(context.Context, *ThingsAuthzReq) (*ThingsAuthzRes, error)
mustEmbedUnimplementedThingsServiceServer()
}
// UnimplementedThingsServiceServer must be embedded to have forward compatible implementations.
type UnimplementedThingsServiceServer struct {
}
func (UnimplementedThingsServiceServer) Authorize(context.Context, *ThingsAuthzReq) (*ThingsAuthzRes, error) {
return nil, status.Errorf(codes.Unimplemented, "method Authorize not implemented")
}
func (UnimplementedThingsServiceServer) mustEmbedUnimplementedThingsServiceServer() {}
// UnsafeThingsServiceServer may be embedded to opt out of forward compatibility for this service.
// Use of this interface is not recommended, as added methods to ThingsServiceServer will
// result in compilation errors.
type UnsafeThingsServiceServer interface {
mustEmbedUnimplementedThingsServiceServer()
}
func RegisterThingsServiceServer(s grpc.ServiceRegistrar, srv ThingsServiceServer) {
s.RegisterService(&ThingsService_ServiceDesc, srv)
}
func _ThingsService_Authorize_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(ThingsAuthzReq)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(ThingsServiceServer).Authorize(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: ThingsService_Authorize_FullMethodName,
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(ThingsServiceServer).Authorize(ctx, req.(*ThingsAuthzReq))
}
return interceptor(ctx, in, info, handler)
}
// ThingsService_ServiceDesc is the grpc.ServiceDesc for ThingsService service.
// It's only intended for direct use with grpc.RegisterService,
// and not to be introspected or modified (even as a copy)
var ThingsService_ServiceDesc = grpc.ServiceDesc{
ServiceName: "magistrala.ThingsService",
HandlerType: (*ThingsServiceServer)(nil),
Methods: []grpc.MethodDesc{
{
MethodName: "Authorize",
Handler: _ThingsService_Authorize_Handler,
},
},
Streams: []grpc.StreamDesc{},
Metadata: "auth.proto",
}
const (
TokenService_Issue_FullMethodName = "/magistrala.TokenService/Issue"
TokenService_Refresh_FullMethodName = "/magistrala.TokenService/Refresh"
)
// TokenServiceClient is the client API for TokenService service.
//
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream.
type TokenServiceClient interface {
Issue(ctx context.Context, in *IssueReq, opts ...grpc.CallOption) (*Token, error)
Refresh(ctx context.Context, in *RefreshReq, opts ...grpc.CallOption) (*Token, error)
}
type tokenServiceClient struct {
cc grpc.ClientConnInterface
}
func NewTokenServiceClient(cc grpc.ClientConnInterface) TokenServiceClient {
return &tokenServiceClient{cc}
}
func (c *tokenServiceClient) Issue(ctx context.Context, in *IssueReq, opts ...grpc.CallOption) (*Token, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(Token)
err := c.cc.Invoke(ctx, TokenService_Issue_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *tokenServiceClient) Refresh(ctx context.Context, in *RefreshReq, opts ...grpc.CallOption) (*Token, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(Token)
err := c.cc.Invoke(ctx, TokenService_Refresh_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
return out, nil
}
// TokenServiceServer is the server API for TokenService service.
// All implementations must embed UnimplementedTokenServiceServer
// for forward compatibility
type TokenServiceServer interface {
Issue(context.Context, *IssueReq) (*Token, error)
Refresh(context.Context, *RefreshReq) (*Token, error)
mustEmbedUnimplementedTokenServiceServer()
}
// UnimplementedTokenServiceServer must be embedded to have forward compatible implementations.
type UnimplementedTokenServiceServer struct {
}
func (UnimplementedTokenServiceServer) Issue(context.Context, *IssueReq) (*Token, error) {
return nil, status.Errorf(codes.Unimplemented, "method Issue not implemented")
}
func (UnimplementedTokenServiceServer) Refresh(context.Context, *RefreshReq) (*Token, error) {
return nil, status.Errorf(codes.Unimplemented, "method Refresh not implemented")
}
func (UnimplementedTokenServiceServer) mustEmbedUnimplementedTokenServiceServer() {}
// UnsafeTokenServiceServer may be embedded to opt out of forward compatibility for this service.
// Use of this interface is not recommended, as added methods to TokenServiceServer will
// result in compilation errors.
type UnsafeTokenServiceServer interface {
mustEmbedUnimplementedTokenServiceServer()
}
func RegisterTokenServiceServer(s grpc.ServiceRegistrar, srv TokenServiceServer) {
s.RegisterService(&TokenService_ServiceDesc, srv)
}
func _TokenService_Issue_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(IssueReq)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(TokenServiceServer).Issue(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: TokenService_Issue_FullMethodName,
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(TokenServiceServer).Issue(ctx, req.(*IssueReq))
}
return interceptor(ctx, in, info, handler)
}
func _TokenService_Refresh_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(RefreshReq)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(TokenServiceServer).Refresh(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: TokenService_Refresh_FullMethodName,
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(TokenServiceServer).Refresh(ctx, req.(*RefreshReq))
}
return interceptor(ctx, in, info, handler)
}
// TokenService_ServiceDesc is the grpc.ServiceDesc for TokenService service.
// It's only intended for direct use with grpc.RegisterService,
// and not to be introspected or modified (even as a copy)
var TokenService_ServiceDesc = grpc.ServiceDesc{
ServiceName: "magistrala.TokenService",
HandlerType: (*TokenServiceServer)(nil),
Methods: []grpc.MethodDesc{
{
MethodName: "Issue",
Handler: _TokenService_Issue_Handler,
},
{
MethodName: "Refresh",
Handler: _TokenService_Refresh_Handler,
},
},
Streams: []grpc.StreamDesc{},
Metadata: "auth.proto",
}
const (
AuthService_Authorize_FullMethodName = "/magistrala.AuthService/Authorize"
AuthService_Authenticate_FullMethodName = "/magistrala.AuthService/Authenticate"
)
// AuthServiceClient is the client API for AuthService service.
//
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream.
//
// AuthService is a service that provides authentication and authorization
// functionalities for magistrala services.
type AuthServiceClient interface {
Authorize(ctx context.Context, in *AuthZReq, opts ...grpc.CallOption) (*AuthZRes, error)
Authenticate(ctx context.Context, in *AuthNReq, opts ...grpc.CallOption) (*AuthNRes, error)
}
type authServiceClient struct {
cc grpc.ClientConnInterface
}
func NewAuthServiceClient(cc grpc.ClientConnInterface) AuthServiceClient {
return &authServiceClient{cc}
}
func (c *authServiceClient) Authorize(ctx context.Context, in *AuthZReq, opts ...grpc.CallOption) (*AuthZRes, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(AuthZRes)
err := c.cc.Invoke(ctx, AuthService_Authorize_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *authServiceClient) Authenticate(ctx context.Context, in *AuthNReq, opts ...grpc.CallOption) (*AuthNRes, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(AuthNRes)
err := c.cc.Invoke(ctx, AuthService_Authenticate_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
return out, nil
}
// AuthServiceServer is the server API for AuthService service.
// All implementations must embed UnimplementedAuthServiceServer
// for forward compatibility
//
// AuthService is a service that provides authentication and authorization
// functionalities for magistrala services.
type AuthServiceServer interface {
Authorize(context.Context, *AuthZReq) (*AuthZRes, error)
Authenticate(context.Context, *AuthNReq) (*AuthNRes, error)
mustEmbedUnimplementedAuthServiceServer()
}
// UnimplementedAuthServiceServer must be embedded to have forward compatible implementations.
type UnimplementedAuthServiceServer struct {
}
func (UnimplementedAuthServiceServer) Authorize(context.Context, *AuthZReq) (*AuthZRes, error) {
return nil, status.Errorf(codes.Unimplemented, "method Authorize not implemented")
}
func (UnimplementedAuthServiceServer) Authenticate(context.Context, *AuthNReq) (*AuthNRes, error) {
return nil, status.Errorf(codes.Unimplemented, "method Authenticate not implemented")
}
func (UnimplementedAuthServiceServer) mustEmbedUnimplementedAuthServiceServer() {}
// UnsafeAuthServiceServer may be embedded to opt out of forward compatibility for this service.
// Use of this interface is not recommended, as added methods to AuthServiceServer will
// result in compilation errors.
type UnsafeAuthServiceServer interface {
mustEmbedUnimplementedAuthServiceServer()
}
func RegisterAuthServiceServer(s grpc.ServiceRegistrar, srv AuthServiceServer) {
s.RegisterService(&AuthService_ServiceDesc, srv)
}
func _AuthService_Authorize_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(AuthZReq)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(AuthServiceServer).Authorize(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: AuthService_Authorize_FullMethodName,
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(AuthServiceServer).Authorize(ctx, req.(*AuthZReq))
}
return interceptor(ctx, in, info, handler)
}
func _AuthService_Authenticate_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(AuthNReq)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(AuthServiceServer).Authenticate(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: AuthService_Authenticate_FullMethodName,
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(AuthServiceServer).Authenticate(ctx, req.(*AuthNReq))
}
return interceptor(ctx, in, info, handler)
}
// AuthService_ServiceDesc is the grpc.ServiceDesc for AuthService service.
// It's only intended for direct use with grpc.RegisterService,
// and not to be introspected or modified (even as a copy)
var AuthService_ServiceDesc = grpc.ServiceDesc{
ServiceName: "magistrala.AuthService",
HandlerType: (*AuthServiceServer)(nil),
Methods: []grpc.MethodDesc{
{
MethodName: "Authorize",
Handler: _AuthService_Authorize_Handler,
},
{
MethodName: "Authenticate",
Handler: _AuthService_Authenticate_Handler,
},
},
Streams: []grpc.StreamDesc{},
Metadata: "auth.proto",
}
const (
DomainsService_DeleteUserFromDomains_FullMethodName = "/magistrala.DomainsService/DeleteUserFromDomains"
)
// DomainsServiceClient is the client API for DomainsService service.
//
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream.
//
// DomainsService is a service that provides access to domains
// functionalities for magistrala services.
type DomainsServiceClient interface {
DeleteUserFromDomains(ctx context.Context, in *DeleteUserReq, opts ...grpc.CallOption) (*DeleteUserRes, error)
}
type domainsServiceClient struct {
cc grpc.ClientConnInterface
}
func NewDomainsServiceClient(cc grpc.ClientConnInterface) DomainsServiceClient {
return &domainsServiceClient{cc}
}
func (c *domainsServiceClient) DeleteUserFromDomains(ctx context.Context, in *DeleteUserReq, opts ...grpc.CallOption) (*DeleteUserRes, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(DeleteUserRes)
err := c.cc.Invoke(ctx, DomainsService_DeleteUserFromDomains_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
return out, nil
}
// DomainsServiceServer is the server API for DomainsService service.
// All implementations must embed UnimplementedDomainsServiceServer
// for forward compatibility
//
// DomainsService is a service that provides access to domains
// functionalities for magistrala services.
type DomainsServiceServer interface {
DeleteUserFromDomains(context.Context, *DeleteUserReq) (*DeleteUserRes, error)
mustEmbedUnimplementedDomainsServiceServer()
}
// UnimplementedDomainsServiceServer must be embedded to have forward compatible implementations.
type UnimplementedDomainsServiceServer struct {
}
func (UnimplementedDomainsServiceServer) DeleteUserFromDomains(context.Context, *DeleteUserReq) (*DeleteUserRes, error) {
return nil, status.Errorf(codes.Unimplemented, "method DeleteUserFromDomains not implemented")
}
func (UnimplementedDomainsServiceServer) mustEmbedUnimplementedDomainsServiceServer() {}
// UnsafeDomainsServiceServer may be embedded to opt out of forward compatibility for this service.
// Use of this interface is not recommended, as added methods to DomainsServiceServer will
// result in compilation errors.
type UnsafeDomainsServiceServer interface {
mustEmbedUnimplementedDomainsServiceServer()
}
func RegisterDomainsServiceServer(s grpc.ServiceRegistrar, srv DomainsServiceServer) {
s.RegisterService(&DomainsService_ServiceDesc, srv)
}
func _DomainsService_DeleteUserFromDomains_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(DeleteUserReq)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(DomainsServiceServer).DeleteUserFromDomains(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: DomainsService_DeleteUserFromDomains_FullMethodName,
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(DomainsServiceServer).DeleteUserFromDomains(ctx, req.(*DeleteUserReq))
}
return interceptor(ctx, in, info, handler)
}
// DomainsService_ServiceDesc is the grpc.ServiceDesc for DomainsService service.
// It's only intended for direct use with grpc.RegisterService,
// and not to be introspected or modified (even as a copy)
var DomainsService_ServiceDesc = grpc.ServiceDesc{
ServiceName: "magistrala.DomainsService",
HandlerType: (*DomainsServiceServer)(nil),
Methods: []grpc.MethodDesc{
{
MethodName: "DeleteUserFromDomains",
Handler: _DomainsService_DeleteUserFromDomains_Handler,
},
},
Streams: []grpc.StreamDesc{},
Metadata: "auth.proto",
}
+46 -46
View File
@@ -2,68 +2,68 @@
New devices need to be configured properly and connected to the Magistrala. Bootstrap service is used in order to accomplish that. This service provides the following features:
1. Creating new Magistrala Things
2. Providing basic configuration for the newly created Things
3. Enabling/disabling Things
1. Creating new Magistrala Clients
2. Providing basic configuration for the newly created Clients
3. Enabling/disabling Clients
Pre-provisioning a new Thing is as simple as sending Configuration data to the Bootstrap service. Once the Thing is online, it sends a request for initial config to Bootstrap service. Bootstrap service provides an API for enabling and disabling Things. Only enabled Things can exchange messages over Magistrala. Bootstrapping does not implicitly enable Things, it has to be done manually.
Pre-provisioning a new Client is as simple as sending Configuration data to the Bootstrap service. Once the Client is online, it sends a request for initial config to Bootstrap service. Bootstrap service provides an API for enabling and disabling Clients. Only enabled Clients can exchange messages over Magistrala. Bootstrapping does not implicitly enable Clients, it has to be done manually.
In order to bootstrap successfully, the Thing needs to send bootstrapping request to the specific URL, as well as a secret key. This key and URL are pre-provisioned during the manufacturing process. If the Thing is provisioned on the Bootstrap service side, the corresponding configuration will be sent as a response. Otherwise, the Thing will be saved so that it can be provisioned later.
In order to bootstrap successfully, the Client needs to send bootstrapping request to the specific URL, as well as a secret key. This key and URL are pre-provisioned during the manufacturing process. If the Client is provisioned on the Bootstrap service side, the corresponding configuration will be sent as a response. Otherwise, the Client will be saved so that it can be provisioned later.
## Thing Configuration Entity
## Client Configuration Entity
Thing Configuration consists of two logical parts: the custom configuration that can be interpreted by the Thing itself and Magistrala-related configuration. Magistrala config contains:
Client Configuration consists of two logical parts: the custom configuration that can be interpreted by the Client itself and Magistrala-related configuration. Magistrala config contains:
1. corresponding Magistrala Thing ID
2. corresponding Magistrala Thing key
3. list of the Magistrala channels the Thing is connected to
1. corresponding Magistrala Client ID
2. corresponding Magistrala Client key
3. list of the Magistrala channels the Client is connected to
> Note: list of channels contains IDs of the Magistrala channels. These channels are _pre-provisioned_ on the Magistrala side and, unlike corresponding Magistrala Thing, Bootstrap service is not able to create Magistrala Channels.
> Note: list of channels contains IDs of the Magistrala channels. These channels are _pre-provisioned_ on the Magistrala side and, unlike corresponding Magistrala Client, Bootstrap service is not able to create Magistrala Channels.
Enabling and disabling Thing (adding Thing to/from whitelist) is as simple as connecting corresponding Magistrala Thing to the given list of Channels. Configuration keeps _state_ of the Thing:
Enabling and disabling Client (adding Client to/from whitelist) is as simple as connecting corresponding Magistrala Client to the given list of Channels. Configuration keeps _state_ of the Client:
| State | What it means |
| -------- | --------------------------------------------- |
| Inactive | Thing is created, but isn't enabled |
| Active | Thing is able to communicate using Magistrala |
| Inactive | Client is created, but isn't enabled |
| Active | Client is able to communicate using Magistrala |
Switching between states `Active` and `Inactive` enables and disables Thing, respectively.
Switching between states `Active` and `Inactive` enables and disables Client, respectively.
Thing configuration also contains the so-called `external ID` and `external key`. An external ID is a unique identifier of corresponding Thing. For example, a device MAC address is a good choice for external ID. External key is a secret key that is used for authentication during the bootstrapping procedure.
Client configuration also contains the so-called `external ID` and `external key`. An external ID is a unique identifier of corresponding Client. For example, a device MAC address is a good choice for external ID. External key is a secret key that is used for authentication during the bootstrapping procedure.
## Configuration
The service is configured using the environment variables presented in the following table. Note that any unset variables will be replaced with their default values.
| Variable | Description | Default |
| ----------------------------- | -------------------------------------------------------------------------------- | -------------------------------- |
| MG_BOOTSTRAP_LOG_LEVEL | Log level for Bootstrap (debug, info, warn, error) | info |
| MG_BOOTSTRAP_DB_HOST | Database host address | localhost |
| MG_BOOTSTRAP_DB_PORT | Database host port | 5432 |
| MG_BOOTSTRAP_DB_USER | Database user | magistrala |
| MG_BOOTSTRAP_DB_PASS | Database password | magistrala |
| MG_BOOTSTRAP_DB_NAME | Name of the database used by the service | bootstrap |
| MG_BOOTSTRAP_DB_SSL_MODE | Database connection SSL mode (disable, require, verify-ca, verify-full) | disable |
| MG_BOOTSTRAP_DB_SSL_CERT | Path to the PEM encoded certificate file | "" |
| MG_BOOTSTRAP_DB_SSL_KEY | Path to the PEM encoded key file | "" |
| MG_BOOTSTRAP_DB_SSL_ROOT_CERT | Path to the PEM encoded root certificate file | "" |
| MG_BOOTSTRAP_ENCRYPT_KEY | Secret key for secure bootstrapping encryption | 12345678910111213141516171819202 |
| MG_BOOTSTRAP_HTTP_HOST | Bootstrap service HTTP host | "" |
| MG_BOOTSTRAP_HTTP_PORT | Bootstrap service HTTP port | 9013 |
| MG_BOOTSTRAP_HTTP_SERVER_CERT | Path to server certificate in pem format | "" |
| MG_BOOTSTRAP_HTTP_SERVER_KEY | Path to server key in pem format | "" |
| MG_BOOTSTRAP_EVENT_CONSUMER | Bootstrap service event source consumer name | bootstrap |
| MG_ES_URL | Event store URL | <nats://localhost:4222> |
| MG_AUTH_GRPC_URL | Auth service Auth gRPC URL | <localhost:8181> |
| MG_AUTH_GRPC_TIMEOUT | Auth service Auth gRPC request timeout in seconds | 1s |
| MG_AUTH_GRPC_CLIENT_CERT | Path to the PEM encoded auth service Auth gRPC client certificate file | "" |
| MG_AUTH_GRPC_CLIENT_KEY | Path to the PEM encoded auth service Auth gRPC client key file | "" |
| MG_AUTH_GRPC_SERVER_CERTS | Path to the PEM encoded auth server Auth gRPC server trusted CA certificate file | "" |
| MG_THINGS_URL | Base url for Magistrala Things | <http://localhost:9000> |
| MG_JAEGER_URL | Jaeger server URL | <http://localhost:4318/v1/traces> |
| MG_JAEGER_TRACE_RATIO | Jaeger sampling ratio | 1.0 |
| MG_SEND_TELEMETRY | Send telemetry to magistrala call home server | true |
| MG_BOOTSTRAP_INSTANCE_ID | Bootstrap service instance ID | "" |
| Variable | Description | Default |
| ----------------------------- | -------------------------------------------------------------------------------- | --------------------------------- |
| MG_BOOTSTRAP_LOG_LEVEL | Log level for Bootstrap (debug, info, warn, error) | info |
| MG_BOOTSTRAP_DB_HOST | Database host address | localhost |
| MG_BOOTSTRAP_DB_PORT | Database host port | 5432 |
| MG_BOOTSTRAP_DB_USER | Database user | magistrala |
| MG_BOOTSTRAP_DB_PASS | Database password | magistrala |
| MG_BOOTSTRAP_DB_NAME | Name of the database used by the service | bootstrap |
| MG_BOOTSTRAP_DB_SSL_MODE | Database connection SSL mode (disable, require, verify-ca, verify-full) | disable |
| MG_BOOTSTRAP_DB_SSL_CERT | Path to the PEM encoded certificate file | "" |
| MG_BOOTSTRAP_DB_SSL_KEY | Path to the PEM encoded key file | "" |
| MG_BOOTSTRAP_DB_SSL_ROOT_CERT | Path to the PEM encoded root certificate file | "" |
| MG_BOOTSTRAP_ENCRYPT_KEY | Secret key for secure bootstrapping encryption | 12345678910111213141516171819202 |
| MG_BOOTSTRAP_HTTP_HOST | Bootstrap service HTTP host | "" |
| MG_BOOTSTRAP_HTTP_PORT | Bootstrap service HTTP port | 9013 |
| MG_BOOTSTRAP_HTTP_SERVER_CERT | Path to server certificate in pem format | "" |
| MG_BOOTSTRAP_HTTP_SERVER_KEY | Path to server key in pem format | "" |
| MG_BOOTSTRAP_EVENT_CONSUMER | Bootstrap service event source consumer name | bootstrap |
| MG_ES_URL | Event store URL | <nats://localhost:4222> |
| MG_AUTH_GRPC_URL | Auth service Auth gRPC URL | <localhost:8181> |
| MG_AUTH_GRPC_TIMEOUT | Auth service Auth gRPC request timeout in seconds | 1s |
| MG_AUTH_GRPC_CLIENT_CERT | Path to the PEM encoded auth service Auth gRPC client certificate file | "" |
| MG_AUTH_GRPC_CLIENT_KEY | Path to the PEM encoded auth service Auth gRPC client key file | "" |
| MG_AUTH_GRPC_SERVER_CERTS | Path to the PEM encoded auth server Auth gRPC server trusted CA certificate file | "" |
| MG_CLIENTS_URL | Base URL for Magistrala Clients | <http://localhost:9000> |
| MG_JAEGER_URL | Jaeger server URL | <http://localhost:4318/v1/traces> |
| MG_JAEGER_TRACE_RATIO | Jaeger sampling ratio | 1.0 |
| MG_SEND_TELEMETRY | Send telemetry to magistrala call home server | true |
| MG_BOOTSTRAP_INSTANCE_ID | Bootstrap service instance ID | "" |
## Deployment
@@ -105,7 +105,7 @@ MG_AUTH_GRPC_TIMEOUT=1s \
MG_AUTH_GRPC_CLIENT_CERT="" \
MG_AUTH_GRPC_CLIENT_KEY="" \
MG_AUTH_GRPC_SERVER_CERTS="" \
MG_THINGS_URL=http://localhost:9000 \
MG_CLIENTS_URL=http://localhost:9000 \
MG_JAEGER_URL=http://localhost:14268/api/traces \
MG_JAEGER_TRACE_RATIO=1.0 \
MG_SEND_TELEMETRY=true \
+24 -24
View File
@@ -33,7 +33,7 @@ func addEndpoint(svc bootstrap.Service) endpoint.Endpoint {
}
config := bootstrap.Config{
ThingID: req.ThingID,
ClientID: req.ClientID,
ExternalID: req.ExternalID,
ExternalKey: req.ExternalKey,
Channels: channels,
@@ -50,7 +50,7 @@ func addEndpoint(svc bootstrap.Service) endpoint.Endpoint {
}
res := configRes{
id: saved.ThingID,
id: saved.ClientID,
created: true,
}
@@ -70,13 +70,13 @@ func updateCertEndpoint(svc bootstrap.Service) endpoint.Endpoint {
return nil, svcerr.ErrAuthorization
}
cfg, err := svc.UpdateCert(ctx, session, req.thingID, req.ClientCert, req.ClientKey, req.CACert)
cfg, err := svc.UpdateCert(ctx, session, req.clientID, req.ClientCert, req.ClientKey, req.CACert)
if err != nil {
return nil, err
}
res := updateConfigRes{
ThingID: cfg.ThingID,
ClientID: cfg.ClientID,
ClientCert: cfg.ClientCert,
CACert: cfg.CACert,
ClientKey: cfg.ClientKey,
@@ -113,14 +113,14 @@ func viewEndpoint(svc bootstrap.Service) endpoint.Endpoint {
}
res := viewRes{
ThingID: config.ThingID,
ThingKey: config.ThingKey,
Channels: channels,
ExternalID: config.ExternalID,
ExternalKey: config.ExternalKey,
Name: config.Name,
Content: config.Content,
State: config.State,
ClientID: config.ClientID,
CLientSecret: config.ClientSecret,
Channels: channels,
ExternalID: config.ExternalID,
ExternalKey: config.ExternalKey,
Name: config.Name,
Content: config.Content,
State: config.State,
}
return res, nil
@@ -140,9 +140,9 @@ func updateEndpoint(svc bootstrap.Service) endpoint.Endpoint {
}
config := bootstrap.Config{
ThingID: req.id,
Name: req.Name,
Content: req.Content,
ClientID: req.id,
Name: req.Name,
Content: req.Content,
}
if err := svc.Update(ctx, session, config); err != nil {
@@ -150,7 +150,7 @@ func updateEndpoint(svc bootstrap.Service) endpoint.Endpoint {
}
res := configRes{
id: config.ThingID,
id: config.ClientID,
created: false,
}
@@ -217,14 +217,14 @@ func listEndpoint(svc bootstrap.Service) endpoint.Endpoint {
}
view := viewRes{
ThingID: cfg.ThingID,
ThingKey: cfg.ThingKey,
Channels: channels,
ExternalID: cfg.ExternalID,
ExternalKey: cfg.ExternalKey,
Name: cfg.Name,
Content: cfg.Content,
State: cfg.State,
ClientID: cfg.ClientID,
CLientSecret: cfg.ClientSecret,
Channels: channels,
ExternalID: cfg.ExternalID,
ExternalKey: cfg.ExternalKey,
Name: cfg.Name,
Content: cfg.Content,
State: cfg.State,
}
res.Configs = append(res.Configs, view)
}
+127 -127
View File
@@ -49,44 +49,44 @@ const (
)
var (
encKey = []byte("1234567891011121")
metadata = map[string]interface{}{"meta": "data"}
addExternalID = testsutil.GenerateUUID(&testing.T{})
addExternalKey = testsutil.GenerateUUID(&testing.T{})
addThingID = testsutil.GenerateUUID(&testing.T{})
addThingKey = testsutil.GenerateUUID(&testing.T{})
addReq = struct {
ThingID string `json:"thing_id"`
ThingKey string `json:"thing_key"`
ExternalID string `json:"external_id"`
ExternalKey string `json:"external_key"`
Channels []string `json:"channels"`
Name string `json:"name"`
Content string `json:"content"`
encKey = []byte("1234567891011121")
metadata = map[string]interface{}{"meta": "data"}
addExternalID = testsutil.GenerateUUID(&testing.T{})
addExternalKey = testsutil.GenerateUUID(&testing.T{})
addClientID = testsutil.GenerateUUID(&testing.T{})
addClientSecret = testsutil.GenerateUUID(&testing.T{})
addReq = struct {
ClientID string `json:"client_id"`
ClientSecret string `json:"client_secret"`
ExternalID string `json:"external_id"`
ExternalKey string `json:"external_key"`
Channels []string `json:"channels"`
Name string `json:"name"`
Content string `json:"content"`
}{
ThingID: addThingID,
ThingKey: addThingKey,
ExternalID: addExternalID,
ExternalKey: addExternalKey,
Channels: []string{"1"},
Name: "name",
Content: "config",
ClientID: addClientID,
ClientSecret: addClientSecret,
ExternalID: addExternalID,
ExternalKey: addExternalKey,
Channels: []string{"1"},
Name: "name",
Content: "config",
}
updateReq = struct {
Channels []string `json:"channels,omitempty"`
Content string `json:"content,omitempty"`
State bootstrap.State `json:"state,omitempty"`
ClientCert string `json:"client_cert,omitempty"`
ClientKey string `json:"client_key,omitempty"`
CACert string `json:"ca_cert,omitempty"`
Channels []string `json:"channels,omitempty"`
Content string `json:"content,omitempty"`
State bootstrap.State `json:"state,omitempty"`
ClientCert string `json:"client_cert,omitempty"`
ClientSecret string `json:"client_secret,omitempty"`
CACert string `json:"ca_cert,omitempty"`
}{
Channels: []string{"1"},
Content: "config update",
State: 1,
ClientCert: "newcert",
ClientKey: "newkey",
CACert: "newca",
Channels: []string{"1"},
Content: "config update",
State: 1,
ClientCert: "newcert",
ClientSecret: "newkey",
CACert: "newca",
}
missingIDRes = toJSON(apiutil.ErrorRes{Err: apiutil.ErrMissingID.Error(), Msg: apiutil.ErrValidation.Error()})
@@ -108,10 +108,10 @@ type testRequest struct {
func newConfig() bootstrap.Config {
return bootstrap.Config{
ThingID: addThingID,
ThingKey: addThingKey,
ExternalID: addExternalID,
ExternalKey: addExternalKey,
ClientID: addClientID,
ClientSecret: addClientSecret,
ExternalID: addExternalID,
ExternalKey: addExternalKey,
Channels: []bootstrap.Channel{
{
ID: "1",
@@ -136,7 +136,7 @@ func (tr testRequest) make() (*http.Response, error) {
req.Header.Set("Authorization", apiutil.BearerPrefix+tr.token)
}
if tr.key != "" {
req.Header.Set("Authorization", apiutil.ThingPrefix+tr.key)
req.Header.Set("Authorization", apiutil.ClientPrefix+tr.key)
}
if tr.contentType != "" {
@@ -200,7 +200,7 @@ func TestAdd(t *testing.T) {
data := toJSON(addReq)
neID := addReq
neID.ThingID = testsutil.GenerateUUID(t)
neID.ClientID = testsutil.GenerateUUID(t)
neData := toJSON(neID)
invalidChannels := addReq
@@ -237,7 +237,7 @@ func TestAdd(t *testing.T) {
token: validToken,
contentType: contentType,
status: http.StatusCreated,
location: "/things/configs/" + c.ThingID,
location: "/clients/configs/" + c.ClientID,
err: nil,
},
{
@@ -332,7 +332,7 @@ func TestAdd(t *testing.T) {
req := testRequest{
client: bs.Client(),
method: http.MethodPost,
url: fmt.Sprintf("%s/%s/things/configs", bs.URL, tc.domainID),
url: fmt.Sprintf("%s/%s/clients/configs", bs.URL, tc.domainID),
contentType: tc.contentType,
token: tc.token,
body: strings.NewReader(tc.req),
@@ -359,14 +359,14 @@ func TestView(t *testing.T) {
}
data := config{
ThingID: c.ThingID,
ThingKey: c.ThingKey,
State: c.State,
Channels: channels,
ExternalID: c.ExternalID,
ExternalKey: c.ExternalKey,
Name: c.Name,
Content: c.Content,
ClientID: c.ClientID,
ClientSecret: c.ClientSecret,
State: c.State,
Channels: channels,
ExternalID: c.ExternalID,
ExternalKey: c.ExternalKey,
Name: c.Name,
Content: c.Content,
}
cases := []struct {
@@ -382,7 +382,7 @@ func TestView(t *testing.T) {
{
desc: "view a config with invalid token",
token: invalidToken,
id: c.ThingID,
id: c.ClientID,
status: http.StatusUnauthorized,
res: config{},
authenticateErr: svcerr.ErrAuthentication,
@@ -391,7 +391,7 @@ func TestView(t *testing.T) {
{
desc: "view a config",
token: validToken,
id: c.ThingID,
id: c.ClientID,
status: http.StatusOK,
res: data,
err: nil,
@@ -407,7 +407,7 @@ func TestView(t *testing.T) {
{
desc: "view a config with an empty token",
token: "",
id: c.ThingID,
id: c.ClientID,
status: http.StatusUnauthorized,
res: config{},
err: apiutil.ErrBearerToken,
@@ -415,7 +415,7 @@ func TestView(t *testing.T) {
{
desc: "view config without authorization",
token: validToken,
id: c.ThingID,
id: c.ClientID,
status: http.StatusForbidden,
res: config{},
err: svcerr.ErrAuthorization,
@@ -432,7 +432,7 @@ func TestView(t *testing.T) {
req := testRequest{
client: bs.Client(),
method: http.MethodGet,
url: fmt.Sprintf("%s/%s/things/configs/%s", bs.URL, domainID, tc.id),
url: fmt.Sprintf("%s/%s/clients/configs/%s", bs.URL, domainID, tc.id),
token: tc.token,
}
res, err := req.make()
@@ -476,7 +476,7 @@ func TestUpdate(t *testing.T) {
{
desc: "update with invalid token",
req: data,
id: c.ThingID,
id: c.ClientID,
token: invalidToken,
contentType: contentType,
status: http.StatusUnauthorized,
@@ -486,7 +486,7 @@ func TestUpdate(t *testing.T) {
{
desc: "update with an empty token",
req: data,
id: c.ThingID,
id: c.ClientID,
token: "",
contentType: contentType,
status: http.StatusUnauthorized,
@@ -495,7 +495,7 @@ func TestUpdate(t *testing.T) {
{
desc: "update a valid config",
req: data,
id: c.ThingID,
id: c.ClientID,
token: validToken,
contentType: contentType,
status: http.StatusOK,
@@ -504,7 +504,7 @@ func TestUpdate(t *testing.T) {
{
desc: "update a config with wrong content type",
req: data,
id: c.ThingID,
id: c.ClientID,
token: validToken,
contentType: "",
status: http.StatusUnsupportedMediaType,
@@ -522,7 +522,7 @@ func TestUpdate(t *testing.T) {
{
desc: "update a config with invalid request format",
req: "}",
id: c.ThingID,
id: c.ClientID,
token: validToken,
contentType: contentType,
status: http.StatusBadRequest,
@@ -530,7 +530,7 @@ func TestUpdate(t *testing.T) {
},
{
desc: "update a config with an empty request",
id: c.ThingID,
id: c.ClientID,
req: "",
token: validToken,
contentType: contentType,
@@ -549,7 +549,7 @@ func TestUpdate(t *testing.T) {
req := testRequest{
client: bs.Client(),
method: http.MethodPut,
url: fmt.Sprintf("%s/%s/things/configs/%s", bs.URL, domainID, tc.id),
url: fmt.Sprintf("%s/%s/clients/configs/%s", bs.URL, domainID, tc.id),
contentType: tc.contentType,
token: tc.token,
body: strings.NewReader(tc.req),
@@ -584,7 +584,7 @@ func TestUpdateCert(t *testing.T) {
{
desc: "update with invalid token",
req: data,
id: c.ThingID,
id: c.ClientID,
token: invalidToken,
contentType: contentType,
status: http.StatusUnauthorized,
@@ -594,7 +594,7 @@ func TestUpdateCert(t *testing.T) {
{
desc: "update with an empty token",
req: data,
id: c.ThingID,
id: c.ClientID,
token: "",
contentType: contentType,
status: http.StatusUnauthorized,
@@ -603,7 +603,7 @@ func TestUpdateCert(t *testing.T) {
{
desc: "update a valid config",
req: data,
id: c.ThingID,
id: c.ClientID,
token: validToken,
contentType: contentType,
status: http.StatusOK,
@@ -612,7 +612,7 @@ func TestUpdateCert(t *testing.T) {
{
desc: "update a config with wrong content type",
req: data,
id: c.ThingID,
id: c.ClientID,
token: validToken,
contentType: "",
status: http.StatusUnsupportedMediaType,
@@ -630,7 +630,7 @@ func TestUpdateCert(t *testing.T) {
{
desc: "update a config with invalid request format",
req: "}",
id: c.ThingKey,
id: c.ClientSecret,
token: validToken,
contentType: contentType,
status: http.StatusBadRequest,
@@ -638,7 +638,7 @@ func TestUpdateCert(t *testing.T) {
},
{
desc: "update a config with an empty request",
id: c.ThingID,
id: c.ClientID,
req: "",
token: validToken,
contentType: contentType,
@@ -657,7 +657,7 @@ func TestUpdateCert(t *testing.T) {
req := testRequest{
client: bs.Client(),
method: http.MethodPatch,
url: fmt.Sprintf("%s/%s/things/configs/certs/%s", bs.URL, domainID, tc.id),
url: fmt.Sprintf("%s/%s/clients/configs/certs/%s", bs.URL, domainID, tc.id),
contentType: tc.contentType,
token: tc.token,
body: strings.NewReader(tc.req),
@@ -696,7 +696,7 @@ func TestUpdateConnections(t *testing.T) {
{
desc: "update connections with invalid token",
req: data,
id: c.ThingID,
id: c.ClientID,
token: invalidToken,
contentType: contentType,
status: http.StatusUnauthorized,
@@ -706,7 +706,7 @@ func TestUpdateConnections(t *testing.T) {
{
desc: "update connections with an empty token",
req: data,
id: c.ThingID,
id: c.ClientID,
token: "",
contentType: contentType,
status: http.StatusUnauthorized,
@@ -715,7 +715,7 @@ func TestUpdateConnections(t *testing.T) {
{
desc: "update connections valid config",
req: data,
id: c.ThingID,
id: c.ClientID,
token: validToken,
contentType: contentType,
status: http.StatusOK,
@@ -724,7 +724,7 @@ func TestUpdateConnections(t *testing.T) {
{
desc: "update connections with wrong content type",
req: data,
id: c.ThingID,
id: c.ClientID,
token: validToken,
contentType: "",
status: http.StatusUnsupportedMediaType,
@@ -742,7 +742,7 @@ func TestUpdateConnections(t *testing.T) {
{
desc: "update connections with invalid channels",
req: wrongData,
id: c.ThingID,
id: c.ClientID,
token: validToken,
contentType: contentType,
status: http.StatusNotFound,
@@ -751,7 +751,7 @@ func TestUpdateConnections(t *testing.T) {
{
desc: "update a config with invalid request format",
req: "}",
id: c.ThingID,
id: c.ClientID,
token: validToken,
contentType: contentType,
status: http.StatusBadRequest,
@@ -759,7 +759,7 @@ func TestUpdateConnections(t *testing.T) {
},
{
desc: "update a config with an empty request",
id: c.ThingID,
id: c.ClientID,
req: "",
token: validToken,
contentType: contentType,
@@ -778,7 +778,7 @@ func TestUpdateConnections(t *testing.T) {
req := testRequest{
client: bs.Client(),
method: http.MethodPut,
url: fmt.Sprintf("%s/%s/things/configs/connections/%s", bs.URL, domainID, tc.id),
url: fmt.Sprintf("%s/%s/clients/configs/connections/%s", bs.URL, domainID, tc.id),
contentType: tc.contentType,
token: tc.token,
body: strings.NewReader(tc.req),
@@ -800,13 +800,13 @@ func TestList(t *testing.T) {
bs, svc, auth := newBootstrapServer()
defer bs.Close()
path := fmt.Sprintf("%s/%s/%s", bs.URL, domainID, "things/configs")
path := fmt.Sprintf("%s/%s/%s", bs.URL, domainID, "clients/configs")
c := newConfig()
for i := 0; i < configNum; i++ {
c.ExternalID = strconv.Itoa(i)
c.ThingKey = c.ExternalID
c.ClientSecret = c.ExternalID
c.Name = fmt.Sprintf("%s-%d", addName, i)
c.ExternalKey = fmt.Sprintf("%s%s", addExternalKey, strconv.Itoa(i))
@@ -815,14 +815,14 @@ func TestList(t *testing.T) {
channels = append(channels, channel{ID: ch.ID, Name: ch.Name, Metadata: ch.Metadata})
}
s := config{
ThingID: c.ThingID,
ThingKey: c.ThingKey,
Channels: channels,
ExternalID: c.ExternalID,
ExternalKey: c.ExternalKey,
Name: c.Name,
Content: c.Content,
State: c.State,
ClientID: c.ClientID,
ClientSecret: c.ClientSecret,
Channels: channels,
ExternalID: c.ExternalID,
ExternalKey: c.ExternalKey,
Name: c.Name,
Content: c.Content,
State: c.State,
}
list[i] = s
}
@@ -833,7 +833,7 @@ func TestList(t *testing.T) {
state = bootstrap.Inactive
}
svcCall := svc.On("ChangeState", context.Background(), mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(nil)
err := svc.ChangeState(context.Background(), mgauthn.Session{}, validToken, list[i].ThingID, state)
err := svc.ChangeState(context.Background(), mgauthn.Session{}, validToken, list[i].ClientID, state)
assert.Nil(t, err, fmt.Sprintf("Changing state expected to succeed: %s.\n", err))
svcCall.Unset()
@@ -1084,7 +1084,7 @@ func TestRemove(t *testing.T) {
}{
{
desc: "remove with invalid token",
id: c.ThingID,
id: c.ClientID,
token: invalidToken,
status: http.StatusUnauthorized,
authenticateErr: svcerr.ErrAuthentication,
@@ -1092,7 +1092,7 @@ func TestRemove(t *testing.T) {
},
{
desc: "remove with an empty token",
id: c.ThingID,
id: c.ClientID,
token: "",
status: http.StatusUnauthorized,
err: apiutil.ErrBearerToken,
@@ -1106,7 +1106,7 @@ func TestRemove(t *testing.T) {
},
{
desc: "remove config",
id: c.ThingID,
id: c.ClientID,
token: validToken,
status: http.StatusNoContent,
err: nil,
@@ -1130,7 +1130,7 @@ func TestRemove(t *testing.T) {
req := testRequest{
client: bs.Client(),
method: http.MethodDelete,
url: fmt.Sprintf("%s/%s/things/configs/%s", bs.URL, domainID, tc.id),
url: fmt.Sprintf("%s/%s/clients/configs/%s", bs.URL, domainID, tc.id),
token: tc.token,
}
res, err := req.make()
@@ -1156,21 +1156,21 @@ func TestBootstrap(t *testing.T) {
}
s := struct {
ThingID string `json:"thing_id"`
ThingKey string `json:"thing_key"`
Channels []channel `json:"channels"`
Content string `json:"content"`
ClientCert string `json:"client_cert"`
ClientKey string `json:"client_key"`
CACert string `json:"ca_cert"`
ClientID string `json:"client_id"`
ClientSecret string `json:"client_secret"`
Channels []channel `json:"channels"`
Content string `json:"content"`
ClientCert string `json:"client_cert"`
ClientKey string `json:"client_key"`
CACert string `json:"ca_cert"`
}{
ThingID: c.ThingID,
ThingKey: c.ThingKey,
Channels: channels,
Content: c.Content,
ClientCert: c.ClientCert,
ClientKey: c.ClientKey,
CACert: c.CACert,
ClientID: c.ClientID,
ClientSecret: c.ClientSecret,
Channels: channels,
Content: c.Content,
ClientCert: c.ClientCert,
ClientKey: c.ClientKey,
CACert: c.CACert,
}
data := toJSON(s)
@@ -1185,7 +1185,7 @@ func TestBootstrap(t *testing.T) {
err error
}{
{
desc: "bootstrap a Thing with unknown ID",
desc: "bootstrap a Client with unknown ID",
externalID: unknown,
externalKey: c.ExternalKey,
status: http.StatusNotFound,
@@ -1194,7 +1194,7 @@ func TestBootstrap(t *testing.T) {
err: bootstrap.ErrBootstrap,
},
{
desc: "bootstrap a Thing with an empty ID",
desc: "bootstrap a Client with an empty ID",
externalID: "",
externalKey: c.ExternalKey,
status: http.StatusBadRequest,
@@ -1203,7 +1203,7 @@ func TestBootstrap(t *testing.T) {
err: errors.Wrap(bootstrap.ErrBootstrap, svcerr.ErrMalformedEntity),
},
{
desc: "bootstrap a Thing with unknown key",
desc: "bootstrap a Client with unknown key",
externalID: c.ExternalID,
externalKey: unknown,
status: http.StatusForbidden,
@@ -1212,7 +1212,7 @@ func TestBootstrap(t *testing.T) {
err: errors.Wrap(bootstrap.ErrExternalKey, errors.New("")),
},
{
desc: "bootstrap a Thing with an empty key",
desc: "bootstrap a Client with an empty key",
externalID: c.ExternalID,
externalKey: "",
status: http.StatusBadRequest,
@@ -1221,7 +1221,7 @@ func TestBootstrap(t *testing.T) {
err: errors.Wrap(bootstrap.ErrBootstrap, svcerr.ErrAuthentication),
},
{
desc: "bootstrap known Thing",
desc: "bootstrap known Client",
externalID: c.ExternalID,
externalKey: c.ExternalKey,
status: http.StatusOK,
@@ -1255,7 +1255,7 @@ func TestBootstrap(t *testing.T) {
req := testRequest{
client: bs.Client(),
method: http.MethodGet,
url: fmt.Sprintf("%s/things/bootstrap/%s", bs.URL, tc.externalID),
url: fmt.Sprintf("%s/clients/bootstrap/%s", bs.URL, tc.externalID),
key: tc.externalKey,
}
res, err := req.make()
@@ -1296,7 +1296,7 @@ func TestChangeState(t *testing.T) {
}{
{
desc: "change state with invalid token",
id: c.ThingID,
id: c.ClientID,
token: invalidToken,
state: active,
contentType: contentType,
@@ -1306,7 +1306,7 @@ func TestChangeState(t *testing.T) {
},
{
desc: "change state with an empty token",
id: c.ThingID,
id: c.ClientID,
token: "",
state: active,
contentType: contentType,
@@ -1315,7 +1315,7 @@ func TestChangeState(t *testing.T) {
},
{
desc: "change state with invalid content type",
id: c.ThingID,
id: c.ClientID,
token: validToken,
state: active,
contentType: "",
@@ -1324,7 +1324,7 @@ func TestChangeState(t *testing.T) {
},
{
desc: "change state to active",
id: c.ThingID,
id: c.ClientID,
token: validToken,
state: active,
contentType: contentType,
@@ -1333,7 +1333,7 @@ func TestChangeState(t *testing.T) {
},
{
desc: "change state to inactive",
id: c.ThingID,
id: c.ClientID,
token: validToken,
state: inactive,
contentType: contentType,
@@ -1351,7 +1351,7 @@ func TestChangeState(t *testing.T) {
},
{
desc: "change state to invalid value",
id: c.ThingID,
id: c.ClientID,
token: validToken,
state: fmt.Sprintf("{\"state\": %d}", -3),
contentType: contentType,
@@ -1360,7 +1360,7 @@ func TestChangeState(t *testing.T) {
},
{
desc: "change state with invalid data",
id: c.ThingID,
id: c.ClientID,
token: validToken,
state: "",
contentType: contentType,
@@ -1379,7 +1379,7 @@ func TestChangeState(t *testing.T) {
req := testRequest{
client: bs.Client(),
method: http.MethodPut,
url: fmt.Sprintf("%s/%s/things/state/%s", bs.URL, domainID, tc.id),
url: fmt.Sprintf("%s/%s/clients/state/%s", bs.URL, domainID, tc.id),
token: tc.token,
contentType: tc.contentType,
body: strings.NewReader(tc.state),
@@ -1400,14 +1400,14 @@ type channel struct {
}
type config struct {
ThingID string `json:"thing_id,omitempty"`
ThingKey string `json:"thing_key,omitempty"`
Channels []channel `json:"channels,omitempty"`
ExternalID string `json:"external_id"`
ExternalKey string `json:"external_key,omitempty"`
Content string `json:"content,omitempty"`
Name string `json:"name"`
State bootstrap.State `json:"state"`
ClientID string `json:"client_id,omitempty"`
ClientSecret string `json:"client_secret,omitempty"`
Channels []channel `json:"channels,omitempty"`
ExternalID string `json:"external_id"`
ExternalKey string `json:"external_key,omitempty"`
Content string `json:"content,omitempty"`
Name string `json:"name"`
State bootstrap.State `json:"state"`
}
type configPage struct {
+3 -3
View File
@@ -12,7 +12,7 @@ const maxLimitSize = 100
type addReq struct {
token string
ThingID string `json:"thing_id"`
ClientID string `json:"client_id"`
ExternalID string `json:"external_id"`
ExternalKey string `json:"external_key"`
Channels []string `json:"channels"`
@@ -76,14 +76,14 @@ func (req updateReq) validate() error {
}
type updateCertReq struct {
thingID string
clientID string
ClientCert string `json:"client_cert"`
ClientKey string `json:"client_key"`
CACert string `json:"ca_cert"`
}
func (req updateCertReq) validate() error {
if req.thingID == "" {
if req.clientID == "" {
return apiutil.ErrMissingID
}
+7 -7
View File
@@ -151,20 +151,20 @@ func TestUpdateReqValidation(t *testing.T) {
func TestUpdateCertReqValidation(t *testing.T) {
cases := []struct {
desc string
thingID string
err error
desc string
clientID string
err error
}{
{
desc: "empty thing id",
thingID: "",
err: apiutil.ErrMissingID,
desc: "empty client id",
clientID: "",
err: apiutil.ErrMissingID,
},
}
for _, tc := range cases {
req := updateCertReq{
thingID: tc.thingID,
clientID: tc.clientID,
}
err := req.validate()
+13 -13
View File
@@ -49,7 +49,7 @@ func (res configRes) Code() int {
func (res configRes) Headers() map[string]string {
if res.created {
return map[string]string{
"Location": fmt.Sprintf("/things/configs/%s", res.id),
"Location": fmt.Sprintf("/clients/configs/%s", res.id),
}
}
@@ -67,16 +67,16 @@ type channelRes struct {
}
type viewRes struct {
ThingID string `json:"thing_id,omitempty"`
ThingKey string `json:"thing_key,omitempty"`
Channels []channelRes `json:"channels,omitempty"`
ExternalID string `json:"external_id"`
ExternalKey string `json:"external_key,omitempty"`
Content string `json:"content,omitempty"`
Name string `json:"name,omitempty"`
State bootstrap.State `json:"state"`
ClientCert string `json:"client_cert,omitempty"`
CACert string `json:"ca_cert,omitempty"`
ClientID string `json:"client_id,omitempty"`
CLientSecret string `json:"client_secret,omitempty"`
Channels []channelRes `json:"channels,omitempty"`
ExternalID string `json:"external_id"`
ExternalKey string `json:"external_key,omitempty"`
Content string `json:"content,omitempty"`
Name string `json:"name,omitempty"`
State bootstrap.State `json:"state"`
ClientCert string `json:"client_cert,omitempty"`
CACert string `json:"ca_cert,omitempty"`
}
func (res viewRes) Code() int {
@@ -125,9 +125,9 @@ func (res stateRes) Empty() bool {
}
type updateConfigRes struct {
ThingID string `json:"thing_id,omitempty"`
ClientCert string `json:"client_cert,omitempty"`
ClientID string `json:"client_id,omitempty"`
CACert string `json:"ca_cert,omitempty"`
ClientCert string `json:"client_cert,omitempty"`
ClientKey string `json:"client_key,omitempty"`
}
+7 -7
View File
@@ -33,7 +33,7 @@ const (
)
var (
fullMatch = []string{"state", "external_id", "thing_id", "thing_key"}
fullMatch = []string{"state", "external_id", "client_id", "client_key"}
partialMatch = []string{"name"}
// ErrBootstrap indicates error in getting bootstrap configuration.
ErrBootstrap = errors.New("failed to read bootstrap configuration")
@@ -47,7 +47,7 @@ func MakeHandler(svc bootstrap.Service, authn mgauthn.Authentication, reader boo
r := chi.NewRouter()
r.Route("/{domainID}/things", func(r chi.Router) {
r.Route("/{domainID}/clients", func(r chi.Router) {
r.Group(func(r chi.Router) {
r.Use(api.AuthenticateMiddleware(authn, true))
@@ -96,14 +96,14 @@ func MakeHandler(svc bootstrap.Service, authn mgauthn.Authentication, reader boo
})
})
r.With(api.AuthenticateMiddleware(authn, true)).Put("/state/{thingID}", otelhttp.NewHandler(kithttp.NewServer(
r.With(api.AuthenticateMiddleware(authn, true)).Put("/state/{clientID}", otelhttp.NewHandler(kithttp.NewServer(
stateEndpoint(svc),
decodeStateRequest,
api.EncodeResponse,
opts...), "update_state").ServeHTTP)
})
r.Route("/things/bootstrap", func(r chi.Router) {
r.Route("/clients/bootstrap", func(r chi.Router) {
r.Get("/", otelhttp.NewHandler(kithttp.NewServer(
bootstrapEndpoint(svc, reader, false),
decodeBootstrapRequest,
@@ -163,7 +163,7 @@ func decodeUpdateCertRequest(_ context.Context, r *http.Request) (interface{}, e
}
req := updateCertReq{
thingID: chi.URLParam(r, "certID"),
clientID: chi.URLParam(r, "certID"),
}
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
return nil, errors.Wrap(apiutil.ErrValidation, errors.Wrap(err, errors.ErrMalformedEntity))
@@ -216,7 +216,7 @@ func decodeListRequest(_ context.Context, r *http.Request) (interface{}, error)
func decodeBootstrapRequest(_ context.Context, r *http.Request) (interface{}, error) {
req := bootstrapReq{
id: chi.URLParam(r, "externalID"),
key: apiutil.ExtractThingKey(r),
key: apiutil.ExtractClientSecret(r),
}
return req, nil
@@ -229,7 +229,7 @@ func decodeStateRequest(_ context.Context, r *http.Request) (interface{}, error)
req := changeStateReq{
token: apiutil.ExtractBearerToken(r),
id: chi.URLParam(r, "thingID"),
id: chi.URLParam(r, "clientID"),
}
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
return nil, errors.Wrap(apiutil.ErrValidation, errors.Wrap(err, errors.ErrMalformedEntity))
+27 -27
View File
@@ -7,30 +7,30 @@ import (
"context"
"time"
"github.com/absmach/magistrala/things"
"github.com/absmach/magistrala/clients"
)
// Config represents Configuration entity. It wraps information about external entity
// as well as info about corresponding Magistrala entities.
// MGThing represents corresponding Magistrala Thing ID.
// MGKey is key of corresponding Magistrala Thing.
// MGChannels is a list of Magistrala Channels corresponding Magistrala Thing connects to.
// MGClient represents corresponding Magistrala Client ID.
// MGKey is key of corresponding Magistrala Client.
// MGChannels is a list of Magistrala Channels corresponding Magistrala Client connects to.
type Config struct {
ThingID string `json:"thing_id"`
DomainID string `json:"domain_id,omitempty"`
Name string `json:"name,omitempty"`
ClientCert string `json:"client_cert,omitempty"`
ClientKey string `json:"client_key,omitempty"`
CACert string `json:"ca_cert,omitempty"`
ThingKey string `json:"thing_key"`
Channels []Channel `json:"channels,omitempty"`
ExternalID string `json:"external_id"`
ExternalKey string `json:"external_key"`
Content string `json:"content,omitempty"`
State State `json:"state"`
ClientID string `json:"client_id"`
ClientSecret string `json:"client_secret"`
DomainID string `json:"domain_id,omitempty"`
Name string `json:"name,omitempty"`
ClientCert string `json:"client_cert,omitempty"`
ClientKey string `json:"client_key,omitempty"`
CACert string `json:"ca_cert,omitempty"`
Channels []Channel `json:"channels,omitempty"`
ExternalID string `json:"external_id"`
ExternalKey string `json:"external_key"`
Content string `json:"content,omitempty"`
State State `json:"state"`
}
// Channel represents Magistrala channel corresponding Magistrala Thing is connected to.
// Channel represents Magistrala channel corresponding Magistrala Client is connected to.
type Channel struct {
ID string `json:"id"`
Name string `json:"name,omitempty"`
@@ -41,7 +41,7 @@ type Channel struct {
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at,omitempty"`
UpdatedBy string `json:"updated_by,omitempty"`
Status things.Status `json:"status"`
Status clients.Status `json:"status"`
}
// Filter is used for the search filters.
@@ -73,7 +73,7 @@ type ConfigRepository interface {
// RetrieveAll retrieves a subset of Configs that are owned
// by the specific user, with given filter parameters.
RetrieveAll(ctx context.Context, domainID string, thingIDs []string, filter Filter, offset, limit uint64) ConfigsPage
RetrieveAll(ctx context.Context, domainID string, clientIDs []string, filter Filter, offset, limit uint64) ConfigsPage
// RetrieveByExternalID returns Config for given external ID.
RetrieveByExternalID(ctx context.Context, externalID string) (Config, error)
@@ -84,7 +84,7 @@ type ConfigRepository interface {
// UpdateCerts updates and returns an existing Config certificate and domainID.
// A non-nil error is returned to indicate operation failure.
UpdateCert(ctx context.Context, domainID, thingID, clientCert, clientKey, caCert string) (Config, error)
UpdateCert(ctx context.Context, domainID, clientID, clientCert, clientKey, caCert string) (Config, error)
// UpdateConnections updates a list of Channels the Config is connected to
// adding new Channels if needed.
@@ -100,11 +100,11 @@ type ConfigRepository interface {
// ListExisting retrieves those channels from the given list that exist in DB.
ListExisting(ctx context.Context, domainID string, ids []string) ([]Channel, error)
// Methods RemoveThing, UpdateChannel, and RemoveChannel are related to
// Methods RemoveClient, UpdateChannel, and RemoveChannel are related to
// event sourcing. That's why these methods surpass ownership check.
// RemoveThing removes Config of the Thing with the given ID.
RemoveThing(ctx context.Context, id string) error
// RemoveClient removes Config of the Client with the given ID.
RemoveClient(ctx context.Context, id string) error
// UpdateChannel updates channel with the given ID.
UpdateChannel(ctx context.Context, c Channel) error
@@ -112,9 +112,9 @@ type ConfigRepository interface {
// RemoveChannel removes channel with the given ID.
RemoveChannel(ctx context.Context, id string) error
// ConnectThing changes state of the Config when the corresponding Thing is connected to the Channel.
ConnectThing(ctx context.Context, channelID, thingID string) error
// ConnectClient changes state of the Config when the corresponding Client is connected to the Channel.
ConnectClient(ctx context.Context, channelID, clientID string) error
// DisconnectThing changes state of the Config when the corresponding Thing is disconnected from the Channel.
DisconnectThing(ctx context.Context, channelID, thingID string) error
// DisconnectClient changes state of the Config when the corresponding Client is disconnected from the Channel.
DisconnectClient(ctx context.Context, channelID, clientID string) error
}
+1 -1
View File
@@ -19,6 +19,6 @@ type updateChannelEvent struct {
// Connection event is either connect or disconnect event.
type connectionEvent struct {
thingIDs []string
clientIDs []string
channelID string
}
+25 -25
View File
@@ -13,15 +13,15 @@ import (
)
const (
thingRemove = "thing.remove"
thingConnect = "group.assign"
thingDisconnect = "group.unassign"
clientRemove = "client.remove"
clientConnect = "group.assign"
clientDisconnect = "group.unassign"
channelPrefix = "group."
channelPrefix = "channels."
channelUpdate = channelPrefix + "update"
channelRemove = channelPrefix + "remove"
memberKind = "things"
memberKind = "client"
relation = "group"
)
@@ -43,35 +43,35 @@ func (es *eventHandler) Handle(ctx context.Context, event events.Event) error {
}
switch msg["operation"] {
case thingRemove:
rte := decodeRemoveThing(msg)
case clientRemove:
rte := decodeRemoveClient(msg)
err = es.svc.RemoveConfigHandler(ctx, rte.id)
case thingConnect:
cte := decodeConnectThing(msg)
if cte.channelID == "" || len(cte.thingIDs) == 0 {
case clientConnect:
cte := decodeConnectClient(msg)
if cte.channelID == "" || len(cte.clientIDs) == 0 {
return svcerr.ErrMalformedEntity
}
for _, thingID := range cte.thingIDs {
if thingID == "" {
for _, clientID := range cte.clientIDs {
if clientID == "" {
return svcerr.ErrMalformedEntity
}
if err := es.svc.ConnectThingHandler(ctx, cte.channelID, thingID); err != nil {
if err := es.svc.ConnectClientHandler(ctx, cte.channelID, clientID); err != nil {
return err
}
}
case thingDisconnect:
dte := decodeDisconnectThing(msg)
if dte.channelID == "" || len(dte.thingIDs) == 0 {
case clientDisconnect:
dte := decodeDisconnectClient(msg)
if dte.channelID == "" || len(dte.clientIDs) == 0 {
return svcerr.ErrMalformedEntity
}
for _, thingID := range dte.thingIDs {
if thingID == "" {
for _, clientID := range dte.clientIDs {
if clientID == "" {
return svcerr.ErrMalformedEntity
}
}
for _, thingID := range dte.thingIDs {
if err = es.svc.DisconnectThingHandler(ctx, dte.channelID, thingID); err != nil {
for _, c := range dte.clientIDs {
if err = es.svc.DisconnectClientHandler(ctx, dte.channelID, c); err != nil {
return err
}
}
@@ -89,7 +89,7 @@ func (es *eventHandler) Handle(ctx context.Context, event events.Event) error {
return nil
}
func decodeRemoveThing(event map[string]interface{}) removeEvent {
func decodeRemoveClient(event map[string]interface{}) removeEvent {
return removeEvent{
id: events.Read(event, "id", ""),
}
@@ -113,25 +113,25 @@ func decodeRemoveChannel(event map[string]interface{}) removeEvent {
}
}
func decodeConnectThing(event map[string]interface{}) connectionEvent {
func decodeConnectClient(event map[string]interface{}) connectionEvent {
if events.Read(event, "memberKind", "") != memberKind && events.Read(event, "relation", "") != relation {
return connectionEvent{}
}
return connectionEvent{
channelID: events.Read(event, "group_id", ""),
thingIDs: events.ReadStringSlice(event, "member_ids"),
clientIDs: events.ReadStringSlice(event, "member_ids"),
}
}
func decodeDisconnectThing(event map[string]interface{}) connectionEvent {
func decodeDisconnectClient(event map[string]interface{}) connectionEvent {
if events.Read(event, "memberKind", "") != memberKind && events.Read(event, "relation", "") != relation {
return connectionEvent{}
}
return connectionEvent{
channelID: events.Read(event, "group_id", ""),
thingIDs: events.ReadStringSlice(event, "member_ids"),
clientIDs: events.ReadStringSlice(event, "member_ids"),
}
}
+35 -32
View File
@@ -17,12 +17,12 @@ const (
configList = configPrefix + "list"
configHandlerRemove = configPrefix + "remove_handler"
thingPrefix = "bootstrap.thing."
thingBootstrap = thingPrefix + "bootstrap"
thingStateChange = thingPrefix + "change_state"
thingUpdateConnections = thingPrefix + "update_connections"
thingConnect = thingPrefix + "connect"
thingDisconnect = thingPrefix + "disconnect"
clientPrefix = "bootstrap.client."
clientBootstrap = clientPrefix + "bootstrap"
clientStateChange = clientPrefix + "change_state"
clientUpdateConnections = clientPrefix + "update_connections"
clientConnect = clientPrefix + "connect"
clientDisconnect = clientPrefix + "disconnect"
channelPrefix = "bootstrap.channel."
channelHandlerRemove = channelPrefix + "remove_handler"
@@ -52,8 +52,8 @@ func (ce configEvent) Encode() (map[string]interface{}, error) {
"state": ce.State.String(),
"operation": ce.operation,
}
if ce.ThingID != "" {
val["thing_id"] = ce.ThingID
if ce.ClientID != "" {
val["client_id"] = ce.ClientID
}
if ce.Content != "" {
val["content"] = ce.Content
@@ -91,12 +91,12 @@ func (ce configEvent) Encode() (map[string]interface{}, error) {
}
type removeConfigEvent struct {
mgThing string
client string
}
func (rce removeConfigEvent) Encode() (map[string]interface{}, error) {
return map[string]interface{}{
"thing_id": rce.mgThing,
"client_id": rce.client,
"operation": configRemove,
}, nil
}
@@ -134,11 +134,11 @@ func (be bootstrapEvent) Encode() (map[string]interface{}, error) {
val := map[string]interface{}{
"external_id": be.externalID,
"success": be.success,
"operation": thingBootstrap,
"operation": clientBootstrap,
}
if be.ThingID != "" {
val["thing_id"] = be.ThingID
if be.ClientID != "" {
val["client_id"] = be.ClientID
}
if be.Content != "" {
val["content"] = be.Content
@@ -175,38 +175,41 @@ func (be bootstrapEvent) Encode() (map[string]interface{}, error) {
}
type changeStateEvent struct {
mgThing string
state bootstrap.State
mgClient string
state bootstrap.State
}
func (cse changeStateEvent) Encode() (map[string]interface{}, error) {
return map[string]interface{}{
"thing_id": cse.mgThing,
"client_id": cse.mgClient,
"state": cse.state.String(),
"operation": thingStateChange,
"operation": clientStateChange,
}, nil
}
type updateConnectionsEvent struct {
mgThing string
mgClient string
mgChannels []string
}
func (uce updateConnectionsEvent) Encode() (map[string]interface{}, error) {
return map[string]interface{}{
"thing_id": uce.mgThing,
"client_id": uce.mgClient,
"channels": uce.mgChannels,
"operation": thingUpdateConnections,
"operation": clientUpdateConnections,
}, nil
}
type updateCertEvent struct {
thingKey, clientCert, clientKey, caCert string
clientID string
clientCert string
clientKey string
caCert string
}
func (uce updateCertEvent) Encode() (map[string]interface{}, error) {
return map[string]interface{}{
"thing_key": uce.thingKey,
"client_id": uce.clientID,
"client_cert": uce.clientCert,
"client_key": uce.clientKey,
"ca_cert": uce.caCert,
@@ -247,28 +250,28 @@ func (uche updateChannelHandlerEvent) Encode() (map[string]interface{}, error) {
return val, nil
}
type connectThingEvent struct {
thingID string
type connectClientEvent struct {
clientID string
channelID string
}
func (cte connectThingEvent) Encode() (map[string]interface{}, error) {
func (cte connectClientEvent) Encode() (map[string]interface{}, error) {
return map[string]interface{}{
"thing_id": cte.thingID,
"client_id": cte.clientID,
"channel_id": cte.channelID,
"operation": thingConnect,
"operation": clientConnect,
}, nil
}
type disconnectThingEvent struct {
thingID string
type disconnectClientEvent struct {
clientID string
channelID string
}
func (dte disconnectThingEvent) Encode() (map[string]interface{}, error) {
func (dte disconnectClientEvent) Encode() (map[string]interface{}, error) {
return map[string]interface{}{
"thing_id": dte.thingID,
"client_id": dte.clientID,
"channel_id": dte.channelID,
"operation": thingDisconnect,
"operation": clientDisconnect,
}, nil
}
+15 -15
View File
@@ -72,14 +72,14 @@ func (es *eventStore) Update(ctx context.Context, session mgauthn.Session, cfg b
return es.Publish(ctx, ev)
}
func (es eventStore) UpdateCert(ctx context.Context, session mgauthn.Session, thingKey, clientCert, clientKey, caCert string) (bootstrap.Config, error) {
cfg, err := es.svc.UpdateCert(ctx, session, thingKey, clientCert, clientKey, caCert)
func (es eventStore) UpdateCert(ctx context.Context, session mgauthn.Session, clientID, clientCert, clientKey, caCert string) (bootstrap.Config, error) {
cfg, err := es.svc.UpdateCert(ctx, session, clientID, clientCert, clientKey, caCert)
if err != nil {
return cfg, err
}
ev := updateCertEvent{
thingKey: thingKey,
clientID: clientID,
clientCert: clientCert,
clientKey: clientKey,
caCert: caCert,
@@ -98,7 +98,7 @@ func (es *eventStore) UpdateConnections(ctx context.Context, session mgauthn.Ses
}
ev := updateConnectionsEvent{
mgThing: id,
mgClient: id,
mgChannels: connections,
}
@@ -131,7 +131,7 @@ func (es *eventStore) Remove(ctx context.Context, session mgauthn.Session, id st
}
ev := removeConfigEvent{
mgThing: id,
client: id,
}
return es.Publish(ctx, ev)
@@ -163,8 +163,8 @@ func (es *eventStore) ChangeState(ctx context.Context, session mgauthn.Session,
}
ev := changeStateEvent{
mgThing: id,
state: state,
mgClient: id,
state: state,
}
return es.Publish(ctx, ev)
@@ -208,26 +208,26 @@ func (es *eventStore) UpdateChannelHandler(ctx context.Context, channel bootstra
return es.Publish(ctx, ev)
}
func (es *eventStore) ConnectThingHandler(ctx context.Context, channelID, thingID string) error {
if err := es.svc.ConnectThingHandler(ctx, channelID, thingID); err != nil {
func (es *eventStore) ConnectClientHandler(ctx context.Context, channelID, clientID string) error {
if err := es.svc.ConnectClientHandler(ctx, channelID, clientID); err != nil {
return err
}
ev := connectThingEvent{
thingID: thingID,
ev := connectClientEvent{
clientID: clientID,
channelID: channelID,
}
return es.Publish(ctx, ev)
}
func (es *eventStore) DisconnectThingHandler(ctx context.Context, channelID, thingID string) error {
if err := es.svc.DisconnectThingHandler(ctx, channelID, thingID); err != nil {
func (es *eventStore) DisconnectClientHandler(ctx context.Context, channelID, clientID string) error {
if err := es.svc.DisconnectClientHandler(ctx, channelID, clientID); err != nil {
return err
}
ev := disconnectThingEvent{
thingID: thingID,
ev := disconnectClientEvent{
clientID: clientID,
channelID: channelID,
}
+126 -126
View File
@@ -11,11 +11,11 @@ import (
"testing"
"time"
"github.com/absmach/magistrala"
"github.com/absmach/magistrala/bootstrap"
"github.com/absmach/magistrala/bootstrap/events/producer"
"github.com/absmach/magistrala/bootstrap/mocks"
"github.com/absmach/magistrala/internal/testsutil"
"github.com/absmach/magistrala/pkg/authn"
mgauthn "github.com/absmach/magistrala/pkg/authn"
"github.com/absmach/magistrala/pkg/errors"
svcerr "github.com/absmach/magistrala/pkg/errors/service"
@@ -32,13 +32,13 @@ import (
)
const (
streamID = "magistrala.bootstrap"
email = "user@example.com"
validToken = "validToken"
invalidToken = "invalid"
unknownThingID = "unknown"
channelsNum = 3
defaultTimout = 5
streamID = "magistrala.bootstrap"
email = "user@example.com"
validToken = "validToken"
invalidToken = "invalid"
unknownClientID = "unknown"
channelsNum = 3
defaultTimout = 5
configPrefix = "config."
configCreate = configPrefix + "create"
@@ -48,12 +48,12 @@ const (
configList = configPrefix + "list"
configHandlerRemove = configPrefix + "remove_handler"
thingPrefix = "thing."
thingBootstrap = thingPrefix + "bootstrap"
thingStateChange = thingPrefix + "change_state"
thingUpdateConnections = thingPrefix + "update_connections"
thingConnect = thingPrefix + "connect"
thingDisconnect = thingPrefix + "disconnect"
clientPrefix = "client."
clientBootstrap = clientPrefix + "bootstrap"
clientStateChange = clientPrefix + "change_state"
clientUpdateConnections = clientPrefix + "update_connections"
clientConnect = clientPrefix + "connect"
clientDisconnect = clientPrefix + "disconnect"
channelPrefix = "group."
channelHandlerRemove = channelPrefix + "remove_handler"
@@ -76,12 +76,12 @@ var (
}
config = bootstrap.Config{
ThingID: testsutil.GenerateUUID(&testing.T{}),
ThingKey: testsutil.GenerateUUID(&testing.T{}),
ExternalID: testsutil.GenerateUUID(&testing.T{}),
ExternalKey: testsutil.GenerateUUID(&testing.T{}),
Channels: []bootstrap.Channel{channel},
Content: "config",
ClientID: testsutil.GenerateUUID(&testing.T{}),
ClientSecret: testsutil.GenerateUUID(&testing.T{}),
ExternalID: testsutil.GenerateUUID(&testing.T{}),
ExternalKey: testsutil.GenerateUUID(&testing.T{}),
Channels: []bootstrap.Channel{channel},
Content: "config",
}
)
@@ -125,18 +125,18 @@ func TestAdd(t *testing.T) {
invalidConfig.Channels = []bootstrap.Channel{{ID: "empty"}}
cases := []struct {
desc string
config bootstrap.Config
token string
session mgauthn.Session
id string
domainID string
thingErr error
channel []bootstrap.Channel
listErr error
saveErr error
err error
event map[string]interface{}
desc string
config bootstrap.Config
token string
session mgauthn.Session
id string
domainID string
clientErr error
channel []bootstrap.Channel
listErr error
saveErr error
err error
event map[string]interface{}
}{
{
desc: "create config successfully",
@@ -146,7 +146,7 @@ func TestAdd(t *testing.T) {
domainID: domainID,
channel: config.Channels,
event: map[string]interface{}{
"thing_id": "1",
"client_id": "1",
"domain_id": domainID,
"name": config.Name,
"channels": channels,
@@ -158,14 +158,14 @@ func TestAdd(t *testing.T) {
err: nil,
},
{
desc: "create config with failed to fetch thing",
config: config,
token: validToken,
id: validID,
domainID: domainID,
event: nil,
thingErr: svcerr.ErrNotFound,
err: svcerr.ErrNotFound,
desc: "create config with failed to fetch client",
config: config,
token: validToken,
id: validID,
domainID: domainID,
event: nil,
clientErr: svcerr.ErrNotFound,
err: svcerr.ErrNotFound,
},
{
desc: "create config with failed to list existing",
@@ -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.ClientCredentials{Secret: tc.config.ThingKey}}, errors.NewSDKError(tc.thingErr))
sdkCall := tv.sdk.On("Client", tc.config.ClientID, tc.domainID, tc.token).Return(mgsdk.Client{ID: tc.config.ClientID, Credentials: mgsdk.ClientCredentials{Secret: tc.config.ClientSecret}}, errors.NewSDKError(tc.clientErr))
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)
@@ -226,7 +226,7 @@ func TestView(t *testing.T) {
tv := newTestVariable(t, redisURL)
nonExisting := config
nonExisting.ThingID = unknownThingID
nonExisting.ClientID = unknownClientID
cases := []struct {
desc string
@@ -247,7 +247,7 @@ func TestView(t *testing.T) {
domainID: domainID,
err: nil,
event: map[string]interface{}{
"thing_id": config.ThingID,
"client_id": config.ClientID,
"domain_id": config.DomainID,
"name": config.Name,
"channels": config.Channels,
@@ -272,8 +272,8 @@ func TestView(t *testing.T) {
lastID := "0"
for _, tc := range cases {
tc.session = mgauthn.Session{UserID: validID, DomainID: tc.domainID, DomainUserID: validID}
repoCall := tv.boot.On("RetrieveByID", context.Background(), tc.domainID, tc.config.ThingID).Return(config, tc.retrieveErr)
_, err := tv.svc.View(context.Background(), tc.session, tc.config.ThingID)
repoCall := tv.boot.On("RetrieveByID", context.Background(), tc.domainID, tc.config.ClientID).Return(config, tc.retrieveErr)
_, err := tv.svc.View(context.Background(), tc.session, tc.config.ClientID)
assert.True(t, errors.Contains(err, tc.err), fmt.Sprintf("%s: expected %s got %s\n", tc.desc, tc.err, err))
streams := redisClient.XRead(context.Background(), &redis.XReadArgs{
@@ -316,7 +316,7 @@ func TestUpdate(t *testing.T) {
modified.Name = "new name"
nonExisting := config
nonExisting.ThingID = unknownThingID
nonExisting.ClientID = unknownClientID
channels := []string{modified.Channels[0].ID, modified.Channels[1].ID}
@@ -345,7 +345,7 @@ func TestUpdate(t *testing.T) {
"operation": configUpdate,
"channels": channels,
"external_id": modified.ExternalID,
"thing_id": modified.ThingID,
"client_id": modified.ClientID,
"domain_id": domainID,
"state": "0",
"occurred_at": time.Now().UnixNano(),
@@ -403,7 +403,7 @@ func TestUpdateConnections(t *testing.T) {
token string
session mgauthn.Session
connections []string
thingErr error
clientErr error
channelErr error
retrieveErr error
listErr error
@@ -413,22 +413,22 @@ func TestUpdateConnections(t *testing.T) {
}{
{
desc: "update connections successfully",
configID: config.ThingID,
configID: config.ClientID,
token: validToken,
id: validID,
domainID: domainID,
connections: []string{config.Channels[0].ID},
err: nil,
event: map[string]interface{}{
"thing_id": config.ThingID,
"client_id": config.ClientID,
"channels": "2",
"timestamp": time.Now().Unix(),
"operation": thingUpdateConnections,
"operation": clientUpdateConnections,
},
},
{
desc: "update connections with failed channel fetch",
configID: config.ThingID,
configID: config.ClientID,
token: validToken,
id: validID,
domainID: domainID,
@@ -439,7 +439,7 @@ func TestUpdateConnections(t *testing.T) {
},
{
desc: "update connections with failed RetrieveByID",
configID: config.ThingID,
configID: config.ClientID,
token: validToken,
id: validID,
domainID: domainID,
@@ -450,7 +450,7 @@ func TestUpdateConnections(t *testing.T) {
},
{
desc: "update connections with failed ListExisting",
configID: config.ThingID,
configID: config.ClientID,
token: validToken,
id: validID,
domainID: domainID,
@@ -461,7 +461,7 @@ func TestUpdateConnections(t *testing.T) {
},
{
desc: "update connections with failed UpdateConnections",
configID: config.ThingID,
configID: config.ClientID,
token: validToken,
id: validID,
domainID: domainID,
@@ -524,7 +524,7 @@ func TestUpdateCert(t *testing.T) {
}{
{
desc: "update cert successfully",
configID: config.ThingID,
configID: config.ClientID,
userID: validID,
domainID: domainID,
token: validToken,
@@ -533,16 +533,16 @@ func TestUpdateCert(t *testing.T) {
caCert: "caCert",
err: nil,
event: map[string]interface{}{
"thing_key": config.ThingKey,
"client_cert": "clientCert",
"client_key": "clientKey",
"ca_cert": "caCert",
"operation": certUpdate,
"client_secret": config.ClientSecret,
"client_cert": "clientCert",
"client_key": "clientKey",
"ca_cert": "caCert",
"operation": certUpdate,
},
},
{
desc: "update cert with failed update",
configID: "invalidThingID",
configID: "clientID",
token: validToken,
userID: validID,
domainID: domainID,
@@ -555,7 +555,7 @@ func TestUpdateCert(t *testing.T) {
},
{
desc: "update cert with empty client certificate",
configID: config.ThingID,
configID: config.ClientID,
token: validToken,
userID: validID,
domainID: domainID,
@@ -567,7 +567,7 @@ func TestUpdateCert(t *testing.T) {
},
{
desc: "update cert with empty client key",
configID: config.ThingID,
configID: config.ClientID,
token: validToken,
userID: validID,
domainID: domainID,
@@ -579,7 +579,7 @@ func TestUpdateCert(t *testing.T) {
},
{
desc: "update cert with empty CA certificate",
configID: config.ThingID,
configID: config.ClientID,
token: validToken,
userID: validID,
domainID: domainID,
@@ -591,7 +591,7 @@ func TestUpdateCert(t *testing.T) {
},
{
desc: "successful update without CA certificate",
configID: config.ThingID,
configID: config.ClientID,
token: validToken,
userID: validID,
domainID: domainID,
@@ -600,12 +600,12 @@ func TestUpdateCert(t *testing.T) {
caCert: "",
err: nil,
event: map[string]interface{}{
"thing_key": config.ThingKey,
"client_cert": "clientCert",
"client_key": "clientKey",
"ca_cert": "caCert",
"operation": certUpdate,
"timestamp": time.Now().Unix(),
"client_secret": config.ClientSecret,
"client_cert": "clientCert",
"client_key": "clientKey",
"ca_cert": "caCert",
"operation": certUpdate,
"timestamp": time.Now().Unix(),
},
},
}
@@ -639,10 +639,10 @@ func TestUpdateCert(t *testing.T) {
func TestList(t *testing.T) {
tv := newTestVariable(t, redisURL)
numThings := 101
numClients := 101
var c bootstrap.Config
saved := make([]bootstrap.Config, 0)
for i := 0; i < numThings; i++ {
for i := 0; i < numClients; i++ {
c := config
c.ExternalID = testsutil.GenerateUUID(t)
c.ExternalKey = testsutil.GenerateUUID(t)
@@ -687,7 +687,7 @@ func TestList(t *testing.T) {
listObjectsResponse: policysvc.PolicyPage{},
err: nil,
event: map[string]interface{}{
"thing_id": c.ThingID,
"client_id": c.ClientID,
"domain_id": c.DomainID,
"name": c.Name,
"channels": c.Channels,
@@ -715,7 +715,7 @@ func TestList(t *testing.T) {
listObjectsResponse: policysvc.PolicyPage{},
err: nil,
event: map[string]interface{}{
"thing_id": c.ThingID,
"client_id": c.ClientID,
"domain_id": c.DomainID,
"name": c.Name,
"channels": c.Channels,
@@ -743,7 +743,7 @@ func TestList(t *testing.T) {
listObjectsResponse: policysvc.PolicyPage{},
err: nil,
event: map[string]interface{}{
"thing_id": c.ThingID,
"client_id": c.ClientID,
"domain_id": c.DomainID,
"name": c.Name,
"channels": c.Channels,
@@ -818,7 +818,7 @@ func TestList(t *testing.T) {
SubjectType: policysvc.UserType,
Subject: tc.userID,
Permission: policysvc.ViewPermission,
ObjectType: policysvc.ThingType,
ObjectType: policysvc.ClientType,
}).Return(tc.listObjectsResponse, tc.listObjectsErr)
repoCall := tv.boot.On("RetrieveAll", context.Background(), mock.Anything, mock.Anything, tc.filter, tc.offset, tc.limit).Return(tc.config, tc.retrieveErr)
@@ -851,7 +851,7 @@ func TestRemove(t *testing.T) {
tv := newTestVariable(t, redisURL)
nonExisting := config
nonExisting.ThingID = unknownThingID
nonExisting.ClientID = unknownClientID
cases := []struct {
desc string
@@ -866,20 +866,20 @@ func TestRemove(t *testing.T) {
}{
{
desc: "remove config successfully",
configID: config.ThingID,
configID: config.ClientID,
token: validToken,
userID: validID,
domainID: domainID,
err: nil,
event: map[string]interface{}{
"thing_id": config.ThingID,
"client_id": config.ClientID,
"timestamp": time.Now().Unix(),
"operation": configRemove,
},
},
{
desc: "remove config with failed removal",
configID: nonExisting.ThingID,
configID: nonExisting.ClientID,
token: validToken,
userID: validID,
domainID: domainID,
@@ -936,7 +936,7 @@ func TestBootstrap(t *testing.T) {
"external_id": config.ExternalID,
"success": "1",
"timestamp": time.Now().Unix(),
"operation": thingBootstrap,
"operation": clientBootstrap,
},
},
{
@@ -949,7 +949,7 @@ func TestBootstrap(t *testing.T) {
"external_id": "external_id",
"success": "0",
"timestamp": time.Now().Unix(),
"operation": thingBootstrap,
"operation": clientBootstrap,
},
},
}
@@ -990,7 +990,7 @@ func TestChangeState(t *testing.T) {
token string
session mgauthn.Session
state bootstrap.State
authResponse *magistrala.AuthZRes
authResponse authn.Session
authorizeErr error
connectErr error
retrieveErr error
@@ -1001,18 +1001,18 @@ func TestChangeState(t *testing.T) {
}{
{
desc: "change state to active",
id: config.ThingID,
id: config.ClientID,
token: validToken,
userID: validID,
domainID: domainID,
state: bootstrap.Active,
authResponse: &magistrala.AuthZRes{Authorized: true},
authResponse: authn.Session{},
err: nil,
event: map[string]interface{}{
"thing_id": config.ThingID,
"client_id": config.ClientID,
"state": bootstrap.Active.String(),
"timestamp": time.Now().Unix(),
"operation": thingStateChange,
"operation": clientStateChange,
},
},
{
@@ -1028,18 +1028,18 @@ func TestChangeState(t *testing.T) {
},
{
desc: "change state with failed connect",
id: config.ThingID,
id: config.ClientID,
token: validToken,
userID: validID,
domainID: domainID,
state: bootstrap.Active,
connectErr: bootstrap.ErrThings,
err: bootstrap.ErrThings,
connectErr: bootstrap.ErrClients,
err: bootstrap.ErrClients,
event: nil,
},
{
desc: "change state unsuccessfully",
id: config.ThingID,
id: config.ClientID,
token: validToken,
userID: validID,
domainID: domainID,
@@ -1054,7 +1054,7 @@ func TestChangeState(t *testing.T) {
for _, tc := range cases {
tc.session = mgauthn.Session{UserID: validID, DomainID: tc.domainID, DomainUserID: validID}
repoCall := tv.boot.On("RetrieveByID", context.Background(), tc.domainID, tc.id).Return(config, tc.retrieveErr)
sdkCall1 := tv.sdk.On("Connect", mock.Anything, mock.Anything, mock.Anything).Return(errors.NewSDKError(tc.connectErr))
sdkCall1 := tv.sdk.On("ConnectClient", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(errors.NewSDKError(tc.connectErr))
repoCall1 := tv.boot.On("ChangeState", context.Background(), mock.Anything, mock.Anything, mock.Anything).Return(tc.stateErr)
err := tv.svc.ChangeState(context.Background(), tc.session, tc.token, tc.id, tc.state)
assert.True(t, errors.Contains(err, tc.err), fmt.Sprintf("%s: expected %s got %s\n", tc.desc, tc.err, err))
@@ -1261,7 +1261,7 @@ func TestRemoveConfigHandler(t *testing.T) {
lastID := "0"
for _, tc := range cases {
repoCall := tv.boot.On("RemoveThing", context.Background(), mock.Anything).Return(tc.err)
repoCall := tv.boot.On("RemoveClient", context.Background(), mock.Anything).Return(tc.err)
err := tv.svc.RemoveConfigHandler(context.Background(), tc.configID)
assert.Equal(t, tc.err, err, fmt.Sprintf("%s: expected %s got %s\n", tc.desc, tc.err, err))
@@ -1284,7 +1284,7 @@ func TestRemoveConfigHandler(t *testing.T) {
}
}
func TestConnectThingHandler(t *testing.T) {
func TestConnectClientHandler(t *testing.T) {
err := redisClient.FlushAll(context.Background()).Err()
assert.Nil(t, err, fmt.Sprintf("got unexpected error: %s", err))
@@ -1293,41 +1293,41 @@ func TestConnectThingHandler(t *testing.T) {
cases := []struct {
desc string
channelID string
thingID string
clientID string
err error
event map[string]interface{}
}{
{
desc: "connect thing handler successfully",
desc: "connect client handler successfully",
channelID: channel.ID,
thingID: "1",
clientID: "1",
err: nil,
event: map[string]interface{}{
"channel_id": channel.ID,
"thing_id": "1",
"operation": thingConnect,
"client_id": "1",
"operation": clientConnect,
"timestamp": time.Now().UnixNano(),
"occurred_at": time.Now().UnixNano(),
},
},
{
desc: "connect non-existing thing handler",
desc: "connect non-existing client handler",
channelID: channel.ID,
thingID: "unknown",
clientID: "unknown",
err: nil,
event: nil,
},
{
desc: "connect thing handler with empty thing ID",
desc: "connect client handler with empty client ID",
channelID: channel.ID,
thingID: "",
clientID: "",
err: nil,
event: nil,
},
{
desc: "connect thing handler with empty channel ID",
desc: "connect client handler with empty channel ID",
channelID: "",
thingID: "1",
clientID: "1",
err: nil,
event: nil,
},
@@ -1335,8 +1335,8 @@ func TestConnectThingHandler(t *testing.T) {
lastID := "0"
for _, tc := range cases {
repoCall := tv.boot.On("ConnectThing", context.Background(), mock.Anything, mock.Anything).Return(tc.err)
err := tv.svc.ConnectThingHandler(context.Background(), tc.channelID, tc.thingID)
repoCall := tv.boot.On("ConnectClient", context.Background(), mock.Anything, mock.Anything).Return(tc.err)
err := tv.svc.ConnectClientHandler(context.Background(), tc.channelID, tc.clientID)
assert.Equal(t, tc.err, err, fmt.Sprintf("%s: expected %s got %s\n", tc.desc, tc.err, err))
streams := redisClient.XRead(context.Background(), &redis.XReadArgs{
@@ -1358,7 +1358,7 @@ func TestConnectThingHandler(t *testing.T) {
}
}
func TestDisconnectThingHandler(t *testing.T) {
func TestDisconnectClientHandler(t *testing.T) {
err := redisClient.FlushAll(context.Background()).Err()
assert.Nil(t, err, fmt.Sprintf("got unexpected error: %s", err))
@@ -1367,50 +1367,50 @@ func TestDisconnectThingHandler(t *testing.T) {
cases := []struct {
desc string
channelID string
thingID string
clientID string
err error
event map[string]interface{}
}{
{
desc: "disconnect thing handler successfully",
desc: "disconnect client handler successfully",
channelID: channel.ID,
thingID: "1",
clientID: "1",
err: nil,
event: map[string]interface{}{
"channel_id": channel.ID,
"thing_id": "1",
"operation": thingDisconnect,
"client_id": "1",
"operation": clientDisconnect,
"timestamp": time.Now().UnixNano(),
"occurred_at": time.Now().UnixNano(),
},
},
{
desc: "remove non-existing thing handler",
desc: "remove non-existing client handler",
channelID: "unknown",
err: nil,
},
{
desc: "remove thing handler with empty thing ID",
desc: "remove client handler with empty client ID",
channelID: channel.ID,
thingID: "",
clientID: "",
err: nil,
event: nil,
},
{
desc: "remove thing handler with empty channel ID",
desc: "remove client handler with empty channel ID",
channelID: "",
err: nil,
event: nil,
},
{
desc: "remove thing handler successfully",
desc: "remove client handler successfully",
channelID: channel.ID,
thingID: "1",
clientID: "1",
err: nil,
event: map[string]interface{}{
"channel_id": channel.ID,
"thing_id": "1",
"operation": thingDisconnect,
"client_id": "1",
"operation": clientDisconnect,
"timestamp": time.Now().UnixNano(),
"occurred_at": time.Now().UnixNano(),
},
@@ -1419,8 +1419,8 @@ func TestDisconnectThingHandler(t *testing.T) {
lastID := "0"
for _, tc := range cases {
repoCall := tv.boot.On("DisconnectThing", context.Background(), tc.channelID, tc.thingID).Return(tc.err)
err := tv.svc.DisconnectThingHandler(context.Background(), tc.channelID, tc.thingID)
repoCall := tv.boot.On("DisconnectClient", context.Background(), tc.channelID, tc.clientID).Return(tc.err)
err := tv.svc.DisconnectClientHandler(context.Background(), tc.channelID, tc.clientID)
assert.Equal(t, tc.err, err, fmt.Sprintf("%s: expected %s got %s\n", tc.desc, tc.err, err))
streams := redisClient.XRead(context.Background(), &redis.XReadArgs{
+11 -11
View File
@@ -37,7 +37,7 @@ func (am *authorizationMiddleware) Add(ctx context.Context, session mgauthn.Sess
}
func (am *authorizationMiddleware) View(ctx context.Context, session mgauthn.Session, id string) (bootstrap.Config, error) {
if err := am.authorize(ctx, session.DomainID, policies.UserType, policies.UsersKind, session.DomainUserID, policies.ViewPermission, policies.ThingType, id); err != nil {
if err := am.authorize(ctx, session.DomainID, policies.UserType, policies.UsersKind, session.DomainUserID, policies.ViewPermission, policies.ClientType, id); err != nil {
return bootstrap.Config{}, err
}
@@ -45,23 +45,23 @@ func (am *authorizationMiddleware) View(ctx context.Context, session mgauthn.Ses
}
func (am *authorizationMiddleware) Update(ctx context.Context, session mgauthn.Session, cfg bootstrap.Config) error {
if err := am.authorize(ctx, session.DomainID, policies.UserType, policies.UsersKind, session.DomainUserID, policies.EditPermission, policies.ThingType, cfg.ThingID); err != nil {
if err := am.authorize(ctx, session.DomainID, policies.UserType, policies.UsersKind, session.DomainUserID, policies.EditPermission, policies.ClientType, cfg.ClientID); err != nil {
return err
}
return am.svc.Update(ctx, session, cfg)
}
func (am *authorizationMiddleware) UpdateCert(ctx context.Context, session mgauthn.Session, thingID, clientCert, clientKey, caCert string) (bootstrap.Config, error) {
if err := am.authorize(ctx, session.DomainID, policies.UserType, policies.UsersKind, session.DomainUserID, policies.EditPermission, policies.ThingType, thingID); err != nil {
func (am *authorizationMiddleware) UpdateCert(ctx context.Context, session mgauthn.Session, clientID, clientCert, clientKey, caCert string) (bootstrap.Config, error) {
if err := am.authorize(ctx, session.DomainID, policies.UserType, policies.UsersKind, session.DomainUserID, policies.EditPermission, policies.ClientType, clientID); err != nil {
return bootstrap.Config{}, err
}
return am.svc.UpdateCert(ctx, session, thingID, clientCert, clientKey, caCert)
return am.svc.UpdateCert(ctx, session, clientID, clientCert, clientKey, caCert)
}
func (am *authorizationMiddleware) UpdateConnections(ctx context.Context, session mgauthn.Session, token, id string, connections []string) error {
if err := am.authorize(ctx, session.DomainID, policies.UserType, policies.UsersKind, session.DomainUserID, policies.EditPermission, policies.ThingType, id); err != nil {
if err := am.authorize(ctx, session.DomainID, policies.UserType, policies.UsersKind, session.DomainUserID, policies.EditPermission, policies.ClientType, id); err != nil {
return err
}
@@ -80,7 +80,7 @@ func (am *authorizationMiddleware) List(ctx context.Context, session mgauthn.Ses
}
func (am *authorizationMiddleware) Remove(ctx context.Context, session mgauthn.Session, id string) error {
if err := am.authorize(ctx, session.DomainID, policies.UserType, policies.UsersKind, session.DomainUserID, policies.DeletePermission, policies.ThingType, id); err != nil {
if err := am.authorize(ctx, session.DomainID, policies.UserType, policies.UsersKind, session.DomainUserID, policies.DeletePermission, policies.ClientType, id); err != nil {
return err
}
@@ -107,12 +107,12 @@ func (am *authorizationMiddleware) RemoveChannelHandler(ctx context.Context, id
return am.svc.RemoveChannelHandler(ctx, id)
}
func (am *authorizationMiddleware) ConnectThingHandler(ctx context.Context, channelID, ThingID string) error {
return am.svc.ConnectThingHandler(ctx, channelID, ThingID)
func (am *authorizationMiddleware) ConnectClientHandler(ctx context.Context, channelID, clientID string) error {
return am.svc.ConnectClientHandler(ctx, channelID, clientID)
}
func (am *authorizationMiddleware) DisconnectThingHandler(ctx context.Context, channelID, ThingID string) error {
return am.svc.DisconnectThingHandler(ctx, channelID, ThingID)
func (am *authorizationMiddleware) DisconnectClientHandler(ctx context.Context, channelID, clientID string) error {
return am.svc.DisconnectClientHandler(ctx, channelID, clientID)
}
func (am *authorizationMiddleware) checkSuperAdmin(ctx context.Context, adminID string) error {
+26 -26
View File
@@ -26,13 +26,13 @@ func LoggingMiddleware(svc bootstrap.Service, logger *slog.Logger) bootstrap.Ser
return &loggingMiddleware{logger, svc}
}
// Add logs the add request. It logs the thing ID and the time it took to complete the request.
// Add logs the add request. It logs the client ID and the time it took to complete the request.
// If the request fails, it logs the error.
func (lm *loggingMiddleware) Add(ctx context.Context, session mgauthn.Session, token string, cfg bootstrap.Config) (saved bootstrap.Config, err error) {
defer func(begin time.Time) {
args := []any{
slog.String("duration", time.Since(begin).String()),
slog.String("thing_id", saved.ThingID),
slog.String("client_id", saved.ClientID),
}
if err != nil {
args = append(args, slog.Any("error", err))
@@ -45,33 +45,33 @@ func (lm *loggingMiddleware) Add(ctx context.Context, session mgauthn.Session, t
return lm.svc.Add(ctx, session, token, cfg)
}
// View logs the view request. It logs the thing ID and the time it took to complete the request.
// View logs the view request. It logs the client ID and the time it took to complete the request.
// If the request fails, it logs the error.
func (lm *loggingMiddleware) View(ctx context.Context, session mgauthn.Session, id string) (saved bootstrap.Config, err error) {
defer func(begin time.Time) {
args := []any{
slog.String("duration", time.Since(begin).String()),
slog.String("thing_id", id),
slog.String("client_id", id),
}
if err != nil {
args = append(args, slog.Any("error", err))
lm.logger.Warn("View thing config failed", args...)
lm.logger.Warn("View client config failed", args...)
return
}
lm.logger.Info("View thing config completed successfully", args...)
lm.logger.Info("View client config completed successfully", args...)
}(time.Now())
return lm.svc.View(ctx, session, id)
}
// Update logs the update request. It logs bootstrap thing ID and the time it took to complete the request.
// Update logs the update request. It logs bootstrap client ID and the time it took to complete the request.
// If the request fails, it logs the error.
func (lm *loggingMiddleware) Update(ctx context.Context, session mgauthn.Session, cfg bootstrap.Config) (err error) {
defer func(begin time.Time) {
args := []any{
slog.String("duration", time.Since(begin).String()),
slog.Group("config",
slog.String("thing_id", cfg.ThingID),
slog.String("client_id", cfg.ClientID),
slog.String("name", cfg.Name),
),
}
@@ -86,13 +86,13 @@ func (lm *loggingMiddleware) Update(ctx context.Context, session mgauthn.Session
return lm.svc.Update(ctx, session, cfg)
}
// UpdateCert logs the update_cert request. It logs thing ID and the time it took to complete the request.
// UpdateCert logs the update_cert request. It logs client ID and the time it took to complete the request.
// If the request fails, it logs the error.
func (lm *loggingMiddleware) UpdateCert(ctx context.Context, session mgauthn.Session, thingID, clientCert, clientKey, caCert string) (cfg bootstrap.Config, err error) {
func (lm *loggingMiddleware) UpdateCert(ctx context.Context, session mgauthn.Session, clientID, clientCert, clientKey, caCert string) (cfg bootstrap.Config, err error) {
defer func(begin time.Time) {
args := []any{
slog.String("duration", time.Since(begin).String()),
slog.String("thing_id", cfg.ThingID),
slog.String("client_id", cfg.ClientID),
}
if err != nil {
args = append(args, slog.Any("error", err))
@@ -102,7 +102,7 @@ func (lm *loggingMiddleware) UpdateCert(ctx context.Context, session mgauthn.Ses
lm.logger.Info("Update bootstrap config certificate completed successfully", args...)
}(time.Now())
return lm.svc.UpdateCert(ctx, session, thingID, clientCert, clientKey, caCert)
return lm.svc.UpdateCert(ctx, session, clientID, clientCert, clientKey, caCert)
}
// UpdateConnections logs the update_connections request. It logs bootstrap ID and the time it took to complete the request.
@@ -111,7 +111,7 @@ func (lm *loggingMiddleware) UpdateConnections(ctx context.Context, session mgau
defer func(begin time.Time) {
args := []any{
slog.String("duration", time.Since(begin).String()),
slog.String("thing_id", id),
slog.String("client_id", id),
slog.Any("connections", connections),
}
if err != nil {
@@ -155,7 +155,7 @@ func (lm *loggingMiddleware) Remove(ctx context.Context, session mgauthn.Session
defer func(begin time.Time) {
args := []any{
slog.String("duration", time.Since(begin).String()),
slog.String("thing_id", id),
slog.String("client_id", id),
}
if err != nil {
args = append(args, slog.Any("error", err))
@@ -194,10 +194,10 @@ func (lm *loggingMiddleware) ChangeState(ctx context.Context, session mgauthn.Se
}
if err != nil {
args = append(args, slog.Any("error", err))
lm.logger.Warn("Change thing state failed", args...)
lm.logger.Warn("Change client state failed", args...)
return
}
lm.logger.Info("Change thing state completed successfully", args...)
lm.logger.Info("Change client state completed successfully", args...)
}(time.Now())
return lm.svc.ChangeState(ctx, session, token, id, state)
@@ -258,38 +258,38 @@ func (lm *loggingMiddleware) RemoveChannelHandler(ctx context.Context, id string
return lm.svc.RemoveChannelHandler(ctx, id)
}
func (lm *loggingMiddleware) ConnectThingHandler(ctx context.Context, channelID, thingID string) (err error) {
func (lm *loggingMiddleware) ConnectClientHandler(ctx context.Context, channelID, clientID string) (err error) {
defer func(begin time.Time) {
args := []any{
slog.String("duration", time.Since(begin).String()),
slog.String("channel_id", channelID),
slog.String("thing_id", thingID),
slog.String("client_id", clientID),
}
if err != nil {
args = append(args, slog.Any("error", err))
lm.logger.Warn("Connect thing handler failed", args...)
lm.logger.Warn("Connect client handler failed", args...)
return
}
lm.logger.Info("Connect thing handler completed successfully", args...)
lm.logger.Info("Connect client handler completed successfully", args...)
}(time.Now())
return lm.svc.ConnectThingHandler(ctx, channelID, thingID)
return lm.svc.ConnectClientHandler(ctx, channelID, clientID)
}
func (lm *loggingMiddleware) DisconnectThingHandler(ctx context.Context, channelID, thingID string) (err error) {
func (lm *loggingMiddleware) DisconnectClientHandler(ctx context.Context, channelID, clientID string) (err error) {
defer func(begin time.Time) {
args := []any{
slog.String("duration", time.Since(begin).String()),
slog.String("channel_id", channelID),
slog.String("thing_id", thingID),
slog.String("client_id", clientID),
}
if err != nil {
args = append(args, slog.Any("error", err))
lm.logger.Warn("Disconnect thing handler failed", args...)
lm.logger.Warn("Disconnect client handler failed", args...)
return
}
lm.logger.Info("Disconnect thing handler completed successfully", args...)
lm.logger.Info("Disconnect client handler completed successfully", args...)
}(time.Now())
return lm.svc.DisconnectThingHandler(ctx, channelID, thingID)
return lm.svc.DisconnectClientHandler(ctx, channelID, clientID)
}
+12 -12
View File
@@ -62,13 +62,13 @@ func (mm *metricsMiddleware) Update(ctx context.Context, session mgauthn.Session
}
// UpdateCert instruments UpdateCert method with metrics.
func (mm *metricsMiddleware) UpdateCert(ctx context.Context, session mgauthn.Session, thingKey, clientCert, clientKey, caCert string) (cfg bootstrap.Config, err error) {
func (mm *metricsMiddleware) UpdateCert(ctx context.Context, session mgauthn.Session, clientID, clientCert, clientKey, caCert string) (cfg bootstrap.Config, err error) {
defer func(begin time.Time) {
mm.counter.With("method", "update_cert").Add(1)
mm.latency.With("method", "update_cert").Observe(time.Since(begin).Seconds())
}(time.Now())
return mm.svc.UpdateCert(ctx, session, thingKey, clientCert, clientKey, caCert)
return mm.svc.UpdateCert(ctx, session, clientID, clientCert, clientKey, caCert)
}
// UpdateConnections instruments UpdateConnections method with metrics.
@@ -151,22 +151,22 @@ func (mm *metricsMiddleware) RemoveChannelHandler(ctx context.Context, id string
return mm.svc.RemoveChannelHandler(ctx, id)
}
// ConnectThingHandler instruments ConnectThingHandler method with metrics.
func (mm *metricsMiddleware) ConnectThingHandler(ctx context.Context, channelID, thingID string) (err error) {
// ConnectClientHandler instruments ConnectClientHandler method with metrics.
func (mm *metricsMiddleware) ConnectClientHandler(ctx context.Context, channelID, clientID string) (err error) {
defer func(begin time.Time) {
mm.counter.With("method", "connect_thing_handler").Add(1)
mm.latency.With("method", "connect_thing_handler").Observe(time.Since(begin).Seconds())
mm.counter.With("method", "connect_client_handler").Add(1)
mm.latency.With("method", "connect_client_handler").Observe(time.Since(begin).Seconds())
}(time.Now())
return mm.svc.ConnectThingHandler(ctx, channelID, thingID)
return mm.svc.ConnectClientHandler(ctx, channelID, clientID)
}
// DisconnectThingHandler instruments DisconnectThingHandler method with metrics.
func (mm *metricsMiddleware) DisconnectThingHandler(ctx context.Context, channelID, thingID string) (err error) {
// DisconnectClientHandler instruments DisconnectClientHandler method with metrics.
func (mm *metricsMiddleware) DisconnectClientHandler(ctx context.Context, channelID, clientID string) (err error) {
defer func(begin time.Time) {
mm.counter.With("method", "disconnect_thing_handler").Add(1)
mm.latency.With("method", "disconnect_thing_handler").Observe(time.Since(begin).Seconds())
mm.counter.With("method", "disconnect_client_handler").Add(1)
mm.latency.With("method", "disconnect_client_handler").Observe(time.Since(begin).Seconds())
}(time.Now())
return mm.svc.DisconnectThingHandler(ctx, channelID, thingID)
return mm.svc.DisconnectClientHandler(ctx, channelID, clientID)
}
+23 -23
View File
@@ -35,17 +35,17 @@ func (_m *ConfigRepository) ChangeState(ctx context.Context, domainID string, id
return r0
}
// ConnectThing provides a mock function with given fields: ctx, channelID, thingID
func (_m *ConfigRepository) ConnectThing(ctx context.Context, channelID string, thingID string) error {
ret := _m.Called(ctx, channelID, thingID)
// ConnectClient provides a mock function with given fields: ctx, channelID, clientID
func (_m *ConfigRepository) ConnectClient(ctx context.Context, channelID string, clientID string) error {
ret := _m.Called(ctx, channelID, clientID)
if len(ret) == 0 {
panic("no return value specified for ConnectThing")
panic("no return value specified for ConnectClient")
}
var r0 error
if rf, ok := ret.Get(0).(func(context.Context, string, string) error); ok {
r0 = rf(ctx, channelID, thingID)
r0 = rf(ctx, channelID, clientID)
} else {
r0 = ret.Error(0)
}
@@ -53,17 +53,17 @@ func (_m *ConfigRepository) ConnectThing(ctx context.Context, channelID string,
return r0
}
// DisconnectThing provides a mock function with given fields: ctx, channelID, thingID
func (_m *ConfigRepository) DisconnectThing(ctx context.Context, channelID string, thingID string) error {
ret := _m.Called(ctx, channelID, thingID)
// DisconnectClient provides a mock function with given fields: ctx, channelID, clientID
func (_m *ConfigRepository) DisconnectClient(ctx context.Context, channelID string, clientID string) error {
ret := _m.Called(ctx, channelID, clientID)
if len(ret) == 0 {
panic("no return value specified for DisconnectThing")
panic("no return value specified for DisconnectClient")
}
var r0 error
if rf, ok := ret.Get(0).(func(context.Context, string, string) error); ok {
r0 = rf(ctx, channelID, thingID)
r0 = rf(ctx, channelID, clientID)
} else {
r0 = ret.Error(0)
}
@@ -137,12 +137,12 @@ func (_m *ConfigRepository) RemoveChannel(ctx context.Context, id string) error
return r0
}
// RemoveThing provides a mock function with given fields: ctx, id
func (_m *ConfigRepository) RemoveThing(ctx context.Context, id string) error {
// RemoveClient provides a mock function with given fields: ctx, id
func (_m *ConfigRepository) RemoveClient(ctx context.Context, id string) error {
ret := _m.Called(ctx, id)
if len(ret) == 0 {
panic("no return value specified for RemoveThing")
panic("no return value specified for RemoveClient")
}
var r0 error
@@ -155,9 +155,9 @@ func (_m *ConfigRepository) RemoveThing(ctx context.Context, id string) error {
return r0
}
// RetrieveAll provides a mock function with given fields: ctx, domainID, thingIDs, filter, offset, limit
func (_m *ConfigRepository) RetrieveAll(ctx context.Context, domainID string, thingIDs []string, filter bootstrap.Filter, offset uint64, limit uint64) bootstrap.ConfigsPage {
ret := _m.Called(ctx, domainID, thingIDs, filter, offset, limit)
// RetrieveAll provides a mock function with given fields: ctx, domainID, clientIDs, filter, offset, limit
func (_m *ConfigRepository) RetrieveAll(ctx context.Context, domainID string, clientIDs []string, filter bootstrap.Filter, offset uint64, limit uint64) bootstrap.ConfigsPage {
ret := _m.Called(ctx, domainID, clientIDs, filter, offset, limit)
if len(ret) == 0 {
panic("no return value specified for RetrieveAll")
@@ -165,7 +165,7 @@ func (_m *ConfigRepository) RetrieveAll(ctx context.Context, domainID string, th
var r0 bootstrap.ConfigsPage
if rf, ok := ret.Get(0).(func(context.Context, string, []string, bootstrap.Filter, uint64, uint64) bootstrap.ConfigsPage); ok {
r0 = rf(ctx, domainID, thingIDs, filter, offset, limit)
r0 = rf(ctx, domainID, clientIDs, filter, offset, limit)
} else {
r0 = ret.Get(0).(bootstrap.ConfigsPage)
}
@@ -275,9 +275,9 @@ func (_m *ConfigRepository) Update(ctx context.Context, cfg bootstrap.Config) er
return r0
}
// UpdateCert provides a mock function with given fields: ctx, domainID, thingID, clientCert, clientKey, caCert
func (_m *ConfigRepository) UpdateCert(ctx context.Context, domainID string, thingID string, clientCert string, clientKey string, caCert string) (bootstrap.Config, error) {
ret := _m.Called(ctx, domainID, thingID, clientCert, clientKey, caCert)
// UpdateCert provides a mock function with given fields: ctx, domainID, clientID, clientCert, clientKey, caCert
func (_m *ConfigRepository) UpdateCert(ctx context.Context, domainID string, clientID string, clientCert string, clientKey string, caCert string) (bootstrap.Config, error) {
ret := _m.Called(ctx, domainID, clientID, clientCert, clientKey, caCert)
if len(ret) == 0 {
panic("no return value specified for UpdateCert")
@@ -286,16 +286,16 @@ func (_m *ConfigRepository) UpdateCert(ctx context.Context, domainID string, thi
var r0 bootstrap.Config
var r1 error
if rf, ok := ret.Get(0).(func(context.Context, string, string, string, string, string) (bootstrap.Config, error)); ok {
return rf(ctx, domainID, thingID, clientCert, clientKey, caCert)
return rf(ctx, domainID, clientID, clientCert, clientKey, caCert)
}
if rf, ok := ret.Get(0).(func(context.Context, string, string, string, string, string) bootstrap.Config); ok {
r0 = rf(ctx, domainID, thingID, clientCert, clientKey, caCert)
r0 = rf(ctx, domainID, clientID, clientCert, clientKey, caCert)
} else {
r0 = ret.Get(0).(bootstrap.Config)
}
if rf, ok := ret.Get(1).(func(context.Context, string, string, string, string, string) error); ok {
r1 = rf(ctx, domainID, thingID, clientCert, clientKey, caCert)
r1 = rf(ctx, domainID, clientID, clientCert, clientKey, caCert)
} else {
r1 = ret.Error(1)
}
+16 -16
View File
@@ -92,17 +92,17 @@ func (_m *Service) ChangeState(ctx context.Context, session authn.Session, token
return r0
}
// ConnectThingHandler provides a mock function with given fields: ctx, channelID, ThingID
func (_m *Service) ConnectThingHandler(ctx context.Context, channelID string, ThingID string) error {
ret := _m.Called(ctx, channelID, ThingID)
// ConnectClientHandler provides a mock function with given fields: ctx, channelID, clientID
func (_m *Service) ConnectClientHandler(ctx context.Context, channelID string, clientID string) error {
ret := _m.Called(ctx, channelID, clientID)
if len(ret) == 0 {
panic("no return value specified for ConnectThingHandler")
panic("no return value specified for ConnectClientHandler")
}
var r0 error
if rf, ok := ret.Get(0).(func(context.Context, string, string) error); ok {
r0 = rf(ctx, channelID, ThingID)
r0 = rf(ctx, channelID, clientID)
} else {
r0 = ret.Error(0)
}
@@ -110,17 +110,17 @@ func (_m *Service) ConnectThingHandler(ctx context.Context, channelID string, Th
return r0
}
// DisconnectThingHandler provides a mock function with given fields: ctx, channelID, ThingID
func (_m *Service) DisconnectThingHandler(ctx context.Context, channelID string, ThingID string) error {
ret := _m.Called(ctx, channelID, ThingID)
// DisconnectClientHandler provides a mock function with given fields: ctx, channelID, clientID
func (_m *Service) DisconnectClientHandler(ctx context.Context, channelID string, clientID string) error {
ret := _m.Called(ctx, channelID, clientID)
if len(ret) == 0 {
panic("no return value specified for DisconnectThingHandler")
panic("no return value specified for DisconnectClientHandler")
}
var r0 error
if rf, ok := ret.Get(0).(func(context.Context, string, string) error); ok {
r0 = rf(ctx, channelID, ThingID)
r0 = rf(ctx, channelID, clientID)
} else {
r0 = ret.Error(0)
}
@@ -228,9 +228,9 @@ func (_m *Service) Update(ctx context.Context, session authn.Session, cfg bootst
return r0
}
// UpdateCert provides a mock function with given fields: ctx, session, thingID, clientCert, clientKey, caCert
func (_m *Service) UpdateCert(ctx context.Context, session authn.Session, thingID string, clientCert string, clientKey string, caCert string) (bootstrap.Config, error) {
ret := _m.Called(ctx, session, thingID, clientCert, clientKey, caCert)
// UpdateCert provides a mock function with given fields: ctx, session, clientID, clientCert, clientKey, caCert
func (_m *Service) UpdateCert(ctx context.Context, session authn.Session, clientID string, clientCert string, clientKey string, caCert string) (bootstrap.Config, error) {
ret := _m.Called(ctx, session, clientID, clientCert, clientKey, caCert)
if len(ret) == 0 {
panic("no return value specified for UpdateCert")
@@ -239,16 +239,16 @@ func (_m *Service) UpdateCert(ctx context.Context, session authn.Session, thingI
var r0 bootstrap.Config
var r1 error
if rf, ok := ret.Get(0).(func(context.Context, authn.Session, string, string, string, string) (bootstrap.Config, error)); ok {
return rf(ctx, session, thingID, clientCert, clientKey, caCert)
return rf(ctx, session, clientID, clientCert, clientKey, caCert)
}
if rf, ok := ret.Get(0).(func(context.Context, authn.Session, string, string, string, string) bootstrap.Config); ok {
r0 = rf(ctx, session, thingID, clientCert, clientKey, caCert)
r0 = rf(ctx, session, clientID, clientCert, clientKey, caCert)
} else {
r0 = ret.Get(0).(bootstrap.Config)
}
if rf, ok := ret.Get(1).(func(context.Context, authn.Session, string, string, string, string) error); ok {
r1 = rf(ctx, session, thingID, clientCert, clientKey, caCert)
r1 = rf(ctx, session, clientID, clientCert, clientKey, caCert)
} else {
r1 = ret.Error(1)
}
+77 -77
View File
@@ -13,10 +13,10 @@ import (
"time"
"github.com/absmach/magistrala/bootstrap"
"github.com/absmach/magistrala/clients"
"github.com/absmach/magistrala/pkg/errors"
repoerr "github.com/absmach/magistrala/pkg/errors/repository"
"github.com/absmach/magistrala/pkg/postgres"
"github.com/absmach/magistrala/things"
"github.com/jackc/pgerrcode"
"github.com/jackc/pgtype"
"github.com/jackc/pgx/v5/pgconn"
@@ -24,12 +24,12 @@ import (
)
var (
errSaveChannels = errors.New("failed to insert channels to database")
errSaveConnections = errors.New("failed to insert connections to database")
errUpdateChannels = errors.New("failed to update channels in bootstrap configuration database")
errRemoveChannels = errors.New("failed to remove channels from bootstrap configuration in database")
errConnectThing = errors.New("failed to connect thing in bootstrap configuration in database")
errDisconnectThing = errors.New("failed to disconnect thing in bootstrap configuration in database")
errSaveChannels = errors.New("failed to insert channels to database")
errSaveConnections = errors.New("failed to insert connections to database")
errUpdateChannels = errors.New("failed to update channels in bootstrap configuration database")
errRemoveChannels = errors.New("failed to remove channels from bootstrap configuration in database")
errConnectClient = errors.New("failed to connect client in bootstrap configuration in database")
errDisconnectClient = errors.New("failed to disconnect client in bootstrap configuration in database")
)
const cleanupQuery = `DELETE FROM channels ch WHERE NOT EXISTS (
@@ -48,9 +48,9 @@ func NewConfigRepository(db postgres.Database, log *slog.Logger) bootstrap.Confi
return &configRepository{db: db, log: log}
}
func (cr configRepository) Save(ctx context.Context, cfg bootstrap.Config, chsConnIDs []string) (thingID string, err error) {
q := `INSERT INTO configs (magistrala_thing, domain_id, name, client_cert, client_key, ca_cert, magistrala_key, external_id, external_key, content, state)
VALUES (:magistrala_thing, :domain_id, :name, :client_cert, :client_key, :ca_cert, :magistrala_key, :external_id, :external_key, :content, :state)`
func (cr configRepository) Save(ctx context.Context, cfg bootstrap.Config, chsConnIDs []string) (clientID string, err error) {
q := `INSERT INTO configs (magistrala_client, domain_id, name, client_cert, client_key, ca_cert, magistrala_secret, external_id, external_key, content, state)
VALUES (:magistrala_client, :domain_id, :name, :client_cert, :client_key, :ca_cert, :magistrala_secret, :external_id, :external_key, :content, :state)`
tx, err := cr.db.BeginTxx(ctx, nil)
if err != nil {
@@ -86,16 +86,16 @@ func (cr configRepository) Save(ctx context.Context, cfg bootstrap.Config, chsCo
return "", commitErr
}
return cfg.ThingID, nil
return cfg.ClientID, nil
}
func (cr configRepository) RetrieveByID(ctx context.Context, domainID, id string) (bootstrap.Config, error) {
q := `SELECT magistrala_thing, magistrala_key, external_id, external_key, name, content, state, client_cert, ca_cert
q := `SELECT magistrala_client, magistrala_secret, external_id, external_key, name, content, state, client_cert, ca_cert
FROM configs
WHERE magistrala_thing = :magistrala_thing AND domain_id = :domain_id`
WHERE magistrala_client = :magistrala_client AND domain_id = :domain_id`
dbcfg := dbConfig{
ThingID: id,
ClientID: id,
DomainID: domainID,
}
row, err := cr.db.NamedQueryContext(ctx, q, dbcfg)
@@ -118,7 +118,7 @@ func (cr configRepository) RetrieveByID(ctx context.Context, domainID, id string
q = `SELECT magistrala_channel, name, metadata FROM channels ch
INNER JOIN connections conn
ON ch.magistrala_channel = conn.channel_id AND ch.domain_id = conn.domain_id
WHERE conn.config_id = :magistrala_thing AND conn.domain_id = :domain_id`
WHERE conn.config_id = :magistrala_client AND conn.domain_id = :domain_id`
rows, err := cr.db.NamedQueryContext(ctx, q, dbcfg)
if err != nil {
@@ -131,7 +131,7 @@ func (cr configRepository) RetrieveByID(ctx context.Context, domainID, id string
for rows.Next() {
dbch := dbChannel{}
if err := rows.StructScan(&dbch); err != nil {
cr.log.Error(fmt.Sprintf("Failed to read connected thing due to %s", err))
cr.log.Error(fmt.Sprintf("Failed to read connected client due to %s", err))
return bootstrap.Config{}, errors.Wrap(repoerr.ErrViewEntity, err)
}
dbch.DomainID = nullString(dbcfg.DomainID)
@@ -149,12 +149,12 @@ func (cr configRepository) RetrieveByID(ctx context.Context, domainID, id string
return cfg, nil
}
func (cr configRepository) RetrieveAll(ctx context.Context, domainID string, thingIDs []string, filter bootstrap.Filter, offset, limit uint64) bootstrap.ConfigsPage {
search, params := buildRetrieveQueryParams(domainID, thingIDs, filter)
func (cr configRepository) RetrieveAll(ctx context.Context, domainID string, clientIDs []string, filter bootstrap.Filter, offset, limit uint64) bootstrap.ConfigsPage {
search, params := buildRetrieveQueryParams(domainID, clientIDs, filter)
n := len(params)
q := `SELECT magistrala_thing, magistrala_key, external_id, external_key, name, content, state
FROM configs %s ORDER BY magistrala_thing LIMIT $%d OFFSET $%d`
q := `SELECT magistrala_client, magistrala_secret, external_id, external_key, name, content, state
FROM configs %s ORDER BY magistrala_client LIMIT $%d OFFSET $%d`
q = fmt.Sprintf(q, search, n+1, n+2)
rows, err := cr.db.QueryContext(ctx, q, append(params, limit, offset)...)
@@ -169,7 +169,7 @@ func (cr configRepository) RetrieveAll(ctx context.Context, domainID string, thi
for rows.Next() {
c := bootstrap.Config{DomainID: domainID}
if err := rows.Scan(&c.ThingID, &c.ThingKey, &c.ExternalID, &c.ExternalKey, &name, &content, &c.State); err != nil {
if err := rows.Scan(&c.ClientID, &c.ClientSecret, &c.ExternalID, &c.ExternalKey, &name, &content, &c.State); err != nil {
cr.log.Error(fmt.Sprintf("Failed to read retrieved config due to %s", err))
return bootstrap.ConfigsPage{}
}
@@ -196,7 +196,7 @@ func (cr configRepository) RetrieveAll(ctx context.Context, domainID string, thi
}
func (cr configRepository) RetrieveByExternalID(ctx context.Context, externalID string) (bootstrap.Config, error) {
q := `SELECT magistrala_thing, magistrala_key, external_key, domain_id, name, client_cert, client_key, ca_cert, content, state
q := `SELECT magistrala_client, magistrala_secret, external_key, domain_id, name, client_cert, client_key, ca_cert, content, state
FROM configs
WHERE external_id = :external_id`
dbcfg := dbConfig{
@@ -222,7 +222,7 @@ func (cr configRepository) RetrieveByExternalID(ctx context.Context, externalID
q = `SELECT magistrala_channel, name, metadata FROM channels ch
INNER JOIN connections conn
ON ch.magistrala_channel = conn.channel_id AND ch.domain_id = conn.domain_id
WHERE conn.config_id = :magistrala_thing AND conn.domain_id = :domain_id`
WHERE conn.config_id = :magistrala_client AND conn.domain_id = :domain_id`
rows, err := cr.db.NamedQueryContext(ctx, q, dbcfg)
if err != nil {
@@ -235,7 +235,7 @@ func (cr configRepository) RetrieveByExternalID(ctx context.Context, externalID
for rows.Next() {
dbch := dbChannel{}
if err := rows.StructScan(&dbch); err != nil {
cr.log.Error(fmt.Sprintf("Failed to read connected thing due to %s", err))
cr.log.Error(fmt.Sprintf("Failed to read connected client due to %s", err))
return bootstrap.Config{}, errors.Wrap(repoerr.ErrViewEntity, err)
}
@@ -255,12 +255,12 @@ func (cr configRepository) RetrieveByExternalID(ctx context.Context, externalID
}
func (cr configRepository) Update(ctx context.Context, cfg bootstrap.Config) error {
q := `UPDATE configs SET name = :name, content = :content WHERE magistrala_thing = :magistrala_thing AND domain_id = :domain_id `
q := `UPDATE configs SET name = :name, content = :content WHERE magistrala_client = :magistrala_client AND domain_id = :domain_id `
dbcfg := dbConfig{
Name: nullString(cfg.Name),
Content: nullString(cfg.Content),
ThingID: cfg.ThingID,
ClientID: cfg.ClientID,
DomainID: cfg.DomainID,
}
@@ -281,12 +281,12 @@ func (cr configRepository) Update(ctx context.Context, cfg bootstrap.Config) err
return nil
}
func (cr configRepository) UpdateCert(ctx context.Context, domainID, thingID, clientCert, clientKey, caCert string) (bootstrap.Config, error) {
q := `UPDATE configs SET client_cert = :client_cert, client_key = :client_key, ca_cert = :ca_cert WHERE magistrala_thing = :magistrala_thing AND domain_id = :domain_id
RETURNING magistrala_thing, client_cert, client_key, ca_cert`
func (cr configRepository) UpdateCert(ctx context.Context, domainID, clientID, clientCert, clientKey, caCert string) (bootstrap.Config, error) {
q := `UPDATE configs SET client_cert = :client_cert, client_key = :client_key, ca_cert = :ca_cert WHERE magistrala_client = :magistrala_client AND domain_id = :domain_id
RETURNING magistrala_client, client_cert, client_key, ca_cert`
dbcfg := dbConfig{
ThingID: thingID,
ClientID: clientID,
ClientCert: nullString(clientCert),
DomainID: domainID,
ClientKey: nullString(clientKey),
@@ -345,9 +345,9 @@ func (cr configRepository) UpdateConnections(ctx context.Context, domainID, id s
}
func (cr configRepository) Remove(ctx context.Context, domainID, id string) error {
q := `DELETE FROM configs WHERE magistrala_thing = :magistrala_thing AND domain_id = :domain_id`
q := `DELETE FROM configs WHERE magistrala_client = :magistrala_client AND domain_id = :domain_id`
dbcfg := dbConfig{
ThingID: id,
ClientID: id,
DomainID: domainID,
}
@@ -363,10 +363,10 @@ func (cr configRepository) Remove(ctx context.Context, domainID, id string) erro
}
func (cr configRepository) ChangeState(ctx context.Context, domainID, id string, state bootstrap.State) error {
q := `UPDATE configs SET state = :state WHERE magistrala_thing = :magistrala_thing AND domain_id = :domain_id;`
q := `UPDATE configs SET state = :state WHERE magistrala_client = :magistrala_client AND domain_id = :domain_id;`
dbcfg := dbConfig{
ThingID: id,
ClientID: id,
State: state,
DomainID: domainID,
}
@@ -424,8 +424,8 @@ func (cr configRepository) ListExisting(ctx context.Context, domainID string, id
return channels, nil
}
func (cr configRepository) RemoveThing(ctx context.Context, id string) error {
q := `DELETE FROM configs WHERE magistrala_thing = $1`
func (cr configRepository) RemoveClient(ctx context.Context, id string) error {
q := `DELETE FROM configs WHERE magistrala_client = $1`
_, err := cr.db.ExecContext(ctx, q, id)
if _, err := cr.db.ExecContext(ctx, cleanupQuery); err != nil {
@@ -459,14 +459,14 @@ func (cr configRepository) RemoveChannel(ctx context.Context, id string) error {
return nil
}
func (cr configRepository) ConnectThing(ctx context.Context, channelID, thingID string) error {
func (cr configRepository) ConnectClient(ctx context.Context, channelID, clientID string) error {
q := `UPDATE configs SET state = $1
WHERE magistrala_thing = $2
WHERE magistrala_client = $2
AND EXISTS (SELECT 1 FROM connections WHERE config_id = $2 AND channel_id = $3)`
result, err := cr.db.ExecContext(ctx, q, bootstrap.Active, thingID, channelID)
result, err := cr.db.ExecContext(ctx, q, bootstrap.Active, clientID, channelID)
if err != nil {
return errors.Wrap(errConnectThing, err)
return errors.Wrap(errConnectClient, err)
}
if rows, _ := result.RowsAffected(); rows == 0 {
return repoerr.ErrNotFound
@@ -474,23 +474,23 @@ func (cr configRepository) ConnectThing(ctx context.Context, channelID, thingID
return nil
}
func (cr configRepository) DisconnectThing(ctx context.Context, channelID, thingID string) error {
func (cr configRepository) DisconnectClient(ctx context.Context, channelID, clientID string) error {
q := `UPDATE configs SET state = $1
WHERE magistrala_thing = $2
WHERE magistrala_client = $2
AND EXISTS (SELECT 1 FROM connections WHERE config_id = $2 AND channel_id = $3)`
_, err := cr.db.ExecContext(ctx, q, bootstrap.Inactive, thingID, channelID)
_, err := cr.db.ExecContext(ctx, q, bootstrap.Inactive, clientID, channelID)
if err != nil {
return errors.Wrap(errDisconnectThing, err)
return errors.Wrap(errDisconnectClient, err)
}
return nil
}
func buildRetrieveQueryParams(domainID string, thingIDs []string, filter bootstrap.Filter) (string, []interface{}) {
func buildRetrieveQueryParams(domainID string, clientIDs []string, filter bootstrap.Filter) (string, []interface{}) {
params := []interface{}{}
queries := []string{}
if len(thingIDs) != 0 {
queries = append(queries, fmt.Sprintf("magistrala_thing IN ('%s')", strings.Join(thingIDs, "','")))
if len(clientIDs) != 0 {
queries = append(queries, fmt.Sprintf("magistrala_client IN ('%s')", strings.Join(clientIDs, "','")))
} else if domainID != "" {
params = append(params, domainID)
queries = append(queries, fmt.Sprintf("domain_id = $%d", len(params)))
@@ -560,7 +560,7 @@ func insertConnections(_ context.Context, cfg bootstrap.Config, connections []st
conns := []dbConnection{}
for _, conn := range connections {
dbconn := dbConnection{
Config: cfg.ThingID,
Config: cfg.ClientID,
Channel: conn,
DomainID: cfg.DomainID,
}
@@ -644,43 +644,43 @@ func nullTime(t time.Time) sql.NullTime {
}
type dbConfig struct {
ThingID string `db:"magistrala_thing"`
DomainID string `db:"domain_id"`
Name sql.NullString `db:"name"`
ClientCert sql.NullString `db:"client_cert"`
ClientKey sql.NullString `db:"client_key"`
CaCert sql.NullString `db:"ca_cert"`
ThingKey string `db:"magistrala_key"`
ExternalID string `db:"external_id"`
ExternalKey string `db:"external_key"`
Content sql.NullString `db:"content"`
State bootstrap.State `db:"state"`
DomainID string `db:"domain_id"`
ClientID string `db:"magistrala_client"`
ClientSecret string `db:"magistrala_secret"`
Name sql.NullString `db:"name"`
ClientCert sql.NullString `db:"client_cert"`
ClientKey sql.NullString `db:"client_key"`
CaCert sql.NullString `db:"ca_cert"`
ExternalID string `db:"external_id"`
ExternalKey string `db:"external_key"`
Content sql.NullString `db:"content"`
State bootstrap.State `db:"state"`
}
func toDBConfig(cfg bootstrap.Config) dbConfig {
return dbConfig{
ThingID: cfg.ThingID,
DomainID: cfg.DomainID,
Name: nullString(cfg.Name),
ClientCert: nullString(cfg.ClientCert),
ClientKey: nullString(cfg.ClientKey),
CaCert: nullString(cfg.CACert),
ThingKey: cfg.ThingKey,
ExternalID: cfg.ExternalID,
ExternalKey: cfg.ExternalKey,
Content: nullString(cfg.Content),
State: cfg.State,
ClientID: cfg.ClientID,
ClientSecret: cfg.ClientSecret,
DomainID: cfg.DomainID,
Name: nullString(cfg.Name),
ClientCert: nullString(cfg.ClientCert),
ClientKey: nullString(cfg.ClientKey),
CaCert: nullString(cfg.CACert),
ExternalID: cfg.ExternalID,
ExternalKey: cfg.ExternalKey,
Content: nullString(cfg.Content),
State: cfg.State,
}
}
func toConfig(dbcfg dbConfig) bootstrap.Config {
cfg := bootstrap.Config{
ThingID: dbcfg.ThingID,
DomainID: dbcfg.DomainID,
ThingKey: dbcfg.ThingKey,
ExternalID: dbcfg.ExternalID,
ExternalKey: dbcfg.ExternalKey,
State: dbcfg.State,
ClientID: dbcfg.ClientID,
ClientSecret: dbcfg.ClientSecret,
DomainID: dbcfg.DomainID,
ExternalID: dbcfg.ExternalID,
ExternalKey: dbcfg.ExternalKey,
State: dbcfg.State,
}
if dbcfg.Name.Valid {
@@ -715,7 +715,7 @@ type dbChannel struct {
CreatedAt time.Time `db:"created_at"`
UpdatedAt sql.NullTime `db:"updated_at,omitempty"`
UpdatedBy sql.NullString `db:"updated_by,omitempty"`
Status things.Status `db:"status"`
Status clients.Status `db:"status"`
}
func toDBChannel(domainID string, ch bootstrap.Channel) (dbChannel, error) {
+109 -109
View File
@@ -23,11 +23,11 @@ const numConfigs = 10
var (
config = bootstrap.Config{
ThingID: "mg-thing",
ThingKey: "mg-key",
ExternalID: "external-id",
ExternalKey: "external-key",
DomainID: testsutil.GenerateUUID(&testing.T{}),
ClientID: "mg-client",
ClientSecret: "mg-key",
ExternalID: "external-id",
ExternalKey: "external-key",
DomainID: testsutil.GenerateUUID(&testing.T{}),
Channels: []bootstrap.Channel{
{ID: "1", Name: "name 1", Metadata: map[string]interface{}{"meta": 1.0}},
{ID: "2", Name: "name 2", Metadata: map[string]interface{}{"meta": 2.0}},
@@ -46,20 +46,20 @@ func TestSave(t *testing.T) {
diff := "different"
duplicateThing := config
duplicateThing.ExternalID = diff
duplicateThing.ThingKey = diff
duplicateThing.Channels = []bootstrap.Channel{}
duplicateClient := config
duplicateClient.ExternalID = diff
duplicateClient.ClientSecret = diff
duplicateClient.Channels = []bootstrap.Channel{}
duplicateExternal := config
duplicateExternal.ThingID = diff
duplicateExternal.ThingKey = diff
duplicateExternal.ClientID = diff
duplicateExternal.ClientSecret = diff
duplicateExternal.Channels = []bootstrap.Channel{}
duplicateChannels := config
duplicateChannels.ExternalID = diff
duplicateChannels.ThingKey = diff
duplicateChannels.ThingID = diff
duplicateChannels.ClientSecret = diff
duplicateChannels.ClientID = diff
cases := []struct {
desc string
@@ -74,8 +74,8 @@ func TestSave(t *testing.T) {
err: nil,
},
{
desc: "save config with same Thing ID",
config: duplicateThing,
desc: "save config with same Client ID",
config: duplicateClient,
connections: nil,
err: repoerr.ErrConflict,
},
@@ -96,7 +96,7 @@ func TestSave(t *testing.T) {
id, err := repo.Save(context.Background(), tc.config, tc.connections)
assert.True(t, errors.Contains(err, tc.err), fmt.Sprintf("%s: expected %s got %s\n", tc.desc, tc.err, err))
if err == nil {
assert.Equal(t, id, tc.config.ThingID, fmt.Sprintf("%s: expected %s got %s\n", tc.desc, tc.config.ThingID, id))
assert.Equal(t, id, tc.config.ClientID, fmt.Sprintf("%s: expected %s got %s\n", tc.desc, tc.config.ClientID, id))
}
}
}
@@ -110,8 +110,8 @@ func TestRetrieveByID(t *testing.T) {
// Use UUID to prevent conflicts.
uid, err := uuid.NewV4()
require.Nil(t, err, fmt.Sprintf("Got unexpected error: %s.\n", err))
c.ThingKey = uid.String()
c.ThingID = uid.String()
c.ClientSecret = uid.String()
c.ClientID = uid.String()
c.ExternalID = uid.String()
c.ExternalKey = uid.String()
id, err := repo.Save(context.Background(), c, channels)
@@ -162,7 +162,7 @@ func TestRetrieveAll(t *testing.T) {
err := deleteChannels(context.Background(), repo)
require.Nil(t, err, "Channels cleanup expected to succeed.")
thingIDs := make([]string, numConfigs)
clientIDs := make([]string, numConfigs)
for i := 0; i < numConfigs; i++ {
c := config
@@ -172,10 +172,10 @@ func TestRetrieveAll(t *testing.T) {
require.Nil(t, err, fmt.Sprintf("Got unexpected error: %s.\n", err))
c.ExternalID = uid.String()
c.Name = fmt.Sprintf("name %d", i)
c.ThingID = uid.String()
c.ThingKey = uid.String()
c.ClientID = uid.String()
c.ClientSecret = uid.String()
thingIDs[i] = c.ThingID
clientIDs[i] = c.ClientID
if i%2 == 0 {
c.State = bootstrap.Active
@@ -191,7 +191,7 @@ func TestRetrieveAll(t *testing.T) {
cases := []struct {
desc string
domainID string
thingID []string
clientID []string
offset uint64
limit uint64
filter bootstrap.Filter
@@ -200,7 +200,7 @@ func TestRetrieveAll(t *testing.T) {
{
desc: "retrieve all configs",
domainID: config.DomainID,
thingID: []string{},
clientID: []string{},
offset: 0,
limit: uint64(numConfigs),
size: numConfigs,
@@ -208,7 +208,7 @@ func TestRetrieveAll(t *testing.T) {
{
desc: "retrieve a subset of configs",
domainID: config.DomainID,
thingID: []string{},
clientID: []string{},
offset: 5,
limit: uint64(numConfigs - 5),
size: numConfigs - 5,
@@ -216,7 +216,7 @@ func TestRetrieveAll(t *testing.T) {
{
desc: "retrieve with wrong domain ID ",
domainID: "2",
thingID: []string{},
clientID: []string{},
offset: 0,
limit: uint64(numConfigs),
size: 0,
@@ -224,7 +224,7 @@ func TestRetrieveAll(t *testing.T) {
{
desc: "retrieve all active configs ",
domainID: config.DomainID,
thingID: []string{},
clientID: []string{},
offset: 0,
limit: uint64(numConfigs),
filter: bootstrap.Filter{FullMatch: map[string]string{"state": bootstrap.Active.String()}},
@@ -233,7 +233,7 @@ func TestRetrieveAll(t *testing.T) {
{
desc: "retrieve all with partial match filter",
domainID: config.DomainID,
thingID: []string{},
clientID: []string{},
offset: 0,
limit: uint64(numConfigs),
filter: bootstrap.Filter{PartialMatch: map[string]string{"name": "1"}},
@@ -242,31 +242,31 @@ func TestRetrieveAll(t *testing.T) {
{
desc: "retrieve search by name",
domainID: config.DomainID,
thingID: []string{},
clientID: []string{},
offset: 0,
limit: uint64(numConfigs),
filter: bootstrap.Filter{PartialMatch: map[string]string{"name": "1"}},
size: 1,
},
{
desc: "retrieve by valid thingIDs",
desc: "retrieve by valid clientIDs",
domainID: config.DomainID,
thingID: thingIDs,
clientID: clientIDs,
offset: 0,
limit: uint64(numConfigs),
size: 10,
},
{
desc: "retrieve by non-existing thingID",
desc: "retrieve by non-existing clientID",
domainID: config.DomainID,
thingID: []string{"non-existing"},
clientID: []string{"non-existing"},
offset: 0,
limit: uint64(numConfigs),
size: 0,
},
}
for _, tc := range cases {
ret := repo.RetrieveAll(context.Background(), tc.domainID, tc.thingID, tc.filter, tc.offset, tc.limit)
ret := repo.RetrieveAll(context.Background(), tc.domainID, tc.clientID, tc.filter, tc.offset, tc.limit)
size := len(ret.Configs)
assert.Equal(t, tc.size, size, fmt.Sprintf("%s: expected %d got %d\n", tc.desc, tc.size, size))
}
@@ -281,8 +281,8 @@ func TestRetrieveByExternalID(t *testing.T) {
// Use UUID to prevent conflicts.
uid, err := uuid.NewV4()
assert.Nil(t, err, fmt.Sprintf("Got unexpected error: %s.\n", err))
c.ThingKey = uid.String()
c.ThingID = uid.String()
c.ClientSecret = uid.String()
c.ClientID = uid.String()
c.ExternalID = uid.String()
c.ExternalKey = uid.String()
_, err = repo.Save(context.Background(), c, channels)
@@ -319,8 +319,8 @@ func TestUpdate(t *testing.T) {
// Use UUID to prevent conflicts.
uid, err := uuid.NewV4()
assert.Nil(t, err, fmt.Sprintf("Got unexpected error: %s.\n", err))
c.ThingKey = uid.String()
c.ThingID = uid.String()
c.ClientSecret = uid.String()
c.ClientID = uid.String()
c.ExternalID = uid.String()
c.ExternalKey = uid.String()
_, err = repo.Save(context.Background(), c, channels)
@@ -364,8 +364,8 @@ func TestUpdateCert(t *testing.T) {
// Use UUID to prevent conflicts.
uid, err := uuid.NewV4()
assert.Nil(t, err, fmt.Sprintf("Got unexpected error: %s.\n", err))
c.ThingKey = uid.String()
c.ThingID = uid.String()
c.ClientSecret = uid.String()
c.ClientID = uid.String()
c.ExternalID = uid.String()
c.ExternalKey = uid.String()
_, err = repo.Save(context.Background(), c, channels)
@@ -379,7 +379,7 @@ func TestUpdateCert(t *testing.T) {
cases := []struct {
desc string
thingID string
clientID string
domainID string
cert string
certKey string
@@ -389,7 +389,7 @@ func TestUpdateCert(t *testing.T) {
}{
{
desc: "update with wrong domain ID ",
thingID: "",
clientID: "",
cert: "cert",
certKey: "certKey",
ca: "",
@@ -399,13 +399,13 @@ func TestUpdateCert(t *testing.T) {
},
{
desc: "update a config",
thingID: c.ThingID,
clientID: c.ClientID,
cert: "cert",
certKey: "certKey",
ca: "ca",
domainID: c.DomainID,
expectedConfig: bootstrap.Config{
ThingID: c.ThingID,
ClientID: c.ClientID,
ClientCert: "cert",
CACert: "ca",
ClientKey: "certKey",
@@ -415,7 +415,7 @@ func TestUpdateCert(t *testing.T) {
},
}
for _, tc := range cases {
cfg, err := repo.UpdateCert(context.Background(), tc.domainID, tc.thingID, tc.cert, tc.certKey, tc.ca)
cfg, err := repo.UpdateCert(context.Background(), tc.domainID, tc.clientID, tc.cert, tc.certKey, tc.ca)
assert.True(t, errors.Contains(err, tc.err), fmt.Sprintf("%s: expected %s got %s\n", tc.desc, tc.err, err))
assert.Equal(t, tc.expectedConfig, cfg, fmt.Sprintf("%s: expected %s got %s\n", tc.desc, tc.expectedConfig, cfg))
}
@@ -430,8 +430,8 @@ func TestUpdateConnections(t *testing.T) {
// Use UUID to prevent conflicts.
uid, err := uuid.NewV4()
assert.Nil(t, err, fmt.Sprintf("Got unexpected error: %s.\n", err))
c.ThingKey = uid.String()
c.ThingID = uid.String()
c.ClientSecret = uid.String()
c.ClientID = uid.String()
c.ExternalID = uid.String()
c.ExternalKey = uid.String()
_, err = repo.Save(context.Background(), c, channels)
@@ -439,8 +439,8 @@ func TestUpdateConnections(t *testing.T) {
// Use UUID to prevent conflicts.
uid, err = uuid.NewV4()
assert.Nil(t, err, fmt.Sprintf("Got unexpected error: %s.\n", err))
c.ThingKey = uid.String()
c.ThingID = uid.String()
c.ClientSecret = uid.String()
c.ClientID = uid.String()
c.ExternalID = uid.String()
c.ExternalKey = uid.String()
c.Channels = []bootstrap.Channel{}
@@ -466,7 +466,7 @@ func TestUpdateConnections(t *testing.T) {
{
desc: "update connections",
domainID: config.DomainID,
id: c.ThingID,
id: c.ClientID,
channels: nil,
connections: []string{channels[1]},
err: nil,
@@ -482,7 +482,7 @@ func TestUpdateConnections(t *testing.T) {
{
desc: "update connections no channels",
domainID: config.DomainID,
id: c.ThingID,
id: c.ClientID,
channels: nil,
connections: nil,
err: nil,
@@ -503,8 +503,8 @@ func TestRemove(t *testing.T) {
// Use UUID to prevent conflicts.
uid, err := uuid.NewV4()
assert.Nil(t, err, fmt.Sprintf("Got unexpected error: %s.\n", err))
c.ThingKey = uid.String()
c.ThingID = uid.String()
c.ClientSecret = uid.String()
c.ClientID = uid.String()
c.ExternalID = uid.String()
c.ExternalKey = uid.String()
id, err := repo.Save(context.Background(), c, channels)
@@ -530,8 +530,8 @@ func TestChangeState(t *testing.T) {
// Use UUID to prevent conflicts.
uid, err := uuid.NewV4()
assert.Nil(t, err, fmt.Sprintf("Got unexpected error: %s.\n", err))
c.ThingKey = uid.String()
c.ThingID = uid.String()
c.ClientSecret = uid.String()
c.ClientID = uid.String()
c.ExternalID = uid.String()
c.ExternalKey = uid.String()
saved, err := repo.Save(context.Background(), c, channels)
@@ -586,8 +586,8 @@ func TestListExisting(t *testing.T) {
// Use UUID to prevent conflicts.
uid, err := uuid.NewV4()
assert.Nil(t, err, fmt.Sprintf("Got unexpected error: %s.\n", err))
c.ThingKey = uid.String()
c.ThingID = uid.String()
c.ClientSecret = uid.String()
c.ClientID = uid.String()
c.ExternalID = uid.String()
c.ExternalKey = uid.String()
_, err = repo.Save(context.Background(), c, channels)
@@ -628,7 +628,7 @@ func TestListExisting(t *testing.T) {
}
}
func TestRemoveThing(t *testing.T) {
func TestRemoveClient(t *testing.T) {
repo := postgres.NewConfigRepository(db, testLog)
err := deleteChannels(context.Background(), repo)
require.Nil(t, err, "Channels cleanup expected to succeed.")
@@ -637,14 +637,14 @@ func TestRemoveThing(t *testing.T) {
// Use UUID to prevent conflicts.
uid, err := uuid.NewV4()
assert.Nil(t, err, fmt.Sprintf("Got unexpected error: %s.\n", err))
c.ThingKey = uid.String()
c.ThingID = uid.String()
c.ClientSecret = uid.String()
c.ClientID = uid.String()
c.ExternalID = uid.String()
c.ExternalKey = uid.String()
saved, err := repo.Save(context.Background(), c, channels)
assert.Nil(t, err, fmt.Sprintf("Saving config expected to succeed: %s.\n", err))
for i := 0; i < 2; i++ {
err := repo.RemoveThing(context.Background(), saved)
err := repo.RemoveClient(context.Background(), saved)
assert.Nil(t, err, fmt.Sprintf("an unexpected error occurred: %s\n", err))
}
}
@@ -658,8 +658,8 @@ func TestUpdateChannel(t *testing.T) {
// Use UUID to prevent conflicts.
uid, err := uuid.NewV4()
assert.Nil(t, err, fmt.Sprintf("Got unexpected error: %s.\n", err))
c.ThingKey = uid.String()
c.ThingID = uid.String()
c.ClientSecret = uid.String()
c.ClientID = uid.String()
c.ExternalID = uid.String()
c.ExternalKey = uid.String()
_, err = repo.Save(context.Background(), c, channels)
@@ -674,7 +674,7 @@ func TestUpdateChannel(t *testing.T) {
err = repo.UpdateChannel(context.Background(), update)
assert.Nil(t, err, fmt.Sprintf("updating config expected to succeed: %s.\n", err))
cfg, err := repo.RetrieveByID(context.Background(), c.DomainID, c.ThingID)
cfg, err := repo.RetrieveByID(context.Background(), c.DomainID, c.ClientID)
assert.Nil(t, err, fmt.Sprintf("Retrieving config expected to succeed: %s.\n", err))
var retreved bootstrap.Channel
for _, c := range cfg.Channels {
@@ -695,8 +695,8 @@ func TestRemoveChannel(t *testing.T) {
c := config
uid, err := uuid.NewV4()
assert.Nil(t, err, fmt.Sprintf("Got unexpected error: %s.\n", err))
c.ThingKey = uid.String()
c.ThingID = uid.String()
c.ClientSecret = uid.String()
c.ClientID = uid.String()
c.ExternalID = uid.String()
c.ExternalKey = uid.String()
_, err = repo.Save(context.Background(), c, channels)
@@ -705,12 +705,12 @@ func TestRemoveChannel(t *testing.T) {
err = repo.RemoveChannel(context.Background(), c.Channels[0].ID)
assert.Nil(t, err, fmt.Sprintf("Retrieving config expected to succeed: %s.\n", err))
cfg, err := repo.RetrieveByID(context.Background(), c.DomainID, c.ThingID)
cfg, err := repo.RetrieveByID(context.Background(), c.DomainID, c.ClientID)
assert.Nil(t, err, fmt.Sprintf("Retrieving config expected to succeed: %s.\n", err))
assert.NotContains(t, cfg.Channels, c.Channels[0], fmt.Sprintf("expected to remove channel %s from %s", c.Channels[0], cfg.Channels))
}
func TestConnectThing(t *testing.T) {
func TestConnectClient(t *testing.T) {
repo := postgres.NewConfigRepository(db, testLog)
err := deleteChannels(context.Background(), repo)
require.Nil(t, err, "Channels cleanup expected to succeed.")
@@ -719,8 +719,8 @@ func TestConnectThing(t *testing.T) {
// Use UUID to prevent conflicts.
uid, err := uuid.NewV4()
assert.Nil(t, err, fmt.Sprintf("Got unexpected error: %s.\n", err))
c.ThingKey = uid.String()
c.ThingID = uid.String()
c.ClientSecret = uid.String()
c.ClientID = uid.String()
c.ExternalID = uid.String()
c.ExternalKey = uid.String()
c.State = bootstrap.Inactive
@@ -729,14 +729,14 @@ func TestConnectThing(t *testing.T) {
wrongID := testsutil.GenerateUUID(&testing.T{})
connectedThing := c
connectedClient := c
randomThing := c
randomThingID, _ := uuid.NewV4()
randomThing.ThingID = randomThingID.String()
randomClient := c
randomClientID, _ := uuid.NewV4()
randomClient.ClientID = randomClientID.String()
emptyThing := c
emptyThing.ThingID = ""
emptyClient := c
emptyClient.ClientID = ""
cases := []struct {
desc string
@@ -748,7 +748,7 @@ func TestConnectThing(t *testing.T) {
err error
}{
{
desc: "connect disconnected thing",
desc: "connect disconnected client",
domainID: c.DomainID,
id: saved,
state: bootstrap.Inactive,
@@ -757,16 +757,16 @@ func TestConnectThing(t *testing.T) {
err: nil,
},
{
desc: "connect already connected thing",
desc: "connect already connected client",
domainID: c.DomainID,
id: connectedThing.ThingID,
state: connectedThing.State,
id: connectedClient.ClientID,
state: connectedClient.State,
channels: c.Channels,
connections: channels,
err: nil,
},
{
desc: "connect non-existent thing",
desc: "connect non-existent client",
domainID: c.DomainID,
id: wrongID,
channels: c.Channels,
@@ -774,17 +774,17 @@ func TestConnectThing(t *testing.T) {
err: repoerr.ErrNotFound,
},
{
desc: "connect random thing",
desc: "connect random client",
domainID: c.DomainID,
id: randomThing.ThingID,
id: randomClient.ClientID,
channels: c.Channels,
connections: channels,
err: repoerr.ErrNotFound,
},
{
desc: "connect empty thing",
desc: "connect empty client",
domainID: c.DomainID,
id: emptyThing.ThingID,
id: emptyClient.ClientID,
channels: c.Channels,
connections: channels,
err: repoerr.ErrNotFound,
@@ -793,23 +793,23 @@ func TestConnectThing(t *testing.T) {
for _, tc := range cases {
for i, ch := range tc.channels {
if i == 0 {
err = repo.ConnectThing(context.Background(), ch.ID, tc.id)
err = repo.ConnectClient(context.Background(), ch.ID, tc.id)
assert.Equal(t, tc.err, err, fmt.Sprintf("%s: Expected error: %s, got: %s.\n", tc.desc, tc.err, err))
cfg, err := repo.RetrieveByID(context.Background(), c.DomainID, c.ThingID)
cfg, err := repo.RetrieveByID(context.Background(), c.DomainID, c.ClientID)
assert.Nil(t, err, fmt.Sprintf("Retrieving config expected to succeed: %s.\n", err))
assert.Equal(t, cfg.State, bootstrap.Active, fmt.Sprintf("expected to be active when a connection is added from %s", cfg))
} else {
_ = repo.ConnectThing(context.Background(), ch.ID, tc.id)
_ = repo.ConnectClient(context.Background(), ch.ID, tc.id)
}
}
cfg, err := repo.RetrieveByID(context.Background(), c.DomainID, c.ThingID)
cfg, err := repo.RetrieveByID(context.Background(), c.DomainID, c.ClientID)
assert.Nil(t, err, fmt.Sprintf("Retrieving config expected to succeed: %s.\n", err))
assert.Equal(t, cfg.State, bootstrap.Active, fmt.Sprintf("expected to be active when a connection is added from %s", cfg))
}
}
func TestDisconnectThing(t *testing.T) {
func TestDisconnectClient(t *testing.T) {
repo := postgres.NewConfigRepository(db, testLog)
err := deleteChannels(context.Background(), repo)
require.Nil(t, err, "Channels cleanup expected to succeed.")
@@ -818,8 +818,8 @@ func TestDisconnectThing(t *testing.T) {
// Use UUID to prevent conflicts.
uid, err := uuid.NewV4()
assert.Nil(t, err, fmt.Sprintf("Got unexpected error: %s.\n", err))
c.ThingKey = uid.String()
c.ThingID = uid.String()
c.ClientSecret = uid.String()
c.ClientID = uid.String()
c.ExternalID = uid.String()
c.ExternalKey = uid.String()
c.State = bootstrap.Inactive
@@ -828,14 +828,14 @@ func TestDisconnectThing(t *testing.T) {
wrongID := testsutil.GenerateUUID(&testing.T{})
connectedThing := c
connectedClient := c
randomThing := c
randomThingID, _ := uuid.NewV4()
randomThing.ThingID = randomThingID.String()
randomClient := c
randomClientID, _ := uuid.NewV4()
randomClient.ClientID = randomClientID.String()
emptyThing := c
emptyThing.ThingID = ""
emptyClient := c
emptyClient.ClientID = ""
cases := []struct {
desc string
@@ -847,16 +847,16 @@ func TestDisconnectThing(t *testing.T) {
err error
}{
{
desc: "disconnect connected thing",
desc: "disconnect connected client",
domainID: c.DomainID,
id: connectedThing.ThingID,
state: connectedThing.State,
id: connectedClient.ClientID,
state: connectedClient.State,
channels: c.Channels,
connections: channels,
err: nil,
},
{
desc: "disconnect already disconnected thing",
desc: "disconnect already disconnected client",
domainID: c.DomainID,
id: saved,
state: bootstrap.Inactive,
@@ -865,7 +865,7 @@ func TestDisconnectThing(t *testing.T) {
err: nil,
},
{
desc: "disconnect invalid thing",
desc: "disconnect invalid client",
domainID: c.DomainID,
id: wrongID,
channels: c.Channels,
@@ -873,17 +873,17 @@ func TestDisconnectThing(t *testing.T) {
err: nil,
},
{
desc: "disconnect random thing",
desc: "disconnect random client",
domainID: c.DomainID,
id: randomThing.ThingID,
id: randomClient.ClientID,
channels: c.Channels,
connections: channels,
err: nil,
},
{
desc: "disconnect empty thing",
desc: "disconnect empty client",
domainID: c.DomainID,
id: emptyThing.ThingID,
id: emptyClient.ClientID,
channels: c.Channels,
connections: channels,
err: nil,
@@ -892,11 +892,11 @@ func TestDisconnectThing(t *testing.T) {
for _, tc := range cases {
for _, ch := range tc.channels {
err = repo.DisconnectThing(context.Background(), ch.ID, tc.id)
err = repo.DisconnectClient(context.Background(), ch.ID, tc.id)
assert.Equal(t, tc.err, err, fmt.Sprintf("%s: Expected error: %s, got: %s.\n", tc.desc, tc.err, err))
}
cfg, err := repo.RetrieveByID(context.Background(), c.DomainID, c.ThingID)
cfg, err := repo.RetrieveByID(context.Background(), c.DomainID, c.ClientID)
assert.Nil(t, err, fmt.Sprintf("Retrieving config expected to succeed: %s.\n", err))
assert.Equal(t, cfg.State, bootstrap.Inactive, fmt.Sprintf("expected to be inactive when a connection is removed from %s", cfg))
}
+6 -6
View File
@@ -13,7 +13,7 @@ func Migration() *migrate.MemoryMigrationSource {
Id: "configs_1",
Up: []string{
`CREATE TABLE IF NOT EXISTS configs (
mainflux_thing TEXT UNIQUE NOT NULL,
mainflux_client TEXT UNIQUE NOT NULL,
owner VARCHAR(254),
name TEXT,
mainflux_key CHAR(36) UNIQUE NOT NULL,
@@ -24,7 +24,7 @@ func Migration() *migrate.MemoryMigrationSource {
client_key TEXT,
ca_cert TEXT,
state BIGINT NOT NULL,
PRIMARY KEY (mainflux_thing, owner)
PRIMARY KEY (mainflux_client, owner)
)`,
`CREATE TABLE IF NOT EXISTS unknown_configs (
external_id TEXT UNIQUE NOT NULL,
@@ -44,7 +44,7 @@ func Migration() *migrate.MemoryMigrationSource {
config_id TEXT,
config_owner VARCHAR(256),
FOREIGN KEY (channel_id, channel_owner) REFERENCES channels (mainflux_channel, owner) ON DELETE CASCADE ON UPDATE CASCADE,
FOREIGN KEY (config_id, config_owner) REFERENCES configs (mainflux_thing, owner) ON DELETE CASCADE ON UPDATE CASCADE,
FOREIGN KEY (config_id, config_owner) REFERENCES configs (mainflux_client, owner) ON DELETE CASCADE ON UPDATE CASCADE,
PRIMARY KEY (channel_id, channel_owner, config_id, config_owner)
)`,
},
@@ -78,8 +78,8 @@ func Migration() *migrate.MemoryMigrationSource {
{
Id: "configs_4",
Up: []string{
`ALTER TABLE IF EXISTS configs RENAME COLUMN mainflux_thing TO magistrala_thing`,
`ALTER TABLE IF EXISTS configs RENAME COLUMN mainflux_key TO magistrala_key`,
`ALTER TABLE IF EXISTS configs RENAME COLUMN mainflux_client TO magistrala_client`,
`ALTER TABLE IF EXISTS configs RENAME COLUMN mainflux_key TO magistrala_secret`,
`ALTER TABLE IF EXISTS channels RENAME COLUMN mainflux_channel TO magistrala_channel`,
},
},
@@ -100,7 +100,7 @@ func Migration() *migrate.MemoryMigrationSource {
`ALTER TABLE IF EXISTS connections ADD COLUMN IF NOT EXISTS domain_id VARCHAR(256) NOT NULL`,
`ALTER TABLE IF EXISTS connections ADD CONSTRAINT connections_pkey PRIMARY KEY (channel_id, config_id, domain_id)`,
`ALTER TABLE IF EXISTS connections ADD FOREIGN KEY (channel_id, domain_id) REFERENCES channels (magistrala_channel, domain_id) ON DELETE CASCADE ON UPDATE CASCADE`,
`ALTER TABLE IF EXISTS connections ADD FOREIGN KEY (config_id, domain_id) REFERENCES configs (magistrala_thing, domain_id) ON DELETE CASCADE ON UPDATE CASCADE`,
`ALTER TABLE IF EXISTS connections ADD FOREIGN KEY (config_id, domain_id) REFERENCES configs (magistrala_client, domain_id) ON DELETE CASCADE ON UPDATE CASCADE`,
},
},
},
+14 -14
View File
@@ -16,13 +16,13 @@ import (
// This is used as a response from ConfigReader and can easily be
// replace with any other response format.
type bootstrapRes struct {
ThingID string `json:"thing_id"`
ThingKey string `json:"thing_key"`
Channels []channelRes `json:"channels"`
Content string `json:"content,omitempty"`
ClientCert string `json:"client_cert,omitempty"`
ClientKey string `json:"client_key,omitempty"`
CACert string `json:"ca_cert,omitempty"`
ClientID string `json:"client_id"`
ClientSecret string `json:"client_secret"`
Channels []channelRes `json:"channels"`
Content string `json:"content,omitempty"`
ClientCert string `json:"client_cert,omitempty"`
ClientKey string `json:"client_key,omitempty"`
CACert string `json:"ca_cert,omitempty"`
}
type channelRes struct {
@@ -60,13 +60,13 @@ func (r reader) ReadConfig(cfg Config, secure bool) (interface{}, error) {
}
res := bootstrapRes{
ThingKey: cfg.ThingKey,
ThingID: cfg.ThingID,
Channels: channels,
Content: cfg.Content,
ClientCert: cfg.ClientCert,
ClientKey: cfg.ClientKey,
CACert: cfg.CACert,
ClientID: cfg.ClientID,
ClientSecret: cfg.ClientSecret,
Channels: channels,
Content: cfg.Content,
ClientCert: cfg.ClientCert,
ClientKey: cfg.ClientKey,
CACert: cfg.CACert,
}
if secure {
b, err := json.Marshal(res)
+14 -14
View File
@@ -24,13 +24,13 @@ type readChan struct {
}
type readResp struct {
ThingID string `json:"thing_id"`
ThingKey string `json:"thing_key"`
Channels []readChan `json:"channels"`
Content string `json:"content,omitempty"`
ClientCert string `json:"client_cert,omitempty"`
ClientKey string `json:"client_key,omitempty"`
CACert string `json:"ca_cert,omitempty"`
ClientID string `json:"client_id"`
ClientSecret string `json:"client_secret"`
Channels []readChan `json:"channels"`
Content string `json:"content,omitempty"`
ClientCert string `json:"client_cert,omitempty"`
ClientKey string `json:"client_key,omitempty"`
CACert string `json:"ca_cert,omitempty"`
}
func dec(in []byte) ([]byte, error) {
@@ -50,11 +50,11 @@ func dec(in []byte) ([]byte, error) {
func TestReadConfig(t *testing.T) {
cfg := bootstrap.Config{
ThingID: "mg_id",
ClientCert: "client_cert",
ClientKey: "client_key",
CACert: "ca_cert",
ThingKey: "mg_key",
ClientID: "mg_id",
ClientCert: "client_cert",
ClientKey: "client_key",
CACert: "ca_cert",
ClientSecret: "mg_key",
Channels: []bootstrap.Channel{
{
ID: "mg_id",
@@ -65,8 +65,8 @@ func TestReadConfig(t *testing.T) {
Content: "content",
}
ret := readResp{
ThingID: "mg_id",
ThingKey: "mg_key",
ClientID: "mg_id",
ClientSecret: "mg_key",
Channels: []readChan{
{
ID: "mg_id",
+59 -62
View File
@@ -19,9 +19,9 @@ import (
)
var (
// ErrThings indicates failure to communicate with Magistrala Things service.
// ErrClients indicates failure to communicate with Magistrala Clients service.
// It can be due to networking error or invalid/unauthenticated request.
ErrThings = errors.New("failed to receive response from Things service")
ErrClients = errors.New("failed to receive response from Clients service")
// ErrExternalKey indicates a non-existent bootstrap configuration for given external key.
ErrExternalKey = errors.New("failed to get bootstrap configuration for given external key")
@@ -44,12 +44,12 @@ var (
errUpdateChannel = errors.New("failed to update channel")
errRemoveConfig = errors.New("failed to remove bootstrap configuration")
errRemoveChannel = errors.New("failed to remove channel")
errCreateThing = errors.New("failed to create thing")
errConnectThing = errors.New("failed to connect thing")
errDisconnectThing = errors.New("failed to disconnect thing")
errCreateClient = errors.New("failed to create client")
errConnectClient = errors.New("failed to connect client")
errDisconnectClient = errors.New("failed to disconnect client")
errCheckChannels = errors.New("failed to check if channels exists")
errConnectionChannels = errors.New("failed to check channels connections")
errThingNotFound = errors.New("failed to find thing")
errClientNotFound = errors.New("failed to find client")
errUpdateCert = errors.New("failed to update cert")
)
@@ -60,10 +60,10 @@ var _ Service = (*bootstrapService)(nil)
//
//go:generate mockery --name Service --output=./mocks --filename service.go --quiet --note "Copyright (c) Abstract Machines"
type Service interface {
// Add adds new Thing Config to the user identified by the provided token.
// Add adds new Client Config to the user identified by the provided token.
Add(ctx context.Context, session mgauthn.Session, token string, cfg Config) (Config, error)
// View returns Thing Config with given ID belonging to the user identified by the given token.
// View returns Client Config with given ID belonging to the user identified by the given token.
View(ctx context.Context, session mgauthn.Session, id string) (Config, error)
// Update updates editable fields of the provided Config.
@@ -71,7 +71,7 @@ type Service interface {
// UpdateCert updates an existing Config certificate and token.
// A non-nil error is returned to indicate operation failure.
UpdateCert(ctx context.Context, session mgauthn.Session, thingID, clientCert, clientKey, caCert string) (Config, error)
UpdateCert(ctx context.Context, session mgauthn.Session, clientID, clientCert, clientKey, caCert string) (Config, error)
// UpdateConnections updates list of Channels related to given Config.
UpdateConnections(ctx context.Context, session mgauthn.Session, token, id string, connections []string) error
@@ -83,10 +83,10 @@ type Service interface {
// Remove removes Config with specified token that belongs to the user identified by the given token.
Remove(ctx context.Context, session mgauthn.Session, id string) error
// Bootstrap returns Config to the Thing with provided external ID using external key.
// Bootstrap returns Config to the Client with provided external ID using external key.
Bootstrap(ctx context.Context, externalKey, externalID string, secure bool) (Config, error)
// ChangeState changes state of the Thing with given thing ID and domain ID.
// ChangeState changes state of the Client with given client ID and domain ID.
ChangeState(ctx context.Context, session mgauthn.Session, token, id string, state State) error
// Methods RemoveConfig, UpdateChannel, and RemoveChannel are used as
@@ -101,11 +101,11 @@ type Service interface {
// RemoveChannelHandler removes Channel with id received from an event.
RemoveChannelHandler(ctx context.Context, id string) error
// ConnectThingHandler changes state of the Config to active when connect event occurs.
ConnectThingHandler(ctx context.Context, channelID, ThingID string) error
// ConnectClientHandler changes state of the Config to active when connect event occurs.
ConnectClientHandler(ctx context.Context, channelID, clientID string) error
// DisconnectThingHandler changes state of the Config to inactive when disconnect event occurs.
DisconnectThingHandler(ctx context.Context, channelID, ThingID string) error
// DisconnectClientHandler changes state of the Config to inactive when disconnect event occurs.
DisconnectClientHandler(ctx context.Context, channelID, clientID string) error
}
// ConfigReader is used to parse Config into format which will be encoded
@@ -151,36 +151,36 @@ func (bs bootstrapService) Add(ctx context.Context, session mgauthn.Session, tok
return Config{}, errors.Wrap(errConnectionChannels, err)
}
id := cfg.ThingID
mgThing, err := bs.thing(session.DomainID, id, token)
id := cfg.ClientID
mgClient, err := bs.client(session.DomainID, id, token)
if err != nil {
return Config{}, errors.Wrap(errThingNotFound, err)
return Config{}, errors.Wrap(errClientNotFound, err)
}
for _, channel := range cfg.Channels {
if channel.DomainID != mgThing.DomainID {
if channel.DomainID != mgClient.DomainID {
return Config{}, errors.Wrap(svcerr.ErrMalformedEntity, errNotInSameDomain)
}
}
cfg.ThingID = mgThing.ID
cfg.ClientID = mgClient.ID
cfg.DomainID = session.DomainID
cfg.State = Inactive
cfg.ThingKey = mgThing.Credentials.Secret
cfg.ClientSecret = mgClient.Credentials.Secret
saved, err := bs.configs.Save(ctx, cfg, toConnect)
if err != nil {
// If id is empty, then a new thing has been created function - bs.thing(id, token)
// So, on bootstrap config save error , delete the newly created thing.
// If id is empty, then a new client has been created function - bs.client(id, token)
// So, on bootstrap config save error , delete the newly created client.
if id == "" {
if errT := bs.sdk.DeleteThing(cfg.ThingID, cfg.DomainID, token); errT != nil {
if errT := bs.sdk.DeleteClient(cfg.ClientID, cfg.DomainID, token); errT != nil {
err = errors.Wrap(err, errT)
}
}
return Config{}, errors.Wrap(ErrAddBootstrap, err)
}
cfg.ThingID = saved
cfg.ClientID = saved
cfg.Channels = append(cfg.Channels, existing...)
return cfg, nil
@@ -202,8 +202,8 @@ func (bs bootstrapService) Update(ctx context.Context, session mgauthn.Session,
return nil
}
func (bs bootstrapService) UpdateCert(ctx context.Context, session mgauthn.Session, thingID, clientCert, clientKey, caCert string) (Config, error) {
cfg, err := bs.configs.UpdateCert(ctx, session.DomainID, thingID, clientCert, clientKey, caCert)
func (bs bootstrapService) UpdateCert(ctx context.Context, session mgauthn.Session, clientID, clientCert, clientKey, caCert string) (Config, error) {
cfg, err := bs.configs.UpdateCert(ctx, session.DomainID, clientID, clientCert, clientKey, caCert)
if err != nil {
return Config{}, errors.Wrap(errUpdateCert, err)
}
@@ -238,21 +238,22 @@ func (bs bootstrapService) UpdateConnections(ctx context.Context, session mgauth
}
for _, c := range disconnect {
if err := bs.sdk.DisconnectThing(id, c, session.DomainID, token); err != nil {
if err := bs.sdk.DisconnectClient(id, c, []string{"Publish", "Subscribe"}, session.DomainID, token); err != nil {
if errors.Contains(err, repoerr.ErrNotFound) {
continue
}
return ErrThings
return ErrClients
}
}
for _, c := range connect {
conIDs := mgsdk.Connection{
ChannelID: c,
ThingID: id,
ChannelIDs: []string{c},
ClientIDs: []string{id},
Types: []string{"Publish", "Subscribe"},
}
if err := bs.sdk.Connect(conIDs, session.DomainID, token); err != nil {
return ErrThings
return ErrClients
}
}
if err := bs.configs.UpdateConnections(ctx, session.DomainID, id, channels, connections); err != nil {
@@ -266,7 +267,7 @@ func (bs bootstrapService) listClientIDs(ctx context.Context, userID string) ([]
SubjectType: policies.UserType,
Subject: userID,
Permission: policies.ViewPermission,
ObjectType: policies.ThingType,
ObjectType: policies.ClientType,
})
if err != nil {
return nil, errors.Wrap(svcerr.ErrNotFound, err)
@@ -280,12 +281,12 @@ func (bs bootstrapService) List(ctx context.Context, session mgauthn.Session, fi
}
// Handle non-admin users
thingIDs, err := bs.listClientIDs(ctx, session.DomainUserID)
clientIDs, err := bs.listClientIDs(ctx, session.DomainUserID)
if err != nil {
return ConfigsPage{}, errors.Wrap(svcerr.ErrNotFound, err)
}
if len(thingIDs) == 0 {
if len(clientIDs) == 0 {
return ConfigsPage{
Total: 0,
Offset: offset,
@@ -294,7 +295,7 @@ func (bs bootstrapService) List(ctx context.Context, session mgauthn.Session, fi
}, nil
}
return bs.configs.RetrieveAll(ctx, session.DomainID, thingIDs, filter, offset, limit), nil
return bs.configs.RetrieveAll(ctx, session.DomainID, clientIDs, filter, offset, limit), nil
}
func (bs bootstrapService) Remove(ctx context.Context, session mgauthn.Session, id string) error {
@@ -336,25 +337,21 @@ func (bs bootstrapService) ChangeState(ctx context.Context, session mgauthn.Sess
switch state {
case Active:
for _, c := range cfg.Channels {
conIDs := mgsdk.Connection{
ChannelID: c.ID,
ThingID: cfg.ThingID,
}
if err := bs.sdk.Connect(conIDs, session.DomainID, token); err != nil {
if err := bs.sdk.ConnectClient(cfg.ClientID, c.ID, []string{"Publish", "Subscribe"}, session.DomainID, token); err != nil {
// Ignore conflict errors as they indicate the connection already exists.
if errors.Contains(err, svcerr.ErrConflict) {
continue
}
return ErrThings
return ErrClients
}
}
case Inactive:
for _, c := range cfg.Channels {
if err := bs.sdk.DisconnectThing(cfg.ThingID, c.ID, session.DomainID, token); err != nil {
if err := bs.sdk.DisconnectClient(cfg.ClientID, c.ID, []string{"Publish", "Subscribe"}, session.DomainID, token); err != nil {
if errors.Contains(err, repoerr.ErrNotFound) {
continue
}
return ErrThings
return ErrClients
}
}
}
@@ -372,7 +369,7 @@ func (bs bootstrapService) UpdateChannelHandler(ctx context.Context, channel Cha
}
func (bs bootstrapService) RemoveConfigHandler(ctx context.Context, id string) error {
if err := bs.configs.RemoveThing(ctx, id); err != nil {
if err := bs.configs.RemoveClient(ctx, id); err != nil {
return errors.Wrap(errRemoveConfig, err)
}
return nil
@@ -385,41 +382,41 @@ func (bs bootstrapService) RemoveChannelHandler(ctx context.Context, id string)
return nil
}
func (bs bootstrapService) ConnectThingHandler(ctx context.Context, channelID, thingID string) error {
if err := bs.configs.ConnectThing(ctx, channelID, thingID); err != nil {
return errors.Wrap(errConnectThing, err)
func (bs bootstrapService) ConnectClientHandler(ctx context.Context, channelID, clientID string) error {
if err := bs.configs.ConnectClient(ctx, channelID, clientID); err != nil {
return errors.Wrap(errConnectClient, err)
}
return nil
}
func (bs bootstrapService) DisconnectThingHandler(ctx context.Context, channelID, thingID string) error {
if err := bs.configs.DisconnectThing(ctx, channelID, thingID); err != nil {
return errors.Wrap(errDisconnectThing, err)
func (bs bootstrapService) DisconnectClientHandler(ctx context.Context, channelID, clientID string) error {
if err := bs.configs.DisconnectClient(ctx, channelID, clientID); err != nil {
return errors.Wrap(errDisconnectClient, err)
}
return nil
}
// Method thing retrieves Magistrala Thing creating one if an empty ID is passed.
func (bs bootstrapService) thing(domainID, id, token string) (mgsdk.Thing, error) {
// If Thing ID is not provided, then create new thing.
// Method client retrieves Magistrala Client creating one if an empty ID is passed.
func (bs bootstrapService) client(domainID, id, token string) (mgsdk.Client, error) {
// If Client ID is not provided, then create new client.
if id == "" {
id, err := bs.idProvider.ID()
if err != nil {
return mgsdk.Thing{}, errors.Wrap(errCreateThing, err)
return mgsdk.Client{}, errors.Wrap(errCreateClient, err)
}
thing, sdkErr := bs.sdk.CreateThing(mgsdk.Thing{ID: id, Name: "Bootstrapped Thing " + id}, domainID, token)
client, sdkErr := bs.sdk.CreateClient(mgsdk.Client{ID: id, Name: "Bootstrapped Client " + id}, domainID, token)
if sdkErr != nil {
return mgsdk.Thing{}, errors.Wrap(errCreateThing, sdkErr)
return mgsdk.Client{}, errors.Wrap(errCreateClient, sdkErr)
}
return thing, nil
return client, nil
}
// If Thing ID is provided, then retrieve thing
thing, sdkErr := bs.sdk.Thing(id, domainID, token)
// If Client ID is provided, then retrieve client
client, sdkErr := bs.sdk.Client(id, domainID, token)
if sdkErr != nil {
return mgsdk.Thing{}, errors.Wrap(ErrThings, sdkErr)
return mgsdk.Client{}, errors.Wrap(ErrClients, sdkErr)
}
return thing, nil
return client, nil
}
func (bs bootstrapService) connectionChannels(channels, existing []string, domainID, token string) ([]Channel, error) {
+104 -104
View File
@@ -50,12 +50,12 @@ var (
}
config = bootstrap.Config{
ThingID: testsutil.GenerateUUID(&testing.T{}),
ThingKey: testsutil.GenerateUUID(&testing.T{}),
ExternalID: testsutil.GenerateUUID(&testing.T{}),
ExternalKey: testsutil.GenerateUUID(&testing.T{}),
Channels: []bootstrap.Channel{channel},
Content: "config",
ClientID: testsutil.GenerateUUID(&testing.T{}),
ClientSecret: testsutil.GenerateUUID(&testing.T{}),
ExternalID: testsutil.GenerateUUID(&testing.T{}),
ExternalKey: testsutil.GenerateUUID(&testing.T{}),
Channels: []bootstrap.Channel{channel},
Content: "config",
}
)
@@ -92,7 +92,7 @@ func TestAdd(t *testing.T) {
svc := newService()
neID := config
neID.ThingID = "non-existent"
neID.ClientID = "non-existent"
wrongChannels := config
ch := channel
@@ -106,12 +106,12 @@ func TestAdd(t *testing.T) {
session mgauthn.Session
userID string
domainID string
thingErr error
createThingErr error
clientErr error
createClientErr error
channelErr error
listExistingErr error
saveErr error
deleteThingErr error
deleteClientErr error
err error
}{
{
@@ -123,13 +123,13 @@ func TestAdd(t *testing.T) {
err: nil,
},
{
desc: "add a config with an invalid ID",
config: neID,
token: validToken,
userID: validID,
domainID: domainID,
thingErr: errors.NewSDKError(svcerr.ErrNotFound),
err: svcerr.ErrNotFound,
desc: "add a config with an invalid ID",
config: neID,
token: validToken,
userID: validID,
domainID: domainID,
clientErr: errors.NewSDKError(svcerr.ErrNotFound),
err: svcerr.ErrNotFound,
},
{
desc: "add a config with invalid list of channels",
@@ -152,9 +152,9 @@ 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.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)
repoCall := sdk.On("Client", tc.config.ClientID, mock.Anything, tc.token).Return(mgsdk.Client{ID: tc.config.ClientID, Credentials: mgsdk.ClientCredentials{Secret: tc.config.ClientSecret}}, tc.clientErr)
repoCall1 := sdk.On("CreateClient", mock.Anything, tc.domainID, tc.token).Return(mgsdk.Client{}, tc.createClientErr)
repoCall2 := sdk.On("DeleteClient", tc.config.ClientID, tc.domainID, tc.token).Return(tc.deleteClientErr)
repoCall3 := boot.On("ListExisting", context.Background(), tc.domainID, mock.Anything).Return(tc.config.Channels, tc.listExistingErr)
repoCall4 := boot.On("Save", context.Background(), mock.Anything, mock.Anything).Return(mock.Anything, tc.saveErr)
_, err := svc.Add(context.Background(), tc.session, tc.token, tc.config)
@@ -172,53 +172,53 @@ func TestView(t *testing.T) {
svc := newService()
cases := []struct {
desc string
configID string
userID string
domain string
thingDomain string
token string
session mgauthn.Session
retrieveErr error
thingErr error
channelErr error
err error
desc string
configID string
userID string
domain string
clientDomain string
token string
session mgauthn.Session
retrieveErr error
clientErr error
channelErr error
err error
}{
{
desc: "view an existing config",
configID: config.ThingID,
userID: validID,
thingDomain: domainID,
domain: domainID,
token: validToken,
err: nil,
desc: "view an existing config",
configID: config.ClientID,
userID: validID,
clientDomain: domainID,
domain: domainID,
token: validToken,
err: nil,
},
{
desc: "view a non-existing config",
configID: unknown,
userID: validID,
thingDomain: domainID,
domain: domainID,
token: validToken,
retrieveErr: svcerr.ErrNotFound,
err: svcerr.ErrNotFound,
desc: "view a non-existing config",
configID: unknown,
userID: validID,
clientDomain: domainID,
domain: domainID,
token: validToken,
retrieveErr: svcerr.ErrNotFound,
err: svcerr.ErrNotFound,
},
{
desc: "view a config with invalid domain",
configID: config.ThingID,
userID: validID,
thingDomain: invalidDomainID,
domain: invalidDomainID,
token: validToken,
retrieveErr: svcerr.ErrNotFound,
err: svcerr.ErrNotFound,
desc: "view a config with invalid domain",
configID: config.ClientID,
userID: validID,
clientDomain: invalidDomainID,
domain: invalidDomainID,
token: validToken,
retrieveErr: svcerr.ErrNotFound,
err: svcerr.ErrNotFound,
},
}
for _, tc := range cases {
t.Run(tc.desc, func(t *testing.T) {
tc.session = mgauthn.Session{UserID: tc.userID, DomainID: tc.domain, DomainUserID: validID}
repoCall := boot.On("RetrieveByID", context.Background(), tc.thingDomain, tc.configID).Return(config, tc.retrieveErr)
repoCall := boot.On("RetrieveByID", context.Background(), tc.clientDomain, tc.configID).Return(config, tc.retrieveErr)
_, err := svc.View(context.Background(), tc.session, tc.configID)
assert.True(t, errors.Contains(err, tc.err), fmt.Sprintf("%s: expected %s got %s\n", tc.desc, tc.err, err))
repoCall.Unset()
@@ -239,7 +239,7 @@ func TestUpdate(t *testing.T) {
modifiedCreated.Name = "new name"
nonExisting := c
nonExisting.ThingID = unknown
nonExisting.ClientID = unknown
cases := []struct {
desc string
@@ -304,7 +304,7 @@ func TestUpdateCert(t *testing.T) {
session mgauthn.Session
userID string
domainID string
thingID string
clientID string
clientCert string
clientKey string
caCert string
@@ -318,24 +318,24 @@ func TestUpdateCert(t *testing.T) {
desc: "update certs for the valid config",
userID: validID,
domainID: domainID,
thingID: c.ThingID,
clientID: c.ClientID,
clientCert: "newCert",
clientKey: "newKey",
caCert: "newCert",
token: validToken,
expectedConfig: bootstrap.Config{
Name: c.Name,
ThingKey: c.ThingKey,
Channels: c.Channels,
ExternalID: c.ExternalID,
ExternalKey: c.ExternalKey,
Content: c.Content,
State: c.State,
DomainID: c.DomainID,
ThingID: c.ThingID,
ClientCert: "newCert",
CACert: "newCert",
ClientKey: "newKey",
Name: c.Name,
ClientSecret: c.ClientSecret,
Channels: c.Channels,
ExternalID: c.ExternalID,
ExternalKey: c.ExternalKey,
Content: c.Content,
State: c.State,
DomainID: c.DomainID,
ClientID: c.ClientID,
ClientCert: "newCert",
CACert: "newCert",
ClientKey: "newKey",
},
err: nil,
},
@@ -343,7 +343,7 @@ func TestUpdateCert(t *testing.T) {
desc: "update cert for a non-existing config",
userID: validID,
domainID: domainID,
thingID: "empty",
clientID: "empty",
clientCert: "newCert",
clientKey: "newKey",
caCert: "newCert",
@@ -358,7 +358,7 @@ func TestUpdateCert(t *testing.T) {
t.Run(tc.desc, func(t *testing.T) {
tc.session = mgauthn.Session{UserID: tc.userID, DomainID: tc.domainID, DomainUserID: validID}
repoCall := boot.On("UpdateCert", context.Background(), mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(tc.expectedConfig, tc.updateErr)
cfg, err := svc.UpdateCert(context.Background(), tc.session, tc.thingID, tc.clientCert, tc.clientKey, tc.caCert)
cfg, err := svc.UpdateCert(context.Background(), tc.session, tc.clientID, tc.clientCert, tc.clientKey, tc.caCert)
assert.True(t, errors.Contains(err, tc.err), fmt.Sprintf("%s: expected %s got %s\n", tc.desc, tc.err, err))
sort.Slice(cfg.Channels, func(i, j int) bool {
return cfg.Channels[i].ID < cfg.Channels[j].ID
@@ -393,7 +393,7 @@ func TestUpdateConnections(t *testing.T) {
domainID string
connections []string
updateErr error
thingErr error
clientErr error
channelErr error
retrieveErr error
listErr error
@@ -404,7 +404,7 @@ func TestUpdateConnections(t *testing.T) {
token: validToken,
userID: validID,
domainID: domainID,
id: c.ThingID,
id: c.ClientID,
state: c.State,
connections: []string{ch.ID},
err: nil,
@@ -414,7 +414,7 @@ func TestUpdateConnections(t *testing.T) {
token: validToken,
userID: validID,
domainID: domainID,
id: activeConf.ThingID,
id: activeConf.ClientID,
state: activeConf.State,
connections: []string{ch.ID},
err: nil,
@@ -424,7 +424,7 @@ func TestUpdateConnections(t *testing.T) {
token: validToken,
userID: validID,
domainID: domainID,
id: c.ThingID,
id: c.ClientID,
connections: []string{"wrong"},
channelErr: errors.NewSDKError(svcerr.ErrNotFound),
err: svcerr.ErrNotFound,
@@ -451,9 +451,9 @@ func TestUpdateConnections(t *testing.T) {
func TestList(t *testing.T) {
svc := newService()
numThings := 101
numClients := 101
var saved []bootstrap.Config
for i := 0; i < numThings; i++ {
for i := 0; i < numClients; i++ {
c := config
c.ExternalID = testsutil.GenerateUUID(t)
c.ExternalKey = testsutil.GenerateUUID(t)
@@ -722,7 +722,7 @@ func TestList(t *testing.T) {
SubjectType: policysvc.UserType,
Subject: tc.userID,
Permission: policysvc.ViewPermission,
ObjectType: policysvc.ThingType,
ObjectType: policysvc.ClientType,
}).Return(tc.listObjectsResponse, tc.listObjectsErr)
repoCall := boot.On("RetrieveAll", context.Background(), mock.Anything, mock.Anything, tc.filter, tc.offset, tc.limit).Return(tc.config, tc.retrieveErr)
@@ -752,7 +752,7 @@ func TestRemove(t *testing.T) {
}{
{
desc: "remove an existing config",
id: c.ThingID,
id: c.ClientID,
token: validToken,
userID: validID,
domainID: domainID,
@@ -760,7 +760,7 @@ func TestRemove(t *testing.T) {
},
{
desc: "remove removed config",
id: c.ThingID,
id: c.ClientID,
token: validToken,
userID: validID,
domainID: domainID,
@@ -768,7 +768,7 @@ func TestRemove(t *testing.T) {
},
{
desc: "remove a config with failed remove",
id: c.ThingID,
id: c.ClientID,
token: validToken,
userID: validID,
domainID: domainID,
@@ -889,7 +889,7 @@ func TestChangeState(t *testing.T) {
{
desc: "change state to Active",
state: bootstrap.Active,
id: c.ThingID,
id: c.ClientID,
token: validToken,
userID: validID,
domainID: domainID,
@@ -898,7 +898,7 @@ func TestChangeState(t *testing.T) {
{
desc: "change state to current state",
state: bootstrap.Active,
id: c.ThingID,
id: c.ClientID,
token: validToken,
userID: validID,
domainID: domainID,
@@ -907,7 +907,7 @@ func TestChangeState(t *testing.T) {
{
desc: "change state to Inactive",
state: bootstrap.Inactive,
id: c.ThingID,
id: c.ClientID,
token: validToken,
userID: validID,
domainID: domainID,
@@ -916,17 +916,17 @@ func TestChangeState(t *testing.T) {
{
desc: "change state with failed Connect",
state: bootstrap.Active,
id: c.ThingID,
id: c.ClientID,
token: validToken,
userID: validID,
domainID: domainID,
connectErr: errors.NewSDKError(bootstrap.ErrThings),
err: bootstrap.ErrThings,
connectErr: errors.NewSDKError(bootstrap.ErrClients),
err: bootstrap.ErrClients,
},
{
desc: "change state with invalid state",
state: bootstrap.State(2),
id: c.ThingID,
id: c.ClientID,
token: validToken,
userID: validID,
domainID: domainID,
@@ -939,7 +939,7 @@ func TestChangeState(t *testing.T) {
t.Run(tc.desc, func(t *testing.T) {
tc.session = mgauthn.Session{UserID: tc.userID, DomainID: tc.domainID, DomainUserID: validID}
repoCall := boot.On("RetrieveByID", context.Background(), tc.domainID, tc.id).Return(c, tc.retrieveErr)
sdkCall := sdk.On("Connect", mock.Anything, mock.Anything, mock.Anything).Return(tc.connectErr)
sdkCall := sdk.On("ConnectClient", mock.Anything, mock.Anything, []string{"Publish", "Subscribe"}, mock.Anything, tc.token).Return(tc.connectErr)
repoCall1 := boot.On("ChangeState", context.Background(), mock.Anything, mock.Anything, mock.Anything).Return(tc.stateErr)
err := svc.ChangeState(context.Background(), tc.session, tc.token, tc.id, tc.state)
assert.True(t, errors.Contains(err, tc.err), fmt.Sprintf("%s: expected %s got %s\n", tc.desc, tc.err, err))
@@ -1026,7 +1026,7 @@ func TestRemoveConfigHandler(t *testing.T) {
}{
{
desc: "remove an existing config",
id: config.ThingID,
id: config.ClientID,
err: nil,
},
{
@@ -1038,7 +1038,7 @@ func TestRemoveConfigHandler(t *testing.T) {
for _, tc := range cases {
t.Run(tc.desc, func(t *testing.T) {
repoCall := boot.On("RemoveThing", context.Background(), mock.Anything).Return(tc.err)
repoCall := boot.On("RemoveClient", context.Background(), mock.Anything).Return(tc.err)
err := svc.RemoveConfigHandler(context.Background(), tc.id)
assert.True(t, errors.Contains(err, tc.err), fmt.Sprintf("%s: expected %s got %s\n", tc.desc, tc.err, err))
repoCall.Unset()
@@ -1046,66 +1046,66 @@ func TestRemoveConfigHandler(t *testing.T) {
}
}
func TestConnectThingsHandler(t *testing.T) {
func TestConnectClientHandler(t *testing.T) {
svc := newService()
cases := []struct {
desc string
thingID string
clientID string
channelID string
err error
}{
{
desc: "connect",
channelID: channel.ID,
thingID: config.ThingID,
clientID: config.ClientID,
err: nil,
},
{
desc: "connect connected",
channelID: channel.ID,
thingID: config.ThingID,
clientID: config.ClientID,
err: svcerr.ErrAddPolicies,
},
}
for _, tc := range cases {
t.Run(tc.desc, func(t *testing.T) {
repoCall := boot.On("ConnectThing", context.Background(), mock.Anything, mock.Anything).Return(tc.err)
err := svc.ConnectThingHandler(context.Background(), tc.channelID, tc.thingID)
repoCall := boot.On("ConnectClient", context.Background(), mock.Anything, mock.Anything).Return(tc.err)
err := svc.ConnectClientHandler(context.Background(), tc.channelID, tc.clientID)
assert.True(t, errors.Contains(err, tc.err), fmt.Sprintf("%s: expected %s got %s\n", tc.desc, tc.err, err))
repoCall.Unset()
})
}
}
func TestDisconnectThingsHandler(t *testing.T) {
func TestDisconnectClientsHandler(t *testing.T) {
svc := newService()
cases := []struct {
desc string
thingID string
clientID string
channelID string
err error
}{
{
desc: "disconnect",
channelID: channel.ID,
thingID: config.ThingID,
clientID: config.ClientID,
err: nil,
},
{
desc: "disconnect disconnected",
channelID: channel.ID,
thingID: config.ThingID,
clientID: config.ClientID,
err: nil,
},
}
for _, tc := range cases {
t.Run(tc.desc, func(t *testing.T) {
repoCall := boot.On("DisconnectThing", context.Background(), mock.Anything, mock.Anything).Return(tc.err)
err := svc.DisconnectThingHandler(context.Background(), tc.channelID, tc.thingID)
repoCall := boot.On("DisconnectClient", context.Background(), mock.Anything, mock.Anything).Return(tc.err)
err := svc.DisconnectClientHandler(context.Background(), tc.channelID, tc.clientID)
assert.True(t, errors.Contains(err, tc.err), fmt.Sprintf("%s: expected %s got %s\n", tc.desc, tc.err, err))
repoCall.Unset()
})
+5 -5
View File
@@ -6,18 +6,18 @@ package bootstrap
import "strconv"
const (
// Inactive Thing is created, but not able to exchange messages using Magistrala.
// Inactive Client is created, but not able to exchange messages using Magistrala.
Inactive State = iota
// Active Thing is created, configured, and whitelisted.
// Active Client is created, configured, and whitelisted.
Active
)
// State represents corresponding Magistrala Thing state. The possible Config States
// State represents corresponding Magistrala Client state. The possible Config States
// as well as description of what that State represents are given in the table:
// | State | What it means |
// |----------+--------------------------------------------------------------------------------|
// | Inactive | Thing is created, but isn't able to communicate over Magistrala |
// | Active | Thing is able to communicate using Magistrala |.
// | Inactive | Client is created, but isn't able to communicate over Magistrala |
// | Active | Client is able to communicate using Magistrala |.
type State int
// String returns string representation of State.
+15 -15
View File
@@ -27,7 +27,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_user", trace.WithAttributes(
attribute.String("thing_id", cfg.ThingID),
attribute.String("client_id", cfg.ClientID),
attribute.String("domain_id ", cfg.DomainID),
attribute.String("name", cfg.Name),
attribute.String("external_id", cfg.ExternalID),
@@ -54,7 +54,7 @@ func (tm *tracingMiddleware) Update(ctx context.Context, session mgauthn.Session
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),
attribute.String("client_id", cfg.ClientID),
attribute.String("domain_id ", cfg.DomainID),
))
defer span.End()
@@ -63,13 +63,13 @@ func (tm *tracingMiddleware) Update(ctx context.Context, session mgauthn.Session
}
// UpdateCert traces the "UpdateCert" operation of the wrapped bootstrap.Service.
func (tm *tracingMiddleware) UpdateCert(ctx context.Context, session mgauthn.Session, thingID, clientCert, clientKey, caCert string) (bootstrap.Config, error) {
func (tm *tracingMiddleware) UpdateCert(ctx context.Context, session mgauthn.Session, clientID, clientCert, clientKey, caCert string) (bootstrap.Config, error) {
ctx, span := tm.tracer.Start(ctx, "svc_update_cert", trace.WithAttributes(
attribute.String("thing_id", thingID),
attribute.String("client_id", clientID),
))
defer span.End()
return tm.svc.UpdateCert(ctx, session, thingID, clientCert, clientKey, caCert)
return tm.svc.UpdateCert(ctx, session, clientID, clientCert, clientKey, caCert)
}
// UpdateConnections traces the "UpdateConnections" operation of the wrapped bootstrap.Service.
@@ -159,24 +159,24 @@ func (tm *tracingMiddleware) RemoveChannelHandler(ctx context.Context, id string
return tm.svc.RemoveChannelHandler(ctx, id)
}
// ConnectThingHandler traces the "ConnectThingHandler" operation of the wrapped bootstrap.Service.
func (tm *tracingMiddleware) ConnectThingHandler(ctx context.Context, channelID, thingID string) error {
ctx, span := tm.tracer.Start(ctx, "svc_connect_thing_handler", trace.WithAttributes(
// ConnectClientHandler traces the "ConnectClientHandler" operation of the wrapped bootstrap.Service.
func (tm *tracingMiddleware) ConnectClientHandler(ctx context.Context, channelID, clientID string) error {
ctx, span := tm.tracer.Start(ctx, "svc_connect_client_handler", trace.WithAttributes(
attribute.String("channel_id", channelID),
attribute.String("thing_id", thingID),
attribute.String("client_id", clientID),
))
defer span.End()
return tm.svc.ConnectThingHandler(ctx, channelID, thingID)
return tm.svc.ConnectClientHandler(ctx, channelID, clientID)
}
// DisconnectThingHandler traces the "DisconnectThingHandler" operation of the wrapped bootstrap.Service.
func (tm *tracingMiddleware) DisconnectThingHandler(ctx context.Context, channelID, thingID string) error {
ctx, span := tm.tracer.Start(ctx, "svc_disconnect_thing_handler", trace.WithAttributes(
// DisconnectClientHandler traces the "DisconnectClientHandler" operation of the wrapped bootstrap.Service.
func (tm *tracingMiddleware) DisconnectClientHandler(ctx context.Context, channelID, clientID string) error {
ctx, span := tm.tracer.Start(ctx, "svc_disconnect_client_handler", trace.WithAttributes(
attribute.String("channel_id", channelID),
attribute.String("thing_id", thingID),
attribute.String("client_id", clientID),
))
defer span.End()
return tm.svc.DisconnectThingHandler(ctx, channelID, thingID)
return tm.svc.DisconnectClientHandler(ctx, channelID, clientID)
}
+43 -43
View File
@@ -1,6 +1,6 @@
# Certs Service
Issues certificates for things. `Certs` service can create certificates to be used when `Magistrala` is deployed to support mTLS.
Issues certificates for clients. `Certs` service can create certificates to be used when `Magistrala` is deployed to support mTLS.
Certificate service can create certificates using PKI mode - where certificates issued by PKI, when you deploy `Vault` as PKI certificate management `cert` service will proxy requests to `Vault` previously checking access rights and saving info on successfully created certificate.
## PKI mode
@@ -16,60 +16,60 @@ MG_CERTS_VAULT_HOST=<https://vault-domain:8200>
MG_CERTS_VAULT_NAMESPACE=<vault_namespace>
MG_CERTS_VAULT_APPROLE_ROLEID=<vault_approle_roleid>
MG_CERTS_VAULT_APPROLE_SECRET=<vault_approle_sceret>
MG_CERTS_VAULT_THINGS_CERTS_PKI_PATH=<vault_things_certs_pki_path>
MG_CERTS_VAULT_THINGS_CERTS_PKI_ROLE_NAME=<vault_things_certs_issue_role_name>
MG_CERTS_VAULT_CLIENTS_CERTS_PKI_PATH=<vault_clients_certs_pki_path>
MG_CERTS_VAULT_CLIENTS_CERTS_PKI_ROLE_NAME=<vault_clients_certs_issue_role_name>
```
The certificates can also be revoked using `certs` service. To revoke a certificate you need to provide `thing_id` of the thing for which the certificate was issued.
The certificates can also be revoked using `certs` service. To revoke a certificate you need to provide `client_id` of the client for which the certificate was issued.
```bash
curl -s -S -X DELETE http://localhost:9019/certs/revoke -H "Authorization: Bearer $TOK" -H 'Content-Type: application/json' -d '{"thing_id":"c30b8842-507c-4bcd-973c-74008cef3be5"}'
curl -s -S -X DELETE http://localhost:9019/certs/revoke -H "Authorization: Bearer $TOK" -H 'Content-Type: application/json' -d '{"client_id":"c30b8842-507c-4bcd-973c-74008cef3be5"}'
```
## Configuration
The service is configured using the environment variables presented in the following table. Note that any unset variables will be replaced with their default values.
| Variable | Description | Default |
| :---------------------------------------- | --------------------------------------------------------------------------- | -------------------------------------------------------------------- |
| MG_CERTS_LOG_LEVEL | Log level for the Certs (debug, info, warn, error) | info |
| MG_CERTS_HTTP_HOST | Service Certs host | "" |
| MG_CERTS_HTTP_PORT | Service Certs port | 9019 |
| MG_CERTS_HTTP_SERVER_CERT | Path to the PEM encoded server certificate file | "" |
| MG_CERTS_HTTP_SERVER_KEY | Path to the PEM encoded server key file | "" |
| MG_AUTH_GRPC_URL | Auth service gRPC URL | [localhost:8181](localhost:8181) |
| MG_AUTH_GRPC_TIMEOUT | Auth service gRPC request timeout in seconds | 1s |
| MG_AUTH_GRPC_CLIENT_CERT | Path to the PEM encoded auth service gRPC client certificate file | "" |
| MG_AUTH_GRPC_CLIENT_KEY | Path to the PEM encoded auth service gRPC client key file | "" |
| MG_AUTH_GRPC_SERVER_CERTS | Path to the PEM encoded auth server gRPC server trusted CA certificate file | "" |
| MG_CERTS_SIGN_CA_PATH | Path to the PEM encoded CA certificate file | ca.crt |
| MG_CERTS_SIGN_CA_KEY_PATH | Path to the PEM encoded CA key file | ca.key |
| MG_CERTS_VAULT_HOST | Vault host | http://vault:8200 |
| MG_CERTS_VAULT_NAMESPACE | Vault namespace in which pki is present | magistrala |
| MG_CERTS_VAULT_APPROLE_ROLEID | Vault AppRole auth RoleID | magistrala |
| MG_CERTS_VAULT_APPROLE_SECRET | Vault AppRole auth Secret | magistrala |
| MG_CERTS_VAULT_THINGS_CERTS_PKI_PATH | Vault PKI path for issuing Things Certificates | pki_int |
| MG_CERTS_VAULT_THINGS_CERTS_PKI_ROLE_NAME | Vault PKI Role Name for issuing Things Certificates | magistrala_things_certs |
| MG_CERTS_DB_HOST | Database host | localhost |
| MG_CERTS_DB_PORT | Database port | 5432 |
| MG_CERTS_DB_PASS | Database password | magistrala |
| MG_CERTS_DB_USER | Database user | magistrala |
| MG_CERTS_DB_NAME | Database name | certs |
| MG_CERTS_DB_SSL_MODE | Database SSL mode | disable |
| MG_CERTS_DB_SSL_CERT | Database SSL certificate | "" |
| MG_CERTS_DB_SSL_KEY | Database SSL key | "" |
| MG_CERTS_DB_SSL_ROOT_CERT | Database SSL root certificate | "" |
| MG_THINGS_URL | Things service URL | [localhost:9000](localhost:9000) |
| MG_JAEGER_URL | Jaeger server URL | [http://localhost:4318/v1/traces](http://localhost:4318//v1/traces) |
| MG_JAEGER_TRACE_RATIO | Jaeger sampling ratio | 1.0 |
| MG_SEND_TELEMETRY | Send telemetry to magistrala call home server | true |
| MG_CERTS_INSTANCE_ID | Service instance ID | "" |
| Variable | Description | Default |
| :----------------------------------------- | --------------------------------------------------------------------------- | ------------------------------------------------------------------- |
| MG_CERTS_LOG_LEVEL | Log level for the Certs (debug, info, warn, error) | info |
| MG_CERTS_HTTP_HOST | Service Certs host | "" |
| MG_CERTS_HTTP_PORT | Service Certs port | 9019 |
| MG_CERTS_HTTP_SERVER_CERT | Path to the PEM encoded server certificate file | "" |
| MG_CERTS_HTTP_SERVER_KEY | Path to the PEM encoded server key file | "" |
| MG_AUTH_GRPC_URL | Auth service gRPC URL | [localhost:8181](localhost:8181) |
| MG_AUTH_GRPC_TIMEOUT | Auth service gRPC request timeout in seconds | 1s |
| MG_AUTH_GRPC_CLIENT_CERT | Path to the PEM encoded auth service gRPC client certificate file | "" |
| MG_AUTH_GRPC_CLIENT_KEY | Path to the PEM encoded auth service gRPC client key file | "" |
| MG_AUTH_GRPC_SERVER_CERTS | Path to the PEM encoded auth server gRPC server trusted CA certificate file | "" |
| MG_CERTS_SIGN_CA_PATH | Path to the PEM encoded CA certificate file | ca.crt |
| MG_CERTS_SIGN_CA_KEY_PATH | Path to the PEM encoded CA key file | ca.key |
| MG_CERTS_VAULT_HOST | Vault host | http://vault:8200 |
| MG_CERTS_VAULT_NAMESPACE | Vault namespace in which pki is present | magistrala |
| MG_CERTS_VAULT_APPROLE_ROLEID | Vault AppRole auth RoleID | magistrala |
| MG_CERTS_VAULT_APPROLE_SECRET | Vault AppRole auth Secret | magistrala |
| MG_CERTS_VAULT_CLIENTS_CERTS_PKI_PATH | Vault PKI path for issuing Clients Certificates | pki_int |
| MG_CERTS_VAULT_CLIENTS_CERTS_PKI_ROLE_NAME | Vault PKI Role Name for issuing Clients Certificates | magistrala_clients_certs |
| MG_CERTS_DB_HOST | Database host | localhost |
| MG_CERTS_DB_PORT | Database port | 5432 |
| MG_CERTS_DB_PASS | Database password | magistrala |
| MG_CERTS_DB_USER | Database user | magistrala |
| MG_CERTS_DB_NAME | Database name | certs |
| MG_CERTS_DB_SSL_MODE | Database SSL mode | disable |
| MG_CERTS_DB_SSL_CERT | Database SSL certificate | "" |
| MG_CERTS_DB_SSL_KEY | Database SSL key | "" |
| MG_CERTS_DB_SSL_ROOT_CERT | Database SSL root certificate | "" |
| MG_CLIENTS_URL | Clients service URL | [localhost:9000](localhost:9000) |
| MG_JAEGER_URL | Jaeger server URL | [http://localhost:4318/v1/traces](http://localhost:4318//v1/traces) |
| MG_JAEGER_TRACE_RATIO | Jaeger sampling ratio | 1.0 |
| MG_SEND_TELEMETRY | Send telemetry to magistrala call home server | true |
| MG_CERTS_INSTANCE_ID | Service instance ID | "" |
## Deployment
The service is distributed as Docker container. Check the [`certs`](https://github.com/absmach/magistrala/blob/main/docker/addons/bootstrap/docker-compose.yml) service section in docker-compose file to see how the service is deployed.
Running this service outside of container requires working instance of the auth service, things service, postgres database, vault and Jaeger server.
Running this service outside of container requires working instance of the auth service, clients service, postgres database, vault and Jaeger server.
To start the service outside of the container, execute the following shell script:
```bash
@@ -101,8 +101,8 @@ MG_CERTS_VAULT_HOST=http://vault:8200 \
MG_CERTS_VAULT_NAMESPACE=magistrala \
MG_CERTS_VAULT_APPROLE_ROLEID=magistrala \
MG_CERTS_VAULT_APPROLE_SECRET=magistrala \
MG_CERTS_VAULT_THINGS_CERTS_PKI_PATH=pki_int \
MG_CERTS_VAULT_THINGS_CERTS_PKI_ROLE_NAME=magistrala_things_certs \
MG_CERTS_VAULT_CLIENTS_CERTS_PKI_PATH=pki_int \
MG_CERTS_VAULT_CLIENTS_CERTS_PKI_ROLE_NAME=magistrala_clients_certs \
MG_CERTS_DB_HOST=localhost \
MG_CERTS_DB_PORT=5432 \
MG_CERTS_DB_PASS=magistrala \
@@ -112,7 +112,7 @@ MG_CERTS_DB_SSL_MODE=disable \
MG_CERTS_DB_SSL_CERT="" \
MG_CERTS_DB_SSL_KEY="" \
MG_CERTS_DB_SSL_ROOT_CERT="" \
MG_THINGS_URL=localhost:9000 \
MG_CLIENTS_URL=localhost:9000 \
MG_JAEGER_URL=http://localhost:14268/api/traces \
MG_JAEGER_TRACE_RATIO=1.0 \
MG_SEND_TELEMETRY=true \
+5 -5
View File
@@ -18,14 +18,14 @@ func issueCert(svc certs.Service) endpoint.Endpoint {
if err := req.validate(); err != nil {
return nil, errors.Wrap(apiutil.ErrValidation, err)
}
res, err := svc.IssueCert(ctx, req.domainID, req.token, req.ThingID, req.TTL)
res, err := svc.IssueCert(ctx, req.domainID, req.token, req.ClientID, req.TTL)
if err != nil {
return certsRes{}, errors.Wrap(apiutil.ErrValidation, err)
}
return certsRes{
SerialNumber: res.SerialNumber,
ThingID: res.ThingID,
ClientID: res.ClientID,
Certificate: res.Certificate,
ExpiryTime: res.ExpiryTime,
Revoked: res.Revoked,
@@ -41,7 +41,7 @@ func listSerials(svc certs.Service) endpoint.Endpoint {
return nil, errors.Wrap(apiutil.ErrValidation, err)
}
page, err := svc.ListSerials(ctx, req.thingID, req.pm)
page, err := svc.ListSerials(ctx, req.clientID, req.pm)
if err != nil {
return certsPageRes{}, errors.Wrap(apiutil.ErrValidation, err)
}
@@ -59,7 +59,7 @@ func listSerials(svc certs.Service) endpoint.Endpoint {
SerialNumber: cert.SerialNumber,
ExpiryTime: cert.ExpiryTime,
Revoked: cert.Revoked,
ThingID: cert.ThingID,
ClientID: cert.ClientID,
}
res.Certs = append(res.Certs, cr)
}
@@ -80,7 +80,7 @@ func viewCert(svc certs.Service) endpoint.Endpoint {
}
return certsRes{
ThingID: cert.ThingID,
ClientID: cert.ClientID,
Certificate: cert.Certificate,
Key: cert.Key,
SerialNumber: cert.SerialNumber,
+35 -35
View File
@@ -31,11 +31,11 @@ var (
contentType = "application/json"
valid = "valid"
invalid = "invalid"
thingID = testsutil.GenerateUUID(&testing.T{})
clientID = testsutil.GenerateUUID(&testing.T{})
serial = testsutil.GenerateUUID(&testing.T{})
ttl = "1h"
cert = certs.Cert{
ThingID: thingID,
ClientID: clientID,
SerialNumber: serial,
ExpiryTime: time.Now().Add(time.Hour),
}
@@ -79,8 +79,8 @@ func TestIssueCert(t *testing.T) {
cs, svc, auth := newCertServer()
defer cs.Close()
validReqString := `{"thing_id": "%s","ttl": "%s"}`
invalidReqString := `{"thing_id": "%s","ttl": %s}`
validReqString := `{"client_id": "%s","ttl": "%s"}`
invalidReqString := `{"client_id": "%s","ttl": %s}`
cases := []struct {
desc string
@@ -88,7 +88,7 @@ func TestIssueCert(t *testing.T) {
token string
session mgauthn.Session
contentType string
thingID string
clientID string
ttl string
request string
status int
@@ -102,9 +102,9 @@ func TestIssueCert(t *testing.T) {
token: valid,
domainID: valid,
contentType: contentType,
thingID: thingID,
clientID: clientID,
ttl: ttl,
request: fmt.Sprintf(validReqString, thingID, ttl),
request: fmt.Sprintf(validReqString, clientID, ttl),
status: http.StatusCreated,
svcRes: certs.Cert{SerialNumber: serial},
svcErr: nil,
@@ -115,9 +115,9 @@ func TestIssueCert(t *testing.T) {
token: valid,
domainID: valid,
contentType: contentType,
thingID: thingID,
clientID: clientID,
ttl: ttl,
request: fmt.Sprintf(validReqString, thingID, ttl),
request: fmt.Sprintf(validReqString, clientID, ttl),
status: http.StatusUnprocessableEntity,
svcRes: certs.Cert{},
svcErr: svcerr.ErrCreateEntity,
@@ -127,9 +127,9 @@ func TestIssueCert(t *testing.T) {
desc: "issue with invalid token",
token: invalid,
contentType: contentType,
thingID: thingID,
clientID: clientID,
ttl: ttl,
request: fmt.Sprintf(validReqString, thingID, ttl),
request: fmt.Sprintf(validReqString, clientID, ttl),
status: http.StatusUnauthorized,
svcRes: certs.Cert{},
authenticateErr: svcerr.ErrAuthentication,
@@ -139,7 +139,7 @@ func TestIssueCert(t *testing.T) {
desc: "issue with empty token",
domainID: valid,
contentType: contentType,
request: fmt.Sprintf(validReqString, thingID, ttl),
request: fmt.Sprintf(validReqString, clientID, ttl),
status: http.StatusUnauthorized,
svcRes: certs.Cert{},
svcErr: nil,
@@ -150,14 +150,14 @@ func TestIssueCert(t *testing.T) {
token: valid,
domainID: "",
contentType: contentType,
request: fmt.Sprintf(validReqString, thingID, ttl),
request: fmt.Sprintf(validReqString, clientID, ttl),
status: http.StatusBadRequest,
svcRes: certs.Cert{},
svcErr: nil,
err: apiutil.ErrMissingDomainID,
},
{
desc: "issue with empty thing id",
desc: "issue with empty client id",
token: valid,
domainID: valid,
contentType: contentType,
@@ -172,7 +172,7 @@ func TestIssueCert(t *testing.T) {
token: valid,
domainID: valid,
contentType: contentType,
request: fmt.Sprintf(validReqString, thingID, ""),
request: fmt.Sprintf(validReqString, clientID, ""),
status: http.StatusBadRequest,
svcRes: certs.Cert{},
svcErr: nil,
@@ -183,7 +183,7 @@ func TestIssueCert(t *testing.T) {
token: valid,
domainID: valid,
contentType: contentType,
request: fmt.Sprintf(validReqString, thingID, invalid),
request: fmt.Sprintf(validReqString, clientID, invalid),
status: http.StatusBadRequest,
svcRes: certs.Cert{},
svcErr: nil,
@@ -194,7 +194,7 @@ func TestIssueCert(t *testing.T) {
token: valid,
domainID: valid,
contentType: "application/xml",
request: fmt.Sprintf(validReqString, thingID, ttl),
request: fmt.Sprintf(validReqString, clientID, ttl),
status: http.StatusUnsupportedMediaType,
svcRes: certs.Cert{},
svcErr: nil,
@@ -205,7 +205,7 @@ func TestIssueCert(t *testing.T) {
token: valid,
domainID: valid,
contentType: contentType,
request: fmt.Sprintf(invalidReqString, thingID, ttl),
request: fmt.Sprintf(invalidReqString, clientID, ttl),
status: http.StatusInternalServerError,
svcRes: certs.Cert{},
svcErr: nil,
@@ -227,7 +227,7 @@ func TestIssueCert(t *testing.T) {
tc.session = mgauthn.Session{DomainUserID: validID, UserID: validID, DomainID: validID}
}
authCall := auth.On("Authenticate", mock.Anything, tc.token).Return(tc.session, tc.authenticateErr)
svcCall := svc.On("IssueCert", mock.Anything, tc.domainID, tc.token, tc.thingID, tc.ttl).Return(tc.svcRes, tc.svcErr)
svcCall := svc.On("IssueCert", mock.Anything, tc.domainID, tc.token, tc.clientID, tc.ttl).Return(tc.svcRes, tc.svcErr)
res, err := req.make()
assert.Nil(t, err, fmt.Sprintf("%s: unexpected error %s", tc.desc, err))
var errRes respBody
@@ -433,7 +433,7 @@ func TestListSerials(t *testing.T) {
token string
domainID string
session mgauthn.Session
thingID string
clientID string
revoked string
offset uint64
limit uint64
@@ -448,7 +448,7 @@ func TestListSerials(t *testing.T) {
desc: "list certs successfully with default limit",
domainID: valid,
token: valid,
thingID: thingID,
clientID: clientID,
revoked: revoked,
offset: 0,
limit: 10,
@@ -467,7 +467,7 @@ func TestListSerials(t *testing.T) {
desc: "list certs successfully with default revoke",
domainID: valid,
token: valid,
thingID: thingID,
clientID: clientID,
revoked: revoked,
offset: 0,
limit: 10,
@@ -486,7 +486,7 @@ func TestListSerials(t *testing.T) {
desc: "list certs successfully with all certs",
domainID: valid,
token: valid,
thingID: thingID,
clientID: clientID,
revoked: "all",
offset: 0,
limit: 10,
@@ -505,7 +505,7 @@ func TestListSerials(t *testing.T) {
desc: "list certs successfully with limit",
domainID: valid,
token: valid,
thingID: thingID,
clientID: clientID,
revoked: revoked,
offset: 0,
limit: 5,
@@ -524,7 +524,7 @@ func TestListSerials(t *testing.T) {
desc: "list certs successfully with offset",
domainID: valid,
token: valid,
thingID: thingID,
clientID: clientID,
revoked: revoked,
offset: 1,
limit: 10,
@@ -543,7 +543,7 @@ func TestListSerials(t *testing.T) {
desc: "list certs successfully with offset and limit",
domainID: valid,
token: valid,
thingID: thingID,
clientID: clientID,
revoked: revoked,
offset: 1,
limit: 5,
@@ -562,7 +562,7 @@ func TestListSerials(t *testing.T) {
desc: "list with invalid token",
domainID: valid,
token: invalid,
thingID: thingID,
clientID: clientID,
revoked: revoked,
offset: 0,
limit: 10,
@@ -576,7 +576,7 @@ func TestListSerials(t *testing.T) {
desc: "list with empty token",
domainID: valid,
token: "",
thingID: thingID,
clientID: clientID,
revoked: revoked,
offset: 0,
limit: 10,
@@ -590,7 +590,7 @@ func TestListSerials(t *testing.T) {
desc: "list with limit exceeding max limit",
domainID: valid,
token: valid,
thingID: thingID,
clientID: clientID,
revoked: revoked,
query: "?limit=1000",
status: http.StatusBadRequest,
@@ -602,7 +602,7 @@ func TestListSerials(t *testing.T) {
desc: "list with invalid offset",
domainID: valid,
token: valid,
thingID: thingID,
clientID: clientID,
revoked: revoked,
query: "?offset=invalid",
status: http.StatusBadRequest,
@@ -614,7 +614,7 @@ func TestListSerials(t *testing.T) {
desc: "list with invalid limit",
domainID: valid,
token: valid,
thingID: thingID,
clientID: clientID,
revoked: revoked,
query: "?limit=invalid",
status: http.StatusBadRequest,
@@ -623,10 +623,10 @@ func TestListSerials(t *testing.T) {
err: apiutil.ErrValidation,
},
{
desc: "list with invalid thing id",
desc: "list with invalid client id",
domainID: valid,
token: valid,
thingID: invalid,
clientID: invalid,
revoked: revoked,
offset: 0,
limit: 10,
@@ -642,14 +642,14 @@ func TestListSerials(t *testing.T) {
req := testRequest{
client: cs.Client(),
method: http.MethodGet,
url: fmt.Sprintf("%s/%s/serials/%s", cs.URL, tc.domainID, tc.thingID) + tc.query,
url: fmt.Sprintf("%s/%s/serials/%s", cs.URL, tc.domainID, tc.clientID) + tc.query,
token: tc.token,
}
if tc.token == valid {
tc.session = mgauthn.Session{DomainUserID: validID, UserID: validID, DomainID: validID}
}
authCall := auth.On("Authenticate", mock.Anything, tc.token).Return(tc.session, tc.authenticateErr)
svcCall := svc.On("ListSerials", mock.Anything, tc.thingID, certs.PageMetadata{Revoked: tc.revoked, Offset: tc.offset, Limit: tc.limit}).Return(tc.svcRes, tc.svcErr)
svcCall := svc.On("ListSerials", mock.Anything, tc.clientID, certs.PageMetadata{Revoked: tc.revoked, Offset: tc.offset, Limit: tc.limit}).Return(tc.svcRes, tc.svcErr)
res, err := req.make()
assert.Nil(t, err, fmt.Sprintf("%s: unexpected error %s", tc.desc, err))
var errRes respBody
+16 -16
View File
@@ -25,13 +25,13 @@ func LoggingMiddleware(svc certs.Service, logger *slog.Logger) certs.Service {
return &loggingMiddleware{logger, svc}
}
// IssueCert logs the issue_cert request. It logs the ttl, thing ID and the time it took to complete the request.
// IssueCert logs the issue_cert request. It logs the ttl, client ID and the time it took to complete the request.
// If the request fails, it logs the error.
func (lm *loggingMiddleware) IssueCert(ctx context.Context, domainID, token, thingID, ttl string) (c certs.Cert, err error) {
func (lm *loggingMiddleware) IssueCert(ctx context.Context, domainID, token, clientID, ttl string) (c certs.Cert, err error) {
defer func(begin time.Time) {
args := []any{
slog.String("duration", time.Since(begin).String()),
slog.String("thing_id", thingID),
slog.String("client_id", clientID),
slog.String("ttl", ttl),
}
if err != nil {
@@ -42,15 +42,15 @@ func (lm *loggingMiddleware) IssueCert(ctx context.Context, domainID, token, thi
lm.logger.Info("Issue certificate completed successfully", args...)
}(time.Now())
return lm.svc.IssueCert(ctx, domainID, token, thingID, ttl)
return lm.svc.IssueCert(ctx, domainID, token, clientID, ttl)
}
// ListCerts logs the list_certs request. It logs the thing ID and the time it took to complete the request.
func (lm *loggingMiddleware) ListCerts(ctx context.Context, thingID string, pm certs.PageMetadata) (cp certs.CertPage, err error) {
// ListCerts logs the list_certs request. It logs the client ID and the time it took to complete the request.
func (lm *loggingMiddleware) ListCerts(ctx context.Context, clientID string, pm certs.PageMetadata) (cp certs.CertPage, err error) {
defer func(begin time.Time) {
args := []any{
slog.String("duration", time.Since(begin).String()),
slog.String("thing_id", thingID),
slog.String("client_id", clientID),
slog.Group("page",
slog.Uint64("offset", cp.Offset),
slog.Uint64("limit", cp.Limit),
@@ -65,16 +65,16 @@ func (lm *loggingMiddleware) ListCerts(ctx context.Context, thingID string, pm c
lm.logger.Info("List certificates completed successfully", args...)
}(time.Now())
return lm.svc.ListCerts(ctx, thingID, pm)
return lm.svc.ListCerts(ctx, clientID, pm)
}
// ListSerials logs the list_serials request. It logs the thing ID and the time it took to complete the request.
// ListSerials logs the list_serials request. It logs the client ID and the time it took to complete the request.
// If the request fails, it logs the error.
func (lm *loggingMiddleware) ListSerials(ctx context.Context, thingID string, pm certs.PageMetadata) (cp certs.CertPage, err error) {
func (lm *loggingMiddleware) ListSerials(ctx context.Context, clientID string, pm certs.PageMetadata) (cp certs.CertPage, err error) {
defer func(begin time.Time) {
args := []any{
slog.String("duration", time.Since(begin).String()),
slog.String("thing_id", thingID),
slog.String("client_id", clientID),
slog.String("revoke", pm.Revoked),
slog.Group("page",
slog.Uint64("offset", cp.Offset),
@@ -90,7 +90,7 @@ func (lm *loggingMiddleware) ListSerials(ctx context.Context, thingID string, pm
lm.logger.Info("List certificates serials completed successfully", args...)
}(time.Now())
return lm.svc.ListSerials(ctx, thingID, pm)
return lm.svc.ListSerials(ctx, clientID, pm)
}
// ViewCert logs the view_cert request. It logs the serial ID and the time it took to complete the request.
@@ -112,13 +112,13 @@ func (lm *loggingMiddleware) ViewCert(ctx context.Context, serialID string) (c c
return lm.svc.ViewCert(ctx, serialID)
}
// RevokeCert logs the revoke_cert request. It logs the thing ID and the time it took to complete the request.
// RevokeCert logs the revoke_cert request. It logs the client ID and the time it took to complete the request.
// If the request fails, it logs the error.
func (lm *loggingMiddleware) RevokeCert(ctx context.Context, domainID, token, thingID string) (c certs.Revoke, err error) {
func (lm *loggingMiddleware) RevokeCert(ctx context.Context, domainID, token, clientID string) (c certs.Revoke, err error) {
defer func(begin time.Time) {
args := []any{
slog.String("duration", time.Since(begin).String()),
slog.String("thing_id", thingID),
slog.String("client_id", clientID),
}
if err != nil {
args = append(args, slog.Any("error", err))
@@ -128,5 +128,5 @@ func (lm *loggingMiddleware) RevokeCert(ctx context.Context, domainID, token, th
lm.logger.Info("Revoke certificate completed successfully", args...)
}(time.Now())
return lm.svc.RevokeCert(ctx, domainID, token, thingID)
return lm.svc.RevokeCert(ctx, domainID, token, clientID)
}
+8 -8
View File
@@ -31,33 +31,33 @@ func MetricsMiddleware(svc certs.Service, counter metrics.Counter, latency metri
}
// IssueCert instruments IssueCert method with metrics.
func (ms *metricsMiddleware) IssueCert(ctx context.Context, domainID, token, thingID, ttl string) (certs.Cert, error) {
func (ms *metricsMiddleware) IssueCert(ctx context.Context, domainID, token, clientID, ttl string) (certs.Cert, error) {
defer func(begin time.Time) {
ms.counter.With("method", "issue_cert").Add(1)
ms.latency.With("method", "issue_cert").Observe(time.Since(begin).Seconds())
}(time.Now())
return ms.svc.IssueCert(ctx, domainID, token, thingID, ttl)
return ms.svc.IssueCert(ctx, domainID, token, clientID, ttl)
}
// ListCerts instruments ListCerts method with metrics.
func (ms *metricsMiddleware) ListCerts(ctx context.Context, thingID string, pm certs.PageMetadata) (certs.CertPage, error) {
func (ms *metricsMiddleware) ListCerts(ctx context.Context, clientID string, pm certs.PageMetadata) (certs.CertPage, error) {
defer func(begin time.Time) {
ms.counter.With("method", "list_certs").Add(1)
ms.latency.With("method", "list_certs").Observe(time.Since(begin).Seconds())
}(time.Now())
return ms.svc.ListCerts(ctx, thingID, pm)
return ms.svc.ListCerts(ctx, clientID, pm)
}
// ListSerials instruments ListSerials method with metrics.
func (ms *metricsMiddleware) ListSerials(ctx context.Context, thingID string, pm certs.PageMetadata) (certs.CertPage, error) {
func (ms *metricsMiddleware) ListSerials(ctx context.Context, clientID string, pm certs.PageMetadata) (certs.CertPage, error) {
defer func(begin time.Time) {
ms.counter.With("method", "list_serials").Add(1)
ms.latency.With("method", "list_serials").Observe(time.Since(begin).Seconds())
}(time.Now())
return ms.svc.ListSerials(ctx, thingID, pm)
return ms.svc.ListSerials(ctx, clientID, pm)
}
// ViewCert instruments ViewCert method with metrics.
@@ -71,11 +71,11 @@ func (ms *metricsMiddleware) ViewCert(ctx context.Context, serialID string) (cer
}
// RevokeCert instruments RevokeCert method with metrics.
func (ms *metricsMiddleware) RevokeCert(ctx context.Context, domainID, token, thingID string) (certs.Revoke, error) {
func (ms *metricsMiddleware) RevokeCert(ctx context.Context, domainID, token, clientID string) (certs.Revoke, error) {
defer func(begin time.Time) {
ms.counter.With("method", "revoke_cert").Add(1)
ms.latency.With("method", "revoke_cert").Observe(time.Since(begin).Seconds())
}(time.Now())
return ms.svc.RevokeCert(ctx, domainID, token, thingID)
return ms.svc.RevokeCert(ctx, domainID, token, clientID)
}
+4 -4
View File
@@ -15,7 +15,7 @@ const maxLimitSize = 100
type addCertsReq struct {
token string
domainID string
ThingID string `json:"thing_id"`
ClientID string `json:"client_id"`
TTL string `json:"ttl"`
}
@@ -28,7 +28,7 @@ func (req addCertsReq) validate() error {
return apiutil.ErrMissingDomainID
}
if req.ThingID == "" {
if req.ClientID == "" {
return apiutil.ErrMissingID
}
@@ -44,8 +44,8 @@ func (req addCertsReq) validate() error {
}
type listReq struct {
thingID string
pm certs.PageMetadata
clientID string
pm certs.PageMetadata
}
func (req *listReq) validate() error {
+1 -1
View File
@@ -20,7 +20,7 @@ type certsPageRes struct {
}
type certsRes struct {
ThingID string `json:"thing_id"`
ClientID string `json:"client_id"`
Certificate string `json:"certificate,omitempty"`
Key string `json:"key,omitempty"`
SerialNumber string `json:"serial_number"`
+2 -2
View File
@@ -62,7 +62,7 @@ func MakeHandler(svc certs.Service, authn mgauthn.Authentication, logger *slog.L
opts...,
), "revoke").ServeHTTP)
})
r.Get("/serials/{thingID}", otelhttp.NewHandler(kithttp.NewServer(
r.Get("/serials/{clientID}", otelhttp.NewHandler(kithttp.NewServer(
listSerials(svc),
decodeListCerts,
api.EncodeResponse,
@@ -91,7 +91,7 @@ func decodeListCerts(_ context.Context, r *http.Request) (interface{}, error) {
}
req := listReq{
thingID: chi.URLParam(r, "thingID"),
clientID: chi.URLParam(r, "clientID"),
pm: certs.PageMetadata{
Offset: o,
Limit: l,
+2 -2
View File
@@ -19,7 +19,7 @@ type Cert struct {
Key string `json:"key,omitempty"`
Revoked bool `json:"revoked"`
ExpiryTime time.Time `json:"expiry_time"`
ThingID string `json:"entity_id"`
ClientID string `json:"entity_id"`
}
type CertPage struct {
@@ -33,7 +33,7 @@ type PageMetadata struct {
Total uint64 `json:"total,omitempty"`
Offset uint64 `json:"offset,omitempty"`
Limit uint64 `json:"limit,omitempty"`
ThingID string `json:"thing_id,omitempty"`
ClientID string `json:"client_id,omitempty"`
Token string `json:"token,omitempty"`
CommonName string `json:"common_name,omitempty"`
Revoked string `json:"revoked,omitempty"`
+24 -24
View File
@@ -17,9 +17,9 @@ type Service struct {
mock.Mock
}
// IssueCert provides a mock function with given fields: ctx, domainID, token, thingID, ttl
func (_m *Service) IssueCert(ctx context.Context, domainID string, token string, thingID string, ttl string) (certs.Cert, error) {
ret := _m.Called(ctx, domainID, token, thingID, ttl)
// IssueCert provides a mock function with given fields: ctx, domainID, token, clientID, ttl
func (_m *Service) IssueCert(ctx context.Context, domainID string, token string, clientID string, ttl string) (certs.Cert, error) {
ret := _m.Called(ctx, domainID, token, clientID, ttl)
if len(ret) == 0 {
panic("no return value specified for IssueCert")
@@ -28,16 +28,16 @@ func (_m *Service) IssueCert(ctx context.Context, domainID string, token string,
var r0 certs.Cert
var r1 error
if rf, ok := ret.Get(0).(func(context.Context, string, string, string, string) (certs.Cert, error)); ok {
return rf(ctx, domainID, token, thingID, ttl)
return rf(ctx, domainID, token, clientID, ttl)
}
if rf, ok := ret.Get(0).(func(context.Context, string, string, string, string) certs.Cert); ok {
r0 = rf(ctx, domainID, token, thingID, ttl)
r0 = rf(ctx, domainID, token, clientID, ttl)
} else {
r0 = ret.Get(0).(certs.Cert)
}
if rf, ok := ret.Get(1).(func(context.Context, string, string, string, string) error); ok {
r1 = rf(ctx, domainID, token, thingID, ttl)
r1 = rf(ctx, domainID, token, clientID, ttl)
} else {
r1 = ret.Error(1)
}
@@ -45,9 +45,9 @@ func (_m *Service) IssueCert(ctx context.Context, domainID string, token string,
return r0, r1
}
// ListCerts provides a mock function with given fields: ctx, thingID, pm
func (_m *Service) ListCerts(ctx context.Context, thingID string, pm certs.PageMetadata) (certs.CertPage, error) {
ret := _m.Called(ctx, thingID, pm)
// ListCerts provides a mock function with given fields: ctx, clientID, pm
func (_m *Service) ListCerts(ctx context.Context, clientID string, pm certs.PageMetadata) (certs.CertPage, error) {
ret := _m.Called(ctx, clientID, pm)
if len(ret) == 0 {
panic("no return value specified for ListCerts")
@@ -56,16 +56,16 @@ func (_m *Service) ListCerts(ctx context.Context, thingID string, pm certs.PageM
var r0 certs.CertPage
var r1 error
if rf, ok := ret.Get(0).(func(context.Context, string, certs.PageMetadata) (certs.CertPage, error)); ok {
return rf(ctx, thingID, pm)
return rf(ctx, clientID, pm)
}
if rf, ok := ret.Get(0).(func(context.Context, string, certs.PageMetadata) certs.CertPage); ok {
r0 = rf(ctx, thingID, pm)
r0 = rf(ctx, clientID, pm)
} else {
r0 = ret.Get(0).(certs.CertPage)
}
if rf, ok := ret.Get(1).(func(context.Context, string, certs.PageMetadata) error); ok {
r1 = rf(ctx, thingID, pm)
r1 = rf(ctx, clientID, pm)
} else {
r1 = ret.Error(1)
}
@@ -73,9 +73,9 @@ func (_m *Service) ListCerts(ctx context.Context, thingID string, pm certs.PageM
return r0, r1
}
// ListSerials provides a mock function with given fields: ctx, thingID, pm
func (_m *Service) ListSerials(ctx context.Context, thingID string, pm certs.PageMetadata) (certs.CertPage, error) {
ret := _m.Called(ctx, thingID, pm)
// ListSerials provides a mock function with given fields: ctx, clientID, pm
func (_m *Service) ListSerials(ctx context.Context, clientID string, pm certs.PageMetadata) (certs.CertPage, error) {
ret := _m.Called(ctx, clientID, pm)
if len(ret) == 0 {
panic("no return value specified for ListSerials")
@@ -84,16 +84,16 @@ func (_m *Service) ListSerials(ctx context.Context, thingID string, pm certs.Pag
var r0 certs.CertPage
var r1 error
if rf, ok := ret.Get(0).(func(context.Context, string, certs.PageMetadata) (certs.CertPage, error)); ok {
return rf(ctx, thingID, pm)
return rf(ctx, clientID, pm)
}
if rf, ok := ret.Get(0).(func(context.Context, string, certs.PageMetadata) certs.CertPage); ok {
r0 = rf(ctx, thingID, pm)
r0 = rf(ctx, clientID, pm)
} else {
r0 = ret.Get(0).(certs.CertPage)
}
if rf, ok := ret.Get(1).(func(context.Context, string, certs.PageMetadata) error); ok {
r1 = rf(ctx, thingID, pm)
r1 = rf(ctx, clientID, pm)
} else {
r1 = ret.Error(1)
}
@@ -101,9 +101,9 @@ func (_m *Service) ListSerials(ctx context.Context, thingID string, pm certs.Pag
return r0, r1
}
// RevokeCert provides a mock function with given fields: ctx, domainID, token, thingID
func (_m *Service) RevokeCert(ctx context.Context, domainID string, token string, thingID string) (certs.Revoke, error) {
ret := _m.Called(ctx, domainID, token, thingID)
// RevokeCert provides a mock function with given fields: ctx, domainID, token, clientID
func (_m *Service) RevokeCert(ctx context.Context, domainID string, token string, clientID string) (certs.Revoke, error) {
ret := _m.Called(ctx, domainID, token, clientID)
if len(ret) == 0 {
panic("no return value specified for RevokeCert")
@@ -112,16 +112,16 @@ func (_m *Service) RevokeCert(ctx context.Context, domainID string, token string
var r0 certs.Revoke
var r1 error
if rf, ok := ret.Get(0).(func(context.Context, string, string, string) (certs.Revoke, error)); ok {
return rf(ctx, domainID, token, thingID)
return rf(ctx, domainID, token, clientID)
}
if rf, ok := ret.Get(0).(func(context.Context, string, string, string) certs.Revoke); ok {
r0 = rf(ctx, domainID, token, thingID)
r0 = rf(ctx, domainID, token, clientID)
} else {
r0 = ret.Get(0).(certs.Revoke)
}
if rf, ok := ret.Get(1).(func(context.Context, string, string, string) error); ok {
r1 = rf(ctx, domainID, token, thingID)
r1 = rf(ctx, domainID, token, clientID)
} else {
r1 = ret.Error(1)
}
+4 -4
View File
@@ -14,7 +14,7 @@ type Cert struct {
Key string `json:"key,omitempty"`
Revoked bool `json:"revoked"`
ExpiryTime time.Time `json:"expiry_time"`
ThingID string `json:"entity_id"`
ClientID string `json:"entity_id"`
DownloadUrl string `json:"-"`
}
@@ -64,7 +64,7 @@ func (c sdkAgent) Issue(entityId, ttl string, ipAddrs []string) (Cert, error) {
Certificate: cert.Certificate,
Revoked: cert.Revoked,
ExpiryTime: cert.ExpiryTime,
ThingID: cert.EntityID,
ClientID: cert.EntityID,
}, nil
}
@@ -79,7 +79,7 @@ func (c sdkAgent) View(serial string) (Cert, error) {
Key: cert.Key,
Revoked: cert.Revoked,
ExpiryTime: cert.ExpiryTime,
ThingID: cert.EntityID,
ClientID: cert.EntityID,
}, nil
}
@@ -105,7 +105,7 @@ func (c sdkAgent) ListCerts(pm sdk.PageMetadata) (CertPage, error) {
Key: c.Key,
Revoked: c.Revoked,
ExpiryTime: c.ExpiryTime,
ThingID: c.EntityID,
ClientID: c.EntityID,
})
}
+22 -22
View File
@@ -33,20 +33,20 @@ var _ Service = (*certsService)(nil)
//
//go:generate mockery --name Service --output=./mocks --filename service.go --quiet --note "Copyright (c) Abstract Machines"
type Service interface {
// IssueCert issues certificate for given thing id if access is granted with token
IssueCert(ctx context.Context, domainID, token, thingID, ttl string) (Cert, error)
// IssueCert issues certificate for given client id if access is granted with token
IssueCert(ctx context.Context, domainID, token, clientID, ttl string) (Cert, error)
// ListCerts lists certificates issued for a given thing ID
ListCerts(ctx context.Context, thingID string, pm PageMetadata) (CertPage, error)
// ListCerts lists certificates issued for a given client ID
ListCerts(ctx context.Context, clientID string, pm PageMetadata) (CertPage, error)
// ListSerials lists certificate serial IDs issued for a given thing ID
ListSerials(ctx context.Context, thingID string, pm PageMetadata) (CertPage, error)
// ListSerials lists certificate serial IDs issued for a given client ID
ListSerials(ctx context.Context, clientID string, pm PageMetadata) (CertPage, error)
// ViewCert retrieves the certificate issued for a given serial ID
ViewCert(ctx context.Context, serialID string) (Cert, error)
// RevokeCert revokes a certificate for a given thing ID
RevokeCert(ctx context.Context, domainID, token, thingID string) (Revoke, error)
// RevokeCert revokes a certificate for a given client ID
RevokeCert(ctx context.Context, domainID, token, clientID string) (Revoke, error)
}
type certsService struct {
@@ -67,15 +67,15 @@ type Revoke struct {
RevocationTime time.Time `mapstructure:"revocation_time"`
}
func (cs *certsService) IssueCert(ctx context.Context, domainID, token, thingID, ttl string) (Cert, error) {
func (cs *certsService) IssueCert(ctx context.Context, domainID, token, clientID, ttl string) (Cert, error) {
var err error
thing, err := cs.sdk.Thing(thingID, domainID, token)
client, err := cs.sdk.Client(clientID, domainID, token)
if err != nil {
return Cert{}, errors.Wrap(ErrFailedCertCreation, err)
}
cert, err := cs.pki.Issue(thing.ID, ttl, []string{})
cert, err := cs.pki.Issue(client.ID, ttl, []string{})
if err != nil {
return Cert{}, errors.Wrap(ErrFailedCertCreation, err)
}
@@ -86,20 +86,20 @@ func (cs *certsService) IssueCert(ctx context.Context, domainID, token, thingID,
Key: cert.Key,
Revoked: cert.Revoked,
ExpiryTime: cert.ExpiryTime,
ThingID: cert.ThingID,
ClientID: cert.ClientID,
}, err
}
func (cs *certsService) RevokeCert(ctx context.Context, domainID, token, thingID string) (Revoke, error) {
func (cs *certsService) RevokeCert(ctx context.Context, domainID, token, clientID string) (Revoke, error) {
var revoke Revoke
var err error
thing, err := cs.sdk.Thing(thingID, domainID, token)
client, err := cs.sdk.Client(clientID, domainID, token)
if err != nil {
return revoke, errors.Wrap(ErrFailedCertRevocation, err)
}
cp, err := cs.pki.ListCerts(sdk.PageMetadata{Offset: 0, Limit: 10000, EntityID: thing.ID})
cp, err := cs.pki.ListCerts(sdk.PageMetadata{Offset: 0, Limit: 10000, EntityID: client.ID})
if err != nil {
return revoke, errors.Wrap(ErrFailedCertRevocation, err)
}
@@ -115,8 +115,8 @@ func (cs *certsService) RevokeCert(ctx context.Context, domainID, token, thingID
return revoke, nil
}
func (cs *certsService) ListCerts(ctx context.Context, thingID string, pm PageMetadata) (CertPage, error) {
cp, err := cs.pki.ListCerts(sdk.PageMetadata{Offset: pm.Offset, Limit: pm.Limit, EntityID: thingID})
func (cs *certsService) ListCerts(ctx context.Context, clientID string, pm PageMetadata) (CertPage, error) {
cp, err := cs.pki.ListCerts(sdk.PageMetadata{Offset: pm.Offset, Limit: pm.Limit, EntityID: clientID})
if err != nil {
return CertPage{}, errors.Wrap(svcerr.ErrViewEntity, err)
}
@@ -130,7 +130,7 @@ func (cs *certsService) ListCerts(ctx context.Context, thingID string, pm PageMe
Key: c.Key,
Revoked: c.Revoked,
ExpiryTime: c.ExpiryTime,
ThingID: c.ThingID,
ClientID: c.ClientID,
})
}
@@ -142,8 +142,8 @@ func (cs *certsService) ListCerts(ctx context.Context, thingID string, pm PageMe
}, nil
}
func (cs *certsService) ListSerials(ctx context.Context, thingID string, pm PageMetadata) (CertPage, error) {
cp, err := cs.pki.ListCerts(sdk.PageMetadata{Offset: pm.Offset, Limit: pm.Limit, EntityID: thingID})
func (cs *certsService) ListSerials(ctx context.Context, clientID string, pm PageMetadata) (CertPage, error) {
cp, err := cs.pki.ListCerts(sdk.PageMetadata{Offset: pm.Offset, Limit: pm.Limit, EntityID: clientID})
if err != nil {
return CertPage{}, errors.Wrap(svcerr.ErrViewEntity, err)
}
@@ -153,7 +153,7 @@ func (cs *certsService) ListSerials(ctx context.Context, thingID string, pm Page
if (pm.Revoked == "true" && c.Revoked) || (pm.Revoked == "false" && !c.Revoked) || (pm.Revoked == "all") {
certs = append(certs, Cert{
SerialNumber: c.SerialNumber,
ThingID: c.ThingID,
ClientID: c.ClientID,
ExpiryTime: c.ExpiryTime,
Revoked: c.Revoked,
})
@@ -180,6 +180,6 @@ func (cs *certsService) ViewCert(ctx context.Context, serialID string) (Cert, er
Key: cert.Key,
Revoked: cert.Revoked,
ExpiryTime: cert.ExpiryTime,
ThingID: cert.ThingID,
ClientID: cert.ClientID,
}, nil
}
+106 -106
View File
@@ -21,16 +21,16 @@ import (
)
const (
invalid = "invalid"
email = "user@example.com"
domain = "domain"
token = "token"
thingsNum = 1
thingKey = "thingKey"
thingID = "1"
ttl = "1h"
certNum = 10
validID = "d4ebb847-5d0e-4e46-bdd9-b6aceaaa3a22"
invalid = "invalid"
email = "user@example.com"
domain = "domain"
token = "token"
clientsNum = 1
clientKey = "clientKey"
clientID = "1"
ttl = "1h"
certNum = 10
validID = "d4ebb847-5d0e-4e46-bdd9-b6aceaaa3a22"
)
func newService(_ *testing.T) (certs.Service, *mocks.Agent, *sdkmocks.SDK) {
@@ -41,7 +41,7 @@ func newService(_ *testing.T) (certs.Service, *mocks.Agent, *sdkmocks.SDK) {
}
var cert = mgcrt.Cert{
ThingID: thingID,
ClientID: clientID,
SerialNumber: "Serial",
ExpiryTime: time.Now().Add(time.Duration(1000)),
Revoked: false,
@@ -53,12 +53,12 @@ func TestIssueCert(t *testing.T) {
domainID string
token string
desc string
thingID string
clientID string
ttl string
ipAddr []string
key string
cert mgcrt.Cert
thingErr errors.SDKError
clientErr errors.SDKError
issueCertErr error
err error
}{
@@ -66,7 +66,7 @@ func TestIssueCert(t *testing.T) {
desc: "issue new cert",
domainID: domain,
token: token,
thingID: thingID,
clientID: clientID,
ttl: ttl,
ipAddr: []string{},
cert: cert,
@@ -75,40 +75,40 @@ func TestIssueCert(t *testing.T) {
desc: "issue new for failed pki",
domainID: domain,
token: token,
thingID: thingID,
clientID: clientID,
ttl: ttl,
ipAddr: []string{},
thingErr: nil,
clientErr: nil,
issueCertErr: certs.ErrFailedCertCreation,
err: certs.ErrFailedCertCreation,
},
{
desc: "issue new cert for non existing thing id",
domainID: domain,
token: token,
thingID: "2",
ttl: ttl,
ipAddr: []string{},
thingErr: errors.NewSDKError(errors.ErrMalformedEntity),
err: certs.ErrFailedCertCreation,
desc: "issue new cert for non existing client id",
domainID: domain,
token: token,
clientID: "2",
ttl: ttl,
ipAddr: []string{},
clientErr: errors.NewSDKError(errors.ErrMalformedEntity),
err: certs.ErrFailedCertCreation,
},
{
desc: "issue new cert for invalid token",
domainID: domain,
token: invalid,
thingID: thingID,
ttl: ttl,
ipAddr: []string{},
thingErr: errors.NewSDKError(svcerr.ErrAuthentication),
err: svcerr.ErrAuthentication,
desc: "issue new cert for invalid token",
domainID: domain,
token: invalid,
clientID: clientID,
ttl: ttl,
ipAddr: []string{},
clientErr: errors.NewSDKError(svcerr.ErrAuthentication),
err: svcerr.ErrAuthentication,
},
}
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.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)
sdkCall := sdk.On("Client", tc.clientID, tc.domainID, tc.token).Return(mgsdk.Client{ID: tc.clientID, Credentials: mgsdk.ClientCredentials{Secret: clientKey}}, tc.clientErr)
agentCall := agent.On("Issue", clientID, tc.ttl, tc.ipAddr).Return(tc.cert, tc.issueCertErr)
resp, err := svc.IssueCert(context.Background(), tc.domainID, tc.token, tc.clientID, tc.ttl)
assert.True(t, errors.Contains(err, tc.err), fmt.Sprintf("%s: expected %s got %s\n", tc.desc, tc.err, err))
assert.Equal(t, tc.cert.SerialNumber, resp.SerialNumber, fmt.Sprintf("%s: expected %s got %s\n", tc.desc, tc.cert.SerialNumber, resp.SerialNumber))
sdkCall.Unset()
@@ -123,10 +123,10 @@ func TestRevokeCert(t *testing.T) {
domainID string
token string
desc string
thingID string
clientID string
page mgcrt.CertPage
authErr error
thingErr errors.SDKError
clientErr errors.SDKError
revokeErr error
listErr error
err error
@@ -135,32 +135,32 @@ func TestRevokeCert(t *testing.T) {
desc: "revoke cert",
domainID: domain,
token: token,
thingID: thingID,
clientID: clientID,
page: mgcrt.CertPage{Limit: 10000, Offset: 0, Total: 1, Certificates: []mgcrt.Cert{cert}},
},
{
desc: "revoke cert for failed pki revoke",
domainID: domain,
token: token,
thingID: thingID,
clientID: clientID,
page: mgcrt.CertPage{Limit: 10000, Offset: 0, Total: 1, Certificates: []mgcrt.Cert{cert}},
revokeErr: certs.ErrFailedCertRevocation,
err: certs.ErrFailedCertRevocation,
},
{
desc: "revoke cert for invalid thing id",
domainID: domain,
token: token,
thingID: "2",
page: mgcrt.CertPage{},
thingErr: errors.NewSDKError(certs.ErrFailedCertCreation),
err: certs.ErrFailedCertRevocation,
desc: "revoke cert for invalid client id",
domainID: domain,
token: token,
clientID: "2",
page: mgcrt.CertPage{},
clientErr: errors.NewSDKError(certs.ErrFailedCertCreation),
err: certs.ErrFailedCertRevocation,
},
{
desc: "revoke cert with failed to list certs",
domainID: domain,
token: token,
thingID: thingID,
clientID: clientID,
page: mgcrt.CertPage{},
listErr: certs.ErrFailedCertRevocation,
err: certs.ErrFailedCertRevocation,
@@ -169,10 +169,10 @@ 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.ClientCredentials{Secret: thingKey}}, tc.thingErr)
sdkCall := sdk.On("Client", tc.clientID, tc.domainID, tc.token).Return(mgsdk.Client{ID: tc.clientID, Credentials: mgsdk.ClientCredentials{Secret: clientKey}}, tc.clientErr)
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)
_, err := svc.RevokeCert(context.Background(), tc.domainID, tc.token, tc.clientID)
assert.True(t, errors.Contains(err, tc.err), fmt.Sprintf("%s: expected %s got %s\n", tc.desc, tc.err, err))
sdkCall.Unset()
agentCall.Unset()
@@ -186,7 +186,7 @@ func TestListCerts(t *testing.T) {
var mycerts []mgcrt.Cert
for i := 0; i < certNum; i++ {
c := mgcrt.Cert{
ThingID: thingID,
ClientID: clientID,
SerialNumber: fmt.Sprintf("%d", i),
ExpiryTime: time.Now().Add(time.Hour),
}
@@ -194,40 +194,40 @@ func TestListCerts(t *testing.T) {
}
cases := []struct {
desc string
thingID string
page mgcrt.CertPage
listErr error
err error
desc string
clientID string
page mgcrt.CertPage
listErr error
err error
}{
{
desc: "list all certs successfully",
thingID: thingID,
page: mgcrt.CertPage{Limit: certNum, Offset: 0, Total: certNum, Certificates: mycerts},
desc: "list all certs successfully",
clientID: clientID,
page: mgcrt.CertPage{Limit: certNum, Offset: 0, Total: certNum, Certificates: mycerts},
},
{
desc: "list all certs with failed pki",
thingID: thingID,
page: mgcrt.CertPage{},
listErr: svcerr.ErrViewEntity,
err: svcerr.ErrViewEntity,
desc: "list all certs with failed pki",
clientID: clientID,
page: mgcrt.CertPage{},
listErr: svcerr.ErrViewEntity,
err: svcerr.ErrViewEntity,
},
{
desc: "list half certs successfully",
thingID: thingID,
page: mgcrt.CertPage{Limit: certNum, Offset: certNum / 2, Total: certNum / 2, Certificates: mycerts[certNum/2:]},
desc: "list half certs successfully",
clientID: clientID,
page: mgcrt.CertPage{Limit: certNum, Offset: certNum / 2, Total: certNum / 2, Certificates: mycerts[certNum/2:]},
},
{
desc: "list last cert successfully",
thingID: thingID,
page: mgcrt.CertPage{Limit: certNum, Offset: certNum - 1, Total: 1, Certificates: []mgcrt.Cert{mycerts[certNum-1]}},
desc: "list last cert successfully",
clientID: clientID,
page: mgcrt.CertPage{Limit: certNum, Offset: certNum - 1, Total: 1, Certificates: []mgcrt.Cert{mycerts[certNum-1]}},
},
}
for _, tc := range cases {
t.Run(tc.desc, func(t *testing.T) {
agentCall := agent.On("ListCerts", mock.Anything).Return(tc.page, tc.listErr)
page, err := svc.ListCerts(context.Background(), tc.thingID, certs.PageMetadata{Offset: tc.page.Offset, Limit: tc.page.Limit})
page, err := svc.ListCerts(context.Background(), tc.clientID, certs.PageMetadata{Offset: tc.page.Offset, Limit: tc.page.Limit})
size := uint64(len(page.Certificates))
assert.Equal(t, tc.page.Total, size, fmt.Sprintf("%s: expected %d got %d\n", tc.desc, tc.page.Total, size))
assert.True(t, errors.Contains(err, tc.err), fmt.Sprintf("%s: expected %s got %s\n", tc.desc, tc.err, err))
@@ -243,7 +243,7 @@ func TestListSerials(t *testing.T) {
var issuedCerts []mgcrt.Cert
for i := 0; i < certNum; i++ {
crt := mgcrt.Cert{
ThingID: cert.ThingID,
ClientID: cert.ClientID,
SerialNumber: cert.SerialNumber,
ExpiryTime: cert.ExpiryTime,
Revoked: false,
@@ -252,55 +252,55 @@ func TestListSerials(t *testing.T) {
}
cases := []struct {
desc string
thingID string
revoke string
offset uint64
limit uint64
certs []mgcrt.Cert
listErr error
err error
desc string
clientID string
revoke string
offset uint64
limit uint64
certs []mgcrt.Cert
listErr error
err error
}{
{
desc: "list all certs successfully",
thingID: thingID,
revoke: revoke,
offset: 0,
limit: certNum,
certs: issuedCerts,
desc: "list all certs successfully",
clientID: clientID,
revoke: revoke,
offset: 0,
limit: certNum,
certs: issuedCerts,
},
{
desc: "list all certs with failed pki",
thingID: thingID,
revoke: revoke,
offset: 0,
limit: certNum,
certs: nil,
listErr: svcerr.ErrViewEntity,
err: svcerr.ErrViewEntity,
desc: "list all certs with failed pki",
clientID: clientID,
revoke: revoke,
offset: 0,
limit: certNum,
certs: nil,
listErr: svcerr.ErrViewEntity,
err: svcerr.ErrViewEntity,
},
{
desc: "list half certs successfully",
thingID: thingID,
revoke: revoke,
offset: certNum / 2,
limit: certNum,
certs: issuedCerts[certNum/2:],
desc: "list half certs successfully",
clientID: clientID,
revoke: revoke,
offset: certNum / 2,
limit: certNum,
certs: issuedCerts[certNum/2:],
},
{
desc: "list last cert successfully",
thingID: thingID,
revoke: revoke,
offset: certNum - 1,
limit: certNum,
certs: []mgcrt.Cert{issuedCerts[certNum-1]},
desc: "list last cert successfully",
clientID: clientID,
revoke: revoke,
offset: certNum - 1,
limit: certNum,
certs: []mgcrt.Cert{issuedCerts[certNum-1]},
},
}
for _, tc := range cases {
t.Run(tc.desc, func(t *testing.T) {
agentCall := agent.On("ListCerts", mock.Anything).Return(mgcrt.CertPage{Certificates: tc.certs}, tc.listErr)
page, err := svc.ListSerials(context.Background(), tc.thingID, certs.PageMetadata{Revoked: tc.revoke, Offset: tc.offset, Limit: tc.limit})
page, err := svc.ListSerials(context.Background(), tc.clientID, certs.PageMetadata{Revoked: tc.revoke, Offset: tc.offset, Limit: tc.limit})
assert.Equal(t, len(tc.certs), len(page.Certificates), fmt.Sprintf("%s: expected %v got %v\n", tc.desc, tc.certs, page.Certificates))
assert.True(t, errors.Contains(err, tc.err), fmt.Sprintf("%s: expected %s got %s\n", tc.desc, tc.err, err))
agentCall.Unset()
+9 -9
View File
@@ -24,38 +24,38 @@ func New(svc certs.Service, tracer trace.Tracer) certs.Service {
}
// IssueCert traces the "IssueCert" operation of the wrapped certs.Service.
func (tm *tracingMiddleware) IssueCert(ctx context.Context, domainID, token, thingID, ttl string) (certs.Cert, error) {
func (tm *tracingMiddleware) IssueCert(ctx context.Context, domainID, token, clientID, ttl string) (certs.Cert, error) {
ctx, span := tm.tracer.Start(ctx, "svc_create_group", trace.WithAttributes(
attribute.String("thing_id", thingID),
attribute.String("client_id", clientID),
attribute.String("ttl", ttl),
))
defer span.End()
return tm.svc.IssueCert(ctx, domainID, token, thingID, ttl)
return tm.svc.IssueCert(ctx, domainID, token, clientID, ttl)
}
// ListCerts traces the "ListCerts" operation of the wrapped certs.Service.
func (tm *tracingMiddleware) ListCerts(ctx context.Context, thingID string, pm certs.PageMetadata) (certs.CertPage, error) {
func (tm *tracingMiddleware) ListCerts(ctx context.Context, clientID string, pm certs.PageMetadata) (certs.CertPage, error) {
ctx, span := tm.tracer.Start(ctx, "svc_list_certs", trace.WithAttributes(
attribute.String("thing_id", thingID),
attribute.String("client_id", clientID),
attribute.Int64("offset", int64(pm.Offset)),
attribute.Int64("limit", int64(pm.Limit)),
))
defer span.End()
return tm.svc.ListCerts(ctx, thingID, pm)
return tm.svc.ListCerts(ctx, clientID, pm)
}
// ListSerials traces the "ListSerials" operation of the wrapped certs.Service.
func (tm *tracingMiddleware) ListSerials(ctx context.Context, thingID string, pm certs.PageMetadata) (certs.CertPage, error) {
func (tm *tracingMiddleware) ListSerials(ctx context.Context, clientID string, pm certs.PageMetadata) (certs.CertPage, error) {
ctx, span := tm.tracer.Start(ctx, "svc_list_serials", trace.WithAttributes(
attribute.String("thing_id", thingID),
attribute.String("client_id", clientID),
attribute.Int64("offset", int64(pm.Offset)),
attribute.Int64("limit", int64(pm.Limit)),
))
defer span.End()
return tm.svc.ListSerials(ctx, thingID, pm)
return tm.svc.ListSerials(ctx, clientID, pm)
}
// ViewCert traces the "ViewCert" operation of the wrapped certs.Service.
+195
View File
@@ -0,0 +1,195 @@
// Copyright (c) Abstract Machines
// SPDX-License-Identifier: Apache-2.0
package grpc
import (
"context"
"fmt"
"time"
grpcChannelsV1 "github.com/absmach/magistrala/internal/grpc/channels/v1"
grpcCommonV1 "github.com/absmach/magistrala/internal/grpc/common/v1"
"github.com/absmach/magistrala/pkg/connections"
"github.com/absmach/magistrala/pkg/errors"
svcerr "github.com/absmach/magistrala/pkg/errors/service"
"github.com/go-kit/kit/endpoint"
kitgrpc "github.com/go-kit/kit/transport/grpc"
"google.golang.org/grpc"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
)
const svcName = "channels.v1.ChannelsService"
var _ grpcChannelsV1.ChannelsServiceClient = (*grpcClient)(nil)
type grpcClient struct {
timeout time.Duration
authorize endpoint.Endpoint
removeClientConnections endpoint.Endpoint
unsetParentGroupFromChannels endpoint.Endpoint
retrieveEntity endpoint.Endpoint
}
// NewClient returns new gRPC client instance.
func NewClient(conn *grpc.ClientConn, timeout time.Duration) grpcChannelsV1.ChannelsServiceClient {
return &grpcClient{
authorize: kitgrpc.NewClient(
conn,
svcName,
"Authorize",
encodeAuthorizeRequest,
decodeAuthorizeResponse,
grpcChannelsV1.AuthzRes{},
).Endpoint(),
removeClientConnections: kitgrpc.NewClient(
conn,
svcName,
"RemoveClientConnections",
encodeRemoveClientConnectionsRequest,
decodeRemoveClientConnectionsResponse,
grpcChannelsV1.RemoveClientConnectionsRes{},
).Endpoint(),
unsetParentGroupFromChannels: kitgrpc.NewClient(
conn,
svcName,
"UnsetParentGroupFromChannels",
encodeUnsetParentGroupFromChannelsRequest,
decodeUnsetParentGroupFromChannelsResponse,
grpcChannelsV1.UnsetParentGroupFromChannelsRes{},
).Endpoint(),
retrieveEntity: kitgrpc.NewClient(
conn,
svcName,
"RetrieveEntity",
encodeRetrieveEntityRequest,
decodeRetrieveEntityResponse,
grpcCommonV1.RetrieveEntityRes{},
).Endpoint(),
timeout: timeout,
}
}
func (client grpcClient) Authorize(ctx context.Context, req *grpcChannelsV1.AuthzReq, _ ...grpc.CallOption) (r *grpcChannelsV1.AuthzRes, err error) {
ctx, cancel := context.WithTimeout(ctx, client.timeout)
defer cancel()
res, err := client.authorize(ctx, authorizeReq{
domainID: req.GetDomainId(),
clientID: req.GetClientId(),
clientType: req.GetClientType(),
channelID: req.GetChannelId(),
connType: connections.ConnType(req.GetType()),
})
if err != nil {
return &grpcChannelsV1.AuthzRes{}, decodeError(err)
}
ar := res.(authorizeRes)
return &grpcChannelsV1.AuthzRes{Authorized: ar.authorized}, nil
}
func encodeAuthorizeRequest(_ context.Context, grpcReq interface{}) (interface{}, error) {
req := grpcReq.(authorizeReq)
return &grpcChannelsV1.AuthzReq{
DomainId: req.domainID,
ClientId: req.clientID,
ClientType: req.clientType,
ChannelId: req.channelID,
Type: uint32(req.connType),
}, nil
}
func decodeAuthorizeResponse(_ context.Context, grpcRes interface{}) (interface{}, error) {
res := grpcRes.(*grpcChannelsV1.AuthzRes)
return authorizeRes{authorized: res.GetAuthorized()}, nil
}
func (client grpcClient) RemoveClientConnections(ctx context.Context, req *grpcChannelsV1.RemoveClientConnectionsReq, _ ...grpc.CallOption) (r *grpcChannelsV1.RemoveClientConnectionsRes, err error) {
ctx, cancel := context.WithTimeout(ctx, client.timeout)
defer cancel()
if _, err := client.removeClientConnections(ctx, req); err != nil {
return &grpcChannelsV1.RemoveClientConnectionsRes{}, decodeError(err)
}
return &grpcChannelsV1.RemoveClientConnectionsRes{}, nil
}
func encodeRemoveClientConnectionsRequest(_ context.Context, grpcReq interface{}) (interface{}, error) {
return grpcReq.(*grpcChannelsV1.RemoveClientConnectionsReq), nil
}
func decodeRemoveClientConnectionsResponse(_ context.Context, grpcRes interface{}) (interface{}, error) {
return grpcRes.(*grpcChannelsV1.RemoveClientConnectionsRes), nil
}
func (client grpcClient) UnsetParentGroupFromChannels(ctx context.Context, req *grpcChannelsV1.UnsetParentGroupFromChannelsReq, _ ...grpc.CallOption) (r *grpcChannelsV1.UnsetParentGroupFromChannelsRes, err error) {
ctx, cancel := context.WithTimeout(ctx, client.timeout)
defer cancel()
if _, err := client.unsetParentGroupFromChannels(ctx, req); err != nil {
return &grpcChannelsV1.UnsetParentGroupFromChannelsRes{}, decodeError(err)
}
return &grpcChannelsV1.UnsetParentGroupFromChannelsRes{}, nil
}
func encodeUnsetParentGroupFromChannelsRequest(_ context.Context, grpcReq interface{}) (interface{}, error) {
return grpcReq.(*grpcChannelsV1.UnsetParentGroupFromChannelsReq), nil
}
func decodeUnsetParentGroupFromChannelsResponse(_ context.Context, grpcRes interface{}) (interface{}, error) {
return grpcRes.(*grpcChannelsV1.UnsetParentGroupFromChannelsRes), nil
}
func (client grpcClient) RetrieveEntity(ctx context.Context, req *grpcCommonV1.RetrieveEntityReq, _ ...grpc.CallOption) (r *grpcCommonV1.RetrieveEntityRes, err error) {
ctx, cancel := context.WithTimeout(ctx, client.timeout)
defer cancel()
res, err := client.retrieveEntity(ctx, req)
if err != nil {
return &grpcCommonV1.RetrieveEntityRes{}, decodeError(err)
}
return res.(*grpcCommonV1.RetrieveEntityRes), nil
}
func encodeRetrieveEntityRequest(_ context.Context, grpcReq interface{}) (interface{}, error) {
return grpcReq.(*grpcCommonV1.RetrieveEntityReq), nil
}
func decodeRetrieveEntityResponse(_ context.Context, grpcRes interface{}) (interface{}, error) {
return grpcRes.(*grpcCommonV1.RetrieveEntityRes), nil
}
func decodeError(err error) error {
if st, ok := status.FromError(err); ok {
switch st.Code() {
case codes.Unauthenticated:
return errors.Wrap(svcerr.ErrAuthentication, errors.New(st.Message()))
case codes.PermissionDenied:
return errors.Wrap(svcerr.ErrAuthorization, errors.New(st.Message()))
case codes.InvalidArgument:
return errors.Wrap(errors.ErrMalformedEntity, errors.New(st.Message()))
case codes.FailedPrecondition:
return errors.Wrap(errors.ErrMalformedEntity, errors.New(st.Message()))
case codes.NotFound:
return errors.Wrap(svcerr.ErrNotFound, errors.New(st.Message()))
case codes.AlreadyExists:
return errors.Wrap(svcerr.ErrConflict, errors.New(st.Message()))
case codes.OK:
if msg := st.Message(); msg != "" {
return errors.Wrap(errors.ErrUnidentified, errors.New(msg))
}
return nil
default:
return errors.Wrap(fmt.Errorf("unexpected gRPC status: %s (status code:%v)", st.Code().String(), st.Code()), errors.New(st.Message()))
}
}
return err
}
+66
View File
@@ -0,0 +1,66 @@
// Copyright (c) Abstract Machines
// SPDX-License-Identifier: Apache-2.0
package grpc
import (
"context"
ch "github.com/absmach/magistrala/channels"
channels "github.com/absmach/magistrala/channels/private"
"github.com/go-kit/kit/endpoint"
)
func authorizeEndpoint(svc channels.Service) endpoint.Endpoint {
return func(ctx context.Context, request interface{}) (interface{}, error) {
req := request.(authorizeReq)
if err := svc.Authorize(ctx, ch.AuthzReq{
DomainID: req.domainID,
ClientID: req.clientID,
ClientType: req.clientType,
ChannelID: req.channelID,
Type: req.connType,
}); err != nil {
return authorizeRes{}, err
}
return authorizeRes{authorized: true}, nil
}
}
func removeClientConnectionsEndpoint(svc channels.Service) endpoint.Endpoint {
return func(ctx context.Context, request interface{}) (interface{}, error) {
req := request.(removeClientConnectionsReq)
if err := svc.RemoveClientConnections(ctx, req.clientID); err != nil {
return removeClientConnectionsRes{}, err
}
return removeClientConnectionsRes{}, nil
}
}
func unsetParentGroupFromChannelsEndpoint(svc channels.Service) endpoint.Endpoint {
return func(ctx context.Context, request interface{}) (interface{}, error) {
req := request.(unsetParentGroupFromChannelsReq)
if err := svc.UnsetParentGroupFromChannels(ctx, req.parentGroupID); err != nil {
return unsetParentGroupFromChannelsRes{}, err
}
return unsetParentGroupFromChannelsRes{}, nil
}
}
func retrieveEntityEndpoint(svc channels.Service) endpoint.Endpoint {
return func(ctx context.Context, request interface{}) (interface{}, error) {
req := request.(retrieveEntityReq)
channel, err := svc.RetrieveByID(ctx, req.Id)
if err != nil {
return retrieveEntityRes{}, err
}
return retrieveEntityRes{id: channel.ID, domain: channel.Domain, parentGroup: channel.ParentGroup, status: uint8(channel.Status)}, nil
}
}
+267
View File
@@ -0,0 +1,267 @@
// Copyright (c) Abstract Machines
// SPDX-License-Identifier: Apache-2.0
package grpc_test
import (
"context"
"fmt"
"net"
"testing"
"time"
ch "github.com/absmach/magistrala/channels"
grpcapi "github.com/absmach/magistrala/channels/api/grpc"
"github.com/absmach/magistrala/channels/private/mocks"
"github.com/absmach/magistrala/clients"
grpcChannelsV1 "github.com/absmach/magistrala/internal/grpc/channels/v1"
grpcCommonV1 "github.com/absmach/magistrala/internal/grpc/common/v1"
"github.com/absmach/magistrala/internal/testsutil"
"github.com/absmach/magistrala/pkg/connections"
"github.com/absmach/magistrala/pkg/errors"
svcerr "github.com/absmach/magistrala/pkg/errors/service"
"github.com/absmach/magistrala/pkg/policies"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/mock"
"google.golang.org/grpc"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/credentials/insecure"
)
const port = 7005
var (
validID = testsutil.GenerateUUID(&testing.T{})
validChannel = ch.Channel{
ID: validID,
Domain: testsutil.GenerateUUID(&testing.T{}),
Status: clients.EnabledStatus,
}
)
func startGRPCServer(svc *mocks.Service, port int) *grpc.Server {
listener, err := net.Listen("tcp", fmt.Sprintf(":%d", port))
if err != nil {
panic(fmt.Sprintf("failed to obtain port: %s", err))
}
server := grpc.NewServer()
grpcChannelsV1.RegisterChannelsServiceServer(server, grpcapi.NewServer(svc))
go func() {
if err := server.Serve(listener); err != nil {
panic(fmt.Sprintf("failed to serve: %s", err))
}
}()
return server
}
func TestAuthorize(t *testing.T) {
svc := new(mocks.Service)
server := startGRPCServer(svc, port)
defer server.GracefulStop()
authAddr := fmt.Sprintf("localhost:%d", port)
conn, _ := grpc.NewClient(authAddr, grpc.WithTransportCredentials(insecure.NewCredentials()))
client := grpcapi.NewClient(conn, time.Second)
cases := []struct {
desc string
domainID string
clientID string
clientType string
channelID string
connType connections.ConnType
err error
authzErr error
res *grpcChannelsV1.AuthzRes
code codes.Code
}{
{
desc: "authorize successfully",
domainID: validID,
clientID: validID,
clientType: policies.UserType,
channelID: validID,
connType: connections.Publish,
res: &grpcChannelsV1.AuthzRes{Authorized: true},
err: nil,
},
{
desc: "authorize with authorization error",
domainID: validID,
clientID: validID,
clientType: policies.UserType,
channelID: validID,
connType: connections.Publish,
res: &grpcChannelsV1.AuthzRes{Authorized: false},
authzErr: svcerr.ErrAuthorization,
err: svcerr.ErrAuthorization,
},
{
desc: "authorize withnot found error",
domainID: validID,
clientID: validID,
clientType: policies.UserType,
channelID: validID,
connType: connections.Publish,
res: &grpcChannelsV1.AuthzRes{Authorized: false},
authzErr: svcerr.ErrNotFound,
err: svcerr.ErrNotFound,
},
}
for _, tc := range cases {
t.Run(tc.desc, func(t *testing.T) {
authReq := ch.AuthzReq{
DomainID: tc.domainID,
ClientID: tc.clientID,
ClientType: tc.clientType,
ChannelID: tc.channelID,
Type: tc.connType,
}
svcCall := svc.On("Authorize", mock.Anything, authReq).Return(tc.authzErr)
res, err := client.Authorize(context.Background(), &grpcChannelsV1.AuthzReq{
DomainId: tc.domainID,
ClientId: tc.clientID,
ClientType: tc.clientType,
ChannelId: tc.channelID,
Type: uint32(tc.connType),
})
assert.True(t, errors.Contains(err, tc.err), fmt.Sprintf("%s: expected %s got %s", tc.desc, tc.err, err))
assert.Equal(t, tc.res, res, fmt.Sprintf("%s: expected %s got %s", tc.desc, tc.res, res))
svcCall.Unset()
})
}
}
func TestRemoveClientConnections(t *testing.T) {
svc := new(mocks.Service)
server := startGRPCServer(svc, port)
defer server.GracefulStop()
authAddr := fmt.Sprintf("localhost:%d", port)
conn, _ := grpc.NewClient(authAddr, grpc.WithTransportCredentials(insecure.NewCredentials()))
client := grpcapi.NewClient(conn, time.Second)
cases := []struct {
desc string
clientID string
err error
code codes.Code
}{
{
desc: "remove client connections successfully",
clientID: validID,
err: nil,
},
{
desc: "remove client connections with error",
clientID: validID,
err: svcerr.ErrNotFound,
},
}
for _, tc := range cases {
t.Run(tc.desc, func(t *testing.T) {
svcCall := svc.On("RemoveClientConnections", mock.Anything, tc.clientID).Return(tc.err)
res, err := client.RemoveClientConnections(context.Background(), &grpcChannelsV1.RemoveClientConnectionsReq{
ClientId: tc.clientID,
})
assert.True(t, errors.Contains(err, tc.err), fmt.Sprintf("%s: expected %s got %s", tc.desc, tc.err, err))
assert.Equal(t, &grpcChannelsV1.RemoveClientConnectionsRes{}, res)
svcCall.Unset()
})
}
}
func TestUnsetParentGroupFromChannelsEndpoint(t *testing.T) {
svc := new(mocks.Service)
server := startGRPCServer(svc, port)
defer server.GracefulStop()
authAddr := fmt.Sprintf("localhost:%d", port)
conn, _ := grpc.NewClient(authAddr, grpc.WithTransportCredentials(insecure.NewCredentials()))
client := grpcapi.NewClient(conn, time.Second)
cases := []struct {
desc string
parentGroupID string
err error
code codes.Code
}{
{
desc: "unset parent group from channels successfully",
parentGroupID: validID,
err: nil,
},
{
desc: "unset parent group from channels authorization error",
parentGroupID: validID,
err: svcerr.ErrAuthorization,
},
{
desc: "unset parent group from channels with not found error",
parentGroupID: validID,
err: svcerr.ErrNotFound,
},
}
for _, tc := range cases {
t.Run(tc.desc, func(t *testing.T) {
svcCall := svc.On("UnsetParentGroupFromChannels", mock.Anything, tc.parentGroupID).Return(tc.err)
res, err := client.UnsetParentGroupFromChannels(context.Background(), &grpcChannelsV1.UnsetParentGroupFromChannelsReq{
ParentGroupId: tc.parentGroupID,
})
assert.True(t, errors.Contains(err, tc.err), fmt.Sprintf("%s: expected %s got %s", tc.desc, tc.err, err))
assert.Equal(t, &grpcChannelsV1.UnsetParentGroupFromChannelsRes{}, res)
svcCall.Unset()
})
}
}
func TestRetrieveEntity(t *testing.T) {
svc := new(mocks.Service)
server := startGRPCServer(svc, port)
defer server.GracefulStop()
authAddr := fmt.Sprintf("localhost:%d", port)
conn, _ := grpc.NewClient(authAddr, grpc.WithTransportCredentials(insecure.NewCredentials()))
client := grpcapi.NewClient(conn, time.Second)
cases := []struct {
desc string
id string
svcRes ch.Channel
resp *grpcCommonV1.RetrieveEntityRes
code codes.Code
err error
}{
{
desc: "retrieve entity successfully",
id: validID,
svcRes: validChannel,
resp: &grpcCommonV1.RetrieveEntityRes{
Entity: &grpcCommonV1.EntityBasic{
Id: validChannel.ID,
DomainId: validChannel.Domain,
ParentGroupId: validChannel.ParentGroup,
Status: uint32(validChannel.Status),
},
},
err: nil,
},
{
desc: "retrieve entity with error",
id: validID,
resp: &grpcCommonV1.RetrieveEntityRes{},
err: svcerr.ErrNotFound,
},
}
for _, tc := range cases {
t.Run(tc.desc, func(t *testing.T) {
svcCall := svc.On("RetrieveByID", mock.Anything, tc.id).Return(tc.svcRes, tc.err)
res, err := client.RetrieveEntity(context.Background(), &grpcCommonV1.RetrieveEntityReq{
Id: tc.id,
})
assert.True(t, errors.Contains(err, tc.err), fmt.Sprintf("%s: expected %s got %s", tc.desc, tc.err, err))
assert.Equal(t, tc.resp.Entity, res.Entity)
svcCall.Unset()
})
}
}
+25
View File
@@ -0,0 +1,25 @@
// Copyright (c) Abstract Machines
// SPDX-License-Identifier: Apache-2.0
package grpc
import "github.com/absmach/magistrala/pkg/connections"
type authorizeReq struct {
domainID string
channelID string
clientID string
clientType string
connType connections.ConnType
}
type removeClientConnectionsReq struct {
clientID string
}
type unsetParentGroupFromChannelsReq struct {
parentGroupID string
}
type retrieveEntityReq struct {
Id string
}
+21
View File
@@ -0,0 +1,21 @@
// Copyright (c) Abstract Machines
// SPDX-License-Identifier: Apache-2.0
package grpc
type authorizeRes struct {
authorized bool
}
type removeClientConnectionsRes struct{}
type unsetParentGroupFromChannelsRes struct{}
type channelBasic struct {
id string
domain string
parentGroup string
status uint8
}
type retrieveEntityRes channelBasic
+179
View File
@@ -0,0 +1,179 @@
// Copyright (c) Abstract Machines
// SPDX-License-Identifier: Apache-2.0
package grpc
import (
"context"
mgauth "github.com/absmach/magistrala/auth"
channels "github.com/absmach/magistrala/channels/private"
grpcChannelsV1 "github.com/absmach/magistrala/internal/grpc/channels/v1"
grpcCommonV1 "github.com/absmach/magistrala/internal/grpc/common/v1"
"github.com/absmach/magistrala/pkg/apiutil"
"github.com/absmach/magistrala/pkg/connections"
"github.com/absmach/magistrala/pkg/errors"
svcerr "github.com/absmach/magistrala/pkg/errors/service"
kitgrpc "github.com/go-kit/kit/transport/grpc"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
)
var _ grpcChannelsV1.ChannelsServiceServer = (*grpcServer)(nil)
type grpcServer struct {
grpcChannelsV1.UnimplementedChannelsServiceServer
authorize kitgrpc.Handler
removeClientConnections kitgrpc.Handler
unsetParentGroupFromChannels kitgrpc.Handler
retrieveEntity kitgrpc.Handler
}
// NewServer returns new AuthServiceServer instance.
func NewServer(svc channels.Service) grpcChannelsV1.ChannelsServiceServer {
return &grpcServer{
authorize: kitgrpc.NewServer(
authorizeEndpoint(svc),
decodeAuthorizeRequest,
encodeAuthorizeResponse,
),
removeClientConnections: kitgrpc.NewServer(
removeClientConnectionsEndpoint(svc),
decodeRemoveClientConnectionsRequest,
encodeRemoveClientConnectionsResponse,
),
unsetParentGroupFromChannels: kitgrpc.NewServer(
unsetParentGroupFromChannelsEndpoint(svc),
decodeUnsetParentGroupFromChannelsRequest,
encodeUnsetParentGroupFromChannelsResponse,
),
retrieveEntity: kitgrpc.NewServer(
retrieveEntityEndpoint(svc),
decodeRetrieveEntityRequest,
encodeRetrieveEntityResponse,
),
}
}
func (s *grpcServer) Authorize(ctx context.Context, req *grpcChannelsV1.AuthzReq) (*grpcChannelsV1.AuthzRes, error) {
_, res, err := s.authorize.ServeGRPC(ctx, req)
if err != nil {
return nil, encodeError(err)
}
return res.(*grpcChannelsV1.AuthzRes), nil
}
func decodeAuthorizeRequest(_ context.Context, grpcReq interface{}) (interface{}, error) {
req := grpcReq.(*grpcChannelsV1.AuthzReq)
connType := connections.ConnType(req.GetType())
if err := connections.CheckConnType(connType); err != nil {
return nil, err
}
return authorizeReq{
domainID: req.GetDomainId(),
clientID: req.GetClientId(),
clientType: req.GetClientType(),
channelID: req.GetChannelId(),
connType: connType,
}, nil
}
func encodeAuthorizeResponse(_ context.Context, grpcRes interface{}) (interface{}, error) {
res := grpcRes.(authorizeRes)
return &grpcChannelsV1.AuthzRes{Authorized: res.authorized}, nil
}
func (s *grpcServer) RemoveClientConnections(ctx context.Context, req *grpcChannelsV1.RemoveClientConnectionsReq) (*grpcChannelsV1.RemoveClientConnectionsRes, error) {
_, res, err := s.removeClientConnections.ServeGRPC(ctx, req)
if err != nil {
return nil, encodeError(err)
}
return res.(*grpcChannelsV1.RemoveClientConnectionsRes), nil
}
func decodeRemoveClientConnectionsRequest(_ context.Context, grpcReq interface{}) (interface{}, error) {
req := grpcReq.(*grpcChannelsV1.RemoveClientConnectionsReq)
return removeClientConnectionsReq{
clientID: req.GetClientId(),
}, nil
}
func encodeRemoveClientConnectionsResponse(_ context.Context, grpcRes interface{}) (interface{}, error) {
_ = grpcRes.(removeClientConnectionsRes)
return &grpcChannelsV1.RemoveClientConnectionsRes{}, nil
}
func (s *grpcServer) UnsetParentGroupFromChannels(ctx context.Context, req *grpcChannelsV1.UnsetParentGroupFromChannelsReq) (*grpcChannelsV1.UnsetParentGroupFromChannelsRes, error) {
_, res, err := s.unsetParentGroupFromChannels.ServeGRPC(ctx, req)
if err != nil {
return nil, encodeError(err)
}
return res.(*grpcChannelsV1.UnsetParentGroupFromChannelsRes), nil
}
func decodeUnsetParentGroupFromChannelsRequest(_ context.Context, grpcReq interface{}) (interface{}, error) {
req := grpcReq.(*grpcChannelsV1.UnsetParentGroupFromChannelsReq)
return unsetParentGroupFromChannelsReq{
parentGroupID: req.GetParentGroupId(),
}, nil
}
func encodeUnsetParentGroupFromChannelsResponse(_ context.Context, grpcRes interface{}) (interface{}, error) {
_ = grpcRes.(unsetParentGroupFromChannelsRes)
return &grpcChannelsV1.UnsetParentGroupFromChannelsRes{}, nil
}
func (s *grpcServer) RetrieveEntity(ctx context.Context, req *grpcCommonV1.RetrieveEntityReq) (*grpcCommonV1.RetrieveEntityRes, error) {
_, res, err := s.retrieveEntity.ServeGRPC(ctx, req)
if err != nil {
return nil, encodeError(err)
}
return res.(*grpcCommonV1.RetrieveEntityRes), nil
}
func decodeRetrieveEntityRequest(_ context.Context, grpcReq interface{}) (interface{}, error) {
req := grpcReq.(*grpcCommonV1.RetrieveEntityReq)
return retrieveEntityReq{
Id: req.GetId(),
}, nil
}
func encodeRetrieveEntityResponse(_ context.Context, grpcRes interface{}) (interface{}, error) {
res := grpcRes.(retrieveEntityRes)
return &grpcCommonV1.RetrieveEntityRes{
Entity: &grpcCommonV1.EntityBasic{
Id: res.id,
DomainId: res.domain,
ParentGroupId: res.parentGroup,
Status: uint32(res.status),
},
}, nil
}
func encodeError(err error) error {
switch {
case errors.Contains(err, nil):
return nil
case errors.Contains(err, errors.ErrMalformedEntity),
err == apiutil.ErrInvalidAuthKey,
err == apiutil.ErrMissingID,
err == apiutil.ErrMissingMemberType,
err == apiutil.ErrMissingPolicySub,
err == apiutil.ErrMissingPolicyObj,
err == apiutil.ErrMalformedPolicyAct:
return status.Error(codes.InvalidArgument, err.Error())
case errors.Contains(err, svcerr.ErrAuthentication),
errors.Contains(err, mgauth.ErrKeyExpired),
err == apiutil.ErrMissingEmail,
err == apiutil.ErrBearerToken:
return status.Error(codes.Unauthenticated, err.Error())
case errors.Contains(err, svcerr.ErrAuthorization):
return status.Error(codes.PermissionDenied, err.Error())
default:
return status.Error(codes.Internal, err.Error())
}
}
+229
View File
@@ -0,0 +1,229 @@
// Copyright (c) Abstract Machines
// SPDX-License-Identifier: Apache-2.0
package http
import (
"context"
"encoding/json"
"net/http"
"strings"
mgclients "github.com/absmach/magistrala/clients"
"github.com/absmach/magistrala/internal/api"
"github.com/absmach/magistrala/pkg/apiutil"
"github.com/absmach/magistrala/pkg/errors"
"github.com/go-chi/chi/v5"
)
func decodeViewChannel(_ context.Context, r *http.Request) (interface{}, error) {
req := viewChannelReq{
id: chi.URLParam(r, "channelID"),
}
return req, nil
}
func decodeCreateChannelReq(_ 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 := createChannelReq{}
if err := json.NewDecoder(r.Body).Decode(&req.Channel); err != nil {
return nil, errors.Wrap(apiutil.ErrValidation, errors.Wrap(errors.ErrMalformedEntity, err))
}
return req, nil
}
func decodeCreateChannelsReq(_ 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 := createChannelsReq{}
if err := json.NewDecoder(r.Body).Decode(&req.Channels); err != nil {
return nil, errors.Wrap(apiutil.ErrValidation, errors.Wrap(errors.ErrMalformedEntity, err))
}
return req, nil
}
func decodeListChannels(_ 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)
}
o, err := apiutil.ReadNumQuery[uint64](r, api.OffsetKey, api.DefOffset)
if err != nil {
return nil, errors.Wrap(apiutil.ErrValidation, err)
}
l, err := apiutil.ReadNumQuery[uint64](r, api.LimitKey, api.DefLimit)
if err != nil {
return nil, errors.Wrap(apiutil.ErrValidation, err)
}
m, err := apiutil.ReadMetadataQuery(r, api.MetadataKey, nil)
if err != nil {
return nil, errors.Wrap(apiutil.ErrValidation, err)
}
n, err := apiutil.ReadStringQuery(r, api.NameKey, "")
if err != nil {
return nil, errors.Wrap(apiutil.ErrValidation, err)
}
t, err := apiutil.ReadStringQuery(r, api.TagKey, "")
if err != nil {
return nil, errors.Wrap(apiutil.ErrValidation, err)
}
id, err := apiutil.ReadStringQuery(r, api.IDOrder, "")
if err != nil {
return nil, errors.Wrap(apiutil.ErrValidation, err)
}
p, err := apiutil.ReadStringQuery(r, api.PermissionKey, api.DefPermission)
if err != nil {
return nil, errors.Wrap(apiutil.ErrValidation, err)
}
lp, err := apiutil.ReadBoolQuery(r, api.ListPerms, api.DefListPerms)
if err != nil {
return nil, errors.Wrap(apiutil.ErrValidation, err)
}
st, err := mgclients.ToStatus(s)
if err != nil {
return nil, errors.Wrap(apiutil.ErrValidation, err)
}
req := listChannelsReq{
status: st,
offset: o,
limit: l,
metadata: m,
name: n,
tag: t,
permission: p,
listPerms: lp,
userID: chi.URLParam(r, "userID"),
id: id,
}
return req, nil
}
func decodeUpdateChannel(_ 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 := updateChannelReq{
id: chi.URLParam(r, "channelID"),
}
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
return nil, errors.Wrap(apiutil.ErrValidation, errors.Wrap(errors.ErrMalformedEntity, err))
}
return req, nil
}
func decodeUpdateChannelTags(_ 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 := updateChannelTagsReq{
id: chi.URLParam(r, "channelID"),
}
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
return nil, errors.Wrap(apiutil.ErrValidation, errors.Wrap(errors.ErrMalformedEntity, err))
}
return req, nil
}
func decodeSetChannelParentGroupStatus(_ 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 := setChannelParentGroupReq{
id: chi.URLParam(r, "channelID"),
}
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
return nil, errors.Wrap(apiutil.ErrValidation, errors.Wrap(errors.ErrMalformedEntity, err))
}
return req, nil
}
func decodeRemoveChannelParentGroupStatus(_ context.Context, r *http.Request) (interface{}, error) {
req := removeChannelParentGroupReq{
id: chi.URLParam(r, "channelID"),
}
return req, nil
}
func decodeChangeChannelStatus(_ context.Context, r *http.Request) (interface{}, error) {
req := changeChannelStatusReq{
id: chi.URLParam(r, "channelID"),
}
return req, nil
}
func decodeDeleteChannelReq(_ context.Context, r *http.Request) (interface{}, error) {
req := deleteChannelReq{
id: chi.URLParam(r, "channelID"),
}
return req, nil
}
func decodeConnectChannelClientRequest(_ 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 := connectChannelClientsRequest{
channelID: chi.URLParam(r, "channelID"),
}
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
return nil, errors.Wrap(apiutil.ErrValidation, errors.Wrap(errors.ErrMalformedEntity, err))
}
return req, nil
}
func decodeDisconnectChannelClientsRequest(_ 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 := disconnectChannelClientsRequest{
channelID: chi.URLParam(r, "channelID"),
}
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
return nil, errors.Wrap(apiutil.ErrValidation, errors.Wrap(errors.ErrMalformedEntity, err))
}
return req, nil
}
func decodeConnectRequest(_ 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 := connectRequest{}
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
return nil, errors.Wrap(apiutil.ErrValidation, errors.Wrap(errors.ErrMalformedEntity, err))
}
return req, nil
}
func decodeDisconnectRequest(_ 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 := disconnectRequest{}
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
return nil, errors.Wrap(apiutil.ErrValidation, errors.Wrap(errors.ErrMalformedEntity, err))
}
return req, nil
}
File diff suppressed because it is too large Load Diff
+369
View File
@@ -0,0 +1,369 @@
// Copyright (c) Abstract Machines
// SPDX-License-Identifier: Apache-2.0
package http
import (
"context"
"github.com/absmach/magistrala/channels"
"github.com/absmach/magistrala/internal/api"
"github.com/absmach/magistrala/pkg/apiutil"
"github.com/absmach/magistrala/pkg/authn"
"github.com/absmach/magistrala/pkg/errors"
svcerr "github.com/absmach/magistrala/pkg/errors/service"
"github.com/go-kit/kit/endpoint"
)
func createChannelEndpoint(svc channels.Service) endpoint.Endpoint {
return func(ctx context.Context, request interface{}) (interface{}, error) {
req := request.(createChannelReq)
if err := req.validate(); err != nil {
return nil, errors.Wrap(apiutil.ErrValidation, err)
}
session, ok := ctx.Value(api.SessionKey).(authn.Session)
if !ok {
return nil, svcerr.ErrAuthentication
}
channels, err := svc.CreateChannels(ctx, session, req.Channel)
if err != nil {
return nil, err
}
return createChannelRes{
Channel: channels[0],
created: true,
}, nil
}
}
func createChannelsEndpoint(svc channels.Service) endpoint.Endpoint {
return func(ctx context.Context, request interface{}) (interface{}, error) {
req := request.(createChannelsReq)
if err := req.validate(); err != nil {
return nil, errors.Wrap(apiutil.ErrValidation, err)
}
session, ok := ctx.Value(api.SessionKey).(authn.Session)
if !ok {
return nil, svcerr.ErrAuthentication
}
channels, err := svc.CreateChannels(ctx, session, req.Channels...)
if err != nil {
return nil, err
}
res := channelsPageRes{
pageRes: pageRes{
Total: uint64(len(channels)),
},
Channels: []viewChannelRes{},
}
for _, c := range channels {
res.Channels = append(res.Channels, viewChannelRes{Channel: c})
}
return res, nil
}
}
func viewChannelEndpoint(svc channels.Service) endpoint.Endpoint {
return func(ctx context.Context, request interface{}) (interface{}, error) {
req := request.(viewChannelReq)
if err := req.validate(); err != nil {
return nil, errors.Wrap(apiutil.ErrValidation, err)
}
session, ok := ctx.Value(api.SessionKey).(authn.Session)
if !ok {
return nil, svcerr.ErrAuthentication
}
c, err := svc.ViewChannel(ctx, session, req.id)
if err != nil {
return nil, err
}
return viewChannelRes{Channel: c}, nil
}
}
func listChannelsEndpoint(svc channels.Service) endpoint.Endpoint {
return func(ctx context.Context, request interface{}) (interface{}, error) {
req := request.(listChannelsReq)
if err := req.validate(); err != nil {
return nil, errors.Wrap(apiutil.ErrValidation, err)
}
session, ok := ctx.Value(api.SessionKey).(authn.Session)
if !ok {
return nil, svcerr.ErrAuthentication
}
pm := channels.PageMetadata{
Status: req.status,
Offset: req.offset,
Limit: req.limit,
Name: req.name,
Tag: req.tag,
Permission: req.permission,
Metadata: req.metadata,
ListPerms: req.listPerms,
Id: req.id,
}
page, err := svc.ListChannels(ctx, session, pm)
if err != nil {
return nil, err
}
res := channelsPageRes{
pageRes: pageRes{
Total: page.Total,
Offset: page.Offset,
Limit: page.Limit,
},
Channels: []viewChannelRes{},
}
for _, c := range page.Channels {
res.Channels = append(res.Channels, viewChannelRes{Channel: c})
}
return res, nil
}
}
func updateChannelEndpoint(svc channels.Service) endpoint.Endpoint {
return func(ctx context.Context, request interface{}) (interface{}, error) {
req := request.(updateChannelReq)
if err := req.validate(); err != nil {
return nil, errors.Wrap(apiutil.ErrValidation, err)
}
session, ok := ctx.Value(api.SessionKey).(authn.Session)
if !ok {
return nil, svcerr.ErrAuthentication
}
ch := channels.Channel{
ID: req.id,
Name: req.Name,
Metadata: req.Metadata,
}
ch, err := svc.UpdateChannel(ctx, session, ch)
if err != nil {
return nil, err
}
return updateChannelRes{Channel: ch}, nil
}
}
func updateChannelTagsEndpoint(svc channels.Service) endpoint.Endpoint {
return func(ctx context.Context, request interface{}) (interface{}, error) {
req := request.(updateChannelTagsReq)
if err := req.validate(); err != nil {
return nil, errors.Wrap(apiutil.ErrValidation, err)
}
session, ok := ctx.Value(api.SessionKey).(authn.Session)
if !ok {
return nil, svcerr.ErrAuthentication
}
ch := channels.Channel{
ID: req.id,
Tags: req.Tags,
}
ch, err := svc.UpdateChannelTags(ctx, session, ch)
if err != nil {
return nil, err
}
return updateChannelRes{Channel: ch}, nil
}
}
func setChannelParentGroupEndpoint(svc channels.Service) endpoint.Endpoint {
return func(ctx context.Context, request interface{}) (interface{}, error) {
req := request.(setChannelParentGroupReq)
if err := req.validate(); err != nil {
return nil, errors.Wrap(apiutil.ErrValidation, err)
}
session, ok := ctx.Value(api.SessionKey).(authn.Session)
if !ok {
return nil, svcerr.ErrAuthentication
}
if err := svc.SetParentGroup(ctx, session, req.ParentGroupID, req.id); err != nil {
return nil, err
}
return setChannelParentGroupRes{}, nil
}
}
func removeChannelParentGroupEndpoint(svc channels.Service) endpoint.Endpoint {
return func(ctx context.Context, request interface{}) (interface{}, error) {
req := request.(removeChannelParentGroupReq)
if err := req.validate(); err != nil {
return nil, errors.Wrap(apiutil.ErrValidation, err)
}
session, ok := ctx.Value(api.SessionKey).(authn.Session)
if !ok {
return nil, svcerr.ErrAuthentication
}
if err := svc.RemoveParentGroup(ctx, session, req.id); err != nil {
return nil, err
}
return removeChannelParentGroupRes{}, nil
}
}
func enableChannelEndpoint(svc channels.Service) endpoint.Endpoint {
return func(ctx context.Context, request interface{}) (interface{}, error) {
req := request.(changeChannelStatusReq)
if err := req.validate(); err != nil {
return nil, errors.Wrap(apiutil.ErrValidation, err)
}
session, ok := ctx.Value(api.SessionKey).(authn.Session)
if !ok {
return nil, svcerr.ErrAuthentication
}
ch, err := svc.EnableChannel(ctx, session, req.id)
if err != nil {
return nil, err
}
return changeChannelStatusRes{Channel: ch}, nil
}
}
func disableChannelEndpoint(svc channels.Service) endpoint.Endpoint {
return func(ctx context.Context, request interface{}) (interface{}, error) {
req := request.(changeChannelStatusReq)
if err := req.validate(); err != nil {
return nil, errors.Wrap(apiutil.ErrValidation, err)
}
session, ok := ctx.Value(api.SessionKey).(authn.Session)
if !ok {
return nil, svcerr.ErrAuthentication
}
ch, err := svc.DisableChannel(ctx, session, req.id)
if err != nil {
return nil, err
}
return changeChannelStatusRes{Channel: ch}, nil
}
}
func connectChannelClientEndpoint(svc channels.Service) endpoint.Endpoint {
return func(ctx context.Context, request interface{}) (interface{}, error) {
req := request.(connectChannelClientsRequest)
if err := req.validate(); err != nil {
return nil, errors.Wrap(apiutil.ErrValidation, err)
}
session, ok := ctx.Value(api.SessionKey).(authn.Session)
if !ok {
return nil, svcerr.ErrAuthentication
}
if err := svc.Connect(ctx, session, []string{req.channelID}, req.ClientIDs, req.Types); err != nil {
return nil, err
}
return connectChannelClientsRes{}, nil
}
}
func disconnectChannelClientsEndpoint(svc channels.Service) endpoint.Endpoint {
return func(ctx context.Context, request interface{}) (interface{}, error) {
req := request.(disconnectChannelClientsRequest)
if err := req.validate(); err != nil {
return nil, errors.Wrap(apiutil.ErrValidation, err)
}
session, ok := ctx.Value(api.SessionKey).(authn.Session)
if !ok {
return nil, svcerr.ErrAuthentication
}
if err := svc.Disconnect(ctx, session, []string{req.channelID}, req.ClientIds, req.Types); err != nil {
return nil, err
}
return disconnectChannelClientsRes{}, nil
}
}
func connectEndpoint(svc channels.Service) endpoint.Endpoint {
return func(ctx context.Context, request interface{}) (interface{}, error) {
req := request.(connectRequest)
if err := req.validate(); err != nil {
return nil, errors.Wrap(apiutil.ErrValidation, err)
}
session, ok := ctx.Value(api.SessionKey).(authn.Session)
if !ok {
return nil, svcerr.ErrAuthentication
}
if err := svc.Connect(ctx, session, req.ChannelIds, req.ClientIds, req.Types); err != nil {
return nil, err
}
return connectRes{}, nil
}
}
func disconnectEndpoint(svc channels.Service) endpoint.Endpoint {
return func(ctx context.Context, request interface{}) (interface{}, error) {
req := request.(disconnectRequest)
if err := req.validate(); err != nil {
return nil, errors.Wrap(apiutil.ErrValidation, err)
}
session, ok := ctx.Value(api.SessionKey).(authn.Session)
if !ok {
return nil, svcerr.ErrAuthentication
}
if err := svc.Disconnect(ctx, session, req.ChannelIds, req.ClientIds, req.Types); err != nil {
return nil, err
}
return disconnectRes{}, nil
}
}
func deleteChannelEndpoint(svc channels.Service) endpoint.Endpoint {
return func(ctx context.Context, request interface{}) (interface{}, error) {
req := request.(deleteChannelReq)
if err := req.validate(); err != nil {
return nil, errors.Wrap(apiutil.ErrValidation, err)
}
session, ok := ctx.Value(api.SessionKey).(authn.Session)
if !ok {
return nil, svcerr.ErrAuthentication
}
if err := svc.RemoveChannel(ctx, session, req.id); err != nil {
return nil, err
}
return deleteChannelRes{}, nil
}
}
+303
View File
@@ -0,0 +1,303 @@
// Copyright (c) Abstract Machines
// SPDX-License-Identifier: Apache-2.0
package http
import (
"strings"
"github.com/absmach/magistrala/channels"
mgclients "github.com/absmach/magistrala/clients"
"github.com/absmach/magistrala/internal/api"
"github.com/absmach/magistrala/pkg/apiutil"
"github.com/absmach/magistrala/pkg/connections"
)
type createChannelReq struct {
Channel channels.Channel
}
func (req createChannelReq) validate() error {
if len(req.Channel.Name) > api.MaxNameSize {
return apiutil.ErrNameSize
}
if req.Channel.ID != "" {
if strings.TrimSpace(req.Channel.ID) == "" {
return apiutil.ErrMissingChannelID
}
}
return nil
}
type createChannelsReq struct {
Channels []channels.Channel
}
func (req createChannelsReq) validate() error {
if len(req.Channels) == 0 {
return apiutil.ErrEmptyList
}
for _, channel := range req.Channels {
if channel.ID != "" {
if strings.TrimSpace(channel.ID) == "" {
return apiutil.ErrMissingChannelID
}
}
if len(channel.Name) > api.MaxNameSize {
return apiutil.ErrNameSize
}
}
return nil
}
type viewChannelReq struct {
id string
}
func (req viewChannelReq) validate() error {
if req.id == "" {
return apiutil.ErrMissingID
}
return nil
}
type listChannelsReq struct {
status mgclients.Status
offset uint64
limit uint64
name string
tag string
permission string
visibility string
userID string
listPerms bool
metadata mgclients.Metadata
id string
}
func (req listChannelsReq) validate() error {
if req.limit > api.MaxLimitSize || req.limit < 1 {
return apiutil.ErrLimitSize
}
if req.visibility != "" &&
req.visibility != api.AllVisibility &&
req.visibility != api.MyVisibility &&
req.visibility != api.SharedVisibility {
return apiutil.ErrInvalidVisibilityType
}
if len(req.name) > api.MaxNameSize {
return apiutil.ErrNameSize
}
return nil
}
type updateChannelReq struct {
id string
Name string `json:"name,omitempty"`
Metadata map[string]interface{} `json:"metadata,omitempty"`
Tags []string `json:"tags,omitempty"`
}
func (req updateChannelReq) validate() error {
if req.id == "" {
return apiutil.ErrMissingID
}
if len(req.Name) > api.MaxNameSize {
return apiutil.ErrNameSize
}
return nil
}
type updateChannelTagsReq struct {
id string
Tags []string `json:"tags,omitempty"`
}
func (req updateChannelTagsReq) validate() error {
if req.id == "" {
return apiutil.ErrMissingID
}
return nil
}
type setChannelParentGroupReq struct {
id string
ParentGroupID string `json:"parent_group_id"`
}
func (req setChannelParentGroupReq) validate() error {
if req.id == "" {
return apiutil.ErrMissingID
}
if req.ParentGroupID == "" {
return apiutil.ErrMissingParentGroupID
}
return nil
}
type removeChannelParentGroupReq struct {
id string
}
func (req removeChannelParentGroupReq) validate() error {
if req.id == "" {
return apiutil.ErrMissingID
}
return nil
}
type changeChannelStatusReq struct {
id string
}
func (req changeChannelStatusReq) validate() error {
if req.id == "" {
return apiutil.ErrMissingID
}
return nil
}
type connectChannelClientsRequest struct {
channelID string
ClientIDs []string `json:"client_ids,omitempty"`
Types []connections.ConnType `json:"types,omitempty"`
}
func (req *connectChannelClientsRequest) validate() error {
if req.channelID == "" || strings.TrimSpace(req.channelID) == "" {
return apiutil.ErrMissingID
}
if len(req.ClientIDs) == 0 {
return apiutil.ErrMissingID
}
for _, tid := range req.ClientIDs {
if err := api.ValidateUUID(tid); err != nil {
return err
}
}
if len(req.Types) == 0 {
return apiutil.ErrMissingConnectionType
}
return nil
}
type disconnectChannelClientsRequest struct {
channelID string
ClientIds []string `json:"client_ids,omitempty"`
Types []connections.ConnType `json:"types,omitempty"`
}
func (req *disconnectChannelClientsRequest) validate() error {
if req.channelID == "" {
return apiutil.ErrMissingID
}
if err := api.ValidateUUID(req.channelID); err != nil {
return err
}
if len(req.ClientIds) == 0 {
return apiutil.ErrMissingID
}
for _, tid := range req.ClientIds {
if err := api.ValidateUUID(tid); err != nil {
return err
}
}
if len(req.Types) == 0 {
return apiutil.ErrMissingConnectionType
}
return nil
}
type connectRequest struct {
ChannelIds []string `json:"channel_ids,omitempty"`
ClientIds []string `json:"client_ids,omitempty"`
Types []connections.ConnType `json:"types,omitempty"`
}
func (req *connectRequest) validate() error {
if len(req.ChannelIds) == 0 {
return apiutil.ErrMissingID
}
for _, cid := range req.ChannelIds {
if strings.TrimSpace(cid) == "" {
return apiutil.ErrMissingChannelID
}
}
if len(req.ClientIds) == 0 {
return apiutil.ErrMissingID
}
for _, tid := range req.ClientIds {
if strings.TrimSpace(tid) == "" {
return apiutil.ErrMissingChannelID
}
}
if len(req.Types) == 0 {
return apiutil.ErrMissingConnectionType
}
return nil
}
type disconnectRequest struct {
ChannelIds []string `json:"channel_ids,omitempty"`
ClientIds []string `json:"client_ids,omitempty"`
Types []connections.ConnType `json:"types,omitempty"`
}
func (req *disconnectRequest) validate() error {
if len(req.ChannelIds) == 0 {
return apiutil.ErrMissingID
}
for _, cid := range req.ChannelIds {
if err := api.ValidateUUID(cid); err != nil {
return err
}
}
if len(req.ClientIds) == 0 {
return apiutil.ErrMissingID
}
for _, tid := range req.ClientIds {
if err := api.ValidateUUID(tid); err != nil {
return err
}
}
if len(req.Types) == 0 {
return apiutil.ErrMissingConnectionType
}
return nil
}
type deleteChannelReq struct {
id string
}
func (req deleteChannelReq) validate() error {
if req.id == "" {
return apiutil.ErrMissingID
}
return nil
}
+613
View File
@@ -0,0 +1,613 @@
// Copyright (c) Abstract Machines
// SPDX-License-Identifier: Apache-2.0
package http
import (
"fmt"
"strings"
"testing"
"github.com/absmach/magistrala/channels"
"github.com/absmach/magistrala/internal/api"
"github.com/absmach/magistrala/internal/testsutil"
"github.com/absmach/magistrala/pkg/apiutil"
"github.com/absmach/magistrala/pkg/connections"
"github.com/stretchr/testify/assert"
)
func TestCreateChannelReqValidation(t *testing.T) {
cases := []struct {
desc string
req createChannelReq
err error
}{
{
desc: "valid request",
req: createChannelReq{
Channel: channels.Channel{
Name: valid,
},
},
err: nil,
},
{
desc: "long name",
req: createChannelReq{
Channel: channels.Channel{
Name: strings.Repeat("a", api.MaxNameSize+1),
},
},
err: apiutil.ErrNameSize,
},
{
desc: "missing channel ID",
req: createChannelReq{
Channel: channels.Channel{
ID: " ",
},
},
err: apiutil.ErrMissingChannelID,
},
}
for _, tc := range cases {
err := tc.req.validate()
assert.Equal(t, tc.err, err, fmt.Sprintf("%s: expected %s got %s\n", tc.desc, tc.err, err))
}
}
func TestCreateChannelsReqValidation(t *testing.T) {
cases := []struct {
desc string
req createChannelsReq
err error
}{
{
desc: "valid request",
req: createChannelsReq{
Channels: []channels.Channel{
{
Name: valid,
},
},
},
err: nil,
},
{
desc: "long name",
req: createChannelsReq{
Channels: []channels.Channel{
{
Name: strings.Repeat("a", api.MaxNameSize+1),
},
},
},
err: apiutil.ErrNameSize,
},
{
desc: "missing channel ID",
req: createChannelsReq{
Channels: []channels.Channel{
{
ID: " ",
},
},
},
err: apiutil.ErrMissingChannelID,
},
{
desc: "empty list",
req: createChannelsReq{
Channels: []channels.Channel{},
},
err: apiutil.ErrEmptyList,
},
}
for _, tc := range cases {
err := tc.req.validate()
assert.Equal(t, tc.err, err, fmt.Sprintf("%s: expected %s got %s\n", tc.desc, tc.err, err))
}
}
func TestViewChannelReqValidation(t *testing.T) {
cases := []struct {
desc string
req viewChannelReq
err error
}{
{
desc: "valid request",
req: viewChannelReq{
id: valid,
},
err: nil,
},
{
desc: "missing ID",
req: viewChannelReq{
id: "",
},
err: apiutil.ErrMissingID,
},
}
for _, tc := range cases {
err := tc.req.validate()
assert.Equal(t, tc.err, err, fmt.Sprintf("%s: expected %s got %s\n", tc.desc, tc.err, err))
}
}
func TestListChannelsReqValidation(t *testing.T) {
cases := []struct {
desc string
req listChannelsReq
err error
}{
{
desc: "valid request",
req: listChannelsReq{
limit: 10,
},
err: nil,
},
{
desc: "limit is 0",
req: listChannelsReq{
limit: 0,
},
err: apiutil.ErrLimitSize,
},
{
desc: "limit is greater than max limit",
req: listChannelsReq{
limit: api.MaxLimitSize + 1,
},
err: apiutil.ErrLimitSize,
},
{
desc: "name is too long",
req: listChannelsReq{
limit: 10,
name: strings.Repeat("a", api.MaxNameSize+1),
},
err: apiutil.ErrNameSize,
},
{
desc: "invalid visibility",
req: listChannelsReq{
limit: 10,
visibility: "invalid",
},
err: apiutil.ErrInvalidVisibilityType,
},
}
for _, tc := range cases {
err := tc.req.validate()
assert.Equal(t, tc.err, err, fmt.Sprintf("%s: expected %s got %s\n", tc.desc, tc.err, err))
}
}
func TestUpdateChannelReqValidate(t *testing.T) {
cases := []struct {
desc string
req updateChannelReq
err error
}{
{
desc: "valid request",
req: updateChannelReq{
id: valid,
},
err: nil,
},
{
desc: "missing ID",
req: updateChannelReq{
id: "",
},
err: apiutil.ErrMissingID,
},
{
desc: "name is too long",
req: updateChannelReq{
id: valid,
Name: strings.Repeat("a", api.MaxNameSize+1),
},
err: apiutil.ErrNameSize,
},
}
for _, tc := range cases {
err := tc.req.validate()
assert.Equal(t, tc.err, err, fmt.Sprintf("%s: expected %s got %s\n", tc.desc, tc.err, err))
}
}
func TestUpdateChannelTagsReqValidate(t *testing.T) {
cases := []struct {
desc string
req updateChannelTagsReq
err error
}{
{
desc: "valid request",
req: updateChannelTagsReq{
id: valid,
Tags: []string{"tag1", "tag2"},
},
err: nil,
},
{
desc: "missing ID",
req: updateChannelTagsReq{
id: "",
Tags: []string{"tag1", "tag2"},
},
err: apiutil.ErrMissingID,
},
}
for _, tc := range cases {
err := tc.req.validate()
assert.Equal(t, tc.err, err, fmt.Sprintf("%s: expected %s got %s\n", tc.desc, tc.err, err))
}
}
func TestSetChannelsParentGroupReqValidate(t *testing.T) {
cases := []struct {
desc string
req setChannelParentGroupReq
err error
}{
{
desc: "valid request",
req: setChannelParentGroupReq{
id: valid,
ParentGroupID: valid,
},
err: nil,
},
{
desc: "missing ID",
req: setChannelParentGroupReq{
id: "",
ParentGroupID: valid,
},
err: apiutil.ErrMissingID,
},
{
desc: "missing parent group ID",
req: setChannelParentGroupReq{
id: valid,
ParentGroupID: "",
},
err: apiutil.ErrMissingParentGroupID,
},
}
for _, tc := range cases {
err := tc.req.validate()
assert.Equal(t, tc.err, err, fmt.Sprintf("%s: expected %s got %s\n", tc.desc, tc.err, err))
}
}
func TestRemoveChannelParentGroupReqValidate(t *testing.T) {
cases := []struct {
desc string
req removeChannelParentGroupReq
err error
}{
{
desc: "valid request",
req: removeChannelParentGroupReq{
id: valid,
},
err: nil,
},
{
desc: "missing ID",
req: removeChannelParentGroupReq{
id: "",
},
err: apiutil.ErrMissingID,
},
}
for _, tc := range cases {
err := tc.req.validate()
assert.Equal(t, tc.err, err, fmt.Sprintf("%s: expected %s got %s\n", tc.desc, tc.err, err))
}
}
func TestChangeChannelStatusReqValidate(t *testing.T) {
cases := []struct {
desc string
req changeChannelStatusReq
err error
}{
{
desc: "valid request",
req: changeChannelStatusReq{
id: valid,
},
err: nil,
},
{
desc: "missing ID",
req: changeChannelStatusReq{
id: "",
},
err: apiutil.ErrMissingID,
},
}
for _, tc := range cases {
err := tc.req.validate()
assert.Equal(t, tc.err, err, fmt.Sprintf("%s: expected %s got %s\n", tc.desc, tc.err, err))
}
}
func TestConnectChannelClientsReqValidate(t *testing.T) {
cases := []struct {
desc string
req connectChannelClientsRequest
err error
}{
{
desc: "valid request",
req: connectChannelClientsRequest{
channelID: valid,
ClientIDs: []string{testsutil.GenerateUUID(t), testsutil.GenerateUUID(t)},
Types: []connections.ConnType{connections.Publish},
},
err: nil,
},
{
desc: "missing channel ID",
req: connectChannelClientsRequest{
channelID: "",
ClientIDs: []string{testsutil.GenerateUUID(t), testsutil.GenerateUUID(t)},
Types: []connections.ConnType{connections.Publish},
},
err: apiutil.ErrMissingID,
},
{
desc: "missing client IDs",
req: connectChannelClientsRequest{
channelID: valid,
ClientIDs: []string{},
Types: []connections.ConnType{connections.Publish},
},
err: apiutil.ErrMissingID,
},
{
desc: "missing connection types",
req: connectChannelClientsRequest{
channelID: valid,
ClientIDs: []string{testsutil.GenerateUUID(t), testsutil.GenerateUUID(t)},
Types: []connections.ConnType{},
},
err: apiutil.ErrMissingConnectionType,
},
{
desc: "invalid client ID",
req: connectChannelClientsRequest{
channelID: valid,
ClientIDs: []string{"client1", "invalid"},
Types: []connections.ConnType{connections.Publish},
},
err: apiutil.ErrInvalidIDFormat,
},
}
for _, tc := range cases {
err := tc.req.validate()
assert.Equal(t, tc.err, err, fmt.Sprintf("%s: expected %s got %s\n", tc.desc, tc.err, err))
}
}
func TestDisconnectChannelClientReqValidate(t *testing.T) {
cases := []struct {
desc string
req disconnectChannelClientsRequest
err error
}{
{
desc: "valid request",
req: disconnectChannelClientsRequest{
channelID: testsutil.GenerateUUID(t),
ClientIds: []string{testsutil.GenerateUUID(t), testsutil.GenerateUUID(t)},
Types: []connections.ConnType{connections.Publish},
},
err: nil,
},
{
desc: "missing channel ID",
req: disconnectChannelClientsRequest{
channelID: "",
ClientIds: []string{testsutil.GenerateUUID(t), testsutil.GenerateUUID(t)},
Types: []connections.ConnType{connections.Publish},
},
err: apiutil.ErrMissingID,
},
{
desc: "invalid channel ID",
req: disconnectChannelClientsRequest{
channelID: "invalid",
ClientIds: []string{testsutil.GenerateUUID(t), testsutil.GenerateUUID(t)},
Types: []connections.ConnType{connections.Publish},
},
err: apiutil.ErrInvalidIDFormat,
},
{
desc: "missing client IDs",
req: disconnectChannelClientsRequest{
channelID: testsutil.GenerateUUID(t),
ClientIds: []string{},
Types: []connections.ConnType{connections.Publish},
},
err: apiutil.ErrMissingID,
},
{
desc: "missing connection types",
req: disconnectChannelClientsRequest{
channelID: testsutil.GenerateUUID(t),
ClientIds: []string{testsutil.GenerateUUID(t), testsutil.GenerateUUID(t)},
Types: []connections.ConnType{},
},
err: apiutil.ErrMissingConnectionType,
},
{
desc: "invalid client ID",
req: disconnectChannelClientsRequest{
channelID: testsutil.GenerateUUID(t),
ClientIds: []string{"client1", "invalid"},
Types: []connections.ConnType{connections.Publish},
},
err: apiutil.ErrInvalidIDFormat,
},
}
for _, tc := range cases {
err := tc.req.validate()
assert.Equal(t, tc.err, err, fmt.Sprintf("%s: expected %s got %s\n", tc.desc, tc.err, err))
}
}
func TestConnectReqValidate(t *testing.T) {
cases := []struct {
desc string
req connectRequest
err error
}{
{
desc: "valid request",
req: connectRequest{
ChannelIds: []string{testsutil.GenerateUUID(t), testsutil.GenerateUUID(t)},
ClientIds: []string{testsutil.GenerateUUID(t), testsutil.GenerateUUID(t)},
Types: []connections.ConnType{connections.Publish},
},
err: nil,
},
{
desc: "missing channel IDs",
req: connectRequest{
ChannelIds: []string{},
ClientIds: []string{testsutil.GenerateUUID(t), testsutil.GenerateUUID(t)},
Types: []connections.ConnType{connections.Publish},
},
err: apiutil.ErrMissingID,
},
{
desc: "missing client IDs",
req: connectRequest{
ChannelIds: []string{testsutil.GenerateUUID(t), testsutil.GenerateUUID(t)},
ClientIds: []string{},
Types: []connections.ConnType{connections.Publish},
},
err: apiutil.ErrMissingID,
},
{
desc: "missing connection types",
req: connectRequest{
ChannelIds: []string{testsutil.GenerateUUID(t), testsutil.GenerateUUID(t)},
ClientIds: []string{testsutil.GenerateUUID(t), testsutil.GenerateUUID(t)},
Types: []connections.ConnType{},
},
err: apiutil.ErrMissingConnectionType,
},
}
for _, tc := range cases {
err := tc.req.validate()
assert.Equal(t, tc.err, err, fmt.Sprintf("%s: expected %s got %s\n", tc.desc, tc.err, err))
}
}
func TestDisconnectReqValidate(t *testing.T) {
cases := []struct {
desc string
req disconnectRequest
err error
}{
{
desc: "valid request",
req: disconnectRequest{
ChannelIds: []string{testsutil.GenerateUUID(t), testsutil.GenerateUUID(t)},
ClientIds: []string{testsutil.GenerateUUID(t), testsutil.GenerateUUID(t)},
Types: []connections.ConnType{connections.Publish},
},
err: nil,
},
{
desc: "missing channel IDs",
req: disconnectRequest{
ChannelIds: []string{},
ClientIds: []string{testsutil.GenerateUUID(t), testsutil.GenerateUUID(t)},
Types: []connections.ConnType{connections.Publish},
},
err: apiutil.ErrMissingID,
},
{
desc: "missing client IDs",
req: disconnectRequest{
ChannelIds: []string{testsutil.GenerateUUID(t), testsutil.GenerateUUID(t)},
ClientIds: []string{},
Types: []connections.ConnType{connections.Publish},
},
err: apiutil.ErrMissingID,
},
{
desc: "missing connection types",
req: disconnectRequest{
ChannelIds: []string{testsutil.GenerateUUID(t), testsutil.GenerateUUID(t)},
ClientIds: []string{testsutil.GenerateUUID(t), testsutil.GenerateUUID(t)},
Types: []connections.ConnType{},
},
err: apiutil.ErrMissingConnectionType,
},
{
desc: "invalid client ID",
req: disconnectRequest{
ChannelIds: []string{testsutil.GenerateUUID(t), testsutil.GenerateUUID(t)},
ClientIds: []string{"client1", "invalid"},
Types: []connections.ConnType{connections.Publish},
},
err: apiutil.ErrInvalidIDFormat,
},
{
desc: "invalid channel ID",
req: disconnectRequest{
ChannelIds: []string{"invalid", testsutil.GenerateUUID(t)},
ClientIds: []string{testsutil.GenerateUUID(t), testsutil.GenerateUUID(t)},
Types: []connections.ConnType{connections.Publish},
},
err: apiutil.ErrInvalidIDFormat,
},
}
for _, tc := range cases {
err := tc.req.validate()
assert.Equal(t, tc.err, err, fmt.Sprintf("%s: expected %s got %s\n", tc.desc, tc.err, err))
}
}
func TestDeleteChannelReqValidate(t *testing.T) {
cases := []struct {
desc string
req deleteChannelReq
err error
}{
{
desc: "valid request",
req: deleteChannelReq{
id: valid,
},
err: nil,
},
{
desc: "missing ID",
req: deleteChannelReq{
id: "",
},
err: apiutil.ErrMissingID,
},
}
for _, tc := range cases {
err := tc.req.validate()
assert.Equal(t, tc.err, err, fmt.Sprintf("%s: expected %s got %s\n", tc.desc, tc.err, err))
}
}
+221
View File
@@ -0,0 +1,221 @@
// Copyright (c) Abstract Machines
// SPDX-License-Identifier: Apache-2.0
package http
import (
"fmt"
"net/http"
"github.com/absmach/magistrala"
"github.com/absmach/magistrala/channels"
)
var (
_ magistrala.Response = (*createChannelRes)(nil)
_ magistrala.Response = (*viewChannelRes)(nil)
_ magistrala.Response = (*channelsPageRes)(nil)
_ magistrala.Response = (*updateChannelRes)(nil)
_ magistrala.Response = (*deleteChannelRes)(nil)
_ magistrala.Response = (*connectChannelClientsRes)(nil)
_ magistrala.Response = (*disconnectChannelClientsRes)(nil)
_ magistrala.Response = (*connectRes)(nil)
_ magistrala.Response = (*disconnectRes)(nil)
_ magistrala.Response = (*changeChannelStatusRes)(nil)
)
type pageRes struct {
Limit uint64 `json:"limit,omitempty"`
Offset uint64 `json:"offset"`
Total uint64 `json:"total"`
}
type createChannelRes struct {
channels.Channel
created bool
}
func (res createChannelRes) Code() int {
if res.created {
return http.StatusCreated
}
return http.StatusOK
}
func (res createChannelRes) Headers() map[string]string {
if res.created {
return map[string]string{
"Location": fmt.Sprintf("/channels/%s", res.ID),
}
}
return map[string]string{}
}
func (res createChannelRes) Empty() bool {
return false
}
type viewChannelRes struct {
channels.Channel
}
func (res viewChannelRes) Code() int {
return http.StatusOK
}
func (res viewChannelRes) Headers() map[string]string {
return map[string]string{}
}
func (res viewChannelRes) Empty() bool {
return false
}
type channelsPageRes struct {
pageRes
Channels []viewChannelRes `json:"channels"`
}
func (res channelsPageRes) Code() int {
return http.StatusOK
}
func (res channelsPageRes) Headers() map[string]string {
return map[string]string{}
}
func (res channelsPageRes) Empty() bool {
return false
}
type changeChannelStatusRes struct {
channels.Channel
}
func (res changeChannelStatusRes) Code() int {
return http.StatusOK
}
func (res changeChannelStatusRes) Headers() map[string]string {
return map[string]string{}
}
func (res changeChannelStatusRes) Empty() bool {
return false
}
type updateChannelRes struct {
channels.Channel
}
func (res updateChannelRes) Code() int {
return http.StatusOK
}
func (res updateChannelRes) Headers() map[string]string {
return map[string]string{}
}
func (res updateChannelRes) Empty() bool {
return false
}
type setChannelParentGroupRes struct{}
func (res setChannelParentGroupRes) Code() int {
return http.StatusAccepted
}
func (res setChannelParentGroupRes) Headers() map[string]string {
return map[string]string{}
}
func (res setChannelParentGroupRes) Empty() bool {
return true
}
type removeChannelParentGroupRes struct{}
func (res removeChannelParentGroupRes) Code() int {
return http.StatusNoContent
}
func (res removeChannelParentGroupRes) Headers() map[string]string {
return map[string]string{}
}
func (res removeChannelParentGroupRes) Empty() bool {
return true
}
type deleteChannelRes struct{}
func (res deleteChannelRes) Code() int {
return http.StatusNoContent
}
func (res deleteChannelRes) Headers() map[string]string {
return map[string]string{}
}
func (res deleteChannelRes) Empty() bool {
return true
}
type connectChannelClientsRes struct{}
func (res connectChannelClientsRes) Code() int {
return http.StatusCreated
}
func (res connectChannelClientsRes) Headers() map[string]string {
return map[string]string{}
}
func (res connectChannelClientsRes) Empty() bool {
return true
}
type disconnectChannelClientsRes struct{}
func (res disconnectChannelClientsRes) Code() int {
return http.StatusNoContent
}
func (res disconnectChannelClientsRes) Headers() map[string]string {
return map[string]string{}
}
func (res disconnectChannelClientsRes) Empty() bool {
return true
}
type connectRes struct{}
func (res connectRes) Code() int {
return http.StatusCreated
}
func (res connectRes) Headers() map[string]string {
return map[string]string{}
}
func (res connectRes) Empty() bool {
return true
}
type disconnectRes struct{}
func (res disconnectRes) Code() int {
return http.StatusNoContent
}
func (res disconnectRes) Headers() map[string]string {
return map[string]string{}
}
func (res disconnectRes) Empty() bool {
return true
}
+140
View File
@@ -0,0 +1,140 @@
// Copyright (c) Abstract Machines
// SPDX-License-Identifier: Apache-2.0
package http
import (
"log/slog"
"github.com/absmach/magistrala"
"github.com/absmach/magistrala/channels"
"github.com/absmach/magistrala/internal/api"
"github.com/absmach/magistrala/pkg/apiutil"
mgauthn "github.com/absmach/magistrala/pkg/authn"
"github.com/go-chi/chi/v5"
kithttp "github.com/go-kit/kit/transport/http"
"github.com/prometheus/client_golang/prometheus/promhttp"
"go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp"
)
// MakeHandler returns a HTTP handler for Channels API endpoints.
func MakeHandler(svc channels.Service, authn mgauthn.Authentication, mux *chi.Mux, logger *slog.Logger, instanceID string) *chi.Mux {
opts := []kithttp.ServerOption{
kithttp.ServerErrorEncoder(apiutil.LoggingErrorEncoder(logger, api.EncodeError)),
}
mux.Route("/{domainID}/channels", func(r chi.Router) {
r.Use(api.AuthenticateMiddleware(authn, true))
r.Post("/", otelhttp.NewHandler(kithttp.NewServer(
createChannelEndpoint(svc),
decodeCreateChannelReq,
api.EncodeResponse,
opts...,
), "create_channel").ServeHTTP)
r.Post("/bulk", otelhttp.NewHandler(kithttp.NewServer(
createChannelsEndpoint(svc),
decodeCreateChannelsReq,
api.EncodeResponse,
opts...,
), "create_channels").ServeHTTP)
r.Get("/", otelhttp.NewHandler(kithttp.NewServer(
listChannelsEndpoint(svc),
decodeListChannels,
api.EncodeResponse,
opts...,
), "list_channels").ServeHTTP)
r.Post("/connect", otelhttp.NewHandler(kithttp.NewServer(
connectEndpoint(svc),
decodeConnectRequest,
api.EncodeResponse,
opts...,
), "connect").ServeHTTP)
r.Post("/disconnect", otelhttp.NewHandler(kithttp.NewServer(
disconnectEndpoint(svc),
decodeDisconnectRequest,
api.EncodeResponse,
opts...,
), "disconnect").ServeHTTP)
r.Route("/{channelID}", func(r chi.Router) {
r.Get("/", otelhttp.NewHandler(kithttp.NewServer(
viewChannelEndpoint(svc),
decodeViewChannel,
api.EncodeResponse,
opts...,
), "view_channel").ServeHTTP)
r.Patch("/", otelhttp.NewHandler(kithttp.NewServer(
updateChannelEndpoint(svc),
decodeUpdateChannel,
api.EncodeResponse,
opts...,
), "update_channel_name_and_metadata").ServeHTTP)
r.Patch("/tags", otelhttp.NewHandler(kithttp.NewServer(
updateChannelTagsEndpoint(svc),
decodeUpdateChannelTags,
api.EncodeResponse,
opts...,
), "update_channel_tag").ServeHTTP)
r.Delete("/", otelhttp.NewHandler(kithttp.NewServer(
deleteChannelEndpoint(svc),
decodeDeleteChannelReq,
api.EncodeResponse,
opts...,
), "delete_channel").ServeHTTP)
r.Post("/enable", otelhttp.NewHandler(kithttp.NewServer(
enableChannelEndpoint(svc),
decodeChangeChannelStatus,
api.EncodeResponse,
opts...,
), "enable_channel").ServeHTTP)
r.Post("/disable", otelhttp.NewHandler(kithttp.NewServer(
disableChannelEndpoint(svc),
decodeChangeChannelStatus,
api.EncodeResponse,
opts...,
), "disable_channel").ServeHTTP)
r.Post("/parent", otelhttp.NewHandler(kithttp.NewServer(
setChannelParentGroupEndpoint(svc),
decodeSetChannelParentGroupStatus,
api.EncodeResponse,
opts...,
), "set_channel_parent_group").ServeHTTP)
r.Delete("/parent", otelhttp.NewHandler(kithttp.NewServer(
removeChannelParentGroupEndpoint(svc),
decodeRemoveChannelParentGroupStatus,
api.EncodeResponse,
opts...,
), "remove_channel_parent_group").ServeHTTP)
r.Post("/connect", otelhttp.NewHandler(kithttp.NewServer(
connectChannelClientEndpoint(svc),
decodeConnectChannelClientRequest,
api.EncodeResponse,
opts...,
), "connect_channel_client").ServeHTTP)
r.Post("/disconnect", otelhttp.NewHandler(kithttp.NewServer(
disconnectChannelClientsEndpoint(svc),
decodeDisconnectChannelClientsRequest,
api.EncodeResponse,
opts...,
), "disconnect_channel_client").ServeHTTP)
})
})
mux.Get("/health", magistrala.Health("channels", instanceID))
mux.Handle("/metrics", promhttp.Handler())
return mux
}
+170
View File
@@ -0,0 +1,170 @@
// Copyright (c) Abstract Machines
// SPDX-License-Identifier: Apache-2.0
package channels
import (
"context"
"time"
clients "github.com/absmach/magistrala/clients"
"github.com/absmach/magistrala/pkg/authn"
"github.com/absmach/magistrala/pkg/connections"
"github.com/absmach/magistrala/pkg/roles"
)
// Channel represents a Magistrala "communication topic". This topic
// contains the clients that can exchange messages between each other.
type Channel struct {
ID string `json:"id"`
Name string `json:"name,omitempty"`
Tags []string `json:"tags,omitempty"`
ParentGroup string `json:"parent_group_id,omitempty"`
Domain string `json:"domain_id,omitempty"`
Metadata clients.Metadata `json:"metadata,omitempty"`
CreatedAt time.Time `json:"created_at,omitempty"`
UpdatedAt time.Time `json:"updated_at,omitempty"`
UpdatedBy string `json:"updated_by,omitempty"`
Status clients.Status `json:"status,omitempty"` // 1 for enabled, 0 for disabled
Permissions []string `json:"permissions,omitempty"` // 1 for enabled, 0 for disabled
}
type PageMetadata struct {
Total uint64 `json:"total"`
Offset uint64 `json:"offset"`
Limit uint64 `json:"limit"`
Name string `json:"name,omitempty"`
Id string `json:"id,omitempty"`
Order string `json:"order,omitempty"`
Dir string `json:"dir,omitempty"`
Metadata clients.Metadata `json:"metadata,omitempty"`
Domain string `json:"domain,omitempty"`
Tag string `json:"tag,omitempty"`
Permission string `json:"permission,omitempty"`
Status clients.Status `json:"status,omitempty"`
IDs []string `json:"ids,omitempty"`
ListPerms bool `json:"-"`
ClientID string `json:"-"`
}
// ChannelsPage contains page related metadata as well as list of channels that
// belong to this page.
type Page struct {
PageMetadata
Channels []Channel
}
type Connection struct {
ClientID string
ChannelID string
DomainID string
Type connections.ConnType
}
type AuthzReq struct {
DomainID string
ChannelID string
ClientID string
ClientType string
Type connections.ConnType
}
//go:generate mockery --name Service --output=./mocks --filename service.go --quiet --note "Copyright (c) Abstract Machines"
type Service interface {
// CreateChannels adds channels to the user identified by the provided key.
CreateChannels(ctx context.Context, session authn.Session, channels ...Channel) ([]Channel, error)
// ViewChannel retrieves data about the channel identified by the provided
// ID, that belongs to the user identified by the provided key.
ViewChannel(ctx context.Context, session authn.Session, id string) (Channel, error)
// UpdateChannel updates the channel identified by the provided ID, that
// belongs to the user identified by the provided key.
UpdateChannel(ctx context.Context, session authn.Session, channel Channel) (Channel, error)
// UpdateChannelTags updates the channel's tags.
UpdateChannelTags(ctx context.Context, session authn.Session, channel Channel) (Channel, error)
EnableChannel(ctx context.Context, session authn.Session, id string) (Channel, error)
DisableChannel(ctx context.Context, session authn.Session, id string) (Channel, error)
// ListChannels retrieves data about subset of channels that belongs to the
// user identified by the provided key.
ListChannels(ctx context.Context, session authn.Session, pm PageMetadata) (Page, error)
// ListChannelsByClient retrieves data about subset of channels that have
// specified client connected or not connected to them and belong to the user identified by
// the provided key.
ListChannelsByClient(ctx context.Context, session authn.Session, id string, pm PageMetadata) (Page, error)
// RemoveChannel removes the client identified by the provided ID, that
// belongs to the user identified by the provided key.
RemoveChannel(ctx context.Context, session authn.Session, id string) error
// Connect adds clients to the channels list of connected clients.
Connect(ctx context.Context, session authn.Session, chIDs, clIDs []string, connType []connections.ConnType) error
// Disconnect removes clients from the channels list of connected clients.
Disconnect(ctx context.Context, session authn.Session, chIDs, clIDs []string, connType []connections.ConnType) error
SetParentGroup(ctx context.Context, session authn.Session, parentGroupID string, id string) error
RemoveParentGroup(ctx context.Context, session authn.Session, id string) error
roles.RoleManager
}
// ChannelRepository specifies a channel persistence API.
//
//go:generate mockery --name Repository --output=./mocks --filename repository.go --quiet --note "Copyright (c) Abstract Machines"
type Repository interface {
// Save persists multiple channels. Channels are saved using a transaction. If one channel
// fails then none will be saved. Successful operation is indicated by non-nil
// error response.
Save(ctx context.Context, chs ...Channel) ([]Channel, error)
// Update performs an update to the existing channel.
Update(ctx context.Context, c Channel) (Channel, error)
UpdateTags(ctx context.Context, ch Channel) (Channel, error)
ChangeStatus(ctx context.Context, channel Channel) (Channel, error)
// RetrieveByID retrieves the channel having the provided identifier
RetrieveByID(ctx context.Context, id string) (Channel, error)
// RetrieveAll retrieves the subset of channels.
RetrieveAll(ctx context.Context, pm PageMetadata) (Page, error)
// Remove removes the channel having the provided identifier
Remove(ctx context.Context, ids ...string) error
// SetParentGroup set parent group id to a given channel id
SetParentGroup(ctx context.Context, ch Channel) error
// RemoveParentGroup remove parent group id fr given chanel id
RemoveParentGroup(ctx context.Context, ch Channel) error
AddConnections(ctx context.Context, conns []Connection) error
RemoveConnections(ctx context.Context, conns []Connection) error
CheckConnection(ctx context.Context, conn Connection) error
ClientAuthorize(ctx context.Context, conn Connection) error
ChannelConnectionsCount(ctx context.Context, id string) (uint64, error)
DoesChannelHaveConnections(ctx context.Context, id string) (bool, error)
RemoveClientConnections(ctx context.Context, clientID string) error
RemoveChannelConnections(ctx context.Context, channelID string) error
RetrieveParentGroupChannels(ctx context.Context, parentGroupID string) ([]Channel, error)
UnsetParentGroupFromChannels(ctx context.Context, parentGroupID string) error
roles.Repository
}
+6
View File
@@ -0,0 +1,6 @@
// Copyright (c) Abstract Machines
// SPDX-License-Identifier: Apache-2.0
// Package events provides the domain concept definitions
// needed to support clients events functionality.
package events
+313
View File
@@ -0,0 +1,313 @@
// Copyright (c) Abstract Machines
// SPDX-License-Identifier: Apache-2.0
package events
import (
"time"
"github.com/absmach/magistrala/channels"
"github.com/absmach/magistrala/pkg/connections"
"github.com/absmach/magistrala/pkg/events"
)
const (
channelPrefix = "channels."
channelCreate = channelPrefix + "create"
channelUpdate = channelPrefix + "update"
channelChangeStatus = channelPrefix + "change_status"
channelRemove = channelPrefix + "remove"
channelView = channelPrefix + "view"
channelList = channelPrefix + "list"
channelConnect = channelPrefix + "connect"
channelDisconnect = channelPrefix + "disconnect"
channelSetParent = channelPrefix + "set_parent"
channelRemoveParent = channelPrefix + "remove_parent"
)
var (
_ events.Event = (*createChannelEvent)(nil)
_ events.Event = (*updateChannelEvent)(nil)
_ events.Event = (*changeStatusChannelEvent)(nil)
_ events.Event = (*viewChannelEvent)(nil)
_ events.Event = (*listChannelEvent)(nil)
_ events.Event = (*removeChannelEvent)(nil)
_ events.Event = (*connectEvent)(nil)
_ events.Event = (*disconnectEvent)(nil)
)
type createChannelEvent struct {
channels.Channel
}
func (cce createChannelEvent) Encode() (map[string]interface{}, error) {
val := map[string]interface{}{
"operation": channelCreate,
"id": cce.ID,
"status": cce.Status.String(),
"created_at": cce.CreatedAt,
}
if cce.Name != "" {
val["name"] = cce.Name
}
if len(cce.Tags) > 0 {
val["tags"] = cce.Tags
}
if cce.Domain != "" {
val["domain"] = cce.Domain
}
if cce.Metadata != nil {
val["metadata"] = cce.Metadata
}
return val, nil
}
type updateChannelEvent struct {
channels.Channel
operation string
}
func (uce updateChannelEvent) Encode() (map[string]interface{}, error) {
val := map[string]interface{}{
"operation": channelUpdate,
"updated_at": uce.UpdatedAt,
"updated_by": uce.UpdatedBy,
}
if uce.operation != "" {
val["operation"] = channelUpdate + "_" + uce.operation
}
if uce.ID != "" {
val["id"] = uce.ID
}
if uce.Name != "" {
val["name"] = uce.Name
}
if len(uce.Tags) > 0 {
val["tags"] = uce.Tags
}
if uce.Domain != "" {
val["domain"] = uce.Domain
}
if uce.Metadata != nil {
val["metadata"] = uce.Metadata
}
if !uce.CreatedAt.IsZero() {
val["created_at"] = uce.CreatedAt
}
if uce.Status.String() != "" {
val["status"] = uce.Status.String()
}
return val, nil
}
type changeStatusChannelEvent struct {
id string
status string
updatedAt time.Time
updatedBy string
}
func (rce changeStatusChannelEvent) Encode() (map[string]interface{}, error) {
return map[string]interface{}{
"operation": channelChangeStatus,
"id": rce.id,
"status": rce.status,
"updated_at": rce.updatedAt,
"updated_by": rce.updatedBy,
}, nil
}
type viewChannelEvent struct {
channels.Channel
}
func (vce viewChannelEvent) Encode() (map[string]interface{}, error) {
val := map[string]interface{}{
"operation": channelView,
"id": vce.ID,
}
if vce.Name != "" {
val["name"] = vce.Name
}
if len(vce.Tags) > 0 {
val["tags"] = vce.Tags
}
if vce.Domain != "" {
val["domain"] = vce.Domain
}
if vce.Metadata != nil {
val["metadata"] = vce.Metadata
}
if !vce.CreatedAt.IsZero() {
val["created_at"] = vce.CreatedAt
}
if !vce.UpdatedAt.IsZero() {
val["updated_at"] = vce.UpdatedAt
}
if vce.UpdatedBy != "" {
val["updated_by"] = vce.UpdatedBy
}
if vce.Status.String() != "" {
val["status"] = vce.Status.String()
}
return val, nil
}
type listChannelEvent struct {
channels.PageMetadata
}
func (lce listChannelEvent) Encode() (map[string]interface{}, error) {
val := map[string]interface{}{
"operation": channelList,
"total": lce.Total,
"offset": lce.Offset,
"limit": lce.Limit,
}
if lce.Name != "" {
val["name"] = lce.Name
}
if lce.Order != "" {
val["order"] = lce.Order
}
if lce.Dir != "" {
val["dir"] = lce.Dir
}
if lce.Metadata != nil {
val["metadata"] = lce.Metadata
}
if lce.Domain != "" {
val["domain"] = lce.Domain
}
if lce.Tag != "" {
val["tag"] = lce.Tag
}
if lce.Permission != "" {
val["permission"] = lce.Permission
}
if lce.Status.String() != "" {
val["status"] = lce.Status.String()
}
if len(lce.IDs) > 0 {
val["ids"] = lce.IDs
}
return val, nil
}
type listChannelByClientEvent struct {
clientID string
channels.PageMetadata
}
func (lcte listChannelByClientEvent) Encode() (map[string]interface{}, error) {
val := map[string]interface{}{
"operation": channelList,
"client_id": lcte.clientID,
"total": lcte.Total,
"offset": lcte.Offset,
"limit": lcte.Limit,
}
if lcte.Name != "" {
val["name"] = lcte.Name
}
if lcte.Order != "" {
val["order"] = lcte.Order
}
if lcte.Dir != "" {
val["dir"] = lcte.Dir
}
if lcte.Metadata != nil {
val["metadata"] = lcte.Metadata
}
if lcte.Domain != "" {
val["domain"] = lcte.Domain
}
if lcte.Tag != "" {
val["tag"] = lcte.Tag
}
if lcte.Permission != "" {
val["permission"] = lcte.Permission
}
if lcte.Status.String() != "" {
val["status"] = lcte.Status.String()
}
if len(lcte.IDs) > 0 {
val["ids"] = lcte.IDs
}
return val, nil
}
type removeChannelEvent struct {
id string
}
func (dce removeChannelEvent) Encode() (map[string]interface{}, error) {
return map[string]interface{}{
"operation": channelRemove,
"id": dce.id,
}, nil
}
type connectEvent struct {
chIDs []string
thIDs []string
types []connections.ConnType
}
func (ce connectEvent) Encode() (map[string]interface{}, error) {
return map[string]interface{}{
"operation": channelConnect,
"client_ids": ce.thIDs,
"channel_ids": ce.chIDs,
"types": ce.types,
}, nil
}
type disconnectEvent struct {
chIDs []string
thIDs []string
types []connections.ConnType
}
func (de disconnectEvent) Encode() (map[string]interface{}, error) {
return map[string]interface{}{
"operation": channelDisconnect,
"client_ids": de.thIDs,
"channel_ids": de.chIDs,
"types": de.types,
}, nil
}
type setParentGroupEvent struct {
id string
parentGroupID string
}
func (spge setParentGroupEvent) Encode() (map[string]interface{}, error) {
return map[string]interface{}{
"operation": channelSetParent,
"id": spge.id,
"parent_group_id": spge.parentGroupID,
}, nil
}
type removeParentGroupEvent struct {
id string
}
func (rpge removeParentGroupEvent) Encode() (map[string]interface{}, error) {
return map[string]interface{}{
"operation": channelRemoveParent,
"id": rpge.id,
}, nil
}
+238
View File
@@ -0,0 +1,238 @@
// Copyright (c) Abstract Machines
// SPDX-License-Identifier: Apache-2.0
package events
import (
"context"
"github.com/absmach/magistrala/channels"
"github.com/absmach/magistrala/pkg/authn"
"github.com/absmach/magistrala/pkg/connections"
"github.com/absmach/magistrala/pkg/events"
"github.com/absmach/magistrala/pkg/events/store"
rmEvents "github.com/absmach/magistrala/pkg/roles/rolemanager/events"
)
const streamID = "magistrala.clients"
var _ channels.Service = (*eventStore)(nil)
type eventStore struct {
events.Publisher
svc channels.Service
rmEvents.RoleManagerEventStore
}
// NewEventStoreMiddleware returns wrapper around clients service that sends
// events to event store.
func NewEventStoreMiddleware(ctx context.Context, svc channels.Service, url string) (channels.Service, error) {
publisher, err := store.NewPublisher(ctx, url, streamID)
if err != nil {
return nil, err
}
rolesSvcEventStoreMiddleware := rmEvents.NewRoleManagerEventStore("channels", svc, publisher)
return &eventStore{
svc: svc,
Publisher: publisher,
RoleManagerEventStore: rolesSvcEventStoreMiddleware,
}, nil
}
func (es *eventStore) CreateChannels(ctx context.Context, session authn.Session, chs ...channels.Channel) ([]channels.Channel, error) {
chs, err := es.svc.CreateChannels(ctx, session, chs...)
if err != nil {
return chs, err
}
for _, ch := range chs {
event := createChannelEvent{
ch,
}
if err := es.Publish(ctx, event); err != nil {
return chs, err
}
}
return chs, nil
}
func (es *eventStore) UpdateChannel(ctx context.Context, session authn.Session, ch channels.Channel) (channels.Channel, error) {
chann, err := es.svc.UpdateChannel(ctx, session, ch)
if err != nil {
return chann, err
}
return es.update(ctx, "", chann)
}
func (es *eventStore) UpdateChannelTags(ctx context.Context, session authn.Session, ch channels.Channel) (channels.Channel, error) {
chann, err := es.svc.UpdateChannelTags(ctx, session, ch)
if err != nil {
return chann, err
}
return es.update(ctx, "tags", chann)
}
func (es *eventStore) update(ctx context.Context, operation string, ch channels.Channel) (channels.Channel, error) {
event := updateChannelEvent{
ch, operation,
}
if err := es.Publish(ctx, event); err != nil {
return ch, err
}
return ch, nil
}
func (es *eventStore) ViewChannel(ctx context.Context, session authn.Session, id string) (channels.Channel, error) {
chann, err := es.svc.ViewChannel(ctx, session, id)
if err != nil {
return chann, err
}
event := viewChannelEvent{
chann,
}
if err := es.Publish(ctx, event); err != nil {
return chann, err
}
return chann, nil
}
func (es *eventStore) ListChannels(ctx context.Context, session authn.Session, pm channels.PageMetadata) (channels.Page, error) {
cp, err := es.svc.ListChannels(ctx, session, pm)
if err != nil {
return cp, err
}
event := listChannelEvent{
pm,
}
if err := es.Publish(ctx, event); err != nil {
return cp, err
}
return cp, nil
}
func (es *eventStore) ListChannelsByClient(ctx context.Context, session authn.Session, clientID string, pm channels.PageMetadata) (channels.Page, error) {
cp, err := es.svc.ListChannelsByClient(ctx, session, clientID, pm)
if err != nil {
return cp, err
}
event := listChannelByClientEvent{
clientID,
pm,
}
if err := es.Publish(ctx, event); err != nil {
return cp, err
}
return cp, nil
}
func (es *eventStore) EnableChannel(ctx context.Context, session authn.Session, id string) (channels.Channel, error) {
cli, err := es.svc.EnableChannel(ctx, session, id)
if err != nil {
return cli, err
}
return es.changeStatus(ctx, cli)
}
func (es *eventStore) DisableChannel(ctx context.Context, session authn.Session, id string) (channels.Channel, error) {
cli, err := es.svc.DisableChannel(ctx, session, id)
if err != nil {
return cli, err
}
return es.changeStatus(ctx, cli)
}
func (es *eventStore) changeStatus(ctx context.Context, ch channels.Channel) (channels.Channel, error) {
event := changeStatusChannelEvent{
id: ch.ID,
updatedAt: ch.UpdatedAt,
updatedBy: ch.UpdatedBy,
status: ch.Status.String(),
}
if err := es.Publish(ctx, event); err != nil {
return ch, err
}
return ch, nil
}
func (es *eventStore) RemoveChannel(ctx context.Context, session authn.Session, id string) error {
if err := es.svc.RemoveChannel(ctx, session, id); err != nil {
return err
}
event := removeChannelEvent{id}
if err := es.Publish(ctx, event); err != nil {
return err
}
return nil
}
func (es *eventStore) Connect(ctx context.Context, session authn.Session, chIDs, thIDs []string, connTypes []connections.ConnType) error {
if err := es.svc.Connect(ctx, session, chIDs, thIDs, connTypes); err != nil {
return err
}
event := connectEvent{chIDs, thIDs, connTypes}
if err := es.Publish(ctx, event); err != nil {
return err
}
return nil
}
func (es *eventStore) Disconnect(ctx context.Context, session authn.Session, chIDs, thIDs []string, connTypes []connections.ConnType) error {
if err := es.svc.Disconnect(ctx, session, chIDs, thIDs, connTypes); err != nil {
return err
}
event := disconnectEvent{chIDs, thIDs, connTypes}
if err := es.Publish(ctx, event); err != nil {
return err
}
return nil
}
func (es *eventStore) SetParentGroup(ctx context.Context, session authn.Session, parentGroupID string, id string) (err error) {
if err := es.svc.SetParentGroup(ctx, session, parentGroupID, id); err != nil {
return err
}
event := setParentGroupEvent{parentGroupID: parentGroupID, id: id}
if err := es.Publish(ctx, event); err != nil {
return err
}
return nil
}
func (es *eventStore) RemoveParentGroup(ctx context.Context, session authn.Session, id string) (err error) {
if err := es.svc.RemoveParentGroup(ctx, session, id); err != nil {
return err
}
event := removeParentGroupEvent{id: id}
if err := es.Publish(ctx, event); err != nil {
return err
}
return nil
}
+348
View File
@@ -0,0 +1,348 @@
// Copyright (c) Abstract Machines
// SPDX-License-Identifier: Apache-2.0
package middleware
import (
"context"
"fmt"
"github.com/absmach/magistrala/channels"
"github.com/absmach/magistrala/pkg/authn"
"github.com/absmach/magistrala/pkg/authz"
mgauthz "github.com/absmach/magistrala/pkg/authz"
"github.com/absmach/magistrala/pkg/connections"
"github.com/absmach/magistrala/pkg/errors"
svcerr "github.com/absmach/magistrala/pkg/errors/service"
"github.com/absmach/magistrala/pkg/policies"
rmMW "github.com/absmach/magistrala/pkg/roles/rolemanager/middleware"
"github.com/absmach/magistrala/pkg/svcutil"
)
var (
errView = errors.New("not authorized to view channel")
errUpdate = errors.New("not authorized to update channel")
errUpdateTags = errors.New("not authorized to update channel tags")
errEnable = errors.New("not authorized to enable channel")
errDisable = errors.New("not authorized to disable channel")
errDelete = errors.New("not authorized to delete channel")
errConnect = errors.New("not authorized to connect to channel")
errDisconnect = errors.New("not authorized to disconnect from channel")
errSetParentGroup = errors.New("not authorized to set parent group to channel")
errRemoveParentGroup = errors.New("not authorized to remove parent group from channel")
errDomainCreateChannels = errors.New("not authorized to create channel in domain")
errGroupSetChildChannels = errors.New("not authorized to set child channel for group")
errGroupRemoveChildChannels = errors.New("not authorized to remove child channel for group")
errClientDisConnectChannels = errors.New("not authorized to disconnect channel for client")
errClientConnectChannels = errors.New("not authorized to connect channel for client")
)
var _ channels.Service = (*authorizationMiddleware)(nil)
type authorizationMiddleware struct {
svc channels.Service
repo channels.Repository
authz mgauthz.Authorization
opp svcutil.OperationPerm
extOpp svcutil.ExternalOperationPerm
rmMW.RoleManagerAuthorizationMiddleware
}
// AuthorizationMiddleware adds authorization to the channels service.
func AuthorizationMiddleware(svc channels.Service, repo channels.Repository, authz mgauthz.Authorization, channelsOpPerm, rolesOpPerm map[svcutil.Operation]svcutil.Permission, extOpPerm map[svcutil.ExternalOperation]svcutil.Permission) (channels.Service, error) {
opp := channels.NewOperationPerm()
if err := opp.AddOperationPermissionMap(channelsOpPerm); err != nil {
return nil, err
}
if err := opp.Validate(); err != nil {
return nil, err
}
extOpp := channels.NewExternalOperationPerm()
if err := extOpp.AddOperationPermissionMap(extOpPerm); err != nil {
return nil, err
}
if err := extOpp.Validate(); err != nil {
return nil, err
}
ram, err := rmMW.NewRoleManagerAuthorizationMiddleware(policies.ChannelType, svc, authz, rolesOpPerm)
if err != nil {
return nil, err
}
return &authorizationMiddleware{
svc: svc,
repo: repo,
authz: authz,
RoleManagerAuthorizationMiddleware: ram,
opp: opp,
extOpp: extOpp,
}, nil
}
func (am *authorizationMiddleware) CreateChannels(ctx context.Context, session authn.Session, chs ...channels.Channel) ([]channels.Channel, error) {
// If domain is disabled , then this authorization will fail for all non-admin domain users
if err := am.extAuthorize(ctx, channels.DomainOpCreateChannel, authz.PolicyReq{
Domain: session.DomainID,
SubjectType: policies.UserType,
Subject: session.DomainUserID,
ObjectType: policies.DomainType,
Object: session.DomainID,
}); err != nil {
return []channels.Channel{}, errors.Wrap(err, errDomainCreateChannels)
}
for _, ch := range chs {
if ch.ParentGroup != "" {
if err := am.extAuthorize(ctx, channels.GroupOpSetChildChannel, authz.PolicyReq{
Domain: session.DomainID,
SubjectType: policies.UserType,
Subject: session.DomainUserID,
ObjectType: policies.GroupType,
Object: ch.ParentGroup,
}); err != nil {
return []channels.Channel{}, errors.Wrap(err, errors.Wrap(errGroupSetChildChannels, fmt.Errorf("channel name %s parent group id %s", ch.Name, ch.ParentGroup)))
}
}
}
return am.svc.CreateChannels(ctx, session, chs...)
}
func (am *authorizationMiddleware) ViewChannel(ctx context.Context, session authn.Session, id string) (channels.Channel, error) {
if err := am.authorize(ctx, channels.OpViewChannel, authz.PolicyReq{
Domain: session.DomainID,
SubjectType: policies.UserType,
Subject: session.DomainUserID,
ObjectType: policies.ChannelType,
Object: id,
}); err != nil {
return channels.Channel{}, errors.Wrap(err, errView)
}
return am.svc.ViewChannel(ctx, session, id)
}
func (am *authorizationMiddleware) ListChannels(ctx context.Context, session authn.Session, pm channels.PageMetadata) (channels.Page, error) {
if err := am.checkSuperAdmin(ctx, session.UserID); err != nil {
session.SuperAdmin = true
}
return am.svc.ListChannels(ctx, session, pm)
}
func (am *authorizationMiddleware) ListChannelsByClient(ctx context.Context, session authn.Session, clientID string, pm channels.PageMetadata) (channels.Page, error) {
return am.svc.ListChannelsByClient(ctx, session, clientID, pm)
}
func (am *authorizationMiddleware) UpdateChannel(ctx context.Context, session authn.Session, channel channels.Channel) (channels.Channel, error) {
if err := am.authorize(ctx, channels.OpUpdateChannel, authz.PolicyReq{
Domain: session.DomainID,
SubjectType: policies.UserType,
Subject: session.DomainUserID,
ObjectType: policies.ChannelType,
Object: channel.ID,
}); err != nil {
return channels.Channel{}, errors.Wrap(err, errUpdate)
}
return am.svc.UpdateChannel(ctx, session, channel)
}
func (am *authorizationMiddleware) UpdateChannelTags(ctx context.Context, session authn.Session, channel channels.Channel) (channels.Channel, error) {
if err := am.authorize(ctx, channels.OpUpdateChannelTags, authz.PolicyReq{
Domain: session.DomainID,
SubjectType: policies.UserType,
Subject: session.DomainUserID,
ObjectType: policies.ChannelType,
Object: channel.ID,
}); err != nil {
return channels.Channel{}, errors.Wrap(err, errUpdateTags)
}
return am.svc.UpdateChannelTags(ctx, session, channel)
}
func (am *authorizationMiddleware) EnableChannel(ctx context.Context, session authn.Session, id string) (channels.Channel, error) {
if err := am.authorize(ctx, channels.OpEnableChannel, authz.PolicyReq{
Domain: session.DomainID,
SubjectType: policies.UserType,
Subject: session.DomainUserID,
ObjectType: policies.ChannelType,
Object: id,
}); err != nil {
return channels.Channel{}, errors.Wrap(err, errEnable)
}
return am.svc.EnableChannel(ctx, session, id)
}
func (am *authorizationMiddleware) DisableChannel(ctx context.Context, session authn.Session, id string) (channels.Channel, error) {
if err := am.authorize(ctx, channels.OpDisableChannel, authz.PolicyReq{
Domain: session.DomainID,
SubjectType: policies.UserType,
Subject: session.DomainUserID,
ObjectType: policies.ChannelType,
Object: id,
}); err != nil {
return channels.Channel{}, errors.Wrap(err, errDisable)
}
return am.svc.DisableChannel(ctx, session, id)
}
func (am *authorizationMiddleware) RemoveChannel(ctx context.Context, session authn.Session, id string) error {
if err := am.authorize(ctx, channels.OpDeleteChannel, authz.PolicyReq{
Domain: session.DomainID,
SubjectType: policies.UserType,
Subject: session.DomainUserID,
ObjectType: policies.ChannelType,
Object: id,
}); err != nil {
return errors.Wrap(err, errDelete)
}
return am.svc.RemoveChannel(ctx, session, id)
}
func (am *authorizationMiddleware) Connect(ctx context.Context, session authn.Session, chIDs, thIDs []string, connTypes []connections.ConnType) error {
for _, chID := range chIDs {
if err := am.authorize(ctx, channels.OpConnectClient, authz.PolicyReq{
Domain: session.DomainID,
SubjectType: policies.UserType,
Subject: session.DomainUserID,
ObjectType: policies.ChannelType,
Object: chID,
}); err != nil {
return errors.Wrap(err, errConnect)
}
}
for _, thID := range thIDs {
if err := am.extAuthorize(ctx, channels.ClientsOpConnectChannel, authz.PolicyReq{
Domain: session.DomainID,
SubjectType: policies.UserType,
Subject: session.DomainUserID,
ObjectType: policies.ClientType,
Object: thID,
}); err != nil {
return errors.Wrap(err, errClientConnectChannels)
}
}
return am.svc.Connect(ctx, session, chIDs, thIDs, connTypes)
}
func (am *authorizationMiddleware) Disconnect(ctx context.Context, session authn.Session, chIDs, thIDs []string, connTypes []connections.ConnType) error {
for _, chID := range chIDs {
if err := am.authorize(ctx, channels.OpDisconnectClient, authz.PolicyReq{
Domain: session.DomainID,
SubjectType: policies.UserType,
Subject: session.DomainUserID,
ObjectType: policies.ChannelType,
Object: chID,
}); err != nil {
return errors.Wrap(err, errDisconnect)
}
}
for _, thID := range thIDs {
if err := am.extAuthorize(ctx, channels.ClientsOpDisconnectChannel, authz.PolicyReq{
Domain: session.DomainID,
SubjectType: policies.UserType,
Subject: session.DomainUserID,
ObjectType: policies.ClientType,
Object: thID,
}); err != nil {
return errors.Wrap(err, errClientDisConnectChannels)
}
}
return am.svc.Disconnect(ctx, session, chIDs, thIDs, connTypes)
}
func (am *authorizationMiddleware) SetParentGroup(ctx context.Context, session authn.Session, parentGroupID string, id string) error {
if err := am.authorize(ctx, channels.OpSetParentGroup, authz.PolicyReq{
Domain: session.DomainID,
SubjectType: policies.UserType,
Subject: session.DomainUserID,
ObjectType: policies.ChannelType,
Object: id,
}); err != nil {
return errors.Wrap(err, errSetParentGroup)
}
if err := am.extAuthorize(ctx, channels.GroupOpSetChildChannel, authz.PolicyReq{
Domain: session.DomainID,
SubjectType: policies.UserType,
Subject: session.DomainUserID,
ObjectType: policies.GroupType,
Object: parentGroupID,
}); err != nil {
return errors.Wrap(err, errGroupSetChildChannels)
}
return am.svc.SetParentGroup(ctx, session, parentGroupID, id)
}
func (am *authorizationMiddleware) RemoveParentGroup(ctx context.Context, session authn.Session, id string) error {
if err := am.authorize(ctx, channels.OpSetParentGroup, authz.PolicyReq{
Domain: session.DomainID,
SubjectType: policies.UserType,
Subject: session.DomainUserID,
ObjectType: policies.ChannelType,
Object: id,
}); err != nil {
return errors.Wrap(err, errRemoveParentGroup)
}
ch, err := am.repo.RetrieveByID(ctx, id)
if err != nil {
return errors.Wrap(svcerr.ErrRemoveEntity, err)
}
if ch.ParentGroup != "" {
if err := am.extAuthorize(ctx, channels.GroupOpSetChildChannel, authz.PolicyReq{
Domain: session.DomainID,
SubjectType: policies.UserType,
Subject: session.DomainUserID,
ObjectType: policies.GroupType,
Object: ch.ParentGroup,
}); err != nil {
return errors.Wrap(err, errGroupRemoveChildChannels)
}
return am.svc.RemoveParentGroup(ctx, session, id)
}
return nil
}
func (am *authorizationMiddleware) authorize(ctx context.Context, op svcutil.Operation, req authz.PolicyReq) error {
perm, err := am.opp.GetPermission(op)
if err != nil {
return err
}
req.Permission = perm.String()
if err := am.authz.Authorize(ctx, req); err != nil {
return err
}
return nil
}
func (am *authorizationMiddleware) extAuthorize(ctx context.Context, extOp svcutil.ExternalOperation, req authz.PolicyReq) error {
perm, err := am.extOpp.GetPermission(extOp)
if err != nil {
return err
}
req.Permission = perm.String()
if err := am.authz.Authorize(ctx, req); err != nil {
return err
}
return nil
}
func (am *authorizationMiddleware) checkSuperAdmin(ctx context.Context, userID string) error {
if err := am.authz.Authorize(ctx, mgauthz.PolicyReq{
SubjectType: policies.UserType,
Subject: userID,
Permission: policies.AdminPermission,
ObjectType: policies.PlatformType,
Object: policies.MagistralaObject,
}); err != nil {
return err
}
return nil
}
+264
View File
@@ -0,0 +1,264 @@
// Copyright (c) Abstract Machines
// SPDX-License-Identifier: Apache-2.0
package middleware
import (
"context"
"fmt"
"log/slog"
"time"
"github.com/absmach/magistrala/channels"
"github.com/absmach/magistrala/pkg/authn"
"github.com/absmach/magistrala/pkg/connections"
rmMW "github.com/absmach/magistrala/pkg/roles/rolemanager/middleware"
)
var _ channels.Service = (*loggingMiddleware)(nil)
type loggingMiddleware struct {
logger *slog.Logger
svc channels.Service
rmMW.RoleManagerLoggingMiddleware
}
func LoggingMiddleware(svc channels.Service, logger *slog.Logger) channels.Service {
return &loggingMiddleware{logger, svc, rmMW.NewRoleManagerLoggingMiddleware("channels", svc, logger)}
}
func (lm *loggingMiddleware) CreateChannels(ctx context.Context, session authn.Session, clients ...channels.Channel) (cs []channels.Channel, err error) {
defer func(begin time.Time) {
args := []any{
slog.String("duration", time.Since(begin).String()),
}
if err != nil {
args = append(args, slog.String("error", err.Error()))
lm.logger.Warn(fmt.Sprintf("Create %d channels failed", len(clients)), args...)
return
}
lm.logger.Info(fmt.Sprintf("Create %d channel completed successfully", len(clients)), args...)
}(time.Now())
return lm.svc.CreateChannels(ctx, session, clients...)
}
func (lm *loggingMiddleware) ViewChannel(ctx context.Context, session authn.Session, id string) (c channels.Channel, err error) {
defer func(begin time.Time) {
args := []any{
slog.String("duration", time.Since(begin).String()),
slog.Group("channel",
slog.String("id", c.ID),
slog.String("name", c.Name),
),
}
if err != nil {
args = append(args, slog.String("error", err.Error()))
lm.logger.Warn("View channel failed", args...)
return
}
lm.logger.Info("View channel completed successfully", args...)
}(time.Now())
return lm.svc.ViewChannel(ctx, session, id)
}
func (lm *loggingMiddleware) ListChannels(ctx context.Context, session authn.Session, pm channels.PageMetadata) (cp channels.Page, err error) {
defer func(begin time.Time) {
args := []any{
slog.String("duration", time.Since(begin).String()),
slog.Group("page",
slog.Uint64("limit", pm.Limit),
slog.Uint64("offset", pm.Offset),
slog.Uint64("total", cp.Total),
),
}
if err != nil {
args = append(args, slog.String("error", err.Error()))
lm.logger.Warn("List channels failed", args...)
return
}
lm.logger.Info("List channels completed successfully", args...)
}(time.Now())
return lm.svc.ListChannels(ctx, session, pm)
}
func (lm *loggingMiddleware) ListChannelsByClient(ctx context.Context, session authn.Session, clientID string, pm channels.PageMetadata) (cp channels.Page, err error) {
defer func(begin time.Time) {
args := []any{
slog.String("duration", time.Since(begin).String()),
slog.String("client_id", clientID),
slog.Group("page",
slog.Uint64("limit", pm.Limit),
slog.Uint64("offset", pm.Offset),
slog.Uint64("total", cp.Total),
),
}
if err != nil {
args = append(args, slog.String("error", err.Error()))
lm.logger.Warn("List channels by client failed", args...)
return
}
lm.logger.Info("List channels by client completed successfully", args...)
}(time.Now())
return lm.svc.ListChannelsByClient(ctx, session, clientID, pm)
}
func (lm *loggingMiddleware) UpdateChannel(ctx context.Context, session authn.Session, client channels.Channel) (c channels.Channel, err error) {
defer func(begin time.Time) {
args := []any{
slog.String("duration", time.Since(begin).String()),
slog.Group("channel",
slog.String("id", client.ID),
slog.String("name", client.Name),
slog.Any("metadata", client.Metadata),
),
}
if err != nil {
args = append(args, slog.String("error", err.Error()))
lm.logger.Warn("Update channel failed", args...)
return
}
lm.logger.Info("Update channel completed successfully", args...)
}(time.Now())
return lm.svc.UpdateChannel(ctx, session, client)
}
func (lm *loggingMiddleware) UpdateChannelTags(ctx context.Context, session authn.Session, client channels.Channel) (c channels.Channel, err error) {
defer func(begin time.Time) {
args := []any{
slog.String("duration", time.Since(begin).String()),
slog.Group("channel",
slog.String("id", c.ID),
slog.String("name", c.Name),
slog.Any("tags", c.Tags),
),
}
if err != nil {
args := append(args, slog.String("error", err.Error()))
lm.logger.Warn("Update channel tags failed", args...)
return
}
lm.logger.Info("Update channel tags completed successfully", args...)
}(time.Now())
return lm.svc.UpdateChannelTags(ctx, session, client)
}
func (lm *loggingMiddleware) EnableChannel(ctx context.Context, session authn.Session, id string) (c channels.Channel, err error) {
defer func(begin time.Time) {
args := []any{
slog.String("duration", time.Since(begin).String()),
slog.Group("channel",
slog.String("id", id),
slog.String("name", c.Name),
),
}
if err != nil {
args = append(args, slog.String("error", err.Error()))
lm.logger.Warn("Enable channel failed", args...)
return
}
lm.logger.Info("Enable channel completed successfully", args...)
}(time.Now())
return lm.svc.EnableChannel(ctx, session, id)
}
func (lm *loggingMiddleware) DisableChannel(ctx context.Context, session authn.Session, id string) (c channels.Channel, err error) {
defer func(begin time.Time) {
args := []any{
slog.String("duration", time.Since(begin).String()),
slog.Group("channel",
slog.String("id", id),
slog.String("name", c.Name),
),
}
if err != nil {
args = append(args, slog.String("error", err.Error()))
lm.logger.Warn("Disable channel failed", args...)
return
}
lm.logger.Info("Disable channel completed successfully", args...)
}(time.Now())
return lm.svc.DisableChannel(ctx, session, id)
}
func (lm *loggingMiddleware) RemoveChannel(ctx context.Context, session authn.Session, id string) (err error) {
defer func(begin time.Time) {
args := []any{
slog.String("duration", time.Since(begin).String()),
slog.String("channel_id", id),
}
if err != nil {
args = append(args, slog.String("error", err.Error()))
lm.logger.Warn("Delete channel failed", args...)
return
}
lm.logger.Info("Delete channel completed successfully", args...)
}(time.Now())
return lm.svc.RemoveChannel(ctx, session, id)
}
func (lm *loggingMiddleware) Connect(ctx context.Context, session authn.Session, chIDs, clIDs []string, connTypes []connections.ConnType) (err error) {
defer func(begin time.Time) {
args := []any{
slog.String("duration", time.Since(begin).String()),
slog.Any("channel_ids", chIDs),
slog.Any("client_ids", clIDs),
}
if err != nil {
args = append(args, slog.String("error", err.Error()))
lm.logger.Warn("Connect channels and clients failed", args...)
return
}
lm.logger.Info("Connect channels and clients completed successfully", args...)
}(time.Now())
return lm.svc.Connect(ctx, session, chIDs, clIDs, connTypes)
}
func (lm *loggingMiddleware) Disconnect(ctx context.Context, session authn.Session, chIDs, clIDs []string, connTypes []connections.ConnType) (err error) {
defer func(begin time.Time) {
args := []any{
slog.String("duration", time.Since(begin).String()),
slog.Any("channel_ids", chIDs),
slog.Any("client_ids", clIDs),
}
if err != nil {
args = append(args, slog.String("error", err.Error()))
lm.logger.Warn("Disconnect channels and clients failed", args...)
return
}
lm.logger.Info("Disconnect channels and clients completed successfully", args...)
}(time.Now())
return lm.svc.Disconnect(ctx, session, chIDs, clIDs, connTypes)
}
func (lm *loggingMiddleware) SetParentGroup(ctx context.Context, session authn.Session, parentGroupID string, id string) (err error) {
defer func(begin time.Time) {
args := []any{
slog.String("duration", time.Since(begin).String()),
slog.String("parent_group_id", parentGroupID),
slog.String("channel_id", id),
}
if err != nil {
args = append(args, slog.String("error", err.Error()))
lm.logger.Warn("Set parent group to channel failed", args...)
return
}
lm.logger.Info("Set parent group to channel completed successfully", args...)
}(time.Now())
return lm.svc.SetParentGroup(ctx, session, parentGroupID, id)
}
func (lm *loggingMiddleware) RemoveParentGroup(ctx context.Context, session authn.Session, id string) (err error) {
defer func(begin time.Time) {
args := []any{
slog.String("duration", time.Since(begin).String()),
slog.String("channel_id", id),
}
if err != nil {
args = append(args, slog.String("error", err.Error()))
lm.logger.Warn("Remove parent group from channel failed", args...)
return
}
lm.logger.Info("Remove parent group from channel completed successfully", args...)
}(time.Now())
return lm.svc.RemoveParentGroup(ctx, session, id)
}

Some files were not shown because too many files have changed in this diff Show More