mirror of
https://github.com/absmach/supermq.git
synced 2026-06-23 06:40:19 +00:00
NOISSUE - Switch to Google Zanzibar Access control approach (#1919)
* Return Auth service Signed-off-by: dusanb94 <dusan.borovcanin@mainflux.com> Update Compose to run with SpiceDB and Auth svc Signed-off-by: dusanb94 <dusan.borovcanin@mainflux.com> Update auth gRPC API Signed-off-by: dusanb94 <dusan.borovcanin@mainflux.com> Remove Users' policies Signed-off-by: dusanb94 <dusan.borovcanin@mainflux.com> Move Groups to internal Signed-off-by: dusanb94 <dusan.borovcanin@mainflux.com> Use shared groups in Users Signed-off-by: dusanb94 <dusan.borovcanin@mainflux.com> Remove unused code Signed-off-by: dusanb94 <dusan.borovcanin@mainflux.com> Use pkg Groups in Things Signed-off-by: dusanb94 <dusan.borovcanin@mainflux.com> Remove Things groups Signed-off-by: dusanb94 <dusan.borovcanin@mainflux.com> Make imports consistent Signed-off-by: dusanb94 <dusan.borovcanin@mainflux.com> Update Groups networking Signed-off-by: dusanb94 <dusan.borovcanin@mainflux.com> Remove things groups-specific API Signed-off-by: dusanb94 <dusan.borovcanin@mainflux.com> Move Things Clients to the root Signed-off-by: dusanb94 <dusan.borovcanin@mainflux.com> Move Clients to Users root Signed-off-by: dusanb94 <dusan.borovcanin@mainflux.com> Temporarily remove tracing Signed-off-by: dusanb94 <dusan.borovcanin@mainflux.com> Fix imports Signed-off-by: dusanb94 <dusan.borovcanin@mainflux.com> Add buffer config for gRPC Signed-off-by: dusanb94 <dusan.borovcanin@mainflux.com> Update auth type for Things Signed-off-by: dusanb94 <dusan.borovcanin@mainflux.com> Use Auth for login Signed-off-by: dusanb94 <dusan.borovcanin@mainflux.com> Add temporary solution for refresh token Signed-off-by: dusanb94 <dusan.borovcanin@mainflux.com> Update Tokenizer interface Signed-off-by: dusanb94 <dusan.borovcanin@mainflux.com> Updade tokens issuing Signed-off-by: dusanb94 <dusan.borovcanin@mainflux.com> Fix token issuing Signed-off-by: dusanb94 <dusan.borovcanin@mainflux.com> Update JWT validator and refactor Tokenizer Signed-off-by: dusanb94 <dusan.borovcanin@mainflux.com> Rename access timeout Signed-off-by: dusanb94 <dusan.borovcanin@mainflux.com> Rename login to authenticate Signed-off-by: dusanb94 <dusan.borovcanin@mainflux.com> Update Identify to use SubjectID Signed-off-by: dusanb94 <dusan.borovcanin@mainflux.com> Add Auth to Groups Signed-off-by: dusanb94 <dusan.borovcanin@mainflux.com> Use the Auth service for Groups Signed-off-by: dusanb94 <dusan.borovcanin@mainflux.com> Update auth schema Signed-off-by: dusanb94 <dusan.borovcanin@mainflux.com> Fix Auth for Groups Signed-off-by: dusanb94 <dusan.borovcanin@mainflux.com> Add auth for addons (#14) Signed-off-by: Arvindh <arvindh91@gmail.com> Speparate Login and Refresh tokens Signed-off-by: dusanb94 <dusan.borovcanin@mainflux.com> Merge authN and authZ requests for things Signed-off-by: dusanb94 <dusan.borovcanin@mainflux.com> Add connect and disconnect Signed-off-by: dusanb94 <dusan.borovcanin@mainflux.com> Update sharing Signed-off-by: dusanb94 <dusan.borovcanin@mainflux.com> Fix policies addition and removal Signed-off-by: dusanb94 <dusan.borovcanin@mainflux.com> Update relation with roels Signed-off-by: dusanb94 <dusan.borovcanin@mainflux.com> Add gRPC to Things Signed-off-by: dusanb94 <dusan.borovcanin@mainflux.com> Assign and Unassign members to group and Listing of Group members (#15) * add auth for addons Signed-off-by: Arvindh <arvindh91@gmail.com> * add assign and unassign to group Signed-off-by: Arvindh <arvindh91@gmail.com> * add group incomplete repo implementation Signed-off-by: Arvindh <arvindh91@gmail.com> * groups for users Signed-off-by: Arvindh <arvindh91@gmail.com> * groups for users Signed-off-by: Arvindh <arvindh91@gmail.com> * groups for users Signed-off-by: Arvindh <arvindh91@gmail.com> * groups for users Signed-off-by: Arvindh <arvindh91@gmail.com> --------- Signed-off-by: Arvindh <arvindh91@gmail.com> Move coap mqtt and ws policies to spicedb (#16) Signed-off-by: Rodney Osodo <28790446+rodneyosodo@users.noreply.github.com> Remove old policies Signed-off-by: dusanb94 <dusan.borovcanin@mainflux.com> NOISSUE - Things authorize to return thingID (#18) This commit modifies the authorize endpoint to the grpc endpoint to return thingID. The authorize endpoint allows adapters to get the publisher of the message. Signed-off-by: Rodney Osodo <28790446+rodneyosodo@users.noreply.github.com> Add Groups to users service (#17) * add assign and unassign to group Signed-off-by: Arvindh <arvindh91@gmail.com> * add group incomplete repo implementation Signed-off-by: Arvindh <arvindh91@gmail.com> * groups for users Signed-off-by: Arvindh <arvindh91@gmail.com> * groups for users Signed-off-by: Arvindh <arvindh91@gmail.com> * groups for users Signed-off-by: Arvindh <arvindh91@gmail.com> * groups for users Signed-off-by: Arvindh <arvindh91@gmail.com> * groups for users stable 1 Signed-off-by: Arvindh <arvindh91@gmail.com> * groups for users stable 2 Signed-off-by: Arvindh <arvindh91@gmail.com> * groups for users & things Signed-off-by: Arvindh <arvindh91@gmail.com> * Amend signature Signed-off-by: Arvindh <arvindh91@gmail.com> * fix merge error Signed-off-by: Arvindh <arvindh91@gmail.com> --------- Signed-off-by: Arvindh <arvindh91@gmail.com> Signed-off-by: dusanb94 <dusan.borovcanin@mainflux.com> * NOISSUE - Fix es code (#21) Signed-off-by: Rodney Osodo <28790446+rodneyosodo@users.noreply.github.com> Signed-off-by: dusanb94 <dusan.borovcanin@mainflux.com> * NOISSUE - Fix Bugs (#20) * fix bugs Signed-off-by: Arvindh <arvindh91@gmail.com> * fix bugs Signed-off-by: Arvindh <arvindh91@gmail.com> --------- Signed-off-by: Arvindh <arvindh91@gmail.com> Signed-off-by: dusanb94 <dusan.borovcanin@mainflux.com> * NOISSUE - Test e2e (#19) * fix: connect method Signed-off-by: Rodney Osodo <28790446+rodneyosodo@users.noreply.github.com> * fix: e2e Signed-off-by: Rodney Osodo <28790446+rodneyosodo@users.noreply.github.com> * fix changes in sdk and e2e Signed-off-by: Rodney Osodo <28790446+rodneyosodo@users.noreply.github.com> * feat(docker): remove unnecessary port mapping Remove the port mapping for MQTT broker in the docker-compose.yml file. Signed-off-by: Rodney Osodo <28790446+rodneyosodo@users.noreply.github.com> * Enable group listing Signed-off-by: Rodney Osodo <28790446+rodneyosodo@users.noreply.github.com> * feat(responses): update ChannelsPage struct The ChannelsPage struct in the responses.go file has been updated. The "Channels" field has been renamed to "Groups" to provide more accurate naming. This change ensures consistency and clarity in the codebase. Signed-off-by: Rodney Osodo <28790446+rodneyosodo@users.noreply.github.com> * feat(things): add UpdateClientSecret method Add the UpdateClientSecret method to the things service. This method allows updating the client secret for a specific client identified by the provided token, id, and key parameters. Signed-off-by: Rodney Osodo <28790446+rodneyosodo@users.noreply.github.com> --------- Signed-off-by: Rodney Osodo <28790446+rodneyosodo@users.noreply.github.com> Signed-off-by: dusanb94 <dusan.borovcanin@mainflux.com> * Use smaller buffers for gRPC Signed-off-by: dusanb94 <dusan.borovcanin@mainflux.com> * Clean up tests (#22) Signed-off-by: Rodney Osodo <28790446+rodneyosodo@users.noreply.github.com> Signed-off-by: dusanb94 <dusan.borovcanin@mainflux.com> * Add Connect Disconnect endpoints (#23) * fix bugs Signed-off-by: Arvindh <arvindh91@gmail.com> * fix bugs Signed-off-by: Arvindh <arvindh91@gmail.com> * fix list of things in a channel and Add connect disconnect endpoint Signed-off-by: Arvindh <arvindh91@gmail.com> * fix list of things in a channel and Add connect disconnect endpoint Signed-off-by: Arvindh <arvindh91@gmail.com> --------- Signed-off-by: Arvindh <arvindh91@gmail.com> Signed-off-by: dusanb94 <dusan.borovcanin@mainflux.com> * Add: Things share with users (#25) * fix list of things in a channel and Add connect disconnect endpoint Signed-off-by: Arvindh <arvindh91@gmail.com> * add: things share with other users Signed-off-by: Arvindh <arvindh91@gmail.com> --------- Signed-off-by: Arvindh <arvindh91@gmail.com> Signed-off-by: dusanb94 <dusan.borovcanin@mainflux.com> * NOISSUE - Rename gRPC Services (#24) * Rename things and users auth service Signed-off-by: Rodney Osodo <28790446+rodneyosodo@users.noreply.github.com> * docs: add authorization docs for gRPC services Signed-off-by: Rodney Osodo <28790446+rodneyosodo@users.noreply.github.com> * Rename things and users grpc services Signed-off-by: Rodney Osodo <28790446+rodneyosodo@users.noreply.github.com> * Remove mainflux.env package Signed-off-by: Rodney Osodo <28790446+rodneyosodo@users.noreply.github.com> --------- Signed-off-by: Rodney Osodo <28790446+rodneyosodo@users.noreply.github.com> Signed-off-by: dusanb94 <dusan.borovcanin@mainflux.com> * Add: Listing of things, channels, groups, users (#26) * add: listing of channels, users, groups, things Signed-off-by: Arvindh <arvindh91@gmail.com> * add: listing of channels, users, groups, things Signed-off-by: Arvindh <arvindh91@gmail.com> * add: listing of channels, users, groups, things Signed-off-by: Arvindh <arvindh91@gmail.com> * add: listing of channels, users, groups, things Signed-off-by: Arvindh <arvindh91@gmail.com> --------- Signed-off-by: Arvindh <arvindh91@gmail.com> Signed-off-by: dusanb94 <dusan.borovcanin@mainflux.com> * NOISSUE - Clean Up Users (#27) * feat(groups): rename redis package to events - Renamed the `redis` package to `events` in the `internal/groups` directory. - Updated the file paths and names accordingly. - This change reflects the more accurate purpose of the package and improves code organization. Signed-off-by: Rodney Osodo <28790446+rodneyosodo@users.noreply.github.com> * feat(auth): Modify identity method Change request and response of identity method Add accessToken and refreshToken to Token response Signed-off-by: Rodney Osodo <28790446+rodneyosodo@users.noreply.github.com> * clean up users, remove dead code Signed-off-by: Rodney Osodo <28790446+rodneyosodo@users.noreply.github.com> * feat(users): add unit tests for user service This commit adds unit tests for the user service in the `users` package. The tests cover various scenarios and ensure the correct behavior of the service. Signed-off-by: Rodney Osodo <28790446+rodneyosodo@users.noreply.github.com> --------- Signed-off-by: Rodney Osodo <28790446+rodneyosodo@users.noreply.github.com> Signed-off-by: dusanb94 <dusan.borovcanin@mainflux.com> * Add: List of user groups & removed repeating code in groups (#29) * removed repeating code in list groups Signed-off-by: Arvindh <arvindh91@gmail.com> * add: list of user group Signed-off-by: Arvindh <arvindh91@gmail.com> * fix: otel handler operator name for endpoints Signed-off-by: Arvindh <arvindh91@gmail.com> --------- Signed-off-by: Arvindh <arvindh91@gmail.com> Signed-off-by: dusanb94 <dusan.borovcanin@mainflux.com> * NOISSUE - Clean Up Things Service (#28) * Rework things service Signed-off-by: Rodney Osodo <28790446+rodneyosodo@users.noreply.github.com> * add tests Signed-off-by: Rodney Osodo <28790446+rodneyosodo@users.noreply.github.com> --------- Signed-off-by: Rodney Osodo <28790446+rodneyosodo@users.noreply.github.com> Signed-off-by: dusanb94 <dusan.borovcanin@mainflux.com> * NOISSUE - Clean Up Auth Service (#30) * clean up auth service Signed-off-by: Rodney Osodo <28790446+rodneyosodo@users.noreply.github.com> * feat(auth): remove unused import Remove the unused import of `emptypb` in `auth.pb.go`. This import is not being used in the codebase and can be safely removed. Signed-off-by: Rodney Osodo <28790446+rodneyosodo@users.noreply.github.com> --------- Signed-off-by: Rodney Osodo <28790446+rodneyosodo@users.noreply.github.com> Signed-off-by: dusanb94 <dusan.borovcanin@mainflux.com> * NOISSUE - Update API docs (#31) Signed-off-by: rodneyosodo <blackd0t@protonmail.com> Signed-off-by: dusanb94 <dusan.borovcanin@mainflux.com> * Remove TODO comments and cleanup the code Signed-off-by: dusanb94 <dusan.borovcanin@mainflux.com> * Update dependenices Signed-off-by: dusanb94 <dusan.borovcanin@mainflux.com> --------- Signed-off-by: Arvindh <arvindh91@gmail.com> Signed-off-by: dusanb94 <dusan.borovcanin@mainflux.com> Signed-off-by: Rodney Osodo <28790446+rodneyosodo@users.noreply.github.com> Signed-off-by: rodneyosodo <blackd0t@protonmail.com> Co-authored-by: b1ackd0t <28790446+rodneyosodo@users.noreply.github.com> Co-authored-by: Arvindh <30824765+arvindh123@users.noreply.github.com>
This commit is contained in:
@@ -3,7 +3,7 @@
|
||||
|
||||
MF_DOCKER_IMAGE_NAME_PREFIX ?= mainflux
|
||||
BUILD_DIR = build
|
||||
SERVICES = users things http coap ws lora influxdb-writer influxdb-reader mongodb-writer \
|
||||
SERVICES = auth users things http coap ws lora influxdb-writer influxdb-reader mongodb-writer \
|
||||
mongodb-reader cassandra-writer cassandra-reader postgres-writer postgres-reader timescale-writer timescale-reader cli \
|
||||
bootstrap opcua twins mqtt provision certs smtp-notifier smpp-notifier
|
||||
DOCKERS = $(addprefix docker_,$(SERVICES))
|
||||
@@ -113,8 +113,7 @@ test:
|
||||
|
||||
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 users/policies/*.proto
|
||||
protoc -I. --go_out=. --go_opt=paths=source_relative --go-grpc_out=. --go-grpc_opt=paths=source_relative things/policies/*.proto
|
||||
protoc -I. --go_out=. --go_opt=paths=source_relative --go-grpc_out=. --go-grpc_opt=paths=source_relative ./*.proto
|
||||
|
||||
$(FILTERED_SERVICES):
|
||||
$(call compile_service,$(@))
|
||||
|
||||
+792
-819
File diff suppressed because it is too large
Load Diff
+2056
File diff suppressed because it is too large
Load Diff
+169
@@ -0,0 +1,169 @@
|
||||
// Copyright (c) Mainflux
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
syntax = "proto3";
|
||||
|
||||
package mainflux;
|
||||
option go_package = "./mainflux";
|
||||
|
||||
// AuthzService is a service that provides authentication and authorization
|
||||
// functionalities for the things service.
|
||||
service AuthzService {
|
||||
// Authorize checks if the subject is authorized to perform
|
||||
// the action on the object.
|
||||
rpc Authorize(AuthorizeReq) returns (AuthorizeRes) {}
|
||||
}
|
||||
|
||||
// AuthService is a service that provides authentication and authorization
|
||||
// functionalities for the users service.
|
||||
service AuthService {
|
||||
rpc Issue(IssueReq) returns (Token) {}
|
||||
rpc Login(LoginReq) returns (Token) {}
|
||||
rpc Refresh(RefreshReq) returns (Token) {}
|
||||
rpc Identify(IdentityReq) returns (IdentityRes) {}
|
||||
rpc Authorize(AuthorizeReq) returns (AuthorizeRes) {}
|
||||
rpc AddPolicy(AddPolicyReq) returns (AddPolicyRes) {}
|
||||
rpc DeletePolicy(DeletePolicyReq) returns (DeletePolicyRes) {}
|
||||
rpc ListObjects(ListObjectsReq) returns (ListObjectsRes) {}
|
||||
rpc ListAllObjects(ListObjectsReq) returns (ListObjectsRes) {}
|
||||
rpc CountObjects(CountObjectsReq) returns (CountObjectsRes) {}
|
||||
rpc ListSubjects(ListSubjectsReq) returns (ListSubjectsRes) {}
|
||||
rpc ListAllSubjects(ListSubjectsReq) returns (ListSubjectsRes) {}
|
||||
rpc CountSubjects(CountSubjectsReq) returns (CountSubjectsRes) {}
|
||||
}
|
||||
|
||||
// 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 accessToken = 1;
|
||||
optional string refreshToken = 2;
|
||||
string accessType = 3;
|
||||
}
|
||||
|
||||
message IdentityReq {
|
||||
string token = 1;
|
||||
}
|
||||
|
||||
message IdentityRes {
|
||||
string id = 1;
|
||||
}
|
||||
|
||||
message IssueReq {
|
||||
string id = 1;
|
||||
uint32 type = 3;
|
||||
}
|
||||
|
||||
message LoginReq {
|
||||
string id = 1;
|
||||
string domain = 3;
|
||||
}
|
||||
|
||||
message RefreshReq { string value = 1; }
|
||||
|
||||
message AuthorizeReq {
|
||||
string namespace = 1; // Namespace = 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 AuthorizeRes {
|
||||
bool authorized = 1;
|
||||
string id = 2;
|
||||
}
|
||||
|
||||
message AddPolicyReq {
|
||||
string namespace = 1;
|
||||
string subject_type = 2;
|
||||
string subject_relation = 3;
|
||||
string subject = 4;
|
||||
string relation = 5;
|
||||
string permission = 6;
|
||||
string object = 7;
|
||||
string object_type = 8;
|
||||
}
|
||||
|
||||
message AddPolicyRes { bool authorized = 1; }
|
||||
|
||||
message DeletePolicyReq {
|
||||
string namespace = 1;
|
||||
string subject_type = 2;
|
||||
string subject_relation = 3;
|
||||
string subject = 4;
|
||||
string relation = 5;
|
||||
string permission = 6;
|
||||
string object = 7;
|
||||
string object_type = 8;
|
||||
}
|
||||
|
||||
message DeletePolicyRes { bool deleted = 1; }
|
||||
|
||||
message ListObjectsReq {
|
||||
string namespace = 1;
|
||||
string subject_type = 2;
|
||||
string subject_relation = 3;
|
||||
string subject = 4;
|
||||
string relation = 5;
|
||||
string permission = 6;
|
||||
string object = 7;
|
||||
string object_type = 8;
|
||||
string nextPageToken = 9;
|
||||
int32 limit = 10;
|
||||
}
|
||||
|
||||
message ListObjectsRes {
|
||||
repeated string policies = 1;
|
||||
string nextPageToken = 2;
|
||||
}
|
||||
|
||||
message CountObjectsReq {
|
||||
string namespace = 1;
|
||||
string subject_type = 2;
|
||||
string subject_relation = 3;
|
||||
string subject = 4;
|
||||
string relation = 5;
|
||||
string permission = 6;
|
||||
string object = 7;
|
||||
string object_type = 8;
|
||||
string nextPageToken = 9;
|
||||
}
|
||||
|
||||
message CountObjectsRes { int64 count = 1; }
|
||||
|
||||
message ListSubjectsReq {
|
||||
string namespace = 1;
|
||||
string subject_type = 2;
|
||||
string subject_relation = 3;
|
||||
string subject = 4;
|
||||
string relation = 5;
|
||||
string permission = 6;
|
||||
string object = 7;
|
||||
string object_type = 8;
|
||||
string nextPageToken = 9;
|
||||
int32 limit = 10;
|
||||
}
|
||||
|
||||
message ListSubjectsRes {
|
||||
repeated string policies = 1;
|
||||
string nextPageToken = 2;
|
||||
}
|
||||
|
||||
message CountSubjectsReq {
|
||||
string namespace = 1;
|
||||
string subject_type = 2;
|
||||
string subject_relation = 3;
|
||||
string subject = 4;
|
||||
string relation = 5;
|
||||
string permission = 6;
|
||||
string object = 7;
|
||||
string object_type = 8;
|
||||
string nextPageToken = 9;
|
||||
}
|
||||
|
||||
message CountSubjectsRes { int64 count = 1; }
|
||||
+113
@@ -0,0 +1,113 @@
|
||||
# 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`.
|
||||
|
||||
# Authentication
|
||||
User service is using Auth service gRPC API to obtain login token or password reset token. Authentication key consists of the following fields:
|
||||
- ID - key ID
|
||||
- Type - one of the three types described below
|
||||
- IssuerID - an ID of the Mainflux User who issued the key
|
||||
- Subject - user email
|
||||
- IssuedAt - the timestamp when the key is issued
|
||||
- ExpiresAt - the timestamp after which the key is invalid
|
||||
|
||||
There are *three types of authentication keys*:
|
||||
|
||||
- User key - keys issued to the user upon login request
|
||||
- API key - keys issued upon the user request
|
||||
- Recovery key - password recovery key
|
||||
|
||||
Authentication keys are represented and distributed by the corresponding [JWT](jwt.io).
|
||||
|
||||
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*.
|
||||
|
||||
Recovery key is the password recovery key. It's short-lived token used for password recovery process.
|
||||
|
||||
For in-depth explanation of the aforementioned scenarios, as well as thorough
|
||||
understanding of Mainflux, please check out the [official documentation][doc].
|
||||
|
||||
The following actions are supported:
|
||||
|
||||
- create (all key types)
|
||||
- verify (all key types)
|
||||
- obtain (API keys only)
|
||||
- revoke (API keys only)
|
||||
|
||||
# Groups
|
||||
User and Things service are using Auth gRPC API to get the list of ids that are part of a group. Groups can be organized as tree structure.
|
||||
Group consists of the following fields:
|
||||
|
||||
- ID - ULID id uniquely representing group
|
||||
- Name - name of the group, name of the group is unique at the same level of tree hierarchy for a given tree.
|
||||
- ParentID - id of the parent group
|
||||
- OwnerID - id of the user that created a group
|
||||
- Description - free form text, up to 1024 characters
|
||||
- Metadata - Arbitrary, object-encoded group's data
|
||||
- Path - tree path consisting of group ids
|
||||
- CreatedAt - timestamp at which the group is created
|
||||
- UpdatedAt - timestamp at which the group is updated
|
||||
|
||||
## 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 |
|
||||
|-------------------------------|--------------------------------------------------------------------------|----------------|
|
||||
| MF_AUTH_LOG_LEVEL | Service level (debug, info, warn, error) | error |
|
||||
| MF_AUTH_DB_HOST | Database host address | localhost |
|
||||
| MF_AUTH_DB_PORT | Database host port | 5432 |
|
||||
| MF_AUTH_DB_USER | Database user | mainflux |
|
||||
| MF_AUTH_DB_PASSWORD | Database password | mainflux |
|
||||
| MF_AUTH_DB | Name of the database used by the service | auth |
|
||||
| MF_AUTH_DB_SSL_MODE | Database connection SSL mode (disable, require, verify-ca, verify-full) | disable |
|
||||
| MF_AUTH_DB_SSL_CERT | Path to the PEM encoded certificate file | |
|
||||
| MF_AUTH_DB_SSL_KEY | Path to the PEM encoded key file | |
|
||||
| MF_AUTH_DB_SSL_ROOT_CERT | Path to the PEM encoded root certificate file | |
|
||||
| MF_AUTH_HTTP_PORT | Auth service HTTP port | 8180 |
|
||||
| MF_AUTH_GRPC_PORT | Auth service gRPC port | 8181 |
|
||||
| MF_AUTH_SERVER_CERT | Path to server certificate in pem format | |
|
||||
| MF_AUTH_SERVER_KEY | Path to server key in pem format | |
|
||||
| MF_AUTH_SECRET | String used for signing tokens | auth |
|
||||
| MF_AUTH_LOGIN_TOKEN_DURATION | The login token expiration period | 10h |
|
||||
| MF_JAEGER_URL | Jaeger server URL | localhost:6831 |
|
||||
| MF_KETO_READ_REMOTE_HOST | Keto Read Host | mainflux-keto |
|
||||
| MF_KETO_WRITE_REMOTE_HOST | Keto Write Host | mainflux-keto |
|
||||
| MF_KETO_READ_REMOTE_PORT | Keto Read Port | 4466 |
|
||||
| MF_KETO_WRITE_REMOTE_PORT | Keto Write Port | 4467 |
|
||||
|
||||
## Deployment
|
||||
|
||||
The service itself is distributed as Docker container. Check the [`auth`](https://github.com/mainflux/mainflux/blob/master/docker/docker-compose.yml#L71-L94) service section in
|
||||
docker-compose to see how service is deployed.
|
||||
|
||||
|
||||
To start the service outside of the container, execute the following shell script:
|
||||
|
||||
```bash
|
||||
# download the latest version of the service
|
||||
go get github.com/mainflux/mainflux
|
||||
|
||||
cd $GOPATH/src/github.com/mainflux/mainflux
|
||||
|
||||
# compile the service
|
||||
make auth
|
||||
|
||||
# copy binary to bin
|
||||
make install
|
||||
|
||||
# set the environment variables and run the service
|
||||
MF_AUTH_LOG_LEVEL=[Service log level] MF_AUTH_DB_HOST=[Database host address] MF_AUTH_DB_PORT=[Database host port] MF_AUTH_DB_USER=[Database user] MF_AUTH_DB_PASS=[Database password] MF_AUTH_DB=[Name of the database used by the service] MF_AUTH_DB_SSL_MODE=[SSL mode to connect to the database with] MF_AUTH_DB_SSL_CERT=[Path to the PEM encoded certificate file] MF_AUTH_DB_SSL_KEY=[Path to the PEM encoded key file] MF_AUTH_DB_SSL_ROOT_CERT=[Path to the PEM encoded root certificate file] MF_AUTH_HTTP_PORT=[Service HTTP port] MF_AUTH_GRPC_PORT=[Service gRPC port] MF_AUTH_SECRET=[String used for signing tokens] MF_AUTH_SERVER_CERT=[Path to server certificate] MF_AUTH_SERVER_KEY=[Path to server key] MF_JAEGER_URL=[Jaeger server URL] MF_AUTH_LOGIN_TOKEN_DURATION=[The login token expiration period] $GOBIN/mainflux-auth
|
||||
```
|
||||
|
||||
If `MF_EMAIL_TEMPLATE` doesn't point to any file service will function but password reset functionality will not work.
|
||||
|
||||
## Usage
|
||||
|
||||
For more information about service capabilities and its usage, please check out
|
||||
the [API documentation](https://api.mainflux.io/?urls.primaryName=auth-openapi.yml).
|
||||
|
||||
[doc]: https://docs.mainflux.io
|
||||
@@ -0,0 +1,5 @@
|
||||
// Copyright (c) Mainflux
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Package api contains implementation of Auth service HTTP API.
|
||||
package api
|
||||
@@ -0,0 +1,548 @@
|
||||
// Copyright (c) Mainflux
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package grpc
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"github.com/go-kit/kit/endpoint"
|
||||
kitgrpc "github.com/go-kit/kit/transport/grpc"
|
||||
"github.com/mainflux/mainflux"
|
||||
"github.com/mainflux/mainflux/auth"
|
||||
"google.golang.org/grpc"
|
||||
)
|
||||
|
||||
const svcName = "mainflux.AuthService"
|
||||
|
||||
var _ mainflux.AuthServiceClient = (*grpcClient)(nil)
|
||||
|
||||
type grpcClient struct {
|
||||
issue endpoint.Endpoint
|
||||
login endpoint.Endpoint
|
||||
refresh endpoint.Endpoint
|
||||
identify endpoint.Endpoint
|
||||
authorize endpoint.Endpoint
|
||||
addPolicy endpoint.Endpoint
|
||||
deletePolicy endpoint.Endpoint
|
||||
listObjects endpoint.Endpoint
|
||||
listAllObjects endpoint.Endpoint
|
||||
countObjects endpoint.Endpoint
|
||||
listSubjects endpoint.Endpoint
|
||||
listAllSubjects endpoint.Endpoint
|
||||
countSubjects endpoint.Endpoint
|
||||
timeout time.Duration
|
||||
}
|
||||
|
||||
// NewClient returns new gRPC client instance.
|
||||
func NewClient(conn *grpc.ClientConn, timeout time.Duration) mainflux.AuthServiceClient {
|
||||
return &grpcClient{
|
||||
issue: kitgrpc.NewClient(
|
||||
conn,
|
||||
svcName,
|
||||
"Issue",
|
||||
encodeIssueRequest,
|
||||
decodeIssueResponse,
|
||||
mainflux.Token{},
|
||||
).Endpoint(),
|
||||
login: kitgrpc.NewClient(
|
||||
conn,
|
||||
svcName,
|
||||
"Login",
|
||||
encodeLoginRequest,
|
||||
decodeLoginResponse,
|
||||
mainflux.Token{},
|
||||
).Endpoint(),
|
||||
refresh: kitgrpc.NewClient(
|
||||
conn,
|
||||
svcName,
|
||||
"Refresh",
|
||||
encodeRefreshRequest,
|
||||
decodeRefreshResponse,
|
||||
mainflux.Token{},
|
||||
).Endpoint(),
|
||||
identify: kitgrpc.NewClient(
|
||||
conn,
|
||||
svcName,
|
||||
"Identify",
|
||||
encodeIdentifyRequest,
|
||||
decodeIdentifyResponse,
|
||||
mainflux.IdentityRes{},
|
||||
).Endpoint(),
|
||||
authorize: kitgrpc.NewClient(
|
||||
conn,
|
||||
svcName,
|
||||
"Authorize",
|
||||
encodeAuthorizeRequest,
|
||||
decodeAuthorizeResponse,
|
||||
mainflux.AuthorizeRes{},
|
||||
).Endpoint(),
|
||||
addPolicy: kitgrpc.NewClient(
|
||||
conn,
|
||||
svcName,
|
||||
"AddPolicy",
|
||||
encodeAddPolicyRequest,
|
||||
decodeAddPolicyResponse,
|
||||
mainflux.AddPolicyRes{},
|
||||
).Endpoint(),
|
||||
deletePolicy: kitgrpc.NewClient(
|
||||
conn,
|
||||
svcName,
|
||||
"DeletePolicy",
|
||||
encodeDeletePolicyRequest,
|
||||
decodeDeletePolicyResponse,
|
||||
mainflux.DeletePolicyRes{},
|
||||
).Endpoint(),
|
||||
listObjects: kitgrpc.NewClient(
|
||||
conn,
|
||||
svcName,
|
||||
"ListObjects",
|
||||
encodeListObjectsRequest,
|
||||
decodeListObjectsResponse,
|
||||
mainflux.ListObjectsRes{},
|
||||
).Endpoint(),
|
||||
listAllObjects: kitgrpc.NewClient(
|
||||
conn,
|
||||
svcName,
|
||||
"ListAllObjects",
|
||||
encodeListObjectsRequest,
|
||||
decodeListObjectsResponse,
|
||||
mainflux.ListObjectsRes{},
|
||||
).Endpoint(),
|
||||
countObjects: kitgrpc.NewClient(
|
||||
conn,
|
||||
svcName,
|
||||
"CountObjects",
|
||||
encodeCountObjectsRequest,
|
||||
decodeCountObjectsResponse,
|
||||
mainflux.CountObjectsRes{},
|
||||
).Endpoint(),
|
||||
listSubjects: kitgrpc.NewClient(
|
||||
conn,
|
||||
svcName,
|
||||
"ListSubjects",
|
||||
encodeListSubjectsRequest,
|
||||
decodeListSubjectsResponse,
|
||||
mainflux.ListSubjectsRes{},
|
||||
).Endpoint(),
|
||||
listAllSubjects: kitgrpc.NewClient(
|
||||
conn,
|
||||
svcName,
|
||||
"ListAllSubjects",
|
||||
encodeListSubjectsRequest,
|
||||
decodeListSubjectsResponse,
|
||||
mainflux.ListSubjectsRes{},
|
||||
).Endpoint(),
|
||||
countSubjects: kitgrpc.NewClient(
|
||||
conn,
|
||||
svcName,
|
||||
"CountSubjects",
|
||||
encodeCountSubjectsRequest,
|
||||
decodeCountSubjectsResponse,
|
||||
mainflux.CountSubjectsRes{},
|
||||
).Endpoint(),
|
||||
|
||||
timeout: timeout,
|
||||
}
|
||||
}
|
||||
|
||||
func (client grpcClient) Issue(ctx context.Context, req *mainflux.IssueReq, _ ...grpc.CallOption) (*mainflux.Token, error) {
|
||||
ctx, close := context.WithTimeout(ctx, client.timeout)
|
||||
defer close()
|
||||
|
||||
res, err := client.issue(ctx, issueReq{id: req.GetId(), keyType: auth.KeyType(req.Type)})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return res.(*mainflux.Token), nil
|
||||
}
|
||||
|
||||
func encodeIssueRequest(_ context.Context, grpcReq interface{}) (interface{}, error) {
|
||||
req := grpcReq.(issueReq)
|
||||
return &mainflux.IssueReq{Id: req.id, Type: uint32(req.keyType)}, nil
|
||||
}
|
||||
|
||||
func decodeIssueResponse(_ context.Context, grpcRes interface{}) (interface{}, error) {
|
||||
return grpcRes, nil
|
||||
}
|
||||
|
||||
func (client grpcClient) Login(ctx context.Context, req *mainflux.LoginReq, _ ...grpc.CallOption) (*mainflux.Token, error) {
|
||||
ctx, close := context.WithTimeout(ctx, client.timeout)
|
||||
defer close()
|
||||
|
||||
res, err := client.login(ctx, issueReq{id: req.GetId(), keyType: auth.APIKey})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return res.(*mainflux.Token), nil
|
||||
}
|
||||
|
||||
func encodeLoginRequest(_ context.Context, grpcReq interface{}) (interface{}, error) {
|
||||
req := grpcReq.(issueReq)
|
||||
return &mainflux.LoginReq{Id: req.id}, nil
|
||||
}
|
||||
|
||||
func decodeLoginResponse(_ context.Context, grpcRes interface{}) (interface{}, error) {
|
||||
return grpcRes, nil
|
||||
}
|
||||
|
||||
func (client grpcClient) Refresh(ctx context.Context, req *mainflux.RefreshReq, _ ...grpc.CallOption) (*mainflux.Token, error) {
|
||||
ctx, close := context.WithTimeout(ctx, client.timeout)
|
||||
defer close()
|
||||
|
||||
res, err := client.refresh(ctx, refreshReq{value: req.GetValue()})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return res.(*mainflux.Token), nil
|
||||
}
|
||||
|
||||
func encodeRefreshRequest(_ context.Context, grpcReq interface{}) (interface{}, error) {
|
||||
req := grpcReq.(refreshReq)
|
||||
return &mainflux.RefreshReq{Value: req.value}, nil
|
||||
}
|
||||
|
||||
func decodeRefreshResponse(_ context.Context, grpcRes interface{}) (interface{}, error) {
|
||||
return grpcRes, nil
|
||||
}
|
||||
|
||||
func (client grpcClient) Identify(ctx context.Context, token *mainflux.IdentityReq, _ ...grpc.CallOption) (*mainflux.IdentityRes, error) {
|
||||
ctx, close := context.WithTimeout(ctx, client.timeout)
|
||||
defer close()
|
||||
|
||||
res, err := client.identify(ctx, identityReq{token: token.GetToken()})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ir := res.(identityRes)
|
||||
return &mainflux.IdentityRes{Id: ir.id}, nil
|
||||
}
|
||||
|
||||
func encodeIdentifyRequest(_ context.Context, grpcReq interface{}) (interface{}, error) {
|
||||
req := grpcReq.(identityReq)
|
||||
return &mainflux.IdentityReq{Token: req.token}, nil
|
||||
}
|
||||
|
||||
func decodeIdentifyResponse(_ context.Context, grpcRes interface{}) (interface{}, error) {
|
||||
res := grpcRes.(*mainflux.IdentityRes)
|
||||
return identityRes{id: res.GetId()}, nil
|
||||
}
|
||||
|
||||
func (client grpcClient) Authorize(ctx context.Context, req *mainflux.AuthorizeReq, _ ...grpc.CallOption) (r *mainflux.AuthorizeRes, err error) {
|
||||
ctx, close := context.WithTimeout(ctx, client.timeout)
|
||||
defer close()
|
||||
|
||||
res, err := client.authorize(ctx, authReq{
|
||||
Namespace: req.GetNamespace(),
|
||||
SubjectType: req.GetSubjectType(),
|
||||
Subject: req.GetSubject(),
|
||||
SubjectKind: req.GetSubjectKind(),
|
||||
Relation: req.GetRelation(),
|
||||
Permission: req.GetPermission(),
|
||||
ObjectType: req.GetObjectType(),
|
||||
Object: req.GetObject(),
|
||||
})
|
||||
if err != nil {
|
||||
return &mainflux.AuthorizeRes{}, err
|
||||
}
|
||||
|
||||
ar := res.(authorizeRes)
|
||||
return &mainflux.AuthorizeRes{Authorized: ar.authorized, Id: ar.id}, err
|
||||
}
|
||||
|
||||
func decodeAuthorizeResponse(_ context.Context, grpcRes interface{}) (interface{}, error) {
|
||||
res := grpcRes.(*mainflux.AuthorizeRes)
|
||||
return authorizeRes{authorized: res.Authorized, id: res.Id}, nil
|
||||
}
|
||||
|
||||
func encodeAuthorizeRequest(_ context.Context, grpcReq interface{}) (interface{}, error) {
|
||||
req := grpcReq.(authReq)
|
||||
return &mainflux.AuthorizeReq{
|
||||
Namespace: req.Namespace,
|
||||
SubjectType: req.SubjectType,
|
||||
Subject: req.Subject,
|
||||
SubjectKind: req.SubjectKind,
|
||||
Relation: req.Relation,
|
||||
Permission: req.Permission,
|
||||
ObjectType: req.ObjectType,
|
||||
Object: req.Object,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (client grpcClient) AddPolicy(ctx context.Context, in *mainflux.AddPolicyReq, opts ...grpc.CallOption) (*mainflux.AddPolicyRes, error) {
|
||||
ctx, close := context.WithTimeout(ctx, client.timeout)
|
||||
defer close()
|
||||
|
||||
res, err := client.addPolicy(ctx, policyReq{
|
||||
Namespace: in.GetNamespace(),
|
||||
SubjectType: in.GetSubjectType(),
|
||||
Subject: in.GetSubject(),
|
||||
Relation: in.GetRelation(),
|
||||
Permission: in.GetPermission(),
|
||||
ObjectType: in.GetObjectType(),
|
||||
Object: in.GetObject(),
|
||||
})
|
||||
if err != nil {
|
||||
return &mainflux.AddPolicyRes{}, err
|
||||
}
|
||||
|
||||
apr := res.(addPolicyRes)
|
||||
return &mainflux.AddPolicyRes{Authorized: apr.authorized}, err
|
||||
}
|
||||
|
||||
func decodeAddPolicyResponse(_ context.Context, grpcRes interface{}) (interface{}, error) {
|
||||
res := grpcRes.(*mainflux.AddPolicyRes)
|
||||
return addPolicyRes{authorized: res.Authorized}, nil
|
||||
}
|
||||
|
||||
func encodeAddPolicyRequest(_ context.Context, grpcReq interface{}) (interface{}, error) {
|
||||
req := grpcReq.(policyReq)
|
||||
return &mainflux.AddPolicyReq{
|
||||
Namespace: req.Namespace,
|
||||
SubjectType: req.SubjectType,
|
||||
Subject: req.Subject,
|
||||
Relation: req.Relation,
|
||||
Permission: req.Permission,
|
||||
ObjectType: req.ObjectType,
|
||||
Object: req.Object,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (client grpcClient) DeletePolicy(ctx context.Context, in *mainflux.DeletePolicyReq, opts ...grpc.CallOption) (*mainflux.DeletePolicyRes, error) {
|
||||
ctx, close := context.WithTimeout(ctx, client.timeout)
|
||||
defer close()
|
||||
|
||||
res, err := client.deletePolicy(ctx, policyReq{
|
||||
Namespace: in.GetNamespace(),
|
||||
SubjectType: in.GetSubjectType(),
|
||||
Subject: in.GetSubject(),
|
||||
Relation: in.GetRelation(),
|
||||
Permission: in.GetPermission(),
|
||||
ObjectType: in.GetObjectType(),
|
||||
Object: in.GetObject(),
|
||||
})
|
||||
if err != nil {
|
||||
return &mainflux.DeletePolicyRes{}, err
|
||||
}
|
||||
|
||||
dpr := res.(deletePolicyRes)
|
||||
return &mainflux.DeletePolicyRes{Deleted: dpr.deleted}, err
|
||||
}
|
||||
|
||||
func decodeDeletePolicyResponse(_ context.Context, grpcRes interface{}) (interface{}, error) {
|
||||
res := grpcRes.(*mainflux.DeletePolicyRes)
|
||||
return deletePolicyRes{deleted: res.GetDeleted()}, nil
|
||||
}
|
||||
|
||||
func encodeDeletePolicyRequest(_ context.Context, grpcReq interface{}) (interface{}, error) {
|
||||
req := grpcReq.(policyReq)
|
||||
return &mainflux.DeletePolicyReq{
|
||||
Namespace: req.Namespace,
|
||||
SubjectType: req.SubjectType,
|
||||
Subject: req.Subject,
|
||||
Relation: req.Relation,
|
||||
Permission: req.Permission,
|
||||
ObjectType: req.ObjectType,
|
||||
Object: req.Object,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (client grpcClient) ListObjects(ctx context.Context, in *mainflux.ListObjectsReq, opts ...grpc.CallOption) (*mainflux.ListObjectsRes, error) {
|
||||
ctx, close := context.WithTimeout(ctx, client.timeout)
|
||||
defer close()
|
||||
|
||||
res, err := client.listObjects(ctx, listObjectsReq{
|
||||
Namespace: in.GetNamespace(),
|
||||
SubjectType: in.GetSubjectType(),
|
||||
Subject: in.GetSubject(),
|
||||
Relation: in.GetRelation(),
|
||||
Permission: in.GetPermission(),
|
||||
ObjectType: in.GetObjectType(),
|
||||
Object: in.GetObject(),
|
||||
})
|
||||
if err != nil {
|
||||
return &mainflux.ListObjectsRes{}, err
|
||||
}
|
||||
|
||||
lpr := res.(listObjectsRes)
|
||||
return &mainflux.ListObjectsRes{Policies: lpr.policies}, err
|
||||
}
|
||||
|
||||
func decodeListObjectsResponse(_ context.Context, grpcRes interface{}) (interface{}, error) {
|
||||
res := grpcRes.(*mainflux.ListObjectsRes)
|
||||
return listObjectsRes{policies: res.GetPolicies()}, nil
|
||||
}
|
||||
|
||||
func encodeListObjectsRequest(_ context.Context, grpcReq interface{}) (interface{}, error) {
|
||||
req := grpcReq.(listObjectsReq)
|
||||
return &mainflux.ListObjectsReq{
|
||||
Namespace: req.Namespace,
|
||||
SubjectType: req.SubjectType,
|
||||
Subject: req.Subject,
|
||||
Relation: req.Relation,
|
||||
Permission: req.Permission,
|
||||
ObjectType: req.ObjectType,
|
||||
Object: req.Object,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (client grpcClient) ListAllObjects(ctx context.Context, in *mainflux.ListObjectsReq, opts ...grpc.CallOption) (*mainflux.ListObjectsRes, error) {
|
||||
ctx, close := context.WithTimeout(ctx, client.timeout)
|
||||
defer close()
|
||||
|
||||
res, err := client.listAllObjects(ctx, listObjectsReq{
|
||||
Namespace: in.GetNamespace(),
|
||||
SubjectType: in.GetSubjectType(),
|
||||
Subject: in.GetSubject(),
|
||||
Relation: in.GetRelation(),
|
||||
Permission: in.GetPermission(),
|
||||
ObjectType: in.GetObjectType(),
|
||||
Object: in.GetObject(),
|
||||
})
|
||||
if err != nil {
|
||||
return &mainflux.ListObjectsRes{}, err
|
||||
}
|
||||
|
||||
lpr := res.(listObjectsRes)
|
||||
return &mainflux.ListObjectsRes{Policies: lpr.policies}, err
|
||||
}
|
||||
|
||||
func (client grpcClient) CountObjects(ctx context.Context, in *mainflux.CountObjectsReq, opts ...grpc.CallOption) (*mainflux.CountObjectsRes, error) {
|
||||
ctx, close := context.WithTimeout(ctx, client.timeout)
|
||||
defer close()
|
||||
|
||||
res, err := client.countObjects(ctx, listObjectsReq{
|
||||
Namespace: in.GetNamespace(),
|
||||
SubjectType: in.GetSubjectType(),
|
||||
Subject: in.GetSubject(),
|
||||
Relation: in.GetRelation(),
|
||||
Permission: in.GetPermission(),
|
||||
ObjectType: in.GetObjectType(),
|
||||
Object: in.GetObject(),
|
||||
})
|
||||
if err != nil {
|
||||
return &mainflux.CountObjectsRes{}, err
|
||||
}
|
||||
|
||||
cp := res.(countObjectsRes)
|
||||
return &mainflux.CountObjectsRes{Count: int64(cp.count)}, err
|
||||
}
|
||||
|
||||
func decodeCountObjectsResponse(_ context.Context, grpcRes interface{}) (interface{}, error) {
|
||||
res := grpcRes.(*mainflux.CountObjectsRes)
|
||||
return countObjectsRes{count: int(res.GetCount())}, nil
|
||||
}
|
||||
|
||||
func encodeCountObjectsRequest(_ context.Context, grpcReq interface{}) (interface{}, error) {
|
||||
req := grpcReq.(countObjectsReq)
|
||||
return &mainflux.CountObjectsReq{
|
||||
Namespace: req.Namespace,
|
||||
SubjectType: req.SubjectType,
|
||||
Subject: req.Subject,
|
||||
Relation: req.Relation,
|
||||
Permission: req.Permission,
|
||||
ObjectType: req.ObjectType,
|
||||
Object: req.Object,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (client grpcClient) ListSubjects(ctx context.Context, in *mainflux.ListSubjectsReq, opts ...grpc.CallOption) (*mainflux.ListSubjectsRes, error) {
|
||||
ctx, close := context.WithTimeout(ctx, client.timeout)
|
||||
defer close()
|
||||
|
||||
res, err := client.listSubjects(ctx, listSubjectsReq{
|
||||
Namespace: in.GetNamespace(),
|
||||
SubjectType: in.GetSubjectType(),
|
||||
Subject: in.GetSubject(),
|
||||
Relation: in.GetRelation(),
|
||||
Permission: in.GetPermission(),
|
||||
ObjectType: in.GetObjectType(),
|
||||
Object: in.GetObject(),
|
||||
NextPageToken: in.GetNextPageToken()})
|
||||
if err != nil {
|
||||
return &mainflux.ListSubjectsRes{}, err
|
||||
}
|
||||
|
||||
lpr := res.(listSubjectsRes)
|
||||
return &mainflux.ListSubjectsRes{Policies: lpr.policies}, err
|
||||
}
|
||||
|
||||
func decodeListSubjectsResponse(_ context.Context, grpcRes interface{}) (interface{}, error) {
|
||||
res := grpcRes.(*mainflux.ListSubjectsRes)
|
||||
return listSubjectsRes{policies: res.GetPolicies()}, nil
|
||||
}
|
||||
|
||||
func encodeListSubjectsRequest(_ context.Context, grpcReq interface{}) (interface{}, error) {
|
||||
req := grpcReq.(listSubjectsReq)
|
||||
return &mainflux.ListSubjectsReq{
|
||||
Namespace: req.Namespace,
|
||||
SubjectType: req.SubjectType,
|
||||
Subject: req.Subject,
|
||||
Relation: req.Relation,
|
||||
Permission: req.Permission,
|
||||
ObjectType: req.ObjectType,
|
||||
Object: req.Object,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (client grpcClient) ListAllSubjects(ctx context.Context, in *mainflux.ListSubjectsReq, opts ...grpc.CallOption) (*mainflux.ListSubjectsRes, error) {
|
||||
ctx, close := context.WithTimeout(ctx, client.timeout)
|
||||
defer close()
|
||||
|
||||
res, err := client.listAllSubjects(ctx, listSubjectsReq{
|
||||
Namespace: in.GetNamespace(),
|
||||
SubjectType: in.GetSubjectType(),
|
||||
Subject: in.GetSubject(),
|
||||
Relation: in.GetRelation(),
|
||||
Permission: in.GetPermission(),
|
||||
ObjectType: in.GetObjectType(),
|
||||
Object: in.GetObject(),
|
||||
})
|
||||
if err != nil {
|
||||
return &mainflux.ListSubjectsRes{}, err
|
||||
}
|
||||
|
||||
lpr := res.(listSubjectsRes)
|
||||
return &mainflux.ListSubjectsRes{Policies: lpr.policies}, err
|
||||
}
|
||||
|
||||
func (client grpcClient) CountSubjects(ctx context.Context, in *mainflux.CountSubjectsReq, opts ...grpc.CallOption) (*mainflux.CountSubjectsRes, error) {
|
||||
ctx, close := context.WithTimeout(ctx, client.timeout)
|
||||
defer close()
|
||||
|
||||
res, err := client.countSubjects(ctx, countSubjectsReq{
|
||||
Namespace: in.GetNamespace(),
|
||||
SubjectType: in.GetSubjectType(),
|
||||
Subject: in.GetSubject(),
|
||||
Relation: in.GetRelation(),
|
||||
Permission: in.GetPermission(),
|
||||
ObjectType: in.GetObjectType(),
|
||||
Object: in.GetObject(),
|
||||
})
|
||||
if err != nil {
|
||||
return &mainflux.CountSubjectsRes{}, err
|
||||
}
|
||||
|
||||
cp := res.(countSubjectsRes)
|
||||
return &mainflux.CountSubjectsRes{Count: int64(cp.count)}, err
|
||||
}
|
||||
|
||||
func decodeCountSubjectsResponse(_ context.Context, grpcRes interface{}) (interface{}, error) {
|
||||
res := grpcRes.(*mainflux.CountSubjectsRes)
|
||||
return countSubjectsRes{count: int(res.GetCount())}, nil
|
||||
}
|
||||
|
||||
func encodeCountSubjectsRequest(_ context.Context, grpcReq interface{}) (interface{}, error) {
|
||||
req := grpcReq.(countSubjectsReq)
|
||||
return &mainflux.CountSubjectsReq{
|
||||
Namespace: req.Namespace,
|
||||
SubjectType: req.SubjectType,
|
||||
Subject: req.Subject,
|
||||
Relation: req.Relation,
|
||||
Permission: req.Permission,
|
||||
ObjectType: req.ObjectType,
|
||||
Object: req.Object,
|
||||
}, nil
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
// Copyright (c) Mainflux
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Package grpc contains implementation of things service gRPC API.
|
||||
// Package grpc contains implementation of Auth service gRPC API.
|
||||
package grpc
|
||||
@@ -0,0 +1,288 @@
|
||||
// Copyright (c) Mainflux
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package grpc
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"github.com/go-kit/kit/endpoint"
|
||||
"github.com/mainflux/mainflux/auth"
|
||||
)
|
||||
|
||||
func issueEndpoint(svc auth.Service) endpoint.Endpoint {
|
||||
return func(ctx context.Context, request interface{}) (interface{}, error) {
|
||||
req := request.(issueReq)
|
||||
if err := req.validate(); err != nil {
|
||||
return issueRes{}, err
|
||||
}
|
||||
|
||||
key := auth.Key{
|
||||
Type: req.keyType,
|
||||
Subject: req.id,
|
||||
}
|
||||
tkn, err := svc.Issue(ctx, "", key)
|
||||
if err != nil {
|
||||
return issueRes{}, err
|
||||
}
|
||||
ret := issueRes{
|
||||
accessToken: tkn.AccessToken,
|
||||
refreshToken: tkn.RefreshToken,
|
||||
accessType: tkn.AccessType,
|
||||
}
|
||||
return ret, nil
|
||||
}
|
||||
}
|
||||
|
||||
func loginEndpoint(svc auth.Service) endpoint.Endpoint {
|
||||
return func(ctx context.Context, request interface{}) (interface{}, error) {
|
||||
req := request.(issueReq)
|
||||
if err := req.validate(); err != nil {
|
||||
return issueRes{}, err
|
||||
}
|
||||
|
||||
key := auth.Key{
|
||||
Type: req.keyType,
|
||||
Subject: req.id,
|
||||
IssuedAt: time.Now().UTC(),
|
||||
}
|
||||
tkn, err := svc.Issue(ctx, "", key)
|
||||
if err != nil {
|
||||
return issueRes{}, err
|
||||
}
|
||||
ret := issueRes{
|
||||
accessToken: tkn.AccessToken,
|
||||
refreshToken: tkn.RefreshToken,
|
||||
accessType: tkn.AccessType,
|
||||
}
|
||||
return ret, nil
|
||||
}
|
||||
}
|
||||
|
||||
func refreshEndpoint(svc auth.Service) endpoint.Endpoint {
|
||||
return func(ctx context.Context, request interface{}) (interface{}, error) {
|
||||
req := request.(refreshReq)
|
||||
if err := req.validate(); err != nil {
|
||||
return issueRes{}, err
|
||||
}
|
||||
|
||||
key := auth.Key{Type: auth.RefreshKey}
|
||||
tkn, err := svc.Issue(ctx, req.value, key)
|
||||
if err != nil {
|
||||
return issueRes{}, err
|
||||
}
|
||||
ret := issueRes{
|
||||
accessToken: tkn.AccessToken,
|
||||
refreshToken: tkn.RefreshToken,
|
||||
accessType: tkn.AccessType,
|
||||
}
|
||||
return ret, nil
|
||||
}
|
||||
}
|
||||
|
||||
func identifyEndpoint(svc auth.Service) endpoint.Endpoint {
|
||||
return func(ctx context.Context, request interface{}) (interface{}, error) {
|
||||
req := request.(identityReq)
|
||||
if err := req.validate(); err != nil {
|
||||
return identityRes{}, err
|
||||
}
|
||||
|
||||
id, err := svc.Identify(ctx, req.token)
|
||||
if err != nil {
|
||||
return identityRes{}, err
|
||||
}
|
||||
|
||||
return identityRes{id: id}, nil
|
||||
}
|
||||
}
|
||||
|
||||
func authorizeEndpoint(svc auth.Service) endpoint.Endpoint {
|
||||
return func(ctx context.Context, request interface{}) (interface{}, error) {
|
||||
req := request.(authReq)
|
||||
|
||||
if err := req.validate(); err != nil {
|
||||
return authorizeRes{}, err
|
||||
}
|
||||
|
||||
err := svc.Authorize(ctx, auth.PolicyReq{
|
||||
Namespace: req.Namespace,
|
||||
SubjectType: req.SubjectType,
|
||||
SubjectKind: req.SubjectKind,
|
||||
Subject: req.Subject,
|
||||
Relation: req.Relation,
|
||||
Permission: req.Permission,
|
||||
ObjectType: req.ObjectType,
|
||||
Object: req.Object,
|
||||
})
|
||||
if err != nil {
|
||||
return authorizeRes{authorized: false}, err
|
||||
}
|
||||
return authorizeRes{authorized: true}, nil
|
||||
}
|
||||
}
|
||||
|
||||
func addPolicyEndpoint(svc auth.Service) endpoint.Endpoint {
|
||||
return func(ctx context.Context, request interface{}) (interface{}, error) {
|
||||
req := request.(policyReq)
|
||||
if err := req.validate(); err != nil {
|
||||
return addPolicyRes{}, err
|
||||
}
|
||||
|
||||
err := svc.AddPolicy(ctx, auth.PolicyReq{
|
||||
Namespace: req.Namespace,
|
||||
SubjectType: req.SubjectType,
|
||||
Subject: req.Subject,
|
||||
Relation: req.Relation,
|
||||
Permission: req.Permission,
|
||||
ObjectType: req.ObjectType,
|
||||
Object: req.Object})
|
||||
if err != nil {
|
||||
return addPolicyRes{}, err
|
||||
}
|
||||
return addPolicyRes{authorized: true}, err
|
||||
}
|
||||
}
|
||||
|
||||
func deletePolicyEndpoint(svc auth.Service) endpoint.Endpoint {
|
||||
return func(ctx context.Context, request interface{}) (interface{}, error) {
|
||||
req := request.(policyReq)
|
||||
if err := req.validate(); err != nil {
|
||||
return deletePolicyRes{}, err
|
||||
}
|
||||
|
||||
err := svc.DeletePolicy(ctx, auth.PolicyReq{
|
||||
Namespace: req.Namespace,
|
||||
SubjectType: req.SubjectType,
|
||||
Subject: req.Subject,
|
||||
Relation: req.Relation,
|
||||
Permission: req.Permission,
|
||||
ObjectType: req.ObjectType,
|
||||
Object: req.Object,
|
||||
})
|
||||
if err != nil {
|
||||
return deletePolicyRes{}, err
|
||||
}
|
||||
return deletePolicyRes{deleted: true}, nil
|
||||
}
|
||||
}
|
||||
|
||||
func listObjectsEndpoint(svc auth.Service) endpoint.Endpoint {
|
||||
return func(ctx context.Context, request interface{}) (interface{}, error) {
|
||||
req := request.(listObjectsReq)
|
||||
|
||||
page, err := svc.ListObjects(ctx, auth.PolicyReq{
|
||||
Namespace: req.Namespace,
|
||||
SubjectType: req.SubjectType,
|
||||
Subject: req.Subject,
|
||||
Relation: req.Relation,
|
||||
Permission: req.Permission,
|
||||
ObjectType: req.ObjectType,
|
||||
Object: req.Object,
|
||||
}, req.NextPageToken, req.Limit)
|
||||
if err != nil {
|
||||
return listObjectsRes{}, err
|
||||
}
|
||||
return listObjectsRes{policies: page.Policies, nextPageToken: page.NextPageToken}, nil
|
||||
}
|
||||
}
|
||||
|
||||
func listAllObjectsEndpoint(svc auth.Service) endpoint.Endpoint {
|
||||
return func(ctx context.Context, request interface{}) (interface{}, error) {
|
||||
req := request.(listObjectsReq)
|
||||
|
||||
page, err := svc.ListAllObjects(ctx, auth.PolicyReq{
|
||||
Namespace: req.Namespace,
|
||||
SubjectType: req.SubjectType,
|
||||
Subject: req.Subject,
|
||||
Relation: req.Relation,
|
||||
Permission: req.Permission,
|
||||
ObjectType: req.ObjectType,
|
||||
Object: req.Object,
|
||||
})
|
||||
if err != nil {
|
||||
return listObjectsRes{}, err
|
||||
}
|
||||
return listObjectsRes{policies: page.Policies, nextPageToken: page.NextPageToken}, nil
|
||||
}
|
||||
}
|
||||
|
||||
func countObjectsEndpoint(svc auth.Service) endpoint.Endpoint {
|
||||
return func(ctx context.Context, request interface{}) (interface{}, error) {
|
||||
req := request.(countObjectsReq)
|
||||
|
||||
count, err := svc.CountObjects(ctx, auth.PolicyReq{
|
||||
Namespace: req.Namespace,
|
||||
SubjectType: req.SubjectType,
|
||||
Subject: req.Subject,
|
||||
Relation: req.Relation,
|
||||
Permission: req.Permission,
|
||||
ObjectType: req.ObjectType,
|
||||
Object: req.Object,
|
||||
})
|
||||
if err != nil {
|
||||
return countObjectsRes{}, err
|
||||
}
|
||||
return countObjectsRes{count: count}, nil
|
||||
}
|
||||
}
|
||||
|
||||
func listSubjectsEndpoint(svc auth.Service) endpoint.Endpoint {
|
||||
return func(ctx context.Context, request interface{}) (interface{}, error) {
|
||||
req := request.(listSubjectsReq)
|
||||
|
||||
page, err := svc.ListSubjects(ctx, auth.PolicyReq{
|
||||
Namespace: req.Namespace,
|
||||
SubjectType: req.SubjectType,
|
||||
Subject: req.Subject,
|
||||
Relation: req.Relation,
|
||||
Permission: req.Permission,
|
||||
ObjectType: req.ObjectType,
|
||||
Object: req.Object,
|
||||
}, req.NextPageToken, req.Limit)
|
||||
if err != nil {
|
||||
return listSubjectsRes{}, err
|
||||
}
|
||||
return listSubjectsRes{policies: page.Policies, nextPageToken: page.NextPageToken}, nil
|
||||
}
|
||||
}
|
||||
|
||||
func listAllSubjectsEndpoint(svc auth.Service) endpoint.Endpoint {
|
||||
return func(ctx context.Context, request interface{}) (interface{}, error) {
|
||||
req := request.(listSubjectsReq)
|
||||
|
||||
page, err := svc.ListAllSubjects(ctx, auth.PolicyReq{
|
||||
Namespace: req.Namespace,
|
||||
SubjectType: req.SubjectType,
|
||||
Subject: req.Subject,
|
||||
Relation: req.Relation,
|
||||
Permission: req.Permission,
|
||||
ObjectType: req.ObjectType,
|
||||
Object: req.Object,
|
||||
})
|
||||
if err != nil {
|
||||
return listSubjectsRes{}, err
|
||||
}
|
||||
return listSubjectsRes{policies: page.Policies, nextPageToken: page.NextPageToken}, nil
|
||||
}
|
||||
}
|
||||
|
||||
func countSubjectsEndpoint(svc auth.Service) endpoint.Endpoint {
|
||||
return func(ctx context.Context, request interface{}) (interface{}, error) {
|
||||
req := request.(countSubjectsReq)
|
||||
|
||||
count, err := svc.CountSubjects(ctx, auth.PolicyReq{
|
||||
Namespace: req.Namespace,
|
||||
SubjectType: req.SubjectType,
|
||||
Subject: req.Subject,
|
||||
Relation: req.Relation,
|
||||
Permission: req.Permission,
|
||||
ObjectType: req.ObjectType,
|
||||
Object: req.Object,
|
||||
})
|
||||
if err != nil {
|
||||
return countSubjectsRes{}, err
|
||||
}
|
||||
return countSubjectsRes{count: count}, nil
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,383 @@
|
||||
// Copyright (c) Mainflux
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package grpc_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/mainflux/mainflux"
|
||||
"github.com/mainflux/mainflux/auth"
|
||||
grpcapi "github.com/mainflux/mainflux/auth/api/grpc"
|
||||
"github.com/mainflux/mainflux/auth/jwt"
|
||||
"github.com/mainflux/mainflux/auth/mocks"
|
||||
"github.com/mainflux/mainflux/pkg/uuid"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/grpc/codes"
|
||||
"google.golang.org/grpc/credentials/insecure"
|
||||
"google.golang.org/grpc/status"
|
||||
)
|
||||
|
||||
const (
|
||||
port = 8081
|
||||
secret = "secret"
|
||||
email = "test@example.com"
|
||||
id = "testID"
|
||||
thingsType = "things"
|
||||
usersType = "users"
|
||||
description = "Description"
|
||||
|
||||
numOfThings = 5
|
||||
numOfUsers = 5
|
||||
|
||||
authoritiesObj = "authorities"
|
||||
memberRelation = "member"
|
||||
loginDuration = 30 * time.Minute
|
||||
refreshDuration = 24 * time.Hour
|
||||
)
|
||||
|
||||
var svc auth.Service
|
||||
|
||||
func newService() auth.Service {
|
||||
krepo := new(mocks.Keys)
|
||||
prepo := new(mocks.PolicyAgent)
|
||||
idProvider := uuid.NewMock()
|
||||
|
||||
t := jwt.New([]byte(secret))
|
||||
|
||||
return auth.New(krepo, idProvider, t, prepo, loginDuration, refreshDuration)
|
||||
}
|
||||
|
||||
func startGRPCServer(svc auth.Service, port int) {
|
||||
listener, _ := net.Listen("tcp", fmt.Sprintf(":%d", port))
|
||||
server := grpc.NewServer()
|
||||
mainflux.RegisterAuthServiceServer(server, grpcapi.NewServer(svc))
|
||||
go func() {
|
||||
if err := server.Serve(listener); err != nil {
|
||||
panic(fmt.Sprintf("failed to serve: %s", err))
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
func TestIssue(t *testing.T) {
|
||||
authAddr := fmt.Sprintf("localhost:%d", port)
|
||||
conn, _ := grpc.Dial(authAddr, grpc.WithTransportCredentials(insecure.NewCredentials()))
|
||||
client := grpcapi.NewClient(conn, time.Second)
|
||||
|
||||
cases := []struct {
|
||||
desc string
|
||||
id string
|
||||
email string
|
||||
kind auth.KeyType
|
||||
err error
|
||||
code codes.Code
|
||||
}{
|
||||
{
|
||||
desc: "issue for user with valid token",
|
||||
id: id,
|
||||
email: email,
|
||||
kind: auth.AccessKey,
|
||||
err: nil,
|
||||
code: codes.OK,
|
||||
},
|
||||
{
|
||||
desc: "issue recovery key",
|
||||
id: id,
|
||||
email: email,
|
||||
kind: auth.RecoveryKey,
|
||||
err: nil,
|
||||
code: codes.OK,
|
||||
},
|
||||
{
|
||||
desc: "issue API key unauthenticated",
|
||||
id: id,
|
||||
email: email,
|
||||
kind: auth.APIKey,
|
||||
err: nil,
|
||||
code: codes.Unauthenticated,
|
||||
},
|
||||
{
|
||||
desc: "issue for invalid key type",
|
||||
id: id,
|
||||
email: email,
|
||||
kind: 32,
|
||||
err: status.Error(codes.InvalidArgument, "received invalid token request"),
|
||||
code: codes.InvalidArgument,
|
||||
},
|
||||
{
|
||||
desc: "issue for user that exist",
|
||||
id: "",
|
||||
email: "",
|
||||
kind: auth.APIKey,
|
||||
err: status.Error(codes.Unauthenticated, "unauthenticated access"),
|
||||
code: codes.Unauthenticated,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range cases {
|
||||
_, err := client.Issue(context.Background(), &mainflux.IssueReq{Id: tc.id, Type: uint32(tc.kind)})
|
||||
e, ok := status.FromError(err)
|
||||
assert.True(t, ok, "gRPC status can't be extracted from the error")
|
||||
assert.Equal(t, tc.code, e.Code(), fmt.Sprintf("%s: expected %s got %s", tc.desc, tc.code, e.Code()))
|
||||
}
|
||||
}
|
||||
|
||||
func TestIdentify(t *testing.T) {
|
||||
loginToken, err := svc.Issue(context.Background(), "", auth.Key{Type: auth.AccessKey, IssuedAt: time.Now(), Subject: id})
|
||||
assert.Nil(t, err, fmt.Sprintf("Issuing user key expected to succeed: %s", err))
|
||||
|
||||
recoveryToken, err := svc.Issue(context.Background(), "", auth.Key{Type: auth.RecoveryKey, IssuedAt: time.Now(), Subject: id})
|
||||
assert.Nil(t, err, fmt.Sprintf("Issuing recovery key expected to succeed: %s", err))
|
||||
|
||||
apiToken, err := svc.Issue(context.Background(), loginToken.AccessToken, auth.Key{Type: auth.APIKey, IssuedAt: time.Now(), ExpiresAt: time.Now().Add(time.Minute), Subject: id})
|
||||
assert.Nil(t, err, fmt.Sprintf("Issuing API key expected to succeed: %s", err))
|
||||
|
||||
authAddr := fmt.Sprintf("localhost:%d", port)
|
||||
conn, _ := grpc.Dial(authAddr, grpc.WithTransportCredentials(insecure.NewCredentials()))
|
||||
client := grpcapi.NewClient(conn, time.Second)
|
||||
|
||||
cases := []struct {
|
||||
desc string
|
||||
token string
|
||||
idt *mainflux.IdentityRes
|
||||
err error
|
||||
code codes.Code
|
||||
}{
|
||||
{
|
||||
desc: "identify user with user token",
|
||||
token: loginToken.AccessToken,
|
||||
idt: &mainflux.IdentityRes{Id: id},
|
||||
err: nil,
|
||||
code: codes.OK,
|
||||
},
|
||||
{
|
||||
desc: "identify user with recovery token",
|
||||
token: recoveryToken.AccessToken,
|
||||
idt: &mainflux.IdentityRes{Id: id},
|
||||
err: nil,
|
||||
code: codes.OK,
|
||||
},
|
||||
{
|
||||
desc: "identify user with API token",
|
||||
token: apiToken.AccessToken,
|
||||
idt: &mainflux.IdentityRes{Id: id},
|
||||
err: nil,
|
||||
code: codes.OK,
|
||||
},
|
||||
{
|
||||
desc: "identify user with invalid user token",
|
||||
token: "invalid",
|
||||
idt: &mainflux.IdentityRes{},
|
||||
err: status.Error(codes.Unauthenticated, "unauthenticated access"),
|
||||
code: codes.Unauthenticated,
|
||||
},
|
||||
{
|
||||
desc: "identify user with empty token",
|
||||
token: "",
|
||||
idt: &mainflux.IdentityRes{},
|
||||
err: status.Error(codes.InvalidArgument, "received invalid token request"),
|
||||
code: codes.Unauthenticated,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range cases {
|
||||
idt, err := client.Identify(context.Background(), &mainflux.IdentityReq{Token: tc.token})
|
||||
if idt != nil {
|
||||
assert.Equal(t, tc.idt, idt, fmt.Sprintf("%s: expected %v got %v", tc.desc, tc.idt, idt))
|
||||
}
|
||||
e, ok := status.FromError(err)
|
||||
assert.True(t, ok, "gRPC status can't be extracted from the error")
|
||||
assert.Equal(t, tc.code, e.Code(), fmt.Sprintf("%s: expected %s got %s", tc.desc, tc.code, e.Code()))
|
||||
}
|
||||
}
|
||||
|
||||
func TestAuthorize(t *testing.T) {
|
||||
token, err := svc.Issue(context.Background(), "", auth.Key{Type: auth.AccessKey, IssuedAt: time.Now(), Subject: id})
|
||||
assert.Nil(t, err, fmt.Sprintf("Issuing user key expected to succeed: %s", err))
|
||||
|
||||
authAddr := fmt.Sprintf("localhost:%d", port)
|
||||
conn, _ := grpc.Dial(authAddr, grpc.WithTransportCredentials(insecure.NewCredentials()))
|
||||
client := grpcapi.NewClient(conn, time.Second)
|
||||
|
||||
cases := []struct {
|
||||
desc string
|
||||
token string
|
||||
subject string
|
||||
object string
|
||||
relation string
|
||||
ar *mainflux.AuthorizeRes
|
||||
err error
|
||||
code codes.Code
|
||||
}{
|
||||
{
|
||||
desc: "authorize user with authorized token",
|
||||
token: token.AccessToken,
|
||||
subject: id,
|
||||
object: authoritiesObj,
|
||||
relation: memberRelation,
|
||||
ar: &mainflux.AuthorizeRes{Authorized: true},
|
||||
err: nil,
|
||||
code: codes.OK,
|
||||
},
|
||||
{
|
||||
desc: "authorize user with unauthorized relation",
|
||||
token: token.AccessToken,
|
||||
subject: id,
|
||||
object: authoritiesObj,
|
||||
relation: "unauthorizedRelation",
|
||||
ar: &mainflux.AuthorizeRes{Authorized: false},
|
||||
err: nil,
|
||||
code: codes.PermissionDenied,
|
||||
},
|
||||
{
|
||||
desc: "authorize user with unauthorized object",
|
||||
token: token.AccessToken,
|
||||
subject: id,
|
||||
object: "unauthorizedobject",
|
||||
relation: memberRelation,
|
||||
ar: &mainflux.AuthorizeRes{Authorized: false},
|
||||
err: nil,
|
||||
code: codes.PermissionDenied,
|
||||
},
|
||||
{
|
||||
desc: "authorize user with unauthorized subject",
|
||||
token: token.AccessToken,
|
||||
subject: "unauthorizedSubject",
|
||||
object: authoritiesObj,
|
||||
relation: memberRelation,
|
||||
ar: &mainflux.AuthorizeRes{Authorized: false},
|
||||
err: nil,
|
||||
code: codes.PermissionDenied,
|
||||
},
|
||||
{
|
||||
desc: "authorize user with invalid ACL",
|
||||
token: token.AccessToken,
|
||||
subject: "",
|
||||
object: "",
|
||||
relation: "",
|
||||
ar: &mainflux.AuthorizeRes{Authorized: false},
|
||||
err: nil,
|
||||
code: codes.InvalidArgument,
|
||||
},
|
||||
}
|
||||
for _, tc := range cases {
|
||||
ar, err := client.Authorize(context.Background(), &mainflux.AuthorizeReq{Subject: tc.subject, Object: tc.object, Relation: tc.relation})
|
||||
if ar != nil {
|
||||
assert.Equal(t, tc.ar, ar, fmt.Sprintf("%s: expected %v got %v", tc.desc, tc.ar, ar))
|
||||
}
|
||||
|
||||
e, ok := status.FromError(err)
|
||||
assert.True(t, ok, "gRPC status can't be extracted from the error")
|
||||
assert.Equal(t, tc.code, e.Code(), fmt.Sprintf("%s: expected %s got %s", tc.desc, tc.code, e.Code()))
|
||||
}
|
||||
}
|
||||
|
||||
func TestAddPolicy(t *testing.T) {
|
||||
token, err := svc.Issue(context.Background(), "", auth.Key{Type: auth.AccessKey, IssuedAt: time.Now(), Subject: id})
|
||||
assert.Nil(t, err, fmt.Sprintf("Issuing user key expected to succeed: %s", err))
|
||||
|
||||
authAddr := fmt.Sprintf("localhost:%d", port)
|
||||
conn, _ := grpc.Dial(authAddr, grpc.WithTransportCredentials(insecure.NewCredentials()))
|
||||
client := grpcapi.NewClient(conn, time.Second)
|
||||
|
||||
groupAdminObj := "groupadmin"
|
||||
|
||||
cases := []struct {
|
||||
desc string
|
||||
token string
|
||||
subject string
|
||||
object string
|
||||
relation string
|
||||
ar *mainflux.AddPolicyRes
|
||||
err error
|
||||
code codes.Code
|
||||
}{
|
||||
{
|
||||
desc: "add groupadmin policy to user",
|
||||
token: token.AccessToken,
|
||||
subject: id,
|
||||
object: groupAdminObj,
|
||||
relation: memberRelation,
|
||||
ar: &mainflux.AddPolicyRes{Authorized: true},
|
||||
err: nil,
|
||||
code: codes.OK,
|
||||
},
|
||||
{
|
||||
desc: "add policy to user with invalid ACL",
|
||||
token: token.AccessToken,
|
||||
subject: "",
|
||||
object: "",
|
||||
relation: "",
|
||||
ar: &mainflux.AddPolicyRes{Authorized: false},
|
||||
err: nil,
|
||||
code: codes.InvalidArgument,
|
||||
},
|
||||
}
|
||||
for _, tc := range cases {
|
||||
apr, err := client.AddPolicy(context.Background(), &mainflux.AddPolicyReq{Subject: tc.subject, Object: tc.object, Relation: tc.relation})
|
||||
if apr != nil {
|
||||
assert.Equal(t, tc.ar, apr, fmt.Sprintf("%s: expected %v got %v", tc.desc, tc.ar, apr))
|
||||
}
|
||||
|
||||
e, ok := status.FromError(err)
|
||||
assert.True(t, ok, "gRPC status can't be extracted from the error")
|
||||
assert.Equal(t, tc.code, e.Code(), fmt.Sprintf("%s: expected %s got %s", tc.desc, tc.code, e.Code()))
|
||||
}
|
||||
}
|
||||
|
||||
func TestDeletePolicy(t *testing.T) {
|
||||
token, err := svc.Issue(context.Background(), "", auth.Key{Type: auth.AccessKey, IssuedAt: time.Now(), Subject: id})
|
||||
assert.Nil(t, err, fmt.Sprintf("Issuing user key expected to succeed: %s", err))
|
||||
|
||||
authAddr := fmt.Sprintf("localhost:%d", port)
|
||||
conn, _ := grpc.Dial(authAddr, grpc.WithTransportCredentials(insecure.NewCredentials()))
|
||||
client := grpcapi.NewClient(conn, time.Second)
|
||||
|
||||
readRelation := "read"
|
||||
thingID := "thing"
|
||||
|
||||
apr, err := client.AddPolicy(context.Background(), &mainflux.AddPolicyReq{Subject: id, Object: thingID, Permission: readRelation})
|
||||
assert.Nil(t, err, fmt.Sprintf("Adding read policy to user expected to succeed: %s", err))
|
||||
assert.True(t, apr.GetAuthorized(), fmt.Sprintf("Adding read policy expected to make user authorized, expected %v got %v", true, apr.GetAuthorized()))
|
||||
|
||||
cases := []struct {
|
||||
desc string
|
||||
token string
|
||||
subject string
|
||||
object string
|
||||
relation string
|
||||
dpr *mainflux.DeletePolicyRes
|
||||
code codes.Code
|
||||
}{
|
||||
{
|
||||
desc: "delete valid policy",
|
||||
token: token.AccessToken,
|
||||
subject: id,
|
||||
object: thingID,
|
||||
relation: readRelation,
|
||||
dpr: &mainflux.DeletePolicyRes{Deleted: true},
|
||||
code: codes.OK,
|
||||
},
|
||||
{
|
||||
desc: "delete invalid policy",
|
||||
token: token.AccessToken,
|
||||
subject: "",
|
||||
object: "",
|
||||
relation: "",
|
||||
dpr: &mainflux.DeletePolicyRes{Deleted: false},
|
||||
code: codes.InvalidArgument,
|
||||
},
|
||||
}
|
||||
for _, tc := range cases {
|
||||
dpr, err := client.DeletePolicy(context.Background(), &mainflux.DeletePolicyReq{Subject: tc.subject, Object: tc.object, Relation: tc.relation})
|
||||
e, ok := status.FromError(err)
|
||||
assert.True(t, ok, "gRPC status can't be extracted from the error")
|
||||
assert.Equal(t, tc.code, e.Code(), fmt.Sprintf("%s: expected %s got %s", tc.desc, tc.code, e.Code()))
|
||||
assert.Equal(t, tc.dpr.GetDeleted(), dpr.GetDeleted(), fmt.Sprintf("%s: expected %v got %v", tc.desc, tc.dpr.GetDeleted(), dpr.GetDeleted()))
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,155 @@
|
||||
// Copyright (c) Mainflux
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package grpc
|
||||
|
||||
import (
|
||||
"github.com/mainflux/mainflux/auth"
|
||||
"github.com/mainflux/mainflux/internal/apiutil"
|
||||
)
|
||||
|
||||
type identityReq struct {
|
||||
token string
|
||||
}
|
||||
|
||||
func (req identityReq) validate() error {
|
||||
if req.token == "" {
|
||||
return apiutil.ErrBearerToken
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
type issueReq struct {
|
||||
id string
|
||||
keyType auth.KeyType
|
||||
}
|
||||
|
||||
func (req issueReq) validate() error {
|
||||
if req.keyType != auth.AccessKey &&
|
||||
req.keyType != auth.APIKey &&
|
||||
req.keyType != auth.RecoveryKey {
|
||||
return apiutil.ErrInvalidAuthKey
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
type refreshReq struct {
|
||||
value string
|
||||
}
|
||||
|
||||
func (req refreshReq) validate() error {
|
||||
if req.value == "" {
|
||||
return apiutil.ErrMissingSecret
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// authReq represents authorization request. It contains:
|
||||
// 1. subject - an action invoker
|
||||
// 2. object - an entity over which action will be executed
|
||||
// 3. action - type of action that will be executed (read/write).
|
||||
type authReq struct {
|
||||
Namespace string
|
||||
SubjectType string
|
||||
SubjectKind string
|
||||
Subject string
|
||||
Relation string
|
||||
Permission string
|
||||
ObjectType string
|
||||
Object string
|
||||
}
|
||||
|
||||
func (req authReq) validate() error {
|
||||
if req.Subject == "" {
|
||||
return apiutil.ErrMissingPolicySub
|
||||
}
|
||||
|
||||
if req.Object == "" {
|
||||
return apiutil.ErrMissingPolicyObj
|
||||
}
|
||||
|
||||
// if req.SubjectKind == "" {
|
||||
// return apiutil.ErrMissingPolicySub
|
||||
// }
|
||||
|
||||
// if req.Permission == "" {
|
||||
// return apiutil.ErrMalformedPolicyAct
|
||||
// }
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
type policyReq struct {
|
||||
Namespace string
|
||||
SubjectType string
|
||||
Subject string
|
||||
Relation string
|
||||
Permission string
|
||||
ObjectType string
|
||||
Object string
|
||||
}
|
||||
|
||||
func (req policyReq) validate() error {
|
||||
if req.Subject == "" {
|
||||
return apiutil.ErrMissingPolicySub
|
||||
}
|
||||
|
||||
if req.Object == "" {
|
||||
return apiutil.ErrMissingPolicyObj
|
||||
}
|
||||
|
||||
if req.Relation == "" && req.Permission == "" {
|
||||
return apiutil.ErrMalformedPolicyAct
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
type listObjectsReq struct {
|
||||
Namespace string
|
||||
SubjectType string
|
||||
Subject string
|
||||
Relation string
|
||||
Permission string
|
||||
ObjectType string
|
||||
Object string
|
||||
NextPageToken string
|
||||
Limit int32
|
||||
}
|
||||
|
||||
type countObjectsReq struct {
|
||||
Namespace string
|
||||
SubjectType string
|
||||
Subject string
|
||||
Relation string
|
||||
Permission string
|
||||
ObjectType string
|
||||
Object string
|
||||
NextPageToken string
|
||||
}
|
||||
|
||||
type listSubjectsReq struct {
|
||||
Namespace string
|
||||
SubjectType string
|
||||
Subject string
|
||||
Relation string
|
||||
Permission string
|
||||
ObjectType string
|
||||
Object string
|
||||
NextPageToken string
|
||||
Limit int32
|
||||
}
|
||||
|
||||
type countSubjectsReq struct {
|
||||
Namespace string
|
||||
SubjectType string
|
||||
Subject string
|
||||
Relation string
|
||||
Permission string
|
||||
ObjectType string
|
||||
Object string
|
||||
NextPageToken string
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
// Copyright (c) Mainflux
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package grpc
|
||||
|
||||
type identityRes struct {
|
||||
id string
|
||||
}
|
||||
|
||||
type issueRes struct {
|
||||
accessToken string
|
||||
refreshToken string
|
||||
accessType string
|
||||
}
|
||||
|
||||
type authorizeRes struct {
|
||||
id string
|
||||
authorized bool
|
||||
}
|
||||
|
||||
type addPolicyRes struct {
|
||||
authorized bool
|
||||
}
|
||||
|
||||
type deletePolicyRes struct {
|
||||
deleted bool
|
||||
}
|
||||
|
||||
type listObjectsRes struct {
|
||||
policies []string
|
||||
nextPageToken string
|
||||
}
|
||||
|
||||
type countObjectsRes struct {
|
||||
count int
|
||||
}
|
||||
|
||||
type listSubjectsRes struct {
|
||||
policies []string
|
||||
nextPageToken string
|
||||
}
|
||||
|
||||
type countSubjectsRes struct {
|
||||
count int
|
||||
}
|
||||
|
||||
type membersRes struct {
|
||||
total uint64
|
||||
offset uint64
|
||||
limit uint64
|
||||
groupType string
|
||||
members []string
|
||||
}
|
||||
@@ -0,0 +1,384 @@
|
||||
// Copyright (c) Mainflux
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package grpc
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
kitgrpc "github.com/go-kit/kit/transport/grpc"
|
||||
mainflux "github.com/mainflux/mainflux"
|
||||
"github.com/mainflux/mainflux/auth"
|
||||
"github.com/mainflux/mainflux/internal/apiutil"
|
||||
"github.com/mainflux/mainflux/pkg/errors"
|
||||
"google.golang.org/grpc/codes"
|
||||
"google.golang.org/grpc/status"
|
||||
)
|
||||
|
||||
var _ mainflux.AuthServiceServer = (*grpcServer)(nil)
|
||||
|
||||
type grpcServer struct {
|
||||
mainflux.UnimplementedAuthServiceServer
|
||||
issue kitgrpc.Handler
|
||||
login kitgrpc.Handler
|
||||
refresh kitgrpc.Handler
|
||||
identify kitgrpc.Handler
|
||||
authorize kitgrpc.Handler
|
||||
addPolicy kitgrpc.Handler
|
||||
deletePolicy kitgrpc.Handler
|
||||
listObjects kitgrpc.Handler
|
||||
listAllObjects kitgrpc.Handler
|
||||
countObjects kitgrpc.Handler
|
||||
listSubjects kitgrpc.Handler
|
||||
listAllSubjects kitgrpc.Handler
|
||||
countSubjects kitgrpc.Handler
|
||||
}
|
||||
|
||||
// NewServer returns new AuthServiceServer instance.
|
||||
func NewServer(svc auth.Service) mainflux.AuthServiceServer {
|
||||
return &grpcServer{
|
||||
issue: kitgrpc.NewServer(
|
||||
(issueEndpoint(svc)),
|
||||
decodeIssueRequest,
|
||||
encodeIssueResponse,
|
||||
),
|
||||
login: kitgrpc.NewServer(
|
||||
(loginEndpoint(svc)),
|
||||
decodeLoginRequest,
|
||||
encodeIssueResponse,
|
||||
),
|
||||
refresh: kitgrpc.NewServer(
|
||||
(refreshEndpoint(svc)),
|
||||
decodeRefreshRequest,
|
||||
encodeIssueResponse,
|
||||
),
|
||||
identify: kitgrpc.NewServer(
|
||||
(identifyEndpoint(svc)),
|
||||
decodeIdentifyRequest,
|
||||
encodeIdentifyResponse,
|
||||
),
|
||||
authorize: kitgrpc.NewServer(
|
||||
(authorizeEndpoint(svc)),
|
||||
decodeAuthorizeRequest,
|
||||
encodeAuthorizeResponse,
|
||||
),
|
||||
addPolicy: kitgrpc.NewServer(
|
||||
(addPolicyEndpoint(svc)),
|
||||
decodeAddPolicyRequest,
|
||||
encodeAddPolicyResponse,
|
||||
),
|
||||
deletePolicy: kitgrpc.NewServer(
|
||||
(deletePolicyEndpoint(svc)),
|
||||
decodeDeletePolicyRequest,
|
||||
encodeDeletePolicyResponse,
|
||||
),
|
||||
listObjects: kitgrpc.NewServer(
|
||||
(listObjectsEndpoint(svc)),
|
||||
decodeListObjectsRequest,
|
||||
encodeListObjectsResponse,
|
||||
),
|
||||
listAllObjects: kitgrpc.NewServer(
|
||||
(listAllObjectsEndpoint(svc)),
|
||||
decodeListObjectsRequest,
|
||||
encodeListObjectsResponse,
|
||||
),
|
||||
countObjects: kitgrpc.NewServer(
|
||||
(countObjectsEndpoint(svc)),
|
||||
decodeCountObjectsRequest,
|
||||
encodeCountObjectsResponse,
|
||||
),
|
||||
listSubjects: kitgrpc.NewServer(
|
||||
(listSubjectsEndpoint(svc)),
|
||||
decodeListSubjectsRequest,
|
||||
encodeListSubjectsResponse,
|
||||
),
|
||||
listAllSubjects: kitgrpc.NewServer(
|
||||
(listAllSubjectsEndpoint(svc)),
|
||||
decodeListSubjectsRequest,
|
||||
encodeListSubjectsResponse,
|
||||
),
|
||||
countSubjects: kitgrpc.NewServer(
|
||||
(countSubjectsEndpoint(svc)),
|
||||
decodeCountSubjectsRequest,
|
||||
encodeCountSubjectsResponse,
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
func (s *grpcServer) Issue(ctx context.Context, req *mainflux.IssueReq) (*mainflux.Token, error) {
|
||||
_, res, err := s.issue.ServeGRPC(ctx, req)
|
||||
if err != nil {
|
||||
return nil, encodeError(err)
|
||||
}
|
||||
return res.(*mainflux.Token), nil
|
||||
}
|
||||
|
||||
func (s *grpcServer) Login(ctx context.Context, req *mainflux.LoginReq) (*mainflux.Token, error) {
|
||||
_, res, err := s.login.ServeGRPC(ctx, req)
|
||||
if err != nil {
|
||||
return nil, encodeError(err)
|
||||
}
|
||||
return res.(*mainflux.Token), nil
|
||||
}
|
||||
|
||||
func (s *grpcServer) Refresh(ctx context.Context, req *mainflux.RefreshReq) (*mainflux.Token, error) {
|
||||
_, res, err := s.refresh.ServeGRPC(ctx, req)
|
||||
if err != nil {
|
||||
return nil, encodeError(err)
|
||||
}
|
||||
return res.(*mainflux.Token), nil
|
||||
}
|
||||
|
||||
func (s *grpcServer) Identify(ctx context.Context, token *mainflux.IdentityReq) (*mainflux.IdentityRes, error) {
|
||||
_, res, err := s.identify.ServeGRPC(ctx, token)
|
||||
if err != nil {
|
||||
return nil, encodeError(err)
|
||||
}
|
||||
return res.(*mainflux.IdentityRes), nil
|
||||
}
|
||||
|
||||
func (s *grpcServer) Authorize(ctx context.Context, req *mainflux.AuthorizeReq) (*mainflux.AuthorizeRes, error) {
|
||||
_, res, err := s.authorize.ServeGRPC(ctx, req)
|
||||
if err != nil {
|
||||
return nil, encodeError(err)
|
||||
}
|
||||
return res.(*mainflux.AuthorizeRes), nil
|
||||
}
|
||||
|
||||
func (s *grpcServer) AddPolicy(ctx context.Context, req *mainflux.AddPolicyReq) (*mainflux.AddPolicyRes, error) {
|
||||
_, res, err := s.addPolicy.ServeGRPC(ctx, req)
|
||||
if err != nil {
|
||||
return nil, encodeError(err)
|
||||
}
|
||||
return res.(*mainflux.AddPolicyRes), nil
|
||||
}
|
||||
|
||||
func (s *grpcServer) DeletePolicy(ctx context.Context, req *mainflux.DeletePolicyReq) (*mainflux.DeletePolicyRes, error) {
|
||||
_, res, err := s.deletePolicy.ServeGRPC(ctx, req)
|
||||
if err != nil {
|
||||
return nil, encodeError(err)
|
||||
}
|
||||
return res.(*mainflux.DeletePolicyRes), nil
|
||||
}
|
||||
|
||||
func (s *grpcServer) ListObjects(ctx context.Context, req *mainflux.ListObjectsReq) (*mainflux.ListObjectsRes, error) {
|
||||
_, res, err := s.listObjects.ServeGRPC(ctx, req)
|
||||
if err != nil {
|
||||
return nil, encodeError(err)
|
||||
}
|
||||
return res.(*mainflux.ListObjectsRes), nil
|
||||
}
|
||||
|
||||
func (s *grpcServer) ListAllObjects(ctx context.Context, req *mainflux.ListObjectsReq) (*mainflux.ListObjectsRes, error) {
|
||||
_, res, err := s.listAllObjects.ServeGRPC(ctx, req)
|
||||
if err != nil {
|
||||
return nil, encodeError(err)
|
||||
}
|
||||
return res.(*mainflux.ListObjectsRes), nil
|
||||
}
|
||||
|
||||
func (s *grpcServer) CountObjects(ctx context.Context, req *mainflux.CountObjectsReq) (*mainflux.CountObjectsRes, error) {
|
||||
_, res, err := s.countObjects.ServeGRPC(ctx, req)
|
||||
if err != nil {
|
||||
return nil, encodeError(err)
|
||||
}
|
||||
return res.(*mainflux.CountObjectsRes), nil
|
||||
}
|
||||
|
||||
func (s *grpcServer) ListSubjects(ctx context.Context, req *mainflux.ListSubjectsReq) (*mainflux.ListSubjectsRes, error) {
|
||||
_, res, err := s.listSubjects.ServeGRPC(ctx, req)
|
||||
if err != nil {
|
||||
return nil, encodeError(err)
|
||||
}
|
||||
return res.(*mainflux.ListSubjectsRes), nil
|
||||
}
|
||||
|
||||
func (s *grpcServer) ListAllSubjects(ctx context.Context, req *mainflux.ListSubjectsReq) (*mainflux.ListSubjectsRes, error) {
|
||||
_, res, err := s.listAllSubjects.ServeGRPC(ctx, req)
|
||||
if err != nil {
|
||||
return nil, encodeError(err)
|
||||
}
|
||||
return res.(*mainflux.ListSubjectsRes), nil
|
||||
}
|
||||
|
||||
func (s *grpcServer) CountSubjects(ctx context.Context, req *mainflux.CountSubjectsReq) (*mainflux.CountSubjectsRes, error) {
|
||||
_, res, err := s.countSubjects.ServeGRPC(ctx, req)
|
||||
if err != nil {
|
||||
return nil, encodeError(err)
|
||||
}
|
||||
return res.(*mainflux.CountSubjectsRes), nil
|
||||
}
|
||||
|
||||
func decodeIssueRequest(_ context.Context, grpcReq interface{}) (interface{}, error) {
|
||||
req := grpcReq.(*mainflux.IssueReq)
|
||||
return issueReq{id: req.GetId(), keyType: auth.KeyType(req.GetType())}, nil
|
||||
}
|
||||
|
||||
func decodeLoginRequest(_ context.Context, grpcReq interface{}) (interface{}, error) {
|
||||
req := grpcReq.(*mainflux.LoginReq)
|
||||
return issueReq{id: req.GetId(), keyType: auth.AccessKey}, nil
|
||||
}
|
||||
|
||||
func decodeRefreshRequest(_ context.Context, grpcReq interface{}) (interface{}, error) {
|
||||
req := grpcReq.(*mainflux.RefreshReq)
|
||||
return refreshReq{value: req.GetValue()}, nil
|
||||
}
|
||||
|
||||
func encodeIssueResponse(_ context.Context, grpcRes interface{}) (interface{}, error) {
|
||||
res := grpcRes.(issueRes)
|
||||
|
||||
return &mainflux.Token{
|
||||
AccessToken: res.accessToken,
|
||||
RefreshToken: &res.refreshToken,
|
||||
AccessType: res.accessType,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func decodeIdentifyRequest(_ context.Context, grpcReq interface{}) (interface{}, error) {
|
||||
req := grpcReq.(*mainflux.IdentityReq)
|
||||
return identityReq{token: req.GetToken()}, nil
|
||||
}
|
||||
|
||||
func encodeIdentifyResponse(_ context.Context, grpcRes interface{}) (interface{}, error) {
|
||||
res := grpcRes.(identityRes)
|
||||
return &mainflux.IdentityRes{Id: res.id}, nil
|
||||
}
|
||||
|
||||
func decodeAuthorizeRequest(_ context.Context, grpcReq interface{}) (interface{}, error) {
|
||||
req := grpcReq.(*mainflux.AuthorizeReq)
|
||||
return authReq{Namespace: req.GetNamespace(),
|
||||
SubjectType: req.GetSubjectType(),
|
||||
SubjectKind: req.GetSubjectKind(),
|
||||
Subject: req.GetSubject(),
|
||||
Relation: req.GetRelation(),
|
||||
Permission: req.GetPermission(),
|
||||
ObjectType: req.GetObjectType(),
|
||||
Object: req.GetObject()}, nil
|
||||
}
|
||||
|
||||
func encodeAuthorizeResponse(_ context.Context, grpcRes interface{}) (interface{}, error) {
|
||||
res := grpcRes.(authorizeRes)
|
||||
return &mainflux.AuthorizeRes{Authorized: res.authorized, Id: res.id}, nil
|
||||
}
|
||||
|
||||
func decodeAddPolicyRequest(_ context.Context, grpcReq interface{}) (interface{}, error) {
|
||||
req := grpcReq.(*mainflux.AddPolicyReq)
|
||||
return policyReq{Namespace: req.GetNamespace(),
|
||||
SubjectType: req.GetSubjectType(),
|
||||
Subject: req.GetSubject(),
|
||||
Relation: req.GetRelation(),
|
||||
Permission: req.GetPermission(),
|
||||
ObjectType: req.GetObjectType(),
|
||||
Object: req.GetObject()}, nil
|
||||
}
|
||||
|
||||
func encodeAddPolicyResponse(_ context.Context, grpcRes interface{}) (interface{}, error) {
|
||||
res := grpcRes.(addPolicyRes)
|
||||
return &mainflux.AddPolicyRes{Authorized: res.authorized}, nil
|
||||
}
|
||||
|
||||
func decodeDeletePolicyRequest(_ context.Context, grpcReq interface{}) (interface{}, error) {
|
||||
req := grpcReq.(*mainflux.DeletePolicyReq)
|
||||
return policyReq{Namespace: req.GetNamespace(),
|
||||
SubjectType: req.GetSubjectType(),
|
||||
Subject: req.GetSubject(),
|
||||
Relation: req.GetRelation(),
|
||||
Permission: req.GetPermission(),
|
||||
ObjectType: req.GetObjectType(),
|
||||
Object: req.GetObject()}, nil
|
||||
}
|
||||
|
||||
func encodeDeletePolicyResponse(_ context.Context, grpcRes interface{}) (interface{}, error) {
|
||||
res := grpcRes.(deletePolicyRes)
|
||||
return &mainflux.DeletePolicyRes{Deleted: res.deleted}, nil
|
||||
}
|
||||
|
||||
func decodeListObjectsRequest(_ context.Context, grpcReq interface{}) (interface{}, error) {
|
||||
req := grpcReq.(*mainflux.ListObjectsReq)
|
||||
return listObjectsReq{Namespace: req.GetNamespace(),
|
||||
SubjectType: req.GetSubjectType(),
|
||||
Subject: req.GetSubject(),
|
||||
Relation: req.GetRelation(),
|
||||
Permission: req.GetPermission(),
|
||||
ObjectType: req.GetObjectType(),
|
||||
Object: req.GetObject(),
|
||||
NextPageToken: req.GetNextPageToken(),
|
||||
Limit: req.GetLimit()}, nil
|
||||
}
|
||||
|
||||
func encodeListObjectsResponse(_ context.Context, grpcRes interface{}) (interface{}, error) {
|
||||
res := grpcRes.(listObjectsRes)
|
||||
return &mainflux.ListObjectsRes{Policies: res.policies}, nil
|
||||
}
|
||||
|
||||
func decodeCountObjectsRequest(_ context.Context, grpcReq interface{}) (interface{}, error) {
|
||||
req := grpcReq.(*mainflux.CountObjectsReq)
|
||||
return countObjectsReq{Namespace: req.GetNamespace(),
|
||||
SubjectType: req.GetSubjectType(),
|
||||
Subject: req.GetSubject(),
|
||||
Relation: req.GetRelation(),
|
||||
Permission: req.GetPermission(),
|
||||
ObjectType: req.GetObjectType(),
|
||||
Object: req.GetObject()}, nil
|
||||
}
|
||||
|
||||
func encodeCountObjectsResponse(_ context.Context, grpcRes interface{}) (interface{}, error) {
|
||||
res := grpcRes.(countObjectsRes)
|
||||
return &mainflux.CountObjectsRes{Count: int64(res.count)}, nil
|
||||
}
|
||||
|
||||
func decodeListSubjectsRequest(_ context.Context, grpcReq interface{}) (interface{}, error) {
|
||||
req := grpcReq.(*mainflux.ListSubjectsReq)
|
||||
return listSubjectsReq{Namespace: req.GetNamespace(),
|
||||
SubjectType: req.GetSubjectType(),
|
||||
Subject: req.GetSubject(),
|
||||
Relation: req.GetRelation(),
|
||||
Permission: req.GetPermission(),
|
||||
ObjectType: req.GetObjectType(),
|
||||
Object: req.GetObject(), NextPageToken: req.GetNextPageToken(), Limit: req.GetLimit()}, nil
|
||||
}
|
||||
|
||||
func encodeListSubjectsResponse(_ context.Context, grpcRes interface{}) (interface{}, error) {
|
||||
res := grpcRes.(listSubjectsRes)
|
||||
return &mainflux.ListSubjectsRes{Policies: res.policies}, nil
|
||||
}
|
||||
|
||||
func decodeCountSubjectsRequest(_ context.Context, grpcReq interface{}) (interface{}, error) {
|
||||
req := grpcReq.(*mainflux.CountSubjectsReq)
|
||||
return countSubjectsReq{Namespace: req.GetNamespace(),
|
||||
SubjectType: req.GetSubjectType(),
|
||||
Subject: req.GetSubject(),
|
||||
Relation: req.GetRelation(),
|
||||
Permission: req.GetPermission(),
|
||||
ObjectType: req.GetObjectType(),
|
||||
Object: req.GetObject()}, nil
|
||||
}
|
||||
|
||||
func encodeCountSubjectsResponse(_ context.Context, grpcRes interface{}) (interface{}, error) {
|
||||
res := grpcRes.(countObjectsRes)
|
||||
return &mainflux.CountObjectsRes{Count: int64(res.count)}, 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, errors.ErrAuthentication),
|
||||
errors.Contains(err, auth.ErrKeyExpired),
|
||||
err == apiutil.ErrMissingEmail,
|
||||
err == apiutil.ErrBearerToken:
|
||||
return status.Error(codes.Unauthenticated, err.Error())
|
||||
case errors.Contains(err, errors.ErrAuthorization):
|
||||
return status.Error(codes.PermissionDenied, err.Error())
|
||||
default:
|
||||
return status.Error(codes.Internal, err.Error())
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
// Copyright (c) Mainflux
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package grpc_test
|
||||
|
||||
import (
|
||||
"os"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestMain(m *testing.M) {
|
||||
svc = newService()
|
||||
startGRPCServer(svc, port)
|
||||
|
||||
code := m.Run()
|
||||
|
||||
os.Exit(code)
|
||||
}
|
||||
@@ -1,5 +1,3 @@
|
||||
// Copyright (c) Mainflux
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Package http contains implementation of things auth service HTTP API.
|
||||
package http
|
||||
@@ -0,0 +1,90 @@
|
||||
// Copyright (c) Mainflux
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package keys
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"github.com/go-kit/kit/endpoint"
|
||||
"github.com/mainflux/mainflux/auth"
|
||||
)
|
||||
|
||||
func issueEndpoint(svc auth.Service) endpoint.Endpoint {
|
||||
return func(ctx context.Context, request interface{}) (interface{}, error) {
|
||||
req := request.(issueKeyReq)
|
||||
if err := req.validate(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
now := time.Now().UTC()
|
||||
newKey := auth.Key{
|
||||
IssuedAt: now,
|
||||
Type: req.Type,
|
||||
}
|
||||
|
||||
duration := time.Duration(req.Duration * time.Second)
|
||||
if duration != 0 {
|
||||
exp := now.Add(duration)
|
||||
newKey.ExpiresAt = exp
|
||||
}
|
||||
|
||||
tkn, err := svc.Issue(ctx, req.token, newKey)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
res := issueKeyRes{
|
||||
Value: tkn.AccessToken,
|
||||
}
|
||||
// if !key.ExpiresAt.IsZero() {
|
||||
// res.ExpiresAt = &key.ExpiresAt
|
||||
// }
|
||||
return res, nil
|
||||
}
|
||||
}
|
||||
|
||||
func retrieveEndpoint(svc auth.Service) endpoint.Endpoint {
|
||||
return func(ctx context.Context, request interface{}) (interface{}, error) {
|
||||
req := request.(keyReq)
|
||||
|
||||
if err := req.validate(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
key, err := svc.RetrieveKey(ctx, req.token, req.id)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ret := retrieveKeyRes{
|
||||
ID: key.ID,
|
||||
IssuerID: key.Issuer,
|
||||
Subject: key.Subject,
|
||||
Type: key.Type,
|
||||
IssuedAt: key.IssuedAt,
|
||||
}
|
||||
if !key.ExpiresAt.IsZero() {
|
||||
ret.ExpiresAt = &key.ExpiresAt
|
||||
}
|
||||
|
||||
return ret, nil
|
||||
}
|
||||
}
|
||||
|
||||
func revokeEndpoint(svc auth.Service) endpoint.Endpoint {
|
||||
return func(ctx context.Context, request interface{}) (interface{}, error) {
|
||||
req := request.(keyReq)
|
||||
|
||||
if err := req.validate(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := svc.Revoke(ctx, req.token, req.id); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return revokeKeyRes{}, nil
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,295 @@
|
||||
// Copyright (c) Mainflux
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package keys_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/mainflux/mainflux/auth"
|
||||
httpapi "github.com/mainflux/mainflux/auth/api/http"
|
||||
"github.com/mainflux/mainflux/auth/jwt"
|
||||
"github.com/mainflux/mainflux/auth/mocks"
|
||||
"github.com/mainflux/mainflux/internal/apiutil"
|
||||
"github.com/mainflux/mainflux/logger"
|
||||
"github.com/mainflux/mainflux/pkg/uuid"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
const (
|
||||
secret = "secret"
|
||||
contentType = "application/json"
|
||||
id = "123e4567-e89b-12d3-a456-000000000001"
|
||||
email = "user@example.com"
|
||||
loginDuration = 30 * time.Minute
|
||||
refreshDuration = 24 * time.Hour
|
||||
)
|
||||
|
||||
type issueRequest struct {
|
||||
Duration time.Duration `json:"duration,omitempty"`
|
||||
Type uint32 `json:"type,omitempty"`
|
||||
}
|
||||
|
||||
type testRequest struct {
|
||||
client *http.Client
|
||||
method string
|
||||
url string
|
||||
contentType string
|
||||
token string
|
||||
body io.Reader
|
||||
}
|
||||
|
||||
func (tr testRequest) make() (*http.Response, error) {
|
||||
req, err := http.NewRequest(tr.method, tr.url, tr.body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if tr.token != "" {
|
||||
req.Header.Set("Authorization", apiutil.BearerPrefix+tr.token)
|
||||
}
|
||||
if tr.contentType != "" {
|
||||
req.Header.Set("Content-Type", tr.contentType)
|
||||
}
|
||||
|
||||
req.Header.Set("Referer", "http://localhost")
|
||||
return tr.client.Do(req)
|
||||
}
|
||||
|
||||
func newService() auth.Service {
|
||||
krepo := new(mocks.Keys)
|
||||
prepo := new(mocks.PolicyAgent)
|
||||
idProvider := uuid.NewMock()
|
||||
|
||||
t := jwt.New([]byte(secret))
|
||||
|
||||
return auth.New(krepo, idProvider, t, prepo, loginDuration, refreshDuration)
|
||||
}
|
||||
|
||||
func newServer(svc auth.Service) *httptest.Server {
|
||||
logger := logger.NewMock()
|
||||
mux := httpapi.MakeHandler(svc, logger, "")
|
||||
return httptest.NewServer(mux)
|
||||
}
|
||||
|
||||
func toJSON(data interface{}) string {
|
||||
jsonData, _ := json.Marshal(data)
|
||||
return string(jsonData)
|
||||
}
|
||||
|
||||
func TestIssue(t *testing.T) {
|
||||
svc := newService()
|
||||
token, err := svc.Issue(context.Background(), "", auth.Key{Type: auth.AccessKey, IssuedAt: time.Now(), Subject: id})
|
||||
assert.Nil(t, err, fmt.Sprintf("Issuing login key expected to succeed: %s", err))
|
||||
|
||||
ts := newServer(svc)
|
||||
defer ts.Close()
|
||||
client := ts.Client()
|
||||
|
||||
lk := issueRequest{Type: uint32(auth.AccessKey)}
|
||||
ak := issueRequest{Type: uint32(auth.APIKey), Duration: time.Hour}
|
||||
rk := issueRequest{Type: uint32(auth.RecoveryKey)}
|
||||
|
||||
cases := []struct {
|
||||
desc string
|
||||
req string
|
||||
ct string
|
||||
token string
|
||||
status int
|
||||
}{
|
||||
{
|
||||
desc: "issue login key with empty token",
|
||||
req: toJSON(lk),
|
||||
ct: contentType,
|
||||
token: "",
|
||||
status: http.StatusUnauthorized,
|
||||
},
|
||||
{
|
||||
desc: "issue API key",
|
||||
req: toJSON(ak),
|
||||
ct: contentType,
|
||||
token: token.AccessToken,
|
||||
status: http.StatusCreated,
|
||||
},
|
||||
{
|
||||
desc: "issue recovery key",
|
||||
req: toJSON(rk),
|
||||
ct: contentType,
|
||||
token: token.AccessToken,
|
||||
status: http.StatusCreated,
|
||||
},
|
||||
{
|
||||
desc: "issue login key wrong content type",
|
||||
req: toJSON(lk),
|
||||
ct: "",
|
||||
token: token.AccessToken,
|
||||
status: http.StatusUnsupportedMediaType,
|
||||
},
|
||||
{
|
||||
desc: "issue recovery key wrong content type",
|
||||
req: toJSON(rk),
|
||||
ct: "",
|
||||
token: token.AccessToken,
|
||||
status: http.StatusUnsupportedMediaType,
|
||||
},
|
||||
{
|
||||
desc: "issue key with an invalid token",
|
||||
req: toJSON(ak),
|
||||
ct: contentType,
|
||||
token: "wrong",
|
||||
status: http.StatusUnauthorized,
|
||||
},
|
||||
{
|
||||
desc: "issue recovery key with empty token",
|
||||
req: toJSON(rk),
|
||||
ct: contentType,
|
||||
token: "",
|
||||
status: http.StatusUnauthorized,
|
||||
},
|
||||
{
|
||||
desc: "issue key with invalid request",
|
||||
req: "{",
|
||||
ct: contentType,
|
||||
token: token.AccessToken,
|
||||
status: http.StatusBadRequest,
|
||||
},
|
||||
{
|
||||
desc: "issue key with invalid JSON",
|
||||
req: "{invalid}",
|
||||
ct: contentType,
|
||||
token: token.AccessToken,
|
||||
status: http.StatusBadRequest,
|
||||
},
|
||||
{
|
||||
desc: "issue key with invalid JSON content",
|
||||
req: `{"Type":{"key":"AccessToken"}}`,
|
||||
ct: contentType,
|
||||
token: token.AccessToken,
|
||||
status: http.StatusBadRequest,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range cases {
|
||||
req := testRequest{
|
||||
client: client,
|
||||
method: http.MethodPost,
|
||||
url: fmt.Sprintf("%s/keys", ts.URL),
|
||||
contentType: tc.ct,
|
||||
token: tc.token,
|
||||
body: strings.NewReader(tc.req),
|
||||
}
|
||||
res, err := req.make()
|
||||
assert.Nil(t, err, fmt.Sprintf("%s: unexpected error %s", tc.desc, err))
|
||||
assert.Equal(t, tc.status, res.StatusCode, fmt.Sprintf("%s: expected status code %d got %d", tc.desc, tc.status, res.StatusCode))
|
||||
}
|
||||
}
|
||||
|
||||
func TestRetrieve(t *testing.T) {
|
||||
svc := newService()
|
||||
token, err := svc.Issue(context.Background(), "", auth.Key{Type: auth.AccessKey, IssuedAt: time.Now(), Subject: id})
|
||||
assert.Nil(t, err, fmt.Sprintf("Issuing login key expected to succeed: %s", err))
|
||||
key := auth.Key{Type: auth.APIKey, IssuedAt: time.Now(), Subject: id}
|
||||
|
||||
k, err := svc.Issue(context.Background(), token.AccessToken, key)
|
||||
assert.Nil(t, err, fmt.Sprintf("Issuing login key expected to succeed: %s", err))
|
||||
|
||||
ts := newServer(svc)
|
||||
defer ts.Close()
|
||||
client := ts.Client()
|
||||
|
||||
cases := []struct {
|
||||
desc string
|
||||
id string
|
||||
token string
|
||||
status int
|
||||
}{
|
||||
{
|
||||
desc: "retrieve an existing key",
|
||||
id: k.AccessToken,
|
||||
token: token.AccessToken,
|
||||
status: http.StatusOK,
|
||||
},
|
||||
{
|
||||
desc: "retrieve a non-existing key",
|
||||
id: "non-existing",
|
||||
token: token.AccessToken,
|
||||
status: http.StatusNotFound,
|
||||
},
|
||||
{
|
||||
desc: "retrieve a key with an invalid token",
|
||||
id: k.AccessToken,
|
||||
token: "wrong",
|
||||
status: http.StatusUnauthorized,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range cases {
|
||||
req := testRequest{
|
||||
client: client,
|
||||
method: http.MethodGet,
|
||||
url: fmt.Sprintf("%s/keys/%s", ts.URL, tc.id),
|
||||
token: tc.token,
|
||||
}
|
||||
res, err := req.make()
|
||||
assert.Nil(t, err, fmt.Sprintf("%s: unexpected error %s", tc.desc, err))
|
||||
assert.Equal(t, tc.status, res.StatusCode, fmt.Sprintf("%s: expected status code %d got %d", tc.desc, tc.status, res.StatusCode))
|
||||
}
|
||||
}
|
||||
|
||||
func TestRevoke(t *testing.T) {
|
||||
svc := newService()
|
||||
token, err := svc.Issue(context.Background(), "", auth.Key{Type: auth.AccessKey, IssuedAt: time.Now(), Subject: id})
|
||||
assert.Nil(t, err, fmt.Sprintf("Issuing login key expected to succeed: %s", err))
|
||||
key := auth.Key{Type: auth.APIKey, IssuedAt: time.Now(), Subject: id}
|
||||
|
||||
k, err := svc.Issue(context.Background(), token.AccessToken, key)
|
||||
assert.Nil(t, err, fmt.Sprintf("Issuing login key expected to succeed: %s", err))
|
||||
|
||||
ts := newServer(svc)
|
||||
defer ts.Close()
|
||||
client := ts.Client()
|
||||
|
||||
cases := []struct {
|
||||
desc string
|
||||
id string
|
||||
token string
|
||||
status int
|
||||
}{
|
||||
{
|
||||
desc: "revoke an existing key",
|
||||
id: k.AccessToken,
|
||||
token: token.AccessToken,
|
||||
status: http.StatusNoContent,
|
||||
},
|
||||
{
|
||||
desc: "revoke a non-existing key",
|
||||
id: "non-existing",
|
||||
token: token.AccessToken,
|
||||
status: http.StatusNoContent,
|
||||
},
|
||||
{
|
||||
desc: "revoke key with invalid token",
|
||||
id: k.AccessToken,
|
||||
token: "wrong",
|
||||
status: http.StatusUnauthorized},
|
||||
}
|
||||
|
||||
for _, tc := range cases {
|
||||
req := testRequest{
|
||||
client: client,
|
||||
method: http.MethodDelete,
|
||||
url: fmt.Sprintf("%s/keys/%s", ts.URL, tc.id),
|
||||
token: tc.token,
|
||||
}
|
||||
res, err := req.make()
|
||||
assert.Nil(t, err, fmt.Sprintf("%s: unexpected error %s", tc.desc, err))
|
||||
assert.Equal(t, tc.status, res.StatusCode, fmt.Sprintf("%s: expected status code %d got %d", tc.desc, tc.status, res.StatusCode))
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
// Copyright (c) Mainflux
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package keys
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/mainflux/mainflux/auth"
|
||||
"github.com/mainflux/mainflux/internal/apiutil"
|
||||
)
|
||||
|
||||
type issueKeyReq struct {
|
||||
token string
|
||||
Type auth.KeyType `json:"type,omitempty"`
|
||||
Duration time.Duration `json:"duration,omitempty"`
|
||||
}
|
||||
|
||||
// It is not possible to issue Reset key using HTTP API.
|
||||
func (req issueKeyReq) validate() error {
|
||||
if req.token == "" {
|
||||
return apiutil.ErrBearerToken
|
||||
}
|
||||
|
||||
if req.Type != auth.AccessKey &&
|
||||
req.Type != auth.RecoveryKey &&
|
||||
req.Type != auth.APIKey {
|
||||
return apiutil.ErrInvalidAPIKey
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
type keyReq struct {
|
||||
token string
|
||||
id string
|
||||
}
|
||||
|
||||
func (req keyReq) validate() error {
|
||||
if req.token == "" {
|
||||
return apiutil.ErrBearerToken
|
||||
}
|
||||
|
||||
if req.id == "" {
|
||||
return apiutil.ErrMissingID
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
// Copyright (c) Mainflux
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package keys
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/mainflux/mainflux"
|
||||
"github.com/mainflux/mainflux/auth"
|
||||
)
|
||||
|
||||
var (
|
||||
_ mainflux.Response = (*issueKeyRes)(nil)
|
||||
_ mainflux.Response = (*revokeKeyRes)(nil)
|
||||
)
|
||||
|
||||
type issueKeyRes struct {
|
||||
ID string `json:"id,omitempty"`
|
||||
Value string `json:"value,omitempty"`
|
||||
IssuedAt time.Time `json:"issued_at,omitempty"`
|
||||
ExpiresAt *time.Time `json:"expires_at,omitempty"`
|
||||
}
|
||||
|
||||
func (res issueKeyRes) Code() int {
|
||||
return http.StatusCreated
|
||||
}
|
||||
|
||||
func (res issueKeyRes) Headers() map[string]string {
|
||||
return map[string]string{}
|
||||
}
|
||||
|
||||
func (res issueKeyRes) Empty() bool {
|
||||
return res.Value == ""
|
||||
}
|
||||
|
||||
type retrieveKeyRes struct {
|
||||
ID string `json:"id,omitempty"`
|
||||
IssuerID string `json:"issuer_id,omitempty"`
|
||||
Subject string `json:"subject,omitempty"`
|
||||
Type auth.KeyType `json:"type,omitempty"`
|
||||
IssuedAt time.Time `json:"issued_at,omitempty"`
|
||||
ExpiresAt *time.Time `json:"expires_at,omitempty"`
|
||||
}
|
||||
|
||||
func (res retrieveKeyRes) Code() int {
|
||||
return http.StatusOK
|
||||
}
|
||||
|
||||
func (res retrieveKeyRes) Headers() map[string]string {
|
||||
return map[string]string{}
|
||||
}
|
||||
|
||||
func (res retrieveKeyRes) Empty() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
type revokeKeyRes struct {
|
||||
}
|
||||
|
||||
func (res revokeKeyRes) Code() int {
|
||||
return http.StatusNoContent
|
||||
}
|
||||
|
||||
func (res revokeKeyRes) Headers() map[string]string {
|
||||
return map[string]string{}
|
||||
}
|
||||
|
||||
func (res revokeKeyRes) Empty() bool {
|
||||
return true
|
||||
}
|
||||
@@ -0,0 +1,116 @@
|
||||
// Copyright (c) Mainflux
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package keys
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
kithttp "github.com/go-kit/kit/transport/http"
|
||||
"github.com/go-zoo/bone"
|
||||
"github.com/mainflux/mainflux"
|
||||
"github.com/mainflux/mainflux/auth"
|
||||
"github.com/mainflux/mainflux/internal/apiutil"
|
||||
"github.com/mainflux/mainflux/logger"
|
||||
"github.com/mainflux/mainflux/pkg/errors"
|
||||
)
|
||||
|
||||
const contentType = "application/json"
|
||||
|
||||
// MakeHandler returns a HTTP handler for API endpoints.
|
||||
func MakeHandler(svc auth.Service, mux *bone.Mux, logger logger.Logger) *bone.Mux {
|
||||
opts := []kithttp.ServerOption{
|
||||
kithttp.ServerErrorEncoder(apiutil.LoggingErrorEncoder(logger, encodeError)),
|
||||
}
|
||||
mux.Post("/keys", kithttp.NewServer(
|
||||
issueEndpoint(svc),
|
||||
decodeIssue,
|
||||
encodeResponse,
|
||||
opts...,
|
||||
))
|
||||
|
||||
mux.Get("/keys/:id", kithttp.NewServer(
|
||||
(retrieveEndpoint(svc)),
|
||||
decodeKeyReq,
|
||||
encodeResponse,
|
||||
opts...,
|
||||
))
|
||||
|
||||
mux.Delete("/keys/:id", kithttp.NewServer(
|
||||
(revokeEndpoint(svc)),
|
||||
decodeKeyReq,
|
||||
encodeResponse,
|
||||
opts...,
|
||||
))
|
||||
|
||||
return mux
|
||||
}
|
||||
|
||||
func decodeIssue(_ context.Context, r *http.Request) (interface{}, error) {
|
||||
if !strings.Contains(r.Header.Get("Content-Type"), contentType) {
|
||||
return nil, errors.ErrUnsupportedContentType
|
||||
}
|
||||
|
||||
req := issueKeyReq{token: apiutil.ExtractBearerToken(r)}
|
||||
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
|
||||
return nil, errors.Wrap(errors.ErrMalformedEntity, err)
|
||||
}
|
||||
|
||||
return req, nil
|
||||
}
|
||||
|
||||
func decodeKeyReq(_ context.Context, r *http.Request) (interface{}, error) {
|
||||
req := keyReq{
|
||||
token: apiutil.ExtractBearerToken(r),
|
||||
id: bone.GetValue(r, "id"),
|
||||
}
|
||||
return req, nil
|
||||
}
|
||||
|
||||
func encodeResponse(_ context.Context, w http.ResponseWriter, response interface{}) error {
|
||||
w.Header().Set("Content-Type", contentType)
|
||||
|
||||
if ar, ok := response.(mainflux.Response); ok {
|
||||
for k, v := range ar.Headers() {
|
||||
w.Header().Set(k, v)
|
||||
}
|
||||
|
||||
w.WriteHeader(ar.Code())
|
||||
|
||||
if ar.Empty() {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
return json.NewEncoder(w).Encode(response)
|
||||
}
|
||||
|
||||
func encodeError(_ context.Context, err error, w http.ResponseWriter) {
|
||||
switch {
|
||||
case errors.Contains(err, errors.ErrMalformedEntity),
|
||||
err == apiutil.ErrMissingID,
|
||||
err == apiutil.ErrInvalidAPIKey:
|
||||
w.WriteHeader(http.StatusBadRequest)
|
||||
case errors.Contains(err, errors.ErrAuthentication),
|
||||
err == apiutil.ErrBearerToken:
|
||||
w.WriteHeader(http.StatusUnauthorized)
|
||||
case errors.Contains(err, errors.ErrNotFound):
|
||||
w.WriteHeader(http.StatusNotFound)
|
||||
case errors.Contains(err, errors.ErrConflict):
|
||||
w.WriteHeader(http.StatusConflict)
|
||||
case errors.Contains(err, errors.ErrUnsupportedContentType):
|
||||
w.WriteHeader(http.StatusUnsupportedMediaType)
|
||||
default:
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
}
|
||||
|
||||
if errorVal, ok := err.(errors.Error); ok {
|
||||
w.Header().Set("Content-Type", contentType)
|
||||
if err := json.NewEncoder(w).Encode(apiutil.ErrorRes{Err: errorVal.Msg()}); err != nil {
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
package policies
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/go-kit/kit/endpoint"
|
||||
"github.com/mainflux/mainflux/auth"
|
||||
)
|
||||
|
||||
func createPolicyEndpoint(svc auth.Service) endpoint.Endpoint {
|
||||
return func(ctx context.Context, request interface{}) (interface{}, error) {
|
||||
req := request.(policiesReq)
|
||||
if err := req.validate(); err != nil {
|
||||
return createPolicyRes{}, err
|
||||
}
|
||||
|
||||
if err := svc.AddPolicies(ctx, req.token, req.Object, req.SubjectIDs, req.Policies); err != nil {
|
||||
return createPolicyRes{}, err
|
||||
}
|
||||
|
||||
return createPolicyRes{created: true}, nil
|
||||
}
|
||||
}
|
||||
|
||||
func deletePoliciesEndpoint(svc auth.Service) endpoint.Endpoint {
|
||||
return func(ctx context.Context, request interface{}) (interface{}, error) {
|
||||
req := request.(policiesReq)
|
||||
if err := req.validate(); err != nil {
|
||||
return deletePoliciesRes{}, err
|
||||
}
|
||||
|
||||
if err := svc.DeletePolicies(ctx, req.token, req.Object, req.SubjectIDs, req.Policies); err != nil {
|
||||
return deletePoliciesRes{}, err
|
||||
}
|
||||
|
||||
return deletePoliciesRes{deleted: true}, nil
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,332 @@
|
||||
// Copyright (c) Mainflux
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package policies_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/mainflux/mainflux/auth"
|
||||
httpapi "github.com/mainflux/mainflux/auth/api/http"
|
||||
"github.com/mainflux/mainflux/auth/jwt"
|
||||
"github.com/mainflux/mainflux/auth/mocks"
|
||||
"github.com/mainflux/mainflux/internal/apiutil"
|
||||
"github.com/mainflux/mainflux/logger"
|
||||
"github.com/mainflux/mainflux/pkg/uuid"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
const (
|
||||
secret = "secret"
|
||||
contentType = "application/json"
|
||||
id = uuid.Prefix + "-000000000001"
|
||||
email = "user@example.com"
|
||||
unauthzID = uuid.Prefix + "-000000000002"
|
||||
unauthzEmail = "unauthz@example.com"
|
||||
loginDuration = 30 * time.Minute
|
||||
refreshDuration = 24 * time.Hour
|
||||
)
|
||||
|
||||
type testRequest struct {
|
||||
client *http.Client
|
||||
method string
|
||||
url string
|
||||
contentType string
|
||||
token string
|
||||
body io.Reader
|
||||
}
|
||||
|
||||
func (tr testRequest) make() (*http.Response, error) {
|
||||
req, err := http.NewRequest(tr.method, tr.url, tr.body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if tr.token != "" {
|
||||
req.Header.Set("Authorization", apiutil.BearerPrefix+tr.token)
|
||||
}
|
||||
if tr.contentType != "" {
|
||||
req.Header.Set("Content-Type", tr.contentType)
|
||||
}
|
||||
|
||||
req.Header.Set("Referer", "http://localhost")
|
||||
return tr.client.Do(req)
|
||||
}
|
||||
|
||||
func newService() auth.Service {
|
||||
krepo := new(mocks.Keys)
|
||||
prepo := new(mocks.PolicyAgent)
|
||||
idProvider := uuid.NewMock()
|
||||
|
||||
t := jwt.New([]byte(secret))
|
||||
|
||||
return auth.New(krepo, idProvider, t, prepo, loginDuration, refreshDuration)
|
||||
}
|
||||
|
||||
func newServer(svc auth.Service) *httptest.Server {
|
||||
logger := logger.NewMock()
|
||||
mux := httpapi.MakeHandler(svc, logger, "")
|
||||
return httptest.NewServer(mux)
|
||||
}
|
||||
|
||||
func toJSON(data interface{}) string {
|
||||
jsonData, _ := json.Marshal(data)
|
||||
return string(jsonData)
|
||||
}
|
||||
|
||||
type addPolicyRequest struct {
|
||||
SubjectIDs []string `json:"subjects"`
|
||||
Policies []string `json:"policies"`
|
||||
Object string `json:"object"`
|
||||
}
|
||||
|
||||
func TestAddPolicies(t *testing.T) {
|
||||
svc := newService()
|
||||
token, err := svc.Issue(context.Background(), "", auth.Key{Type: auth.AccessKey, IssuedAt: time.Now(), Subject: id})
|
||||
assert.Nil(t, err, fmt.Sprintf("Issuing user key expected to succeed: %s", err))
|
||||
|
||||
userLoginToken, err := svc.Issue(context.Background(), "", auth.Key{Type: auth.AccessKey, IssuedAt: time.Now(), Subject: unauthzID})
|
||||
assert.Nil(t, err, fmt.Sprintf("Issuing unauthorized user's key expected to succeed: %s", err))
|
||||
|
||||
ts := newServer(svc)
|
||||
defer ts.Close()
|
||||
client := ts.Client()
|
||||
|
||||
valid := addPolicyRequest{Object: "obj", Policies: []string{"read"}, SubjectIDs: []string{"user1", "user2"}}
|
||||
multipleValid := addPolicyRequest{Object: "obj", Policies: []string{"write", "delete"}, SubjectIDs: []string{"user1", "user2"}}
|
||||
invalidObject := addPolicyRequest{Object: "", Policies: []string{"read"}, SubjectIDs: []string{"user1", "user2"}}
|
||||
invalidPolicies := addPolicyRequest{Object: "obj", Policies: []string{"read", "invalid"}, SubjectIDs: []string{"user1", "user2"}}
|
||||
invalidSubjects := addPolicyRequest{Object: "obj", Policies: []string{"read", "access"}, SubjectIDs: []string{"", "user2"}}
|
||||
|
||||
cases := []struct {
|
||||
desc string
|
||||
token string
|
||||
ct string
|
||||
status int
|
||||
req string
|
||||
}{
|
||||
{
|
||||
desc: "Add policies with authorized access",
|
||||
token: token.AccessToken,
|
||||
ct: contentType,
|
||||
status: http.StatusCreated,
|
||||
req: toJSON(valid),
|
||||
},
|
||||
{
|
||||
desc: "Add multiple policies to multiple user",
|
||||
token: token.AccessToken,
|
||||
ct: contentType,
|
||||
status: http.StatusCreated,
|
||||
req: toJSON(multipleValid),
|
||||
},
|
||||
{
|
||||
desc: "Add policies with unauthorized access",
|
||||
token: userLoginToken.AccessToken,
|
||||
ct: contentType,
|
||||
status: http.StatusForbidden,
|
||||
req: toJSON(valid),
|
||||
},
|
||||
{
|
||||
desc: "Add policies with invalid token",
|
||||
token: "invalid",
|
||||
ct: contentType,
|
||||
status: http.StatusUnauthorized,
|
||||
req: toJSON(valid),
|
||||
},
|
||||
{
|
||||
desc: "Add policies with empty token",
|
||||
token: "",
|
||||
ct: contentType,
|
||||
status: http.StatusUnauthorized,
|
||||
req: toJSON(valid),
|
||||
},
|
||||
{
|
||||
desc: "Add policies with invalid content type",
|
||||
token: token.AccessToken,
|
||||
ct: "text/html",
|
||||
status: http.StatusUnsupportedMediaType,
|
||||
req: toJSON(valid),
|
||||
},
|
||||
{
|
||||
desc: "Add policies with empty content type",
|
||||
token: token.AccessToken,
|
||||
ct: "",
|
||||
status: http.StatusUnsupportedMediaType,
|
||||
req: toJSON(valid),
|
||||
},
|
||||
{
|
||||
desc: "Add policies with invalid object field in request body",
|
||||
token: token.AccessToken,
|
||||
ct: contentType,
|
||||
status: http.StatusBadRequest,
|
||||
req: toJSON(invalidObject),
|
||||
},
|
||||
{
|
||||
desc: "Add policies with invalid policies field in request body",
|
||||
token: token.AccessToken,
|
||||
ct: contentType,
|
||||
status: http.StatusBadRequest,
|
||||
req: toJSON(invalidPolicies),
|
||||
},
|
||||
{
|
||||
desc: "Add policies with invalid subjects field in request body",
|
||||
token: token.AccessToken,
|
||||
ct: contentType,
|
||||
status: http.StatusBadRequest,
|
||||
req: toJSON(invalidSubjects),
|
||||
},
|
||||
{
|
||||
desc: "Add policies with empty request body",
|
||||
token: token.AccessToken,
|
||||
ct: contentType,
|
||||
status: http.StatusBadRequest,
|
||||
req: "",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range cases {
|
||||
req := testRequest{
|
||||
client: client,
|
||||
method: http.MethodPost,
|
||||
url: fmt.Sprintf("%s/policies", ts.URL),
|
||||
contentType: tc.ct,
|
||||
token: tc.token,
|
||||
body: strings.NewReader(tc.req),
|
||||
}
|
||||
|
||||
res, err := req.make()
|
||||
assert.Nil(t, err, fmt.Sprintf("%s: unexpected error %s", tc.desc, err))
|
||||
assert.Equal(t, tc.status, res.StatusCode, fmt.Sprintf("%s: expected status code %d got %d", tc.desc, tc.status, res.StatusCode))
|
||||
}
|
||||
}
|
||||
|
||||
func TestDeletePolicies(t *testing.T) {
|
||||
svc := newService()
|
||||
token, err := svc.Issue(context.Background(), "", auth.Key{Type: auth.AccessKey, IssuedAt: time.Now(), Subject: id})
|
||||
assert.Nil(t, err, fmt.Sprintf("Issuing user key expected to succeed: %s", err))
|
||||
|
||||
userLoginToken, err := svc.Issue(context.Background(), "", auth.Key{Type: auth.AccessKey, IssuedAt: time.Now(), Subject: unauthzID})
|
||||
assert.Nil(t, err, fmt.Sprintf("Issuing unauthorized user's key expected to succeed: %s", err))
|
||||
|
||||
ts := newServer(svc)
|
||||
defer ts.Close()
|
||||
client := ts.Client()
|
||||
|
||||
policies := addPolicyRequest{Object: "obj", Policies: []string{"read", "write", "delete"}, SubjectIDs: []string{"user1", "user2", "user3"}}
|
||||
err = svc.AddPolicies(context.Background(), token.AccessToken, policies.Object, policies.SubjectIDs, policies.Policies)
|
||||
assert.Nil(t, err, fmt.Sprintf("Adding policies expected to succeed: %s", err))
|
||||
|
||||
validSingleDeleteReq := addPolicyRequest{Object: "obj", Policies: []string{"read"}, SubjectIDs: []string{"user1"}}
|
||||
validMultipleDeleteReq := addPolicyRequest{Object: "obj", Policies: []string{"write", "delete"}, SubjectIDs: []string{"user2", "user3"}}
|
||||
invalidObject := addPolicyRequest{Object: "", Policies: []string{"read"}, SubjectIDs: []string{"user1", "user2"}}
|
||||
invalidPolicies := addPolicyRequest{Object: "obj", Policies: []string{"read", "invalid"}, SubjectIDs: []string{"user1", "user2"}}
|
||||
invalidSubjects := addPolicyRequest{Object: "obj", Policies: []string{"read", "access"}, SubjectIDs: []string{"", "user2"}}
|
||||
|
||||
cases := []struct {
|
||||
desc string
|
||||
token string
|
||||
ct string
|
||||
req string
|
||||
status int
|
||||
}{
|
||||
{
|
||||
desc: "Delete policies with unauthorized access",
|
||||
token: userLoginToken.AccessToken,
|
||||
ct: contentType,
|
||||
status: http.StatusForbidden,
|
||||
req: toJSON(validMultipleDeleteReq),
|
||||
},
|
||||
{
|
||||
desc: "Delete policies with invalid token",
|
||||
token: "invalid",
|
||||
ct: contentType,
|
||||
status: http.StatusUnauthorized,
|
||||
req: toJSON(validSingleDeleteReq),
|
||||
},
|
||||
{
|
||||
desc: "Delete policies with empty token",
|
||||
token: "",
|
||||
ct: contentType,
|
||||
status: http.StatusUnauthorized,
|
||||
req: toJSON(validSingleDeleteReq),
|
||||
},
|
||||
{
|
||||
desc: "Delete policies with authorized access",
|
||||
token: token.AccessToken,
|
||||
ct: contentType,
|
||||
status: http.StatusNoContent,
|
||||
req: toJSON(validSingleDeleteReq),
|
||||
},
|
||||
{
|
||||
desc: "Delete multiple policies to multiple user",
|
||||
token: token.AccessToken,
|
||||
ct: contentType,
|
||||
status: http.StatusNoContent,
|
||||
req: toJSON(validMultipleDeleteReq),
|
||||
},
|
||||
{
|
||||
desc: "Delete policies with invalid content type",
|
||||
token: token.AccessToken,
|
||||
ct: "text/html",
|
||||
status: http.StatusUnsupportedMediaType,
|
||||
req: toJSON(validMultipleDeleteReq),
|
||||
},
|
||||
{
|
||||
desc: "Delete policies with empty content type",
|
||||
token: token.AccessToken,
|
||||
ct: "",
|
||||
status: http.StatusUnsupportedMediaType,
|
||||
req: toJSON(validMultipleDeleteReq),
|
||||
},
|
||||
{
|
||||
desc: "Delete policies with invalid object field in request body",
|
||||
token: token.AccessToken,
|
||||
ct: contentType,
|
||||
status: http.StatusBadRequest,
|
||||
req: toJSON(invalidObject),
|
||||
},
|
||||
{
|
||||
desc: "Delete policies with invalid policies field in request body",
|
||||
token: token.AccessToken,
|
||||
ct: contentType,
|
||||
status: http.StatusBadRequest,
|
||||
req: toJSON(invalidPolicies),
|
||||
},
|
||||
{
|
||||
desc: "Delete policies with invalid subjects field in request body",
|
||||
token: token.AccessToken,
|
||||
ct: contentType,
|
||||
status: http.StatusBadRequest,
|
||||
req: toJSON(invalidSubjects),
|
||||
},
|
||||
{
|
||||
desc: "Delete policies with empty request body",
|
||||
token: token.AccessToken,
|
||||
ct: contentType,
|
||||
status: http.StatusBadRequest,
|
||||
req: "",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range cases {
|
||||
req := testRequest{
|
||||
client: client,
|
||||
method: http.MethodPut,
|
||||
url: fmt.Sprintf("%s/policies", ts.URL),
|
||||
contentType: tc.ct,
|
||||
token: tc.token,
|
||||
body: strings.NewReader(tc.req),
|
||||
}
|
||||
|
||||
res, err := req.make()
|
||||
assert.Nil(t, err, fmt.Sprintf("%s: unexpected error %s", tc.desc, err))
|
||||
assert.Equal(t, tc.status, res.StatusCode, fmt.Sprintf("%s: expected status code %d got %d", tc.desc, tc.status, res.StatusCode))
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
package policies
|
||||
|
||||
import "github.com/mainflux/mainflux/internal/apiutil"
|
||||
|
||||
// Action represents an enum for the policies used in the Mainflux.
|
||||
type Action int
|
||||
|
||||
const (
|
||||
Create Action = iota
|
||||
Read
|
||||
Write
|
||||
Delete
|
||||
Access
|
||||
Member
|
||||
Unknown
|
||||
)
|
||||
|
||||
var actions = map[string]Action{
|
||||
"create": Create,
|
||||
"read": Read,
|
||||
"write": Write,
|
||||
"delete": Delete,
|
||||
"access": Access,
|
||||
"member": Member,
|
||||
}
|
||||
|
||||
type policiesReq struct {
|
||||
token string
|
||||
SubjectIDs []string `json:"subjects"`
|
||||
Policies []string `json:"policies"`
|
||||
Object string `json:"object"`
|
||||
}
|
||||
|
||||
func (req policiesReq) validate() error {
|
||||
if req.token == "" {
|
||||
return apiutil.ErrBearerToken
|
||||
}
|
||||
|
||||
if len(req.SubjectIDs) == 0 {
|
||||
return apiutil.ErrEmptyList
|
||||
}
|
||||
|
||||
if len(req.Policies) == 0 {
|
||||
return apiutil.ErrEmptyList
|
||||
}
|
||||
|
||||
if req.Object == "" {
|
||||
return apiutil.ErrMissingPolicyObj
|
||||
}
|
||||
|
||||
for _, policy := range req.Policies {
|
||||
if _, ok := actions[policy]; !ok {
|
||||
return apiutil.ErrMalformedPolicy
|
||||
}
|
||||
}
|
||||
|
||||
for _, subID := range req.SubjectIDs {
|
||||
if subID == "" {
|
||||
return apiutil.ErrMissingPolicySub
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
package policies
|
||||
|
||||
import "net/http"
|
||||
|
||||
type createPolicyRes struct {
|
||||
created bool
|
||||
}
|
||||
|
||||
func (res createPolicyRes) Code() int {
|
||||
if res.created {
|
||||
return http.StatusCreated
|
||||
}
|
||||
|
||||
return http.StatusOK
|
||||
}
|
||||
|
||||
func (res createPolicyRes) Headers() map[string]string {
|
||||
return map[string]string{}
|
||||
}
|
||||
|
||||
func (res createPolicyRes) Empty() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
type deletePoliciesRes struct {
|
||||
deleted bool
|
||||
}
|
||||
|
||||
func (res deletePoliciesRes) Code() int {
|
||||
if res.deleted {
|
||||
return http.StatusNoContent
|
||||
}
|
||||
|
||||
return http.StatusOK
|
||||
}
|
||||
|
||||
func (res deletePoliciesRes) Headers() map[string]string {
|
||||
return map[string]string{}
|
||||
}
|
||||
|
||||
func (res deletePoliciesRes) Empty() bool {
|
||||
return true
|
||||
}
|
||||
@@ -0,0 +1,103 @@
|
||||
package policies
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
kithttp "github.com/go-kit/kit/transport/http"
|
||||
"github.com/go-zoo/bone"
|
||||
"github.com/mainflux/mainflux"
|
||||
"github.com/mainflux/mainflux/auth"
|
||||
"github.com/mainflux/mainflux/internal/apiutil"
|
||||
"github.com/mainflux/mainflux/logger"
|
||||
"github.com/mainflux/mainflux/pkg/errors"
|
||||
)
|
||||
|
||||
const contentType = "application/json"
|
||||
|
||||
// MakeHandler returns a HTTP handler for API endpoints.
|
||||
func MakeHandler(svc auth.Service, mux *bone.Mux, logger logger.Logger) *bone.Mux {
|
||||
opts := []kithttp.ServerOption{
|
||||
kithttp.ServerErrorEncoder(apiutil.LoggingErrorEncoder(logger, encodeError)),
|
||||
}
|
||||
|
||||
mux.Post("/policies", kithttp.NewServer(
|
||||
(createPolicyEndpoint(svc)),
|
||||
decodePoliciesRequest,
|
||||
encodeResponse,
|
||||
opts...,
|
||||
))
|
||||
|
||||
mux.Put("/policies", kithttp.NewServer(
|
||||
(deletePoliciesEndpoint(svc)),
|
||||
decodePoliciesRequest,
|
||||
encodeResponse,
|
||||
opts...,
|
||||
))
|
||||
|
||||
return mux
|
||||
}
|
||||
|
||||
func decodePoliciesRequest(ctx context.Context, r *http.Request) (interface{}, error) {
|
||||
if !strings.Contains(r.Header.Get("Content-Type"), contentType) {
|
||||
return nil, errors.ErrUnsupportedContentType
|
||||
}
|
||||
|
||||
req := policiesReq{token: apiutil.ExtractBearerToken(r)}
|
||||
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
|
||||
return nil, errors.Wrap(errors.ErrMalformedEntity, err)
|
||||
}
|
||||
|
||||
return req, nil
|
||||
}
|
||||
|
||||
func encodeResponse(_ context.Context, w http.ResponseWriter, response interface{}) error {
|
||||
w.Header().Set("Content-Type", contentType)
|
||||
|
||||
if ar, ok := response.(mainflux.Response); ok {
|
||||
for k, v := range ar.Headers() {
|
||||
w.Header().Set(k, v)
|
||||
}
|
||||
|
||||
w.WriteHeader(ar.Code())
|
||||
|
||||
if ar.Empty() {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
return json.NewEncoder(w).Encode(response)
|
||||
}
|
||||
|
||||
func encodeError(_ context.Context, err error, w http.ResponseWriter) {
|
||||
switch {
|
||||
case errors.Contains(err, errors.ErrMalformedEntity),
|
||||
err == apiutil.ErrEmptyList,
|
||||
err == apiutil.ErrMissingPolicyObj,
|
||||
err == apiutil.ErrMissingPolicySub,
|
||||
err == apiutil.ErrMalformedPolicy:
|
||||
w.WriteHeader(http.StatusBadRequest)
|
||||
case errors.Contains(err, errors.ErrAuthentication),
|
||||
err == apiutil.ErrBearerToken:
|
||||
w.WriteHeader(http.StatusUnauthorized)
|
||||
case errors.Contains(err, errors.ErrNotFound):
|
||||
w.WriteHeader(http.StatusNotFound)
|
||||
case errors.Contains(err, errors.ErrConflict):
|
||||
w.WriteHeader(http.StatusConflict)
|
||||
case errors.Contains(err, errors.ErrAuthorization):
|
||||
w.WriteHeader(http.StatusForbidden)
|
||||
case errors.Contains(err, errors.ErrUnsupportedContentType):
|
||||
w.WriteHeader(http.StatusUnsupportedMediaType)
|
||||
default:
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
}
|
||||
|
||||
if errorVal, ok := err.(errors.Error); ok {
|
||||
w.Header().Set("Content-Type", contentType)
|
||||
if err := json.NewEncoder(w).Encode(apiutil.ErrorRes{Err: errorVal.Msg()}); err != nil {
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
// Copyright (c) Mainflux
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
package http
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/go-zoo/bone"
|
||||
"github.com/mainflux/mainflux"
|
||||
"github.com/mainflux/mainflux/auth"
|
||||
"github.com/mainflux/mainflux/auth/api/http/keys"
|
||||
"github.com/mainflux/mainflux/auth/api/http/policies"
|
||||
"github.com/mainflux/mainflux/logger"
|
||||
"github.com/prometheus/client_golang/prometheus/promhttp"
|
||||
)
|
||||
|
||||
// MakeHandler returns a HTTP handler for API endpoints.
|
||||
func MakeHandler(svc auth.Service, logger logger.Logger, instanceID string) http.Handler {
|
||||
mux := bone.New()
|
||||
|
||||
mux = keys.MakeHandler(svc, mux, logger)
|
||||
mux = policies.MakeHandler(svc, mux, logger)
|
||||
|
||||
mux.GetFunc("/health", mainflux.Health("auth", instanceID))
|
||||
mux.Handle("/metrics", promhttp.Handler())
|
||||
|
||||
return mux
|
||||
}
|
||||
@@ -0,0 +1,220 @@
|
||||
// Copyright (c) Mainflux
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
//go:build !test
|
||||
|
||||
package api
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/mainflux/mainflux/auth"
|
||||
log "github.com/mainflux/mainflux/logger"
|
||||
)
|
||||
|
||||
var _ auth.Service = (*loggingMiddleware)(nil)
|
||||
|
||||
type loggingMiddleware struct {
|
||||
logger log.Logger
|
||||
svc auth.Service
|
||||
}
|
||||
|
||||
// LoggingMiddleware adds logging facilities to the core service.
|
||||
func LoggingMiddleware(svc auth.Service, logger log.Logger) auth.Service {
|
||||
return &loggingMiddleware{logger, svc}
|
||||
}
|
||||
|
||||
func (lm *loggingMiddleware) ListObjects(ctx context.Context, pr auth.PolicyReq, nextPageToken string, limit int32) (p auth.PolicyPage, err error) {
|
||||
defer func(begin time.Time) {
|
||||
message := fmt.Sprintf("Method list_objects took %s to complete", time.Since(begin))
|
||||
if err != nil {
|
||||
lm.logger.Warn(fmt.Sprintf("%s with error: %s.", message, err))
|
||||
return
|
||||
}
|
||||
lm.logger.Info(fmt.Sprintf("%s without errors.", message))
|
||||
}(time.Now())
|
||||
|
||||
return lm.svc.ListObjects(ctx, pr, nextPageToken, limit)
|
||||
}
|
||||
|
||||
func (lm *loggingMiddleware) ListAllObjects(ctx context.Context, pr auth.PolicyReq) (p auth.PolicyPage, err error) {
|
||||
defer func(begin time.Time) {
|
||||
message := fmt.Sprintf("Method list_all_objects took %s to complete", time.Since(begin))
|
||||
if err != nil {
|
||||
lm.logger.Warn(fmt.Sprintf("%s with error: %s.", message, err))
|
||||
return
|
||||
}
|
||||
lm.logger.Info(fmt.Sprintf("%s without errors.", message))
|
||||
}(time.Now())
|
||||
|
||||
return lm.svc.ListAllObjects(ctx, pr)
|
||||
}
|
||||
|
||||
func (lm *loggingMiddleware) CountObjects(ctx context.Context, pr auth.PolicyReq) (count int, err error) {
|
||||
defer func(begin time.Time) {
|
||||
message := fmt.Sprintf("Method count_objects took %s to complete", time.Since(begin))
|
||||
if err != nil {
|
||||
lm.logger.Warn(fmt.Sprintf("%s with error: %s.", message, err))
|
||||
return
|
||||
}
|
||||
lm.logger.Info(fmt.Sprintf("%s without errors.", message))
|
||||
}(time.Now())
|
||||
return lm.svc.CountObjects(ctx, pr)
|
||||
}
|
||||
|
||||
func (lm *loggingMiddleware) ListSubjects(ctx context.Context, pr auth.PolicyReq, nextPageToken string, limit int32) (p auth.PolicyPage, err error) {
|
||||
defer func(begin time.Time) {
|
||||
message := fmt.Sprintf("Method list_subjects took %s to complete", time.Since(begin))
|
||||
if err != nil {
|
||||
lm.logger.Warn(fmt.Sprintf("%s with error: %s.", message, err))
|
||||
return
|
||||
}
|
||||
lm.logger.Info(fmt.Sprintf("%s without errors.", message))
|
||||
}(time.Now())
|
||||
|
||||
return lm.svc.ListSubjects(ctx, pr, nextPageToken, limit)
|
||||
}
|
||||
|
||||
func (lm *loggingMiddleware) ListAllSubjects(ctx context.Context, pr auth.PolicyReq) (p auth.PolicyPage, err error) {
|
||||
defer func(begin time.Time) {
|
||||
message := fmt.Sprintf("Method list_all_subjects took %s to complete", time.Since(begin))
|
||||
if err != nil {
|
||||
lm.logger.Warn(fmt.Sprintf("%s with error: %s.", message, err))
|
||||
return
|
||||
}
|
||||
lm.logger.Info(fmt.Sprintf("%s without errors.", message))
|
||||
}(time.Now())
|
||||
|
||||
return lm.svc.ListAllSubjects(ctx, pr)
|
||||
}
|
||||
|
||||
func (lm *loggingMiddleware) CountSubjects(ctx context.Context, pr auth.PolicyReq) (count int, err error) {
|
||||
defer func(begin time.Time) {
|
||||
message := fmt.Sprintf("Method list_subjects took %s to complete", time.Since(begin))
|
||||
if err != nil {
|
||||
lm.logger.Warn(fmt.Sprintf("%s with error: %s.", message, err))
|
||||
return
|
||||
}
|
||||
lm.logger.Info(fmt.Sprintf("%s without errors.", message))
|
||||
}(time.Now())
|
||||
return lm.svc.CountSubjects(ctx, pr)
|
||||
}
|
||||
|
||||
func (lm *loggingMiddleware) Issue(ctx context.Context, token string, key auth.Key) (tkn auth.Token, err error) {
|
||||
defer func(begin time.Time) {
|
||||
d := ""
|
||||
if key.Type != auth.AccessKey && !key.ExpiresAt.IsZero() {
|
||||
d = fmt.Sprintf("with expiration date %v", key.ExpiresAt)
|
||||
}
|
||||
message := fmt.Sprintf("Method issue for %d key %s took %s to complete", key.Type, d, time.Since(begin))
|
||||
if err != nil {
|
||||
lm.logger.Warn(fmt.Sprintf("%s with error: %s.", message, err))
|
||||
return
|
||||
}
|
||||
lm.logger.Info(fmt.Sprintf("%s without errors.", message))
|
||||
}(time.Now())
|
||||
|
||||
return lm.svc.Issue(ctx, token, key)
|
||||
}
|
||||
|
||||
func (lm *loggingMiddleware) Revoke(ctx context.Context, token, id string) (err error) {
|
||||
defer func(begin time.Time) {
|
||||
message := fmt.Sprintf("Method revoke for key %s took %s to complete", id, time.Since(begin))
|
||||
if err != nil {
|
||||
lm.logger.Warn(fmt.Sprintf("%s with error: %s.", message, err))
|
||||
return
|
||||
}
|
||||
lm.logger.Info(fmt.Sprintf("%s without errors.", message))
|
||||
}(time.Now())
|
||||
|
||||
return lm.svc.Revoke(ctx, token, id)
|
||||
}
|
||||
|
||||
func (lm *loggingMiddleware) RetrieveKey(ctx context.Context, token, id string) (key auth.Key, err error) {
|
||||
defer func(begin time.Time) {
|
||||
message := fmt.Sprintf("Method retrieve for key %s took %s to complete", id, time.Since(begin))
|
||||
if err != nil {
|
||||
lm.logger.Warn(fmt.Sprintf("%s with error: %s.", message, err))
|
||||
return
|
||||
}
|
||||
lm.logger.Info(fmt.Sprintf("%s without errors.", message))
|
||||
}(time.Now())
|
||||
|
||||
return lm.svc.RetrieveKey(ctx, token, id)
|
||||
}
|
||||
|
||||
func (lm *loggingMiddleware) Identify(ctx context.Context, key string) (id string, err error) {
|
||||
defer func(begin time.Time) {
|
||||
message := fmt.Sprintf("Method identify took %s to complete", time.Since(begin))
|
||||
if err != nil {
|
||||
lm.logger.Warn(fmt.Sprintf("%s with error: %s.", message, err))
|
||||
return
|
||||
}
|
||||
lm.logger.Info(fmt.Sprintf("%s without errors.", message))
|
||||
}(time.Now())
|
||||
|
||||
return lm.svc.Identify(ctx, key)
|
||||
}
|
||||
|
||||
func (lm *loggingMiddleware) Authorize(ctx context.Context, pr auth.PolicyReq) (err error) {
|
||||
defer func(begin time.Time) {
|
||||
message := fmt.Sprintf("Method authorize took %s to complete", time.Since(begin))
|
||||
if err != nil {
|
||||
lm.logger.Warn(fmt.Sprintf("%s with error: %s.", message, err))
|
||||
return
|
||||
}
|
||||
lm.logger.Info(fmt.Sprintf("%s without errors.", message))
|
||||
}(time.Now())
|
||||
return lm.svc.Authorize(ctx, pr)
|
||||
}
|
||||
|
||||
func (lm *loggingMiddleware) AddPolicy(ctx context.Context, pr auth.PolicyReq) (err error) {
|
||||
defer func(begin time.Time) {
|
||||
message := fmt.Sprintf("Method add_policy took %s to complete", time.Since(begin))
|
||||
if err != nil {
|
||||
lm.logger.Warn(fmt.Sprintf("%s with error: %s.", message, err))
|
||||
return
|
||||
}
|
||||
lm.logger.Info(fmt.Sprintf("%s without errors.", message))
|
||||
}(time.Now())
|
||||
return lm.svc.AddPolicy(ctx, pr)
|
||||
}
|
||||
|
||||
func (lm *loggingMiddleware) AddPolicies(ctx context.Context, token, object string, subjectIDs, relations []string) (err error) {
|
||||
defer func(begin time.Time) {
|
||||
message := fmt.Sprintf("Method create_policy_bulk took %s to complete", time.Since(begin))
|
||||
if err != nil {
|
||||
lm.logger.Warn(fmt.Sprintf("%s with error: %s.", message, err))
|
||||
return
|
||||
}
|
||||
lm.logger.Info(fmt.Sprintf("%s without errors.", message))
|
||||
}(time.Now())
|
||||
|
||||
return lm.svc.AddPolicies(ctx, token, object, subjectIDs, relations)
|
||||
}
|
||||
|
||||
func (lm *loggingMiddleware) DeletePolicy(ctx context.Context, pr auth.PolicyReq) (err error) {
|
||||
defer func(begin time.Time) {
|
||||
message := fmt.Sprintf("Method delete_policy took %s to complete", time.Since(begin))
|
||||
if err != nil {
|
||||
lm.logger.Warn(fmt.Sprintf("%s with error: %s.", message, err))
|
||||
return
|
||||
}
|
||||
lm.logger.Info(fmt.Sprintf("%s without errors.", message))
|
||||
}(time.Now())
|
||||
return lm.svc.DeletePolicy(ctx, pr)
|
||||
}
|
||||
|
||||
func (lm *loggingMiddleware) DeletePolicies(ctx context.Context, token, object string, subjectIDs, relations []string) (err error) {
|
||||
defer func(begin time.Time) {
|
||||
message := fmt.Sprintf("Method delete_policies took %s to complete", time.Since(begin))
|
||||
if err != nil {
|
||||
lm.logger.Warn(fmt.Sprintf("%s with error: %s.", message, err))
|
||||
return
|
||||
}
|
||||
lm.logger.Info(fmt.Sprintf("%s without errors.", message))
|
||||
}(time.Now())
|
||||
return lm.svc.DeletePolicies(ctx, token, object, subjectIDs, relations)
|
||||
}
|
||||
@@ -0,0 +1,160 @@
|
||||
// Copyright (c) Mainflux
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
//go:build !test
|
||||
|
||||
package api
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"github.com/go-kit/kit/metrics"
|
||||
"github.com/mainflux/mainflux/auth"
|
||||
)
|
||||
|
||||
var _ auth.Service = (*metricsMiddleware)(nil)
|
||||
|
||||
type metricsMiddleware struct {
|
||||
counter metrics.Counter
|
||||
latency metrics.Histogram
|
||||
svc auth.Service
|
||||
}
|
||||
|
||||
// MetricsMiddleware instruments core service by tracking request count and latency.
|
||||
func MetricsMiddleware(svc auth.Service, counter metrics.Counter, latency metrics.Histogram) auth.Service {
|
||||
return &metricsMiddleware{
|
||||
counter: counter,
|
||||
latency: latency,
|
||||
svc: svc,
|
||||
}
|
||||
}
|
||||
|
||||
func (ms *metricsMiddleware) ListObjects(ctx context.Context, pr auth.PolicyReq, nextPageToken string, limit int32) (p auth.PolicyPage, err error) {
|
||||
defer func(begin time.Time) {
|
||||
ms.counter.With("method", "list_objects").Add(1)
|
||||
ms.latency.With("method", "list_objects").Observe(time.Since(begin).Seconds())
|
||||
}(time.Now())
|
||||
|
||||
return ms.svc.ListObjects(ctx, pr, nextPageToken, limit)
|
||||
}
|
||||
|
||||
func (ms *metricsMiddleware) ListAllObjects(ctx context.Context, pr auth.PolicyReq) (p auth.PolicyPage, err error) {
|
||||
defer func(begin time.Time) {
|
||||
ms.counter.With("method", "list_all_objects").Add(1)
|
||||
ms.latency.With("method", "list_all_objects").Observe(time.Since(begin).Seconds())
|
||||
}(time.Now())
|
||||
|
||||
return ms.svc.ListAllObjects(ctx, pr)
|
||||
}
|
||||
|
||||
func (ms *metricsMiddleware) CountObjects(ctx context.Context, pr auth.PolicyReq) (count int, err error) {
|
||||
defer func(begin time.Time) {
|
||||
ms.counter.With("method", "count_objects").Add(1)
|
||||
ms.latency.With("method", "count_objects").Observe(time.Since(begin).Seconds())
|
||||
}(time.Now())
|
||||
return ms.svc.CountObjects(ctx, pr)
|
||||
}
|
||||
|
||||
func (ms *metricsMiddleware) ListSubjects(ctx context.Context, pr auth.PolicyReq, nextPageToken string, limit int32) (p auth.PolicyPage, err error) {
|
||||
defer func(begin time.Time) {
|
||||
ms.counter.With("method", "list_subjects").Add(1)
|
||||
ms.latency.With("method", "list_subjects").Observe(time.Since(begin).Seconds())
|
||||
}(time.Now())
|
||||
|
||||
return ms.svc.ListSubjects(ctx, pr, nextPageToken, limit)
|
||||
}
|
||||
|
||||
func (ms *metricsMiddleware) ListAllSubjects(ctx context.Context, pr auth.PolicyReq) (p auth.PolicyPage, err error) {
|
||||
defer func(begin time.Time) {
|
||||
ms.counter.With("method", "list_all_subjects").Add(1)
|
||||
ms.latency.With("method", "list_all_subjects").Observe(time.Since(begin).Seconds())
|
||||
}(time.Now())
|
||||
|
||||
return ms.svc.ListAllSubjects(ctx, pr)
|
||||
}
|
||||
|
||||
func (ms *metricsMiddleware) CountSubjects(ctx context.Context, pr auth.PolicyReq) (count int, err error) {
|
||||
defer func(begin time.Time) {
|
||||
ms.counter.With("method", "count_subjects").Add(1)
|
||||
ms.latency.With("method", "count_subjects").Observe(time.Since(begin).Seconds())
|
||||
}(time.Now())
|
||||
return ms.svc.CountSubjects(ctx, pr)
|
||||
}
|
||||
|
||||
func (ms *metricsMiddleware) Issue(ctx context.Context, token string, key auth.Key) (auth.Token, error) {
|
||||
defer func(begin time.Time) {
|
||||
ms.counter.With("method", "issue_key").Add(1)
|
||||
ms.latency.With("method", "issue_key").Observe(time.Since(begin).Seconds())
|
||||
}(time.Now())
|
||||
|
||||
return ms.svc.Issue(ctx, token, key)
|
||||
}
|
||||
|
||||
func (ms *metricsMiddleware) Revoke(ctx context.Context, token, id string) error {
|
||||
defer func(begin time.Time) {
|
||||
ms.counter.With("method", "revoke_key").Add(1)
|
||||
ms.latency.With("method", "revoke_key").Observe(time.Since(begin).Seconds())
|
||||
}(time.Now())
|
||||
|
||||
return ms.svc.Revoke(ctx, token, id)
|
||||
}
|
||||
|
||||
func (ms *metricsMiddleware) RetrieveKey(ctx context.Context, token, id string) (auth.Key, error) {
|
||||
defer func(begin time.Time) {
|
||||
ms.counter.With("method", "retrieve_key").Add(1)
|
||||
ms.latency.With("method", "retrieve_key").Observe(time.Since(begin).Seconds())
|
||||
}(time.Now())
|
||||
|
||||
return ms.svc.RetrieveKey(ctx, token, id)
|
||||
}
|
||||
|
||||
func (ms *metricsMiddleware) Identify(ctx context.Context, token string) (string, error) {
|
||||
defer func(begin time.Time) {
|
||||
ms.counter.With("method", "identify").Add(1)
|
||||
ms.latency.With("method", "identify").Observe(time.Since(begin).Seconds())
|
||||
}(time.Now())
|
||||
|
||||
return ms.svc.Identify(ctx, token)
|
||||
}
|
||||
|
||||
func (ms *metricsMiddleware) Authorize(ctx context.Context, pr auth.PolicyReq) error {
|
||||
defer func(begin time.Time) {
|
||||
ms.counter.With("method", "authorize").Add(1)
|
||||
ms.latency.With("method", "authorize").Observe(time.Since(begin).Seconds())
|
||||
}(time.Now())
|
||||
return ms.svc.Authorize(ctx, pr)
|
||||
}
|
||||
|
||||
func (ms *metricsMiddleware) AddPolicy(ctx context.Context, pr auth.PolicyReq) error {
|
||||
defer func(begin time.Time) {
|
||||
ms.counter.With("method", "add_policy").Add(1)
|
||||
ms.latency.With("method", "add_policy").Observe(time.Since(begin).Seconds())
|
||||
}(time.Now())
|
||||
return ms.svc.AddPolicy(ctx, pr)
|
||||
}
|
||||
|
||||
func (ms *metricsMiddleware) AddPolicies(ctx context.Context, token, object string, subjectIDs, relations []string) (err error) {
|
||||
defer func(begin time.Time) {
|
||||
ms.counter.With("method", "create_policy_bulk").Add(1)
|
||||
ms.latency.With("method", "create_policy_bulk").Observe(time.Since(begin).Seconds())
|
||||
}(time.Now())
|
||||
|
||||
return ms.svc.AddPolicies(ctx, token, object, subjectIDs, relations)
|
||||
}
|
||||
|
||||
func (ms *metricsMiddleware) DeletePolicy(ctx context.Context, pr auth.PolicyReq) error {
|
||||
defer func(begin time.Time) {
|
||||
ms.counter.With("method", "delete_policy").Add(1)
|
||||
ms.latency.With("method", "delete_policy").Observe(time.Since(begin).Seconds())
|
||||
}(time.Now())
|
||||
return ms.svc.DeletePolicy(ctx, pr)
|
||||
}
|
||||
|
||||
func (ms *metricsMiddleware) DeletePolicies(ctx context.Context, token, object string, subjectIDs, relations []string) error {
|
||||
defer func(begin time.Time) {
|
||||
ms.counter.With("method", "delete_policies").Add(1)
|
||||
ms.latency.With("method", "delete_policies").Observe(time.Since(begin).Seconds())
|
||||
}(time.Now())
|
||||
return ms.svc.DeletePolicies(ctx, token, object, subjectIDs, relations)
|
||||
}
|
||||
@@ -0,0 +1,112 @@
|
||||
// Copyright (c) Mainflux
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package jwt_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/mainflux/mainflux/auth"
|
||||
"github.com/mainflux/mainflux/auth/jwt"
|
||||
"github.com/mainflux/mainflux/pkg/errors"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
const secret = "test"
|
||||
|
||||
func key() auth.Key {
|
||||
exp := time.Now().UTC().Add(10 * time.Minute).Round(time.Second)
|
||||
return auth.Key{
|
||||
ID: "66af4a67-3823-438a-abd7-efdb613eaef6",
|
||||
Type: auth.AccessKey,
|
||||
Issuer: "mainflux.auth",
|
||||
Subject: "66af4a67-3823-438a-abd7-efdb613eaef6",
|
||||
IssuedAt: time.Now().UTC().Add(-10 * time.Second).Round(time.Second),
|
||||
ExpiresAt: exp,
|
||||
}
|
||||
}
|
||||
|
||||
func TestIssue(t *testing.T) {
|
||||
tokenizer := jwt.New([]byte(secret))
|
||||
|
||||
cases := []struct {
|
||||
desc string
|
||||
key auth.Key
|
||||
err error
|
||||
}{
|
||||
{
|
||||
desc: "issue new token",
|
||||
key: key(),
|
||||
err: nil,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range cases {
|
||||
tkn, err := tokenizer.Issue(tc.key)
|
||||
assert.True(t, errors.Contains(err, tc.err), fmt.Sprintf("%s expected %s, got %s", tc.desc, tc.err, err))
|
||||
if err != nil {
|
||||
assert.NotEmpty(t, tkn, fmt.Sprintf("%s expected token, got empty string", tc.desc))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestParse(t *testing.T) {
|
||||
tokenizer := jwt.New([]byte(secret))
|
||||
|
||||
token, err := tokenizer.Issue(key())
|
||||
require.Nil(t, err, fmt.Sprintf("issuing key expected to succeed: %s", err))
|
||||
|
||||
apiKey := key()
|
||||
apiKey.Type = auth.APIKey
|
||||
apiKey.ExpiresAt = time.Now().UTC().Add(-1 * time.Minute).Round(time.Second)
|
||||
apiToken, err := tokenizer.Issue(apiKey)
|
||||
require.Nil(t, err, fmt.Sprintf("issuing user key expected to succeed: %s", err))
|
||||
|
||||
expKey := key()
|
||||
expKey.ExpiresAt = time.Now().UTC().Add(-1 * time.Minute).Round(time.Second)
|
||||
expToken, err := tokenizer.Issue(expKey)
|
||||
require.Nil(t, err, fmt.Sprintf("issuing expired key expected to succeed: %s", err))
|
||||
|
||||
cases := []struct {
|
||||
desc string
|
||||
key auth.Key
|
||||
token string
|
||||
err error
|
||||
}{
|
||||
{
|
||||
desc: "parse valid key",
|
||||
key: key(),
|
||||
token: token,
|
||||
err: nil,
|
||||
},
|
||||
{
|
||||
desc: "parse ivalid key",
|
||||
key: auth.Key{},
|
||||
token: "invalid",
|
||||
err: errors.ErrAuthentication,
|
||||
},
|
||||
{
|
||||
desc: "parse expired key",
|
||||
key: auth.Key{},
|
||||
token: expToken,
|
||||
err: jwt.ErrExpiry,
|
||||
},
|
||||
{
|
||||
desc: "parse expired API key",
|
||||
key: apiKey,
|
||||
token: apiToken,
|
||||
err: jwt.ErrExpiry,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range cases {
|
||||
key, err := tokenizer.Parse(tc.token)
|
||||
assert.True(t, errors.Contains(err, tc.err), fmt.Sprintf("%s expected %s, got %s", tc.desc, tc.err, err))
|
||||
if err == nil {
|
||||
assert.Equal(t, tc.key, key, fmt.Sprintf("%s expected %v, got %v", tc.desc, tc.key, key))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,114 @@
|
||||
// Copyright (c) Mainflux
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package jwt
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"strconv"
|
||||
|
||||
"github.com/lestrrat-go/jwx/v2/jwa"
|
||||
"github.com/lestrrat-go/jwx/v2/jwt"
|
||||
"github.com/mainflux/mainflux/auth"
|
||||
"github.com/mainflux/mainflux/pkg/errors"
|
||||
)
|
||||
|
||||
var (
|
||||
errInvalidIssuer = errors.New("invalid token issuer value")
|
||||
// errJWTExpiryKey is used to check if the token is expired.
|
||||
errJWTExpiryKey = errors.New(`"exp" not satisfied`)
|
||||
// ErrExpiry indicates that the token is expired.
|
||||
ErrExpiry = errors.New("token is expired")
|
||||
)
|
||||
|
||||
const (
|
||||
issuerName = "mainflux.auth"
|
||||
tokenType = "type"
|
||||
)
|
||||
|
||||
type tokenizer struct {
|
||||
secret []byte
|
||||
}
|
||||
|
||||
var _ auth.Tokenizer = (*tokenizer)(nil)
|
||||
|
||||
// NewRepository instantiates an implementation of Token repository.
|
||||
func New(secret []byte) auth.Tokenizer {
|
||||
return &tokenizer{
|
||||
secret: secret,
|
||||
}
|
||||
}
|
||||
|
||||
func (repo *tokenizer) Issue(key auth.Key) (string, error) {
|
||||
builder := jwt.NewBuilder()
|
||||
builder.
|
||||
Issuer(issuerName).
|
||||
IssuedAt(key.IssuedAt).
|
||||
Subject(key.Subject).
|
||||
Claim(tokenType, key.Type).
|
||||
Expiration(key.ExpiresAt)
|
||||
if key.ID != "" {
|
||||
builder.JwtID(key.ID)
|
||||
}
|
||||
tkn, err := builder.Build()
|
||||
if err != nil {
|
||||
return "", errors.Wrap(errors.ErrAuthentication, err)
|
||||
}
|
||||
signedTkn, err := jwt.Sign(tkn, jwt.WithKey(jwa.HS512, repo.secret))
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return string(signedTkn), nil
|
||||
}
|
||||
|
||||
func (repo *tokenizer) Parse(token string) (auth.Key, error) {
|
||||
tkn, err := jwt.Parse(
|
||||
[]byte(token),
|
||||
jwt.WithValidate(true),
|
||||
jwt.WithKey(jwa.HS512, repo.secret),
|
||||
)
|
||||
if err != nil {
|
||||
if errors.Contains(err, errJWTExpiryKey) {
|
||||
return auth.Key{}, ErrExpiry
|
||||
}
|
||||
|
||||
return auth.Key{}, errors.Wrap(errors.ErrAuthentication, err)
|
||||
}
|
||||
validator := jwt.ValidatorFunc(func(_ context.Context, t jwt.Token) jwt.ValidationError {
|
||||
if t.Issuer() != issuerName {
|
||||
return jwt.NewValidationError(errInvalidIssuer)
|
||||
}
|
||||
return nil
|
||||
})
|
||||
if err := jwt.Validate(tkn, jwt.WithValidator(validator)); err != nil {
|
||||
return auth.Key{}, err
|
||||
}
|
||||
|
||||
jsn, err := json.Marshal(tkn.PrivateClaims())
|
||||
if err != nil {
|
||||
return auth.Key{}, err
|
||||
}
|
||||
var key auth.Key
|
||||
if err := json.Unmarshal(jsn, &key); err != nil {
|
||||
return auth.Key{}, err
|
||||
}
|
||||
|
||||
tType, ok := tkn.Get(tokenType)
|
||||
if !ok {
|
||||
return auth.Key{}, errors.Wrap(errors.ErrAuthentication, err)
|
||||
}
|
||||
ktype, err := strconv.ParseInt(fmt.Sprintf("%v", tType), 10, 64)
|
||||
if err != nil {
|
||||
return auth.Key{}, errors.Wrap(errors.ErrAuthentication, err)
|
||||
}
|
||||
|
||||
key.ID = tkn.JwtID()
|
||||
key.Type = auth.KeyType(ktype)
|
||||
key.Issuer = tkn.Issuer()
|
||||
key.Subject = tkn.Subject()
|
||||
key.IssuedAt = tkn.IssuedAt()
|
||||
key.ExpiresAt = tkn.Expiration()
|
||||
return key, nil
|
||||
}
|
||||
@@ -0,0 +1,99 @@
|
||||
// Copyright (c) Mainflux
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package auth
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"time"
|
||||
)
|
||||
|
||||
var (
|
||||
// ErrInvalidKeyIssuedAt indicates that the Key is being used before it's issued.
|
||||
ErrInvalidKeyIssuedAt = errors.New("invalid issue time")
|
||||
|
||||
// ErrKeyExpired indicates that the Key is expired.
|
||||
ErrKeyExpired = errors.New("use of expired key")
|
||||
|
||||
// ErrAPIKeyExpired indicates that the Key is expired
|
||||
// and that the key type is API key.
|
||||
ErrAPIKeyExpired = errors.New("use of expired API key")
|
||||
)
|
||||
|
||||
type Token struct {
|
||||
AccessToken string // AccessToken contains the security credentials for a login session and identifies the client.
|
||||
RefreshToken string // RefreshToken is a credential artifact that OAuth can use to get a new access token without client interaction.
|
||||
AccessType string // AccessType is the specific type of access token issued. It can be Bearer, Client or Basic.
|
||||
}
|
||||
|
||||
type KeyType uint32
|
||||
|
||||
const (
|
||||
// AccessKey is temporary User key received on successfull login.
|
||||
AccessKey KeyType = iota
|
||||
// RefreshKey is a temporary User key used to generate a new access key.
|
||||
RefreshKey
|
||||
// RecoveryKey represents a key for resseting password.
|
||||
RecoveryKey
|
||||
// APIKey enables the one to act on behalf of the user.
|
||||
APIKey
|
||||
)
|
||||
|
||||
func (kt KeyType) String() string {
|
||||
switch kt {
|
||||
case AccessKey:
|
||||
return "access"
|
||||
case RefreshKey:
|
||||
return "refresh"
|
||||
case RecoveryKey:
|
||||
return "recovery"
|
||||
case APIKey:
|
||||
return "API"
|
||||
default:
|
||||
return "unknown"
|
||||
}
|
||||
}
|
||||
|
||||
// Key represents API key.
|
||||
type Key struct {
|
||||
ID string `json:"id,omitempty"`
|
||||
Type KeyType `json:"type,omitempty"`
|
||||
Issuer string `json:"issuer,omitempty"`
|
||||
Subject string `json:"subject,omitempty"` // user ID
|
||||
IssuedAt time.Time `json:"issued_at,omitempty"`
|
||||
ExpiresAt time.Time `json:"expires_at,omitempty"`
|
||||
}
|
||||
|
||||
func (key Key) String() string {
|
||||
return fmt.Sprintf(`{
|
||||
id: %s,
|
||||
type: %s,
|
||||
issuer_id: %s,
|
||||
subject: %s,
|
||||
iat: %v,
|
||||
eat: %v
|
||||
}`, key.ID, key.Type, key.Issuer, key.Subject, key.IssuedAt, key.ExpiresAt)
|
||||
}
|
||||
|
||||
// Expired verifies if the key is expired.
|
||||
func (key Key) Expired() bool {
|
||||
if key.Type == APIKey && key.ExpiresAt.IsZero() {
|
||||
return false
|
||||
}
|
||||
return key.ExpiresAt.UTC().Before(time.Now().UTC())
|
||||
}
|
||||
|
||||
// KeyRepository specifies Key persistence API.
|
||||
type KeyRepository interface {
|
||||
// Save persists the Key. A non-nil error is returned to indicate
|
||||
// operation failure
|
||||
Save(ctx context.Context, key Key) (id string, err error)
|
||||
|
||||
// Retrieve retrieves Key by its unique identifier.
|
||||
Retrieve(ctx context.Context, issuer string, id string) (key Key, err error)
|
||||
|
||||
// Remove removes Key with provided ID.
|
||||
Remove(ctx context.Context, issuer string, id string) error
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
// Copyright (c) Mainflux
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package auth_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/mainflux/mainflux/auth"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestExpired(t *testing.T) {
|
||||
exp := time.Now().Add(5 * time.Minute)
|
||||
exp1 := time.Now()
|
||||
cases := []struct {
|
||||
desc string
|
||||
key auth.Key
|
||||
expired bool
|
||||
}{
|
||||
{
|
||||
desc: "not expired key",
|
||||
key: auth.Key{
|
||||
IssuedAt: time.Now(),
|
||||
ExpiresAt: exp,
|
||||
},
|
||||
expired: false,
|
||||
},
|
||||
{
|
||||
desc: "expired key",
|
||||
key: auth.Key{
|
||||
IssuedAt: time.Now().UTC().Add(2 * time.Minute),
|
||||
ExpiresAt: exp1,
|
||||
},
|
||||
expired: true,
|
||||
},
|
||||
{
|
||||
desc: "user key with no expiration date",
|
||||
key: auth.Key{
|
||||
IssuedAt: time.Now(),
|
||||
},
|
||||
expired: true,
|
||||
},
|
||||
{
|
||||
desc: "API key with no expiration date",
|
||||
key: auth.Key{
|
||||
IssuedAt: time.Now(),
|
||||
Type: auth.APIKey,
|
||||
},
|
||||
expired: false,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range cases {
|
||||
res := tc.key.Expired()
|
||||
assert.Equal(t, tc.expired, res, fmt.Sprintf("%s: expected %t got %t\n", tc.desc, tc.expired, res))
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,152 @@
|
||||
package mocks
|
||||
|
||||
import (
|
||||
context "context"
|
||||
|
||||
auth "github.com/mainflux/mainflux/auth"
|
||||
"github.com/stretchr/testify/mock"
|
||||
)
|
||||
|
||||
var _ auth.Authz = (*Authz)(nil)
|
||||
var _ auth.PolicyAgent = (*PolicyAgent)(nil)
|
||||
|
||||
type Authz struct {
|
||||
mock.Mock
|
||||
}
|
||||
|
||||
func (m *Authz) AddPolicies(ctx context.Context, token, object string, subjectIDs, relations []string) error {
|
||||
ret := m.Called(ctx, token, object, subjectIDs, relations)
|
||||
|
||||
return ret.Error(0)
|
||||
}
|
||||
|
||||
func (m *Authz) AddPolicy(ctx context.Context, pr auth.PolicyReq) error {
|
||||
ret := m.Called(ctx, pr)
|
||||
|
||||
return ret.Error(0)
|
||||
}
|
||||
|
||||
func (m *Authz) Authorize(ctx context.Context, pr auth.PolicyReq) error {
|
||||
ret := m.Called(ctx, pr)
|
||||
|
||||
return ret.Error(0)
|
||||
}
|
||||
|
||||
func (m *Authz) CountObjects(ctx context.Context, pr auth.PolicyReq) (int, error) {
|
||||
ret := m.Called(ctx, pr)
|
||||
|
||||
return ret.Get(0).(int), ret.Error(1)
|
||||
}
|
||||
|
||||
func (m *Authz) CountSubjects(ctx context.Context, pr auth.PolicyReq) (int, error) {
|
||||
ret := m.Called(ctx, pr)
|
||||
|
||||
return ret.Get(0).(int), ret.Error(1)
|
||||
}
|
||||
|
||||
func (m *Authz) DeletePolicies(ctx context.Context, token, object string, subjectIDs, relations []string) error {
|
||||
ret := m.Called(ctx, token, object, subjectIDs, relations)
|
||||
|
||||
return ret.Error(0)
|
||||
}
|
||||
|
||||
func (m *Authz) DeletePolicy(ctx context.Context, pr auth.PolicyReq) error {
|
||||
ret := m.Called(ctx, pr)
|
||||
|
||||
return ret.Error(0)
|
||||
}
|
||||
|
||||
func (m *Authz) ListAllObjects(ctx context.Context, pr auth.PolicyReq) (auth.PolicyPage, error) {
|
||||
ret := m.Called(ctx, pr)
|
||||
|
||||
return ret.Get(0).(auth.PolicyPage), ret.Error(1)
|
||||
}
|
||||
|
||||
func (m *Authz) ListAllSubjects(ctx context.Context, pr auth.PolicyReq) (auth.PolicyPage, error) {
|
||||
ret := m.Called(ctx, pr)
|
||||
|
||||
return ret.Get(0).(auth.PolicyPage), ret.Error(1)
|
||||
}
|
||||
|
||||
func (m *Authz) ListObjects(ctx context.Context, pr auth.PolicyReq, nextPageToken string, limit int32) (auth.PolicyPage, error) {
|
||||
ret := m.Called(ctx, pr, nextPageToken, limit)
|
||||
|
||||
return ret.Get(0).(auth.PolicyPage), ret.Error(1)
|
||||
}
|
||||
|
||||
func (m *Authz) ListSubjects(ctx context.Context, pr auth.PolicyReq, nextPageToken string, limit int32) (auth.PolicyPage, error) {
|
||||
ret := m.Called(ctx, pr, nextPageToken, limit)
|
||||
|
||||
return ret.Get(0).(auth.PolicyPage), ret.Error(1)
|
||||
|
||||
}
|
||||
|
||||
type PolicyAgent struct {
|
||||
mock.Mock
|
||||
}
|
||||
|
||||
func (m *PolicyAgent) AddPolicies(ctx context.Context, prs []auth.PolicyReq) error {
|
||||
ret := m.Called(ctx, prs)
|
||||
|
||||
return ret.Error(0)
|
||||
}
|
||||
|
||||
func (m *PolicyAgent) AddPolicy(ctx context.Context, pr auth.PolicyReq) error {
|
||||
ret := m.Called(ctx, pr)
|
||||
|
||||
return ret.Error(0)
|
||||
}
|
||||
|
||||
func (m *PolicyAgent) CheckPolicy(ctx context.Context, pr auth.PolicyReq) error {
|
||||
ret := m.Called(ctx, pr)
|
||||
|
||||
return ret.Error(0)
|
||||
}
|
||||
|
||||
func (m *PolicyAgent) DeletePolicies(ctx context.Context, pr []auth.PolicyReq) error {
|
||||
ret := m.Called(ctx, pr)
|
||||
|
||||
return ret.Error(0)
|
||||
}
|
||||
|
||||
func (m *PolicyAgent) DeletePolicy(ctx context.Context, pr auth.PolicyReq) error {
|
||||
ret := m.Called(ctx, pr)
|
||||
|
||||
return ret.Error(0)
|
||||
}
|
||||
|
||||
func (m *PolicyAgent) RetrieveAllObjects(ctx context.Context, pr auth.PolicyReq) ([]auth.PolicyRes, error) {
|
||||
ret := m.Called(ctx, pr)
|
||||
|
||||
return ret.Get(0).([]auth.PolicyRes), ret.Error(1)
|
||||
}
|
||||
|
||||
func (m *PolicyAgent) RetrieveAllObjectsCount(ctx context.Context, pr auth.PolicyReq) (int, error) {
|
||||
ret := m.Called(ctx, pr)
|
||||
|
||||
return ret.Get(0).(int), ret.Error(1)
|
||||
}
|
||||
|
||||
func (m *PolicyAgent) RetrieveAllSubjects(ctx context.Context, pr auth.PolicyReq) ([]auth.PolicyRes, error) {
|
||||
ret := m.Called(ctx, pr)
|
||||
|
||||
return ret.Get(0).([]auth.PolicyRes), ret.Error(1)
|
||||
}
|
||||
|
||||
func (m *PolicyAgent) RetrieveAllSubjectsCount(ctx context.Context, pr auth.PolicyReq) (int, error) {
|
||||
ret := m.Called(ctx, pr)
|
||||
|
||||
return ret.Get(0).(int), ret.Error(1)
|
||||
}
|
||||
|
||||
func (m *PolicyAgent) RetrieveObjects(ctx context.Context, pr auth.PolicyReq, nextPageToken string, limit int32) ([]auth.PolicyRes, string, error) {
|
||||
ret := m.Called(ctx, pr, nextPageToken, limit)
|
||||
|
||||
return ret.Get(0).([]auth.PolicyRes), ret.String(1), ret.Error(2)
|
||||
}
|
||||
|
||||
func (m *PolicyAgent) RetrieveSubjects(ctx context.Context, pr auth.PolicyReq, nextPageToken string, limit int32) ([]auth.PolicyRes, string, error) {
|
||||
ret := m.Called(ctx, pr, nextPageToken, limit)
|
||||
|
||||
return ret.Get(0).([]auth.PolicyRes), ret.String(1), ret.Error(2)
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
package mocks
|
||||
|
||||
import (
|
||||
context "context"
|
||||
|
||||
auth "github.com/mainflux/mainflux/auth"
|
||||
"github.com/stretchr/testify/mock"
|
||||
)
|
||||
|
||||
var _ auth.KeyRepository = (*Keys)(nil)
|
||||
|
||||
type Keys struct {
|
||||
mock.Mock
|
||||
}
|
||||
|
||||
func (m *Keys) Save(ctx context.Context, key auth.Key) (string, error) {
|
||||
ret := m.Called(ctx, key)
|
||||
|
||||
return ret.String(0), ret.Error(1)
|
||||
}
|
||||
|
||||
func (m *Keys) Retrieve(ctx context.Context, issuer, id string) (auth.Key, error) {
|
||||
ret := m.Called(ctx, issuer, id)
|
||||
|
||||
return ret.Get(0).(auth.Key), ret.Error(1)
|
||||
}
|
||||
|
||||
func (m *Keys) Remove(ctx context.Context, issuer, id string) error {
|
||||
ret := m.Called(ctx, issuer, id)
|
||||
|
||||
return ret.Error(0)
|
||||
}
|
||||
@@ -0,0 +1,93 @@
|
||||
package mocks
|
||||
|
||||
import (
|
||||
context "context"
|
||||
|
||||
"github.com/mainflux/mainflux"
|
||||
"github.com/stretchr/testify/mock"
|
||||
"google.golang.org/grpc"
|
||||
)
|
||||
|
||||
var _ mainflux.AuthServiceClient = (*Service)(nil)
|
||||
|
||||
type Service struct {
|
||||
mock.Mock
|
||||
}
|
||||
|
||||
func (m *Service) Issue(ctx context.Context, in *mainflux.IssueReq, opts ...grpc.CallOption) (*mainflux.Token, error) {
|
||||
ret := m.Called(ctx, in)
|
||||
|
||||
return ret.Get(0).(*mainflux.Token), ret.Error(1)
|
||||
}
|
||||
|
||||
func (m *Service) Login(ctx context.Context, in *mainflux.LoginReq, opts ...grpc.CallOption) (*mainflux.Token, error) {
|
||||
ret := m.Called(ctx, in)
|
||||
|
||||
return ret.Get(0).(*mainflux.Token), ret.Error(1)
|
||||
}
|
||||
|
||||
func (m *Service) Refresh(ctx context.Context, in *mainflux.RefreshReq, opts ...grpc.CallOption) (*mainflux.Token, error) {
|
||||
ret := m.Called(ctx, in)
|
||||
|
||||
return ret.Get(0).(*mainflux.Token), ret.Error(1)
|
||||
}
|
||||
|
||||
func (m *Service) Identify(ctx context.Context, in *mainflux.IdentityReq, opts ...grpc.CallOption) (*mainflux.IdentityRes, error) {
|
||||
ret := m.Called(ctx, in)
|
||||
|
||||
return ret.Get(0).(*mainflux.IdentityRes), ret.Error(1)
|
||||
}
|
||||
|
||||
func (m *Service) Authorize(ctx context.Context, in *mainflux.AuthorizeReq, opts ...grpc.CallOption) (*mainflux.AuthorizeRes, error) {
|
||||
ret := m.Called(ctx, in)
|
||||
|
||||
return ret.Get(0).(*mainflux.AuthorizeRes), ret.Error(1)
|
||||
}
|
||||
|
||||
func (m *Service) AddPolicy(ctx context.Context, in *mainflux.AddPolicyReq, opts ...grpc.CallOption) (*mainflux.AddPolicyRes, error) {
|
||||
ret := m.Called(ctx, in)
|
||||
|
||||
return ret.Get(0).(*mainflux.AddPolicyRes), ret.Error(1)
|
||||
}
|
||||
|
||||
func (m *Service) DeletePolicy(ctx context.Context, in *mainflux.DeletePolicyReq, opts ...grpc.CallOption) (*mainflux.DeletePolicyRes, error) {
|
||||
ret := m.Called(ctx, in)
|
||||
|
||||
return ret.Get(0).(*mainflux.DeletePolicyRes), ret.Error(1)
|
||||
}
|
||||
|
||||
func (m *Service) ListObjects(ctx context.Context, in *mainflux.ListObjectsReq, opts ...grpc.CallOption) (*mainflux.ListObjectsRes, error) {
|
||||
ret := m.Called(ctx, in)
|
||||
|
||||
return ret.Get(0).(*mainflux.ListObjectsRes), ret.Error(1)
|
||||
}
|
||||
|
||||
func (m *Service) ListAllObjects(ctx context.Context, in *mainflux.ListObjectsReq, opts ...grpc.CallOption) (*mainflux.ListObjectsRes, error) {
|
||||
ret := m.Called(ctx, in)
|
||||
|
||||
return ret.Get(0).(*mainflux.ListObjectsRes), ret.Error(1)
|
||||
}
|
||||
|
||||
func (m *Service) CountObjects(ctx context.Context, in *mainflux.CountObjectsReq, opts ...grpc.CallOption) (*mainflux.CountObjectsRes, error) {
|
||||
ret := m.Called(ctx, in)
|
||||
|
||||
return ret.Get(0).(*mainflux.CountObjectsRes), ret.Error(1)
|
||||
}
|
||||
|
||||
func (m *Service) ListSubjects(ctx context.Context, in *mainflux.ListSubjectsReq, opts ...grpc.CallOption) (*mainflux.ListSubjectsRes, error) {
|
||||
ret := m.Called(ctx, in)
|
||||
|
||||
return ret.Get(0).(*mainflux.ListSubjectsRes), ret.Error(1)
|
||||
}
|
||||
|
||||
func (m *Service) ListAllSubjects(ctx context.Context, in *mainflux.ListSubjectsReq, opts ...grpc.CallOption) (*mainflux.ListSubjectsRes, error) {
|
||||
ret := m.Called(ctx, in)
|
||||
|
||||
return ret.Get(0).(*mainflux.ListSubjectsRes), ret.Error(1)
|
||||
}
|
||||
|
||||
func (m *Service) CountSubjects(ctx context.Context, in *mainflux.CountSubjectsReq, opts ...grpc.CallOption) (*mainflux.CountSubjectsRes, error) {
|
||||
ret := m.Called(ctx, in)
|
||||
|
||||
return ret.Get(0).(*mainflux.CountSubjectsRes), ret.Error(1)
|
||||
}
|
||||
@@ -0,0 +1,131 @@
|
||||
// Copyright (c) Mainflux
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package auth
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
)
|
||||
|
||||
// PolicyReq represents an argument struct for making a policy related
|
||||
// function calls.
|
||||
type PolicyReq struct {
|
||||
Namespace string `json:",omitempty"`
|
||||
Subject string `json:"subject"`
|
||||
SubjectType string `json:"subject_type"`
|
||||
SubjectKind string `json:"subject_kind"`
|
||||
SubjectRelation string `json:",omitempty"`
|
||||
Object string `json:"object"`
|
||||
ObjectType string `json:"object_type"`
|
||||
Relation string `json:"relation"`
|
||||
Permission string `json:",omitempty"`
|
||||
}
|
||||
|
||||
func (pr PolicyReq) String() string {
|
||||
data, _ := json.Marshal(pr)
|
||||
return string(data)
|
||||
}
|
||||
|
||||
type PolicyRes struct {
|
||||
Namespace string
|
||||
Subject string
|
||||
SubjectType string
|
||||
SubjectRelation string
|
||||
Object string
|
||||
ObjectType string
|
||||
Relation string
|
||||
Permission string
|
||||
}
|
||||
|
||||
type PolicyPage struct {
|
||||
Policies []string
|
||||
NextPageToken string
|
||||
}
|
||||
|
||||
// Authz represents a authorization service. It exposes
|
||||
// functionalities through `auth` to perform authorization.
|
||||
type Authz interface {
|
||||
// Authorize checks authorization of the given `subject`. Basically,
|
||||
// Authorize verifies that Is `subject` allowed to `relation` on
|
||||
// `object`. Authorize returns a non-nil error if the subject has
|
||||
// no relation on the object (which simply means the operation is
|
||||
// denied).
|
||||
Authorize(ctx context.Context, pr PolicyReq) error
|
||||
|
||||
// AddPolicy creates a policy for the given subject, so that, after
|
||||
// AddPolicy, `subject` has a `relation` on `object`. Returns a non-nil
|
||||
// error in case of failures.
|
||||
AddPolicy(ctx context.Context, pr PolicyReq) error
|
||||
|
||||
// AddPolicies adds new policies for given subjects. This method is
|
||||
// only allowed to use as an admin.
|
||||
AddPolicies(ctx context.Context, token, object string, subjectIDs, relations []string) error
|
||||
|
||||
// DeletePolicy removes a policy.
|
||||
DeletePolicy(ctx context.Context, pr PolicyReq) error
|
||||
|
||||
// DeletePolicies deletes policies for given subjects. This method is
|
||||
// only allowed to use as an admin.
|
||||
DeletePolicies(ctx context.Context, token, object string, subjectIDs, relations []string) error
|
||||
|
||||
// ListObjects lists policies based on the given PolicyReq structure.
|
||||
ListObjects(ctx context.Context, pr PolicyReq, nextPageToken string, limit int32) (PolicyPage, error)
|
||||
|
||||
// ListAllObjects lists all policies based on the given PolicyReq structure.
|
||||
ListAllObjects(ctx context.Context, pr PolicyReq) (PolicyPage, error)
|
||||
|
||||
// CountPolicies count policies based on the given PolicyReq structure.
|
||||
CountObjects(ctx context.Context, pr PolicyReq) (int, error)
|
||||
|
||||
// ListSubjects lists subjects based on the given PolicyReq structure.
|
||||
ListSubjects(ctx context.Context, pr PolicyReq, nextPageToken string, limit int32) (PolicyPage, error)
|
||||
|
||||
// ListAllSubjects lists all subjects based on the given PolicyReq structure.
|
||||
ListAllSubjects(ctx context.Context, pr PolicyReq) (PolicyPage, error)
|
||||
|
||||
// CountSubjects count policies based on the given PolicyReq structure.
|
||||
CountSubjects(ctx context.Context, pr PolicyReq) (int, error)
|
||||
}
|
||||
|
||||
// PolicyAgent facilitates the communication to authorization
|
||||
// services and implements Authz functionalities for certain
|
||||
// authorization services (e.g. ORY Keto).
|
||||
type PolicyAgent interface {
|
||||
// CheckPolicy checks if the subject has a relation on the object.
|
||||
// It returns a non-nil error if the subject has no relation on
|
||||
// the object (which simply means the operation is denied).
|
||||
CheckPolicy(ctx context.Context, pr PolicyReq) error
|
||||
|
||||
// AddPolicy creates a policy for the given subject, so that, after
|
||||
// AddPolicy, `subject` has a `relation` on `object`. Returns a non-nil
|
||||
// error in case of failures.
|
||||
AddPolicy(ctx context.Context, pr PolicyReq) error
|
||||
|
||||
// AddPolicies creates a Bulk Policies for the given request
|
||||
AddPolicies(ctx context.Context, prs []PolicyReq) error
|
||||
|
||||
// DeletePolicy removes a policy.
|
||||
DeletePolicy(ctx context.Context, pr PolicyReq) error
|
||||
|
||||
// DeletePolicy removes a policy.
|
||||
DeletePolicies(ctx context.Context, pr []PolicyReq) error
|
||||
|
||||
// RetrieveObjects
|
||||
RetrieveObjects(ctx context.Context, pr PolicyReq, nextPageToken string, limit int32) ([]PolicyRes, string, error)
|
||||
|
||||
// RetrieveAllObjects
|
||||
RetrieveAllObjects(ctx context.Context, pr PolicyReq) ([]PolicyRes, error)
|
||||
|
||||
// RetrieveAllObjectsCount
|
||||
RetrieveAllObjectsCount(ctx context.Context, pr PolicyReq) (int, error)
|
||||
|
||||
// RetrieveSubjects
|
||||
RetrieveSubjects(ctx context.Context, pr PolicyReq, nextPageToken string, limit int32) ([]PolicyRes, string, error)
|
||||
|
||||
// RetrieveAllSubjects
|
||||
RetrieveAllSubjects(ctx context.Context, pr PolicyReq) ([]PolicyRes, error)
|
||||
|
||||
// RetrieveAllSubjectsCount
|
||||
RetrieveAllSubjectsCount(ctx context.Context, pr PolicyReq) (int, error)
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
// Copyright (c) Mainflux
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Package postgres contains Key repository implementations using
|
||||
// PostgreSQL as the underlying database.
|
||||
package postgres
|
||||
@@ -0,0 +1,34 @@
|
||||
// Copyright (c) Mainflux
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package postgres
|
||||
|
||||
import (
|
||||
_ "github.com/jackc/pgx/v5/stdlib" // required for SQL access
|
||||
migrate "github.com/rubenv/sql-migrate"
|
||||
)
|
||||
|
||||
// Migration of Auth service.
|
||||
func Migration() *migrate.MemoryMigrationSource {
|
||||
return &migrate.MemoryMigrationSource{
|
||||
Migrations: []*migrate.Migration{
|
||||
{
|
||||
Id: "auth_1",
|
||||
Up: []string{
|
||||
`CREATE TABLE IF NOT EXISTS keys (
|
||||
id VARCHAR(254) NOT NULL,
|
||||
type SMALLINT,
|
||||
subject VARCHAR(254) NOT NULL,
|
||||
issuer_id VARCHAR(254) NOT NULL,
|
||||
issued_at TIMESTAMP NOT NULL,
|
||||
expires_at TIMESTAMP,
|
||||
PRIMARY KEY (id, issuer_id)
|
||||
)`,
|
||||
},
|
||||
Down: []string{
|
||||
`DROP TABLE IF EXISTS keys`,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,107 @@
|
||||
package postgres
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"time"
|
||||
|
||||
"github.com/mainflux/mainflux/auth"
|
||||
"github.com/mainflux/mainflux/internal/postgres"
|
||||
"github.com/mainflux/mainflux/pkg/errors"
|
||||
)
|
||||
|
||||
var (
|
||||
errSave = errors.New("failed to save key in database")
|
||||
errRetrieve = errors.New("failed to retrieve key from database")
|
||||
errDelete = errors.New("failed to delete key from database")
|
||||
)
|
||||
var _ auth.KeyRepository = (*repo)(nil)
|
||||
|
||||
type repo struct {
|
||||
db postgres.Database
|
||||
}
|
||||
|
||||
// New instantiates a PostgreSQL implementation of key repository.
|
||||
func New(db postgres.Database) auth.KeyRepository {
|
||||
return &repo{
|
||||
db: db,
|
||||
}
|
||||
}
|
||||
|
||||
func (kr *repo) Save(ctx context.Context, key auth.Key) (string, error) {
|
||||
q := `INSERT INTO keys (id, type, issuer_id, subject, issued_at, expires_at)
|
||||
VALUES (:id, :type, :issuer_id, :subject, :issued_at, :expires_at)`
|
||||
|
||||
dbKey := toDBKey(key)
|
||||
if _, err := kr.db.NamedExecContext(ctx, q, dbKey); err != nil {
|
||||
return "", postgres.HandleError(err, errSave)
|
||||
}
|
||||
|
||||
return dbKey.ID, nil
|
||||
}
|
||||
|
||||
func (kr *repo) Retrieve(ctx context.Context, issuerID, id string) (auth.Key, error) {
|
||||
q := `SELECT id, type, issuer_id, subject, issued_at, expires_at FROM keys WHERE issuer_id = $1 AND id = $2`
|
||||
key := dbKey{}
|
||||
if err := kr.db.QueryRowxContext(ctx, q, issuerID, id).StructScan(&key); err != nil {
|
||||
if err == sql.ErrNoRows {
|
||||
return auth.Key{}, errors.ErrNotFound
|
||||
}
|
||||
|
||||
return auth.Key{}, postgres.HandleError(err, errRetrieve)
|
||||
}
|
||||
|
||||
return toKey(key), nil
|
||||
}
|
||||
|
||||
func (kr *repo) Remove(ctx context.Context, issuerID, id string) error {
|
||||
q := `DELETE FROM keys WHERE issuer_id = :issuer_id AND id = :id`
|
||||
key := dbKey{
|
||||
ID: id,
|
||||
Issuer: issuerID,
|
||||
}
|
||||
if _, err := kr.db.NamedExecContext(ctx, q, key); err != nil {
|
||||
return errors.Wrap(errDelete, err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
type dbKey struct {
|
||||
ID string `db:"id"`
|
||||
Type uint32 `db:"type"`
|
||||
Issuer string `db:"issuer_id"`
|
||||
Subject string `db:"subject"`
|
||||
IssuedAt time.Time `db:"issued_at"`
|
||||
ExpiresAt sql.NullTime `db:"expires_at,omitempty"`
|
||||
}
|
||||
|
||||
func toDBKey(key auth.Key) dbKey {
|
||||
ret := dbKey{
|
||||
ID: key.ID,
|
||||
Type: uint32(key.Type),
|
||||
Issuer: key.Issuer,
|
||||
Subject: key.Subject,
|
||||
IssuedAt: key.IssuedAt,
|
||||
}
|
||||
if !key.ExpiresAt.IsZero() {
|
||||
ret.ExpiresAt = sql.NullTime{Time: key.ExpiresAt, Valid: true}
|
||||
}
|
||||
|
||||
return ret
|
||||
}
|
||||
|
||||
func toKey(key dbKey) auth.Key {
|
||||
ret := auth.Key{
|
||||
ID: key.ID,
|
||||
Type: auth.KeyType(key.Type),
|
||||
Issuer: key.Issuer,
|
||||
Subject: key.Subject,
|
||||
IssuedAt: key.IssuedAt,
|
||||
}
|
||||
if key.ExpiresAt.Valid {
|
||||
ret.ExpiresAt = key.ExpiresAt.Time
|
||||
}
|
||||
|
||||
return ret
|
||||
}
|
||||
@@ -0,0 +1,270 @@
|
||||
// Copyright (c) Mainflux
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package postgres_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/mainflux/mainflux/auth"
|
||||
"github.com/mainflux/mainflux/auth/postgres"
|
||||
"github.com/mainflux/mainflux/pkg/errors"
|
||||
"github.com/mainflux/mainflux/pkg/uuid"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
var (
|
||||
expTime = time.Now().Add(5 * time.Minute)
|
||||
idProvider = uuid.New()
|
||||
invalidID = strings.Repeat("a", 255)
|
||||
)
|
||||
|
||||
func generateID(t *testing.T) string {
|
||||
id, err := idProvider.ID()
|
||||
require.Nil(t, err, fmt.Sprintf("got unexpected error: %s", err))
|
||||
return id
|
||||
}
|
||||
|
||||
func TestKeySave(t *testing.T) {
|
||||
repo := postgres.New(database)
|
||||
|
||||
keyID := generateID(t)
|
||||
issuer := generateID(t)
|
||||
|
||||
cases := []struct {
|
||||
desc string
|
||||
key auth.Key
|
||||
err error
|
||||
}{
|
||||
{
|
||||
desc: "save a new key",
|
||||
key: auth.Key{
|
||||
ID: keyID,
|
||||
Type: auth.APIKey,
|
||||
Issuer: issuer,
|
||||
Subject: generateID(t),
|
||||
IssuedAt: time.Now(),
|
||||
ExpiresAt: expTime,
|
||||
},
|
||||
err: nil,
|
||||
},
|
||||
{
|
||||
desc: "save with duplicate id",
|
||||
key: auth.Key{
|
||||
ID: keyID,
|
||||
Type: auth.APIKey,
|
||||
Issuer: issuer,
|
||||
Subject: generateID(t),
|
||||
IssuedAt: time.Now(),
|
||||
ExpiresAt: expTime,
|
||||
},
|
||||
err: errors.ErrConflict,
|
||||
},
|
||||
{
|
||||
desc: "save with empty id",
|
||||
key: auth.Key{
|
||||
Type: auth.APIKey,
|
||||
Issuer: issuer,
|
||||
Subject: generateID(t),
|
||||
IssuedAt: time.Now(),
|
||||
ExpiresAt: expTime,
|
||||
},
|
||||
err: errors.ErrConflict,
|
||||
},
|
||||
{
|
||||
desc: "save with empty subject",
|
||||
key: auth.Key{
|
||||
ID: generateID(t),
|
||||
Type: auth.APIKey,
|
||||
Issuer: issuer,
|
||||
IssuedAt: time.Now(),
|
||||
ExpiresAt: expTime,
|
||||
},
|
||||
err: nil,
|
||||
},
|
||||
{
|
||||
desc: "save with empty issuer",
|
||||
key: auth.Key{
|
||||
ID: generateID(t),
|
||||
Type: auth.APIKey,
|
||||
Issuer: "",
|
||||
Subject: generateID(t),
|
||||
IssuedAt: time.Now(),
|
||||
ExpiresAt: expTime,
|
||||
},
|
||||
err: nil,
|
||||
},
|
||||
{
|
||||
desc: "save with empty issued at",
|
||||
key: auth.Key{
|
||||
ID: generateID(t),
|
||||
Type: auth.APIKey,
|
||||
Issuer: issuer,
|
||||
Subject: generateID(t),
|
||||
IssuedAt: time.Time{},
|
||||
ExpiresAt: expTime,
|
||||
},
|
||||
err: nil,
|
||||
},
|
||||
{
|
||||
desc: "save with invalid id",
|
||||
key: auth.Key{
|
||||
ID: invalidID,
|
||||
Type: auth.APIKey,
|
||||
Issuer: issuer,
|
||||
Subject: generateID(t),
|
||||
IssuedAt: time.Now(),
|
||||
ExpiresAt: expTime,
|
||||
},
|
||||
err: errors.ErrMalformedEntity,
|
||||
},
|
||||
{
|
||||
desc: "save with invalid subject",
|
||||
key: auth.Key{
|
||||
ID: generateID(t),
|
||||
Type: auth.APIKey,
|
||||
Issuer: issuer,
|
||||
Subject: invalidID,
|
||||
IssuedAt: time.Now(),
|
||||
ExpiresAt: expTime,
|
||||
},
|
||||
err: errors.ErrMalformedEntity,
|
||||
},
|
||||
{
|
||||
desc: "save with invalid issuer",
|
||||
key: auth.Key{
|
||||
ID: generateID(t),
|
||||
Type: auth.APIKey,
|
||||
Issuer: invalidID,
|
||||
Subject: generateID(t),
|
||||
IssuedAt: time.Now(),
|
||||
ExpiresAt: expTime,
|
||||
},
|
||||
err: errors.ErrMalformedEntity,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range cases {
|
||||
_, err := repo.Save(context.Background(), tc.key)
|
||||
assert.True(t, errors.Contains(err, tc.err), fmt.Sprintf("%s: expected %s got %s\n", tc.desc, tc.err, err))
|
||||
}
|
||||
}
|
||||
|
||||
func TestKeyRetrieve(t *testing.T) {
|
||||
repo := postgres.New(database)
|
||||
|
||||
key := auth.Key{
|
||||
ID: generateID(t),
|
||||
Subject: generateID(t),
|
||||
IssuedAt: time.Now(),
|
||||
Issuer: generateID(t),
|
||||
ExpiresAt: expTime,
|
||||
}
|
||||
_, err := repo.Save(context.Background(), key)
|
||||
assert.Nil(t, err, fmt.Sprintf("Storing Key expected to succeed: %s", err))
|
||||
|
||||
cases := []struct {
|
||||
desc string
|
||||
id string
|
||||
issuer string
|
||||
err error
|
||||
}{
|
||||
{
|
||||
desc: "retrieve an existing key",
|
||||
id: key.ID,
|
||||
issuer: key.Issuer,
|
||||
err: nil,
|
||||
},
|
||||
{
|
||||
desc: "retrieve key with empty issuer id",
|
||||
id: key.ID,
|
||||
issuer: "",
|
||||
err: errors.ErrNotFound,
|
||||
},
|
||||
{
|
||||
desc: "retrieve non-existent key",
|
||||
id: "",
|
||||
issuer: key.Issuer,
|
||||
err: errors.ErrNotFound,
|
||||
},
|
||||
{
|
||||
desc: "retrieve non-existent key with empty issuer id",
|
||||
id: "",
|
||||
issuer: "",
|
||||
err: errors.ErrNotFound,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range cases {
|
||||
_, err := repo.Retrieve(context.Background(), tc.issuer, tc.id)
|
||||
assert.True(t, errors.Contains(err, tc.err), fmt.Sprintf("%s: expected %s got %s\n", tc.desc, tc.err, err))
|
||||
}
|
||||
}
|
||||
|
||||
func TestKeyRemove(t *testing.T) {
|
||||
repo := postgres.New(database)
|
||||
|
||||
key := auth.Key{
|
||||
ID: generateID(t),
|
||||
Subject: generateID(t),
|
||||
IssuedAt: time.Now(),
|
||||
Issuer: generateID(t),
|
||||
ExpiresAt: expTime,
|
||||
}
|
||||
_, err := repo.Save(context.Background(), key)
|
||||
assert.Nil(t, err, fmt.Sprintf("Storing Key expected to succeed: %s", err))
|
||||
|
||||
cases := []struct {
|
||||
desc string
|
||||
id string
|
||||
issuer string
|
||||
err error
|
||||
}{
|
||||
{
|
||||
desc: "remove an existing key",
|
||||
id: key.ID,
|
||||
issuer: key.Issuer,
|
||||
err: nil,
|
||||
},
|
||||
{
|
||||
desc: "remove key that has already been removed",
|
||||
id: key.ID,
|
||||
issuer: key.Issuer,
|
||||
err: nil,
|
||||
},
|
||||
{
|
||||
desc: "remove key that does not exist",
|
||||
id: generateID(t),
|
||||
issuer: generateID(t),
|
||||
err: nil,
|
||||
},
|
||||
{
|
||||
desc: "remove key with empty issuer id",
|
||||
id: key.ID,
|
||||
issuer: "",
|
||||
err: nil,
|
||||
},
|
||||
{
|
||||
desc: "remove key with empty id",
|
||||
id: "",
|
||||
issuer: key.Issuer,
|
||||
err: nil,
|
||||
},
|
||||
{
|
||||
desc: "remove key with empty id and issuer id",
|
||||
id: "",
|
||||
issuer: "",
|
||||
err: nil,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range cases {
|
||||
err := repo.Remove(context.Background(), tc.issuer, tc.id)
|
||||
assert.True(t, errors.Contains(err, tc.err), fmt.Sprintf("%s: expected %s got %s\n", tc.desc, tc.err, err))
|
||||
}
|
||||
}
|
||||
@@ -14,11 +14,10 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/jmoiron/sqlx"
|
||||
apostgres "github.com/mainflux/mainflux/auth/postgres"
|
||||
pgclient "github.com/mainflux/mainflux/internal/clients/postgres"
|
||||
"github.com/mainflux/mainflux/internal/postgres"
|
||||
upostgres "github.com/mainflux/mainflux/users/postgres"
|
||||
"github.com/ory/dockertest/v3"
|
||||
"github.com/ory/dockertest/v3/docker"
|
||||
dockertest "github.com/ory/dockertest/v3"
|
||||
"go.opentelemetry.io/otel"
|
||||
)
|
||||
|
||||
@@ -34,26 +33,18 @@ func TestMain(m *testing.M) {
|
||||
log.Fatalf("Could not connect to docker: %s", err)
|
||||
}
|
||||
|
||||
container, err := pool.RunWithOptions(&dockertest.RunOptions{
|
||||
Repository: "postgres",
|
||||
Tag: "15.1-alpine",
|
||||
Env: []string{
|
||||
"POSTGRES_USER=test",
|
||||
"POSTGRES_PASSWORD=test",
|
||||
"POSTGRES_DB=test",
|
||||
"listen_addresses = '*'",
|
||||
},
|
||||
}, func(config *docker.HostConfig) {
|
||||
config.AutoRemove = true
|
||||
config.RestartPolicy = docker.RestartPolicy{Name: "no"}
|
||||
})
|
||||
cfg := []string{
|
||||
"POSTGRES_USER=test",
|
||||
"POSTGRES_PASSWORD=test",
|
||||
"POSTGRES_DB=test",
|
||||
}
|
||||
container, err := pool.Run("postgres", "13.3-alpine", cfg)
|
||||
if err != nil {
|
||||
log.Fatalf("Could not start container: %s", err)
|
||||
}
|
||||
|
||||
port := container.GetPort("5432/tcp")
|
||||
|
||||
// exponential backoff-retry, because the application in the container might not be ready to accept connections yet
|
||||
pool.MaxWait = 120 * time.Second
|
||||
if err := pool.Retry(func() error {
|
||||
url := fmt.Sprintf("host=localhost port=%s user=test dbname=test password=test sslmode=disable", port)
|
||||
@@ -78,7 +69,7 @@ func TestMain(m *testing.M) {
|
||||
SSLRootCert: "",
|
||||
}
|
||||
|
||||
if db, err = pgclient.SetupDB(dbConfig, *upostgres.Migration()); err != nil {
|
||||
if db, err = pgclient.SetupDB(dbConfig, *apostgres.Migration()); err != nil {
|
||||
log.Fatalf("Could not setup test DB connection: %s", err)
|
||||
}
|
||||
|
||||
+406
@@ -0,0 +1,406 @@
|
||||
// Copyright (c) Mainflux
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package auth
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/mainflux/mainflux"
|
||||
"github.com/mainflux/mainflux/pkg/errors"
|
||||
)
|
||||
|
||||
const (
|
||||
recoveryDuration = 5 * time.Minute
|
||||
thingsKind = "things"
|
||||
channelsKind = "channels"
|
||||
usersKind = "users"
|
||||
|
||||
thingType = "thing"
|
||||
channelType = "channel"
|
||||
userType = "user"
|
||||
groupType = "group"
|
||||
|
||||
memberRelation = "member"
|
||||
groupRelation = "group"
|
||||
administratorRelation = "administrator"
|
||||
parentGroupRelation = "parent_group"
|
||||
viewerRelation = "viewer"
|
||||
|
||||
mainfluxObject = "mainflux"
|
||||
refreshToken = "refresh_token"
|
||||
)
|
||||
|
||||
const (
|
||||
tokenKind = "token"
|
||||
idKind = "id"
|
||||
)
|
||||
|
||||
var (
|
||||
// ErrFailedToRetrieveMembers failed to retrieve group members.
|
||||
ErrFailedToRetrieveMembers = errors.New("failed to retrieve group members")
|
||||
|
||||
// ErrFailedToRetrieveMembership failed to retrieve memberships.
|
||||
ErrFailedToRetrieveMembership = errors.New("failed to retrieve memberships")
|
||||
|
||||
// ErrFailedToRetrieveAll failed to retrieve groups.
|
||||
ErrFailedToRetrieveAll = errors.New("failed to retrieve all groups")
|
||||
|
||||
// ErrFailedToRetrieveParents failed to retrieve groups.
|
||||
ErrFailedToRetrieveParents = errors.New("failed to retrieve all groups")
|
||||
|
||||
// ErrFailedToRetrieveChildren failed to retrieve groups.
|
||||
ErrFailedToRetrieveChildren = errors.New("failed to retrieve all groups")
|
||||
|
||||
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")
|
||||
)
|
||||
|
||||
// Authn specifies an API that must be fullfiled by the domain service
|
||||
// implementation, and all of its decorators (e.g. logging & metrics).
|
||||
// Token is a string value of the actual Key and is used to authenticate
|
||||
// an Auth service request.
|
||||
type Authn interface {
|
||||
// Issue issues a new Key, returning its token value alongside.
|
||||
Issue(ctx context.Context, token string, key Key) (Token, error)
|
||||
|
||||
// Revoke removes the Key with the provided id that is
|
||||
// issued by the user identified by the provided key.
|
||||
Revoke(ctx context.Context, token, id string) error
|
||||
|
||||
// RetrieveKey retrieves data for the Key identified by the provided
|
||||
// ID, that is issued by the user identified by the provided key.
|
||||
RetrieveKey(ctx context.Context, token, id string) (Key, error)
|
||||
|
||||
// Identify validates token token. If token is valid, content
|
||||
// is returned. If token is invalid, or invocation failed for some
|
||||
// other reason, non-nil error value is returned in response.
|
||||
Identify(ctx context.Context, token string) (string, error)
|
||||
}
|
||||
|
||||
// Service specifies an API that must be fulfilled by the domain service
|
||||
// implementation, and all of its decorators (e.g. logging & metrics).
|
||||
// Token is a string value of the actual Key and is used to authenticate
|
||||
// an Auth service request.
|
||||
type Service interface {
|
||||
Authn
|
||||
Authz
|
||||
}
|
||||
|
||||
var _ Service = (*service)(nil)
|
||||
|
||||
type service struct {
|
||||
keys KeyRepository
|
||||
idProvider mainflux.IDProvider
|
||||
agent PolicyAgent
|
||||
tokenizer Tokenizer
|
||||
loginDuration time.Duration
|
||||
refreshDuration time.Duration
|
||||
}
|
||||
|
||||
// New instantiates the auth service implementation.
|
||||
func New(keys KeyRepository, idp mainflux.IDProvider, tokenizer Tokenizer, policyAgent PolicyAgent, loginDuration, refreshDuration time.Duration) Service {
|
||||
return &service{
|
||||
tokenizer: tokenizer,
|
||||
keys: keys,
|
||||
idProvider: idp,
|
||||
agent: policyAgent,
|
||||
loginDuration: loginDuration,
|
||||
refreshDuration: refreshDuration,
|
||||
}
|
||||
}
|
||||
|
||||
func (svc service) Issue(ctx context.Context, token string, key Key) (Token, error) {
|
||||
key.IssuedAt = time.Now().UTC()
|
||||
switch key.Type {
|
||||
case APIKey:
|
||||
return svc.userKey(ctx, token, key)
|
||||
case RefreshKey:
|
||||
return svc.refreshKey(ctx, token, key)
|
||||
case RecoveryKey:
|
||||
return svc.tmpKey(recoveryDuration, key)
|
||||
default:
|
||||
return svc.accessKey(key)
|
||||
}
|
||||
}
|
||||
|
||||
func (svc service) Revoke(ctx context.Context, token, id string) error {
|
||||
issuerID, _, err := svc.authenticate(token)
|
||||
if err != nil {
|
||||
return errors.Wrap(errRevoke, err)
|
||||
}
|
||||
if err := svc.keys.Remove(ctx, issuerID, id); err != nil {
|
||||
return errors.Wrap(errRevoke, err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (svc service) RetrieveKey(ctx context.Context, token, id string) (Key, error) {
|
||||
issuerID, _, err := svc.authenticate(token)
|
||||
if err != nil {
|
||||
return Key{}, errors.Wrap(errRetrieve, err)
|
||||
}
|
||||
|
||||
return svc.keys.Retrieve(ctx, issuerID, id)
|
||||
}
|
||||
|
||||
func (svc service) Identify(ctx context.Context, token string) (string, error) {
|
||||
key, err := svc.tokenizer.Parse(token)
|
||||
if err == ErrAPIKeyExpired {
|
||||
err = svc.keys.Remove(ctx, key.Issuer, key.ID)
|
||||
return "", errors.Wrap(ErrAPIKeyExpired, err)
|
||||
}
|
||||
if err != nil {
|
||||
return "", errors.Wrap(errIdentify, err)
|
||||
}
|
||||
|
||||
switch key.Type {
|
||||
case RecoveryKey, AccessKey:
|
||||
return key.Subject, nil
|
||||
case APIKey:
|
||||
_, err := svc.keys.Retrieve(context.TODO(), key.Issuer, key.ID)
|
||||
if err != nil {
|
||||
return "", errors.ErrAuthentication
|
||||
}
|
||||
return key.Subject, nil
|
||||
default:
|
||||
return "", errors.ErrAuthentication
|
||||
}
|
||||
}
|
||||
|
||||
func (svc service) Authorize(ctx context.Context, pr PolicyReq) error {
|
||||
if pr.SubjectKind == tokenKind {
|
||||
id, err := svc.Identify(ctx, pr.Subject)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
pr.Subject = id
|
||||
}
|
||||
if err := svc.agent.CheckPolicy(ctx, pr); err != nil {
|
||||
return errors.Wrap(errors.ErrAuthorization, err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (svc service) AddPolicy(ctx context.Context, pr PolicyReq) error {
|
||||
return svc.agent.AddPolicy(ctx, pr)
|
||||
}
|
||||
|
||||
// Yet to do.
|
||||
func (svc service) AddPolicies(ctx context.Context, token, object string, subjectIDs, relations []string) error {
|
||||
user, err := svc.Identify(ctx, token)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := svc.Authorize(ctx, PolicyReq{Object: mainfluxObject, Subject: user}); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var errs error
|
||||
for _, subjectID := range subjectIDs {
|
||||
for _, relation := range relations {
|
||||
if err := svc.AddPolicy(ctx, PolicyReq{Object: object, Relation: relation, Subject: subjectID}); err != nil {
|
||||
errs = errors.Wrap(fmt.Errorf("cannot add '%s' policy on object '%s' for subject '%s': %s", relation, object, subjectID, err), errs)
|
||||
}
|
||||
}
|
||||
}
|
||||
return errs
|
||||
}
|
||||
|
||||
func (svc service) DeletePolicy(ctx context.Context, pr PolicyReq) error {
|
||||
return svc.agent.DeletePolicy(ctx, pr)
|
||||
}
|
||||
|
||||
// Yet to do.
|
||||
func (svc service) DeletePolicies(ctx context.Context, token, object string, subjectIDs, relations []string) error {
|
||||
user, err := svc.Identify(ctx, token)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Check if the user identified by token is the admin.
|
||||
if err := svc.Authorize(ctx, PolicyReq{Object: mainfluxObject, Subject: user}); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var errs error
|
||||
for _, subjectID := range subjectIDs {
|
||||
for _, relation := range relations {
|
||||
if err := svc.DeletePolicy(ctx, PolicyReq{Object: object, Relation: relation, Subject: subjectID}); err != nil {
|
||||
errs = errors.Wrap(fmt.Errorf("cannot delete '%s' policy on object '%s' for subject '%s': %s", relation, object, subjectID, err), errs)
|
||||
}
|
||||
}
|
||||
}
|
||||
return errs
|
||||
}
|
||||
|
||||
func (svc service) AssignGroupAccessRights(ctx context.Context, token, thingGroupID, userGroupID string) error {
|
||||
if _, err := svc.Identify(ctx, token); err != nil {
|
||||
return err
|
||||
}
|
||||
return svc.agent.AddPolicy(ctx, PolicyReq{SubjectType: groupType, Subject: userGroupID, Relation: groupRelation, ObjectType: groupType, Object: thingGroupID})
|
||||
}
|
||||
|
||||
func (svc service) ListObjects(ctx context.Context, pr PolicyReq, nextPageToken string, limit int32) (PolicyPage, error) {
|
||||
if limit <= 0 {
|
||||
limit = 100
|
||||
}
|
||||
res, npt, err := svc.agent.RetrieveObjects(ctx, pr, nextPageToken, limit)
|
||||
if err != nil {
|
||||
return PolicyPage{}, err
|
||||
}
|
||||
var page PolicyPage
|
||||
for _, tuple := range res {
|
||||
page.Policies = append(page.Policies, tuple.Object)
|
||||
}
|
||||
page.NextPageToken = npt
|
||||
return page, err
|
||||
}
|
||||
|
||||
func (svc service) ListAllObjects(ctx context.Context, pr PolicyReq) (PolicyPage, error) {
|
||||
res, err := svc.agent.RetrieveAllObjects(ctx, pr)
|
||||
if err != nil {
|
||||
return PolicyPage{}, err
|
||||
}
|
||||
var page PolicyPage
|
||||
for _, tuple := range res {
|
||||
page.Policies = append(page.Policies, tuple.Object)
|
||||
}
|
||||
return page, err
|
||||
}
|
||||
|
||||
func (svc service) CountObjects(ctx context.Context, pr PolicyReq) (int, error) {
|
||||
return svc.agent.RetrieveAllObjectsCount(ctx, pr)
|
||||
}
|
||||
|
||||
func (svc service) ListSubjects(ctx context.Context, pr PolicyReq, nextPageToken string, limit int32) (PolicyPage, error) {
|
||||
if limit <= 0 {
|
||||
limit = 100
|
||||
}
|
||||
res, npt, err := svc.agent.RetrieveSubjects(ctx, pr, nextPageToken, limit)
|
||||
if err != nil {
|
||||
return PolicyPage{}, err
|
||||
}
|
||||
var page PolicyPage
|
||||
for _, tuple := range res {
|
||||
page.Policies = append(page.Policies, tuple.Subject)
|
||||
}
|
||||
page.NextPageToken = npt
|
||||
return page, err
|
||||
}
|
||||
|
||||
func (svc service) ListAllSubjects(ctx context.Context, pr PolicyReq) (PolicyPage, error) {
|
||||
res, err := svc.agent.RetrieveAllSubjects(ctx, pr)
|
||||
if err != nil {
|
||||
return PolicyPage{}, err
|
||||
}
|
||||
var page PolicyPage
|
||||
for _, tuple := range res {
|
||||
page.Policies = append(page.Policies, tuple.Subject)
|
||||
}
|
||||
return page, err
|
||||
}
|
||||
|
||||
func (svc service) CountSubjects(ctx context.Context, pr PolicyReq) (int, error) {
|
||||
return svc.agent.RetrieveAllSubjectsCount(ctx, pr)
|
||||
}
|
||||
|
||||
func (svc service) tmpKey(duration time.Duration, key Key) (Token, error) {
|
||||
value, err := svc.tokenizer.Issue(key)
|
||||
if err != nil {
|
||||
return Token{}, errors.Wrap(errIssueTmp, err)
|
||||
}
|
||||
|
||||
return Token{AccessToken: value}, nil
|
||||
}
|
||||
|
||||
func (svc service) accessKey(key Key) (Token, error) {
|
||||
key.Type = AccessKey
|
||||
key.ExpiresAt = time.Now().Add(svc.loginDuration)
|
||||
access, err := svc.tokenizer.Issue(key)
|
||||
if err != nil {
|
||||
return Token{}, errors.Wrap(errIssueTmp, err)
|
||||
}
|
||||
key.ExpiresAt = time.Now().Add(svc.refreshDuration)
|
||||
key.Type = RefreshKey
|
||||
refresh, err := svc.tokenizer.Issue(key)
|
||||
if err != nil {
|
||||
return Token{}, errors.Wrap(errIssueTmp, err)
|
||||
}
|
||||
|
||||
return Token{AccessToken: access, RefreshToken: refresh}, nil
|
||||
}
|
||||
|
||||
func (svc service) refreshKey(ctx context.Context, token string, key Key) (Token, error) {
|
||||
k, err := svc.tokenizer.Parse(token)
|
||||
if err != nil {
|
||||
return Token{}, err
|
||||
}
|
||||
if k.Type != RefreshKey {
|
||||
return Token{}, errIssueUser
|
||||
}
|
||||
key.ID = k.ID
|
||||
key.Subject = k.Subject
|
||||
key.Type = AccessKey
|
||||
key.ExpiresAt = time.Now().Add(svc.loginDuration)
|
||||
access, err := svc.tokenizer.Issue(key)
|
||||
if err != nil {
|
||||
return Token{}, errors.Wrap(errIssueTmp, err)
|
||||
}
|
||||
key.ExpiresAt = time.Now().Add(svc.refreshDuration)
|
||||
key.Type = RefreshKey
|
||||
refresh, err := svc.tokenizer.Issue(key)
|
||||
if err != nil {
|
||||
return Token{}, errors.Wrap(errIssueTmp, err)
|
||||
}
|
||||
|
||||
return Token{AccessToken: access, RefreshToken: refresh}, nil
|
||||
}
|
||||
|
||||
func (svc service) userKey(ctx context.Context, token string, key Key) (Token, error) {
|
||||
id, sub, err := svc.authenticate(token)
|
||||
if err != nil {
|
||||
return Token{}, errors.Wrap(errIssueUser, err)
|
||||
}
|
||||
|
||||
key.Issuer = id
|
||||
if key.Subject == "" {
|
||||
key.Subject = sub
|
||||
}
|
||||
|
||||
keyID, err := svc.idProvider.ID()
|
||||
if err != nil {
|
||||
return Token{}, errors.Wrap(errIssueUser, err)
|
||||
}
|
||||
key.ID = keyID
|
||||
|
||||
if _, err := svc.keys.Save(ctx, key); err != nil {
|
||||
return Token{}, errors.Wrap(errIssueUser, err)
|
||||
}
|
||||
|
||||
tkn, err := svc.tokenizer.Issue(key)
|
||||
if err != nil {
|
||||
return Token{}, errors.Wrap(errIssueUser, err)
|
||||
}
|
||||
|
||||
return Token{AccessToken: tkn}, nil
|
||||
}
|
||||
|
||||
func (svc service) authenticate(token string) (string, string, error) {
|
||||
key, err := svc.tokenizer.Parse(token)
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
// Only login key token is valid for login.
|
||||
if key.Type != AccessKey || key.Issuer == "" {
|
||||
return "", "", errors.ErrAuthentication
|
||||
}
|
||||
|
||||
return key.Issuer, key.Subject, nil
|
||||
}
|
||||
@@ -0,0 +1,533 @@
|
||||
// Copyright (c) Mainflux
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package auth_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/mainflux/mainflux/auth"
|
||||
"github.com/mainflux/mainflux/auth/jwt"
|
||||
"github.com/mainflux/mainflux/auth/mocks"
|
||||
"github.com/mainflux/mainflux/pkg/errors"
|
||||
"github.com/mainflux/mainflux/pkg/uuid"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/mock"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
var idProvider = uuid.New()
|
||||
|
||||
const (
|
||||
secret = "secret"
|
||||
email = "test@example.com"
|
||||
id = "testID"
|
||||
groupName = "mfx"
|
||||
description = "Description"
|
||||
|
||||
memberRelation = "member"
|
||||
authoritiesObj = "authorities"
|
||||
loginDuration = 30 * time.Minute
|
||||
refreshDuration = 24 * time.Hour
|
||||
accessToken = "access"
|
||||
)
|
||||
|
||||
func newService() (auth.Service, *mocks.Keys) {
|
||||
krepo := new(mocks.Keys)
|
||||
prepo := new(mocks.PolicyAgent)
|
||||
idProvider := uuid.NewMock()
|
||||
|
||||
t := jwt.New([]byte(secret))
|
||||
|
||||
return auth.New(krepo, idProvider, t, prepo, loginDuration, refreshDuration), krepo
|
||||
}
|
||||
|
||||
func TestIssue(t *testing.T) {
|
||||
svc, krepo := newService()
|
||||
|
||||
cases := []struct {
|
||||
desc string
|
||||
key auth.Key
|
||||
token string
|
||||
err error
|
||||
}{
|
||||
{
|
||||
desc: "issue login key",
|
||||
key: auth.Key{
|
||||
Type: auth.AccessKey,
|
||||
IssuedAt: time.Now(),
|
||||
},
|
||||
token: accessToken,
|
||||
err: nil,
|
||||
},
|
||||
{
|
||||
desc: "issue login key with no time",
|
||||
key: auth.Key{
|
||||
Type: auth.AccessKey,
|
||||
},
|
||||
token: accessToken,
|
||||
err: auth.ErrInvalidKeyIssuedAt,
|
||||
},
|
||||
{
|
||||
desc: "issue API key",
|
||||
key: auth.Key{
|
||||
Type: auth.APIKey,
|
||||
IssuedAt: time.Now(),
|
||||
},
|
||||
token: accessToken,
|
||||
err: nil,
|
||||
},
|
||||
{
|
||||
desc: "issue API key with an invalid token",
|
||||
key: auth.Key{
|
||||
Type: auth.APIKey,
|
||||
IssuedAt: time.Now(),
|
||||
},
|
||||
token: "invalid",
|
||||
err: errors.ErrAuthentication,
|
||||
},
|
||||
{
|
||||
desc: "issue API key with no time",
|
||||
key: auth.Key{
|
||||
Type: auth.APIKey,
|
||||
},
|
||||
token: accessToken,
|
||||
err: auth.ErrInvalidKeyIssuedAt,
|
||||
},
|
||||
{
|
||||
desc: "issue recovery key",
|
||||
key: auth.Key{
|
||||
Type: auth.RecoveryKey,
|
||||
IssuedAt: time.Now(),
|
||||
},
|
||||
token: "",
|
||||
err: nil,
|
||||
},
|
||||
{
|
||||
desc: "issue recovery with no issue time",
|
||||
key: auth.Key{
|
||||
Type: auth.RecoveryKey,
|
||||
},
|
||||
token: accessToken,
|
||||
err: auth.ErrInvalidKeyIssuedAt,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range cases {
|
||||
repocall := krepo.On("Save", mock.Anything, mock.Anything).Return(mock.Anything, tc.err)
|
||||
_, err := svc.Issue(context.Background(), tc.token, tc.key)
|
||||
assert.True(t, errors.Contains(err, tc.err), fmt.Sprintf("%s expected %s got %s\n", tc.desc, tc.err, err))
|
||||
repocall.Unset()
|
||||
}
|
||||
}
|
||||
|
||||
func TestRevoke(t *testing.T) {
|
||||
svc, _ := newService()
|
||||
secret, err := svc.Issue(context.Background(), "", auth.Key{Type: auth.AccessKey, IssuedAt: time.Now(), Subject: id})
|
||||
assert.Nil(t, err, fmt.Sprintf("Issuing login key expected to succeed: %s", err))
|
||||
key := auth.Key{
|
||||
Type: auth.APIKey,
|
||||
IssuedAt: time.Now(),
|
||||
Subject: id,
|
||||
}
|
||||
_, err = svc.Issue(context.Background(), secret.AccessToken, key)
|
||||
assert.Nil(t, err, fmt.Sprintf("Issuing user's key expected to succeed: %s", err))
|
||||
|
||||
cases := []struct {
|
||||
desc string
|
||||
id string
|
||||
token string
|
||||
err error
|
||||
}{
|
||||
{
|
||||
desc: "revoke login key",
|
||||
// id: newKey.ID,
|
||||
token: secret.AccessToken,
|
||||
err: nil,
|
||||
},
|
||||
{
|
||||
desc: "revoke non-existing login key",
|
||||
// id: newKey.ID,
|
||||
token: secret.AccessToken,
|
||||
err: nil,
|
||||
},
|
||||
{
|
||||
desc: "revoke with empty login key",
|
||||
// id: newKey.ID,
|
||||
token: "",
|
||||
err: errors.ErrAuthentication,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range cases {
|
||||
err := svc.Revoke(context.Background(), tc.token, tc.id)
|
||||
assert.True(t, errors.Contains(err, tc.err), fmt.Sprintf("%s expected %s got %s\n", tc.desc, tc.err, err))
|
||||
}
|
||||
}
|
||||
|
||||
func TestRetrieve(t *testing.T) {
|
||||
svc, _ := newService()
|
||||
secret, err := svc.Issue(context.Background(), "", auth.Key{Type: auth.AccessKey, IssuedAt: time.Now(), Subject: id})
|
||||
assert.Nil(t, err, fmt.Sprintf("Issuing login key expected to succeed: %s", err))
|
||||
key := auth.Key{
|
||||
ID: "id",
|
||||
Type: auth.APIKey,
|
||||
Subject: id,
|
||||
IssuedAt: time.Now(),
|
||||
}
|
||||
|
||||
userToken, err := svc.Issue(context.Background(), "", auth.Key{Type: auth.AccessKey, IssuedAt: time.Now(), Subject: id})
|
||||
assert.Nil(t, err, fmt.Sprintf("Issuing login key expected to succeed: %s", err))
|
||||
|
||||
apiToken, err := svc.Issue(context.Background(), secret.AccessToken, key)
|
||||
assert.Nil(t, err, fmt.Sprintf("Issuing login's key expected to succeed: %s", err))
|
||||
|
||||
resetToken, err := svc.Issue(context.Background(), "", auth.Key{Type: auth.RecoveryKey, IssuedAt: time.Now()})
|
||||
assert.Nil(t, err, fmt.Sprintf("Issuing reset key expected to succeed: %s", err))
|
||||
|
||||
cases := []struct {
|
||||
desc string
|
||||
id string
|
||||
token string
|
||||
err error
|
||||
}{
|
||||
{
|
||||
desc: "retrieve login key",
|
||||
// id: apiKey.ID,
|
||||
token: userToken.AccessToken,
|
||||
err: nil,
|
||||
},
|
||||
{
|
||||
desc: "retrieve non-existing login key",
|
||||
id: "invalid",
|
||||
token: userToken.AccessToken,
|
||||
err: errors.ErrNotFound,
|
||||
},
|
||||
{
|
||||
desc: "retrieve with wrong login key",
|
||||
// id: apiKey.ID,
|
||||
token: "wrong",
|
||||
err: errors.ErrAuthentication,
|
||||
},
|
||||
{
|
||||
desc: "retrieve with API token",
|
||||
// id: apiKey.ID,
|
||||
token: apiToken.AccessToken,
|
||||
err: errors.ErrAuthentication,
|
||||
},
|
||||
{
|
||||
desc: "retrieve with reset token",
|
||||
// id: apiKey.ID,
|
||||
token: resetToken.AccessToken,
|
||||
err: errors.ErrAuthentication,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range cases {
|
||||
_, err := svc.RetrieveKey(context.Background(), tc.token, tc.id)
|
||||
assert.True(t, errors.Contains(err, tc.err), fmt.Sprintf("%s expected %s got %s\n", tc.desc, tc.err, err))
|
||||
}
|
||||
}
|
||||
|
||||
func TestIdentify(t *testing.T) {
|
||||
svc, _ := newService()
|
||||
|
||||
loginSecret, err := svc.Issue(context.Background(), "", auth.Key{Type: auth.AccessKey, IssuedAt: time.Now(), Subject: id})
|
||||
assert.Nil(t, err, fmt.Sprintf("Issuing login key expected to succeed: %s", err))
|
||||
|
||||
recoverySecret, err := svc.Issue(context.Background(), "", auth.Key{Type: auth.RecoveryKey, IssuedAt: time.Now(), Subject: id})
|
||||
assert.Nil(t, err, fmt.Sprintf("Issuing reset key expected to succeed: %s", err))
|
||||
|
||||
apiSecret, err := svc.Issue(context.Background(), loginSecret.AccessToken, auth.Key{Type: auth.APIKey, Subject: id, IssuedAt: time.Now(), ExpiresAt: time.Now().Add(time.Minute)})
|
||||
assert.Nil(t, err, fmt.Sprintf("Issuing login key expected to succeed: %s", err))
|
||||
|
||||
exp1 := time.Now().Add(-2 * time.Second)
|
||||
expSecret, err := svc.Issue(context.Background(), loginSecret.AccessToken, auth.Key{Type: auth.APIKey, IssuedAt: time.Now(), ExpiresAt: exp1})
|
||||
assert.Nil(t, err, fmt.Sprintf("Issuing expired login key expected to succeed: %s", err))
|
||||
|
||||
invalidSecret, err := svc.Issue(context.Background(), loginSecret.AccessToken, auth.Key{Type: 22, IssuedAt: time.Now()})
|
||||
assert.Nil(t, err, fmt.Sprintf("Issuing login key expected to succeed: %s", err))
|
||||
|
||||
cases := []struct {
|
||||
desc string
|
||||
key string
|
||||
idt string
|
||||
err error
|
||||
}{
|
||||
{
|
||||
desc: "identify login key",
|
||||
key: loginSecret.AccessToken,
|
||||
idt: id,
|
||||
err: nil,
|
||||
},
|
||||
{
|
||||
desc: "identify recovery key",
|
||||
key: recoverySecret.AccessToken,
|
||||
idt: id,
|
||||
err: nil,
|
||||
},
|
||||
{
|
||||
desc: "identify API key",
|
||||
key: apiSecret.AccessToken,
|
||||
idt: id,
|
||||
err: nil,
|
||||
},
|
||||
{
|
||||
desc: "identify expired API key",
|
||||
key: expSecret.AccessToken,
|
||||
idt: "",
|
||||
err: auth.ErrAPIKeyExpired,
|
||||
},
|
||||
{
|
||||
desc: "identify expired key",
|
||||
key: invalidSecret.AccessToken,
|
||||
idt: "",
|
||||
err: errors.ErrAuthentication,
|
||||
},
|
||||
{
|
||||
desc: "identify invalid key",
|
||||
key: "invalid",
|
||||
idt: "",
|
||||
err: errors.ErrAuthentication,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range cases {
|
||||
idt, err := svc.Identify(context.Background(), tc.key)
|
||||
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.idt, idt, fmt.Sprintf("%s expected %s got %s\n", tc.desc, tc.idt, idt))
|
||||
}
|
||||
}
|
||||
|
||||
func TestAuthorize(t *testing.T) {
|
||||
svc, _ := newService()
|
||||
|
||||
pr := auth.PolicyReq{Object: authoritiesObj, Relation: memberRelation, Subject: id}
|
||||
err := svc.Authorize(context.Background(), pr)
|
||||
require.Nil(t, err, fmt.Sprintf("authorizing initial %v policy expected to succeed: %s", pr, err))
|
||||
}
|
||||
|
||||
func TestAddPolicy(t *testing.T) {
|
||||
svc, _ := newService()
|
||||
|
||||
pr := auth.PolicyReq{Object: "obj", Relation: "rel", Subject: "sub"}
|
||||
err := svc.AddPolicy(context.Background(), pr)
|
||||
require.Nil(t, err, fmt.Sprintf("adding %v policy expected to succeed: %v", pr, err))
|
||||
|
||||
err = svc.Authorize(context.Background(), pr)
|
||||
require.Nil(t, err, fmt.Sprintf("checking shared %v policy expected to be succeed: %#v", pr, err))
|
||||
}
|
||||
|
||||
func TestDeletePolicy(t *testing.T) {
|
||||
svc, _ := newService()
|
||||
|
||||
pr := auth.PolicyReq{Object: authoritiesObj, Relation: memberRelation, Subject: id}
|
||||
err := svc.DeletePolicy(context.Background(), pr)
|
||||
require.Nil(t, err, fmt.Sprintf("deleting %v policy expected to succeed: %s", pr, err))
|
||||
}
|
||||
|
||||
func TestAddPolicies(t *testing.T) {
|
||||
svc, _ := newService()
|
||||
secret, err := svc.Issue(context.Background(), "", auth.Key{Type: auth.AccessKey, IssuedAt: time.Now(), Subject: id})
|
||||
assert.Nil(t, err, fmt.Sprintf("Issuing login key expected to succeed: %s", err))
|
||||
|
||||
key := auth.Key{
|
||||
ID: "id",
|
||||
Type: auth.APIKey,
|
||||
Subject: id,
|
||||
IssuedAt: time.Now(),
|
||||
}
|
||||
|
||||
apiToken, err := svc.Issue(context.Background(), secret.AccessToken, key)
|
||||
assert.Nil(t, err, fmt.Sprintf("Issuing user's key expected to succeed: %s", err))
|
||||
|
||||
thingID, err := idProvider.ID()
|
||||
assert.Nil(t, err, fmt.Sprintf("got unexpected error: %s", err))
|
||||
|
||||
tmpID := "tmpid"
|
||||
readPolicy := "read"
|
||||
writePolicy := "write"
|
||||
deletePolicy := "delete"
|
||||
|
||||
// Add read policy to users.
|
||||
err = svc.AddPolicies(context.Background(), apiToken.AccessToken, thingID, []string{id, tmpID}, []string{readPolicy})
|
||||
assert.Nil(t, err, fmt.Sprintf("adding policies expected to succeed: %s", err))
|
||||
|
||||
// Add write and delete policies to users.
|
||||
err = svc.AddPolicies(context.Background(), apiToken.AccessToken, thingID, []string{id, tmpID}, []string{writePolicy, deletePolicy})
|
||||
assert.Nil(t, err, fmt.Sprintf("adding multiple policies expected to succeed: %s", err))
|
||||
|
||||
cases := []struct {
|
||||
desc string
|
||||
policy auth.PolicyReq
|
||||
err error
|
||||
}{
|
||||
{
|
||||
desc: "check valid 'read' policy of user with id",
|
||||
policy: auth.PolicyReq{Object: thingID, Relation: readPolicy, Subject: id},
|
||||
err: nil,
|
||||
},
|
||||
{
|
||||
desc: "check valid 'write' policy of user with id",
|
||||
policy: auth.PolicyReq{Object: thingID, Relation: writePolicy, Subject: id},
|
||||
err: nil,
|
||||
},
|
||||
{
|
||||
desc: "check valid 'delete' policy of user with id",
|
||||
policy: auth.PolicyReq{Object: thingID, Relation: deletePolicy, Subject: id},
|
||||
err: nil,
|
||||
},
|
||||
{
|
||||
desc: "check valid 'read' policy of user with tmpid",
|
||||
policy: auth.PolicyReq{Object: thingID, Relation: readPolicy, Subject: tmpID},
|
||||
err: nil,
|
||||
},
|
||||
{
|
||||
desc: "check valid 'write' policy of user with tmpid",
|
||||
policy: auth.PolicyReq{Object: thingID, Relation: writePolicy, Subject: tmpID},
|
||||
err: nil,
|
||||
},
|
||||
{
|
||||
desc: "check valid 'delete' policy of user with tmpid",
|
||||
policy: auth.PolicyReq{Object: thingID, Relation: deletePolicy, Subject: tmpID},
|
||||
err: nil,
|
||||
},
|
||||
{
|
||||
desc: "check invalid 'access' policy of user with id",
|
||||
policy: auth.PolicyReq{Object: thingID, Relation: "access", Subject: id},
|
||||
err: errors.ErrAuthorization,
|
||||
},
|
||||
{
|
||||
desc: "check invalid 'access' policy of user with tmpid",
|
||||
policy: auth.PolicyReq{Object: thingID, Relation: "access", Subject: tmpID},
|
||||
err: errors.ErrAuthorization,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range cases {
|
||||
err := svc.Authorize(context.Background(), tc.policy)
|
||||
assert.True(t, errors.Contains(err, tc.err), fmt.Sprintf("%s: expected %v, got %v", tc.desc, tc.err, err))
|
||||
}
|
||||
}
|
||||
|
||||
func TestDeletePolicies(t *testing.T) {
|
||||
svc, _ := newService()
|
||||
secret, err := svc.Issue(context.Background(), "", auth.Key{Type: auth.AccessKey, IssuedAt: time.Now(), Subject: id})
|
||||
assert.Nil(t, err, fmt.Sprintf("Issuing login key expected to succeed: %s", err))
|
||||
|
||||
key := auth.Key{
|
||||
ID: "id",
|
||||
Type: auth.APIKey,
|
||||
Subject: id,
|
||||
IssuedAt: time.Now(),
|
||||
}
|
||||
|
||||
apiToken, err := svc.Issue(context.Background(), secret.AccessToken, key)
|
||||
assert.Nil(t, err, fmt.Sprintf("Issuing user's key expected to succeed: %s", err))
|
||||
|
||||
thingID, err := idProvider.ID()
|
||||
assert.Nil(t, err, fmt.Sprintf("got unexpected error: %s", err))
|
||||
|
||||
tmpID := "tmpid"
|
||||
readPolicy := "read"
|
||||
writePolicy := "write"
|
||||
deletePolicy := "delete"
|
||||
memberPolicy := "member"
|
||||
|
||||
// Add read, write and delete policies to users.
|
||||
err = svc.AddPolicies(context.Background(), apiToken.AccessToken, thingID, []string{id, tmpID}, []string{readPolicy, writePolicy, deletePolicy, memberPolicy})
|
||||
assert.Nil(t, err, fmt.Sprintf("adding policies expected to succeed: %s", err))
|
||||
|
||||
// Delete multiple policies from single user.
|
||||
err = svc.DeletePolicies(context.Background(), apiToken.AccessToken, thingID, []string{id}, []string{readPolicy, writePolicy})
|
||||
assert.Nil(t, err, fmt.Sprintf("deleting policies from single user expected to succeed: %s", err))
|
||||
|
||||
// Delete multiple policies from multiple user.
|
||||
err = svc.DeletePolicies(context.Background(), apiToken.AccessToken, thingID, []string{id, tmpID}, []string{deletePolicy, memberPolicy})
|
||||
assert.Nil(t, err, fmt.Sprintf("deleting policies from multiple user expected to succeed: %s", err))
|
||||
|
||||
cases := []struct {
|
||||
desc string
|
||||
policy auth.PolicyReq
|
||||
err error
|
||||
}{
|
||||
{
|
||||
desc: "check non-existing 'read' policy of user with id",
|
||||
policy: auth.PolicyReq{Object: thingID, Relation: readPolicy, Subject: id},
|
||||
err: errors.ErrAuthorization,
|
||||
},
|
||||
{
|
||||
desc: "check non-existing 'write' policy of user with id",
|
||||
policy: auth.PolicyReq{Object: thingID, Relation: writePolicy, Subject: id},
|
||||
err: errors.ErrAuthorization,
|
||||
},
|
||||
{
|
||||
desc: "check non-existing 'delete' policy of user with id",
|
||||
policy: auth.PolicyReq{Object: thingID, Relation: deletePolicy, Subject: id},
|
||||
err: errors.ErrAuthorization,
|
||||
},
|
||||
{
|
||||
desc: "check non-existing 'member' policy of user with id",
|
||||
policy: auth.PolicyReq{Object: thingID, Relation: memberPolicy, Subject: id},
|
||||
err: errors.ErrAuthorization,
|
||||
},
|
||||
{
|
||||
desc: "check non-existing 'delete' policy of user with tmpid",
|
||||
policy: auth.PolicyReq{Object: thingID, Relation: deletePolicy, Subject: tmpID},
|
||||
err: errors.ErrAuthorization,
|
||||
},
|
||||
{
|
||||
desc: "check non-existing 'member' policy of user with tmpid",
|
||||
policy: auth.PolicyReq{Object: thingID, Relation: memberPolicy, Subject: tmpID},
|
||||
err: errors.ErrAuthorization,
|
||||
},
|
||||
{
|
||||
desc: "check valid 'read' policy of user with tmpid",
|
||||
policy: auth.PolicyReq{Object: thingID, Relation: readPolicy, Subject: tmpID},
|
||||
err: nil,
|
||||
},
|
||||
{
|
||||
desc: "check valid 'write' policy of user with tmpid",
|
||||
policy: auth.PolicyReq{Object: thingID, Relation: writePolicy, Subject: tmpID},
|
||||
err: nil,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range cases {
|
||||
err := svc.Authorize(context.Background(), tc.policy)
|
||||
assert.True(t, errors.Contains(err, tc.err), fmt.Sprintf("%s: expected %v, got %v", tc.desc, tc.err, err))
|
||||
}
|
||||
}
|
||||
|
||||
func TestListPolicies(t *testing.T) {
|
||||
svc, _ := newService()
|
||||
secret, err := svc.Issue(context.Background(), "", auth.Key{Type: auth.AccessKey, IssuedAt: time.Now(), Subject: id})
|
||||
assert.Nil(t, err, fmt.Sprintf("Issuing login key expected to succeed: %s", err))
|
||||
|
||||
key := auth.Key{
|
||||
ID: "id",
|
||||
Type: auth.APIKey,
|
||||
Subject: id,
|
||||
IssuedAt: time.Now(),
|
||||
}
|
||||
|
||||
apiToken, err := svc.Issue(context.Background(), secret.AccessToken, key)
|
||||
assert.Nil(t, err, fmt.Sprintf("Issuing user's key expected to succeed: %s", err))
|
||||
|
||||
readPolicy := "read"
|
||||
pageLen := 15
|
||||
|
||||
// Add arbitrary policies to the user.
|
||||
for i := 0; i < pageLen; i++ {
|
||||
err = svc.AddPolicies(context.Background(), apiToken.AccessToken, fmt.Sprintf("thing-%d", i), []string{id}, []string{readPolicy})
|
||||
assert.Nil(t, err, fmt.Sprintf("adding policies expected to succeed: %s", err))
|
||||
}
|
||||
|
||||
page, err := svc.ListObjects(context.Background(), auth.PolicyReq{Subject: id, Relation: readPolicy}, "", 100)
|
||||
assert.Nil(t, err, fmt.Sprintf("listing policies expected to succeed: %s", err))
|
||||
assert.Equal(t, pageLen, len(page.Policies), fmt.Sprintf("unexpected listing page size, expected %d, got %d: %v", pageLen, len(page.Policies), err))
|
||||
|
||||
}
|
||||
@@ -0,0 +1,346 @@
|
||||
package spicedb
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
|
||||
v1 "github.com/authzed/authzed-go/proto/authzed/api/v1"
|
||||
"github.com/authzed/authzed-go/v1"
|
||||
"github.com/mainflux/mainflux/auth"
|
||||
mflog "github.com/mainflux/mainflux/logger"
|
||||
"github.com/mainflux/mainflux/pkg/errors"
|
||||
)
|
||||
|
||||
const defRetrieveAllLimit = 1000
|
||||
|
||||
type policyAgent struct {
|
||||
client *authzed.Client
|
||||
permissionClient v1.PermissionsServiceClient
|
||||
logger mflog.Logger
|
||||
}
|
||||
|
||||
func NewPolicyAgent(client *authzed.Client, logger mflog.Logger) auth.PolicyAgent {
|
||||
return &policyAgent{
|
||||
client: client,
|
||||
permissionClient: client.PermissionsServiceClient,
|
||||
logger: logger,
|
||||
}
|
||||
}
|
||||
|
||||
func (pa *policyAgent) CheckPolicy(ctx context.Context, pr auth.PolicyReq) error {
|
||||
checkReq := v1.CheckPermissionRequest{
|
||||
Resource: &v1.ObjectReference{ObjectType: pr.ObjectType, ObjectId: pr.Object},
|
||||
Permission: pr.Permission,
|
||||
Subject: &v1.SubjectReference{Object: &v1.ObjectReference{ObjectType: pr.SubjectType, ObjectId: pr.Subject}, OptionalRelation: pr.SubjectRelation},
|
||||
}
|
||||
|
||||
resp, err := pa.permissionClient.CheckPermission(context.Background(), &checkReq)
|
||||
if err != nil {
|
||||
return errors.Wrap(errors.ErrMalformedEntity, fmt.Errorf("failed to check permission: %w", err))
|
||||
}
|
||||
if resp.Permissionship == v1.CheckPermissionResponse_PERMISSIONSHIP_HAS_PERMISSION {
|
||||
return nil
|
||||
}
|
||||
if reason, ok := v1.CheckPermissionResponse_Permissionship_name[int32(resp.Permissionship)]; ok {
|
||||
return errors.Wrap(errors.ErrAuthorization, fmt.Errorf("%s", reason))
|
||||
}
|
||||
return errors.ErrAuthorization
|
||||
}
|
||||
|
||||
func (pa *policyAgent) AddPolicies(ctx context.Context, prs []auth.PolicyReq) error {
|
||||
updates := []*v1.RelationshipUpdate{}
|
||||
for _, pr := range prs {
|
||||
updates = append(updates, &v1.RelationshipUpdate{
|
||||
Operation: v1.RelationshipUpdate_OPERATION_CREATE,
|
||||
Relationship: &v1.Relationship{
|
||||
Resource: &v1.ObjectReference{ObjectType: pr.ObjectType, ObjectId: pr.Object},
|
||||
Relation: pr.Relation,
|
||||
Subject: &v1.SubjectReference{Object: &v1.ObjectReference{ObjectType: pr.SubjectType, ObjectId: pr.Subject}, OptionalRelation: pr.SubjectRelation},
|
||||
},
|
||||
})
|
||||
}
|
||||
if len(updates) > 0 {
|
||||
_, err := pa.permissionClient.WriteRelationships(ctx, &v1.WriteRelationshipsRequest{Updates: updates})
|
||||
if err != nil {
|
||||
return errors.Wrap(errors.ErrMalformedEntity, fmt.Errorf("failed to add policy: %w", err))
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
func (pa *policyAgent) AddPolicy(ctx context.Context, pr auth.PolicyReq) error {
|
||||
updates := []*v1.RelationshipUpdate{
|
||||
{
|
||||
Operation: v1.RelationshipUpdate_OPERATION_CREATE,
|
||||
Relationship: &v1.Relationship{
|
||||
Resource: &v1.ObjectReference{ObjectType: pr.ObjectType, ObjectId: pr.Object},
|
||||
Relation: pr.Relation,
|
||||
Subject: &v1.SubjectReference{Object: &v1.ObjectReference{ObjectType: pr.SubjectType, ObjectId: pr.Subject}, OptionalRelation: pr.SubjectRelation},
|
||||
},
|
||||
},
|
||||
}
|
||||
_, err := pa.permissionClient.WriteRelationships(ctx, &v1.WriteRelationshipsRequest{Updates: updates})
|
||||
if err != nil {
|
||||
return errors.Wrap(errors.ErrMalformedEntity, fmt.Errorf("failed to add policy: %w", err))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (pa *policyAgent) DeletePolicies(ctx context.Context, prs []auth.PolicyReq) error {
|
||||
updates := []*v1.RelationshipUpdate{}
|
||||
for _, pr := range prs {
|
||||
updates = append(updates, &v1.RelationshipUpdate{
|
||||
Operation: v1.RelationshipUpdate_OPERATION_DELETE,
|
||||
Relationship: &v1.Relationship{
|
||||
Resource: &v1.ObjectReference{ObjectType: pr.ObjectType, ObjectId: pr.Object},
|
||||
Relation: pr.Relation,
|
||||
Subject: &v1.SubjectReference{Object: &v1.ObjectReference{ObjectType: pr.SubjectType, ObjectId: pr.Subject}, OptionalRelation: pr.SubjectRelation},
|
||||
},
|
||||
})
|
||||
}
|
||||
if len(updates) > 0 {
|
||||
_, err := pa.permissionClient.WriteRelationships(ctx, &v1.WriteRelationshipsRequest{Updates: updates})
|
||||
if err != nil {
|
||||
return errors.Wrap(errors.ErrMalformedEntity, fmt.Errorf("failed to delete policy: %w", err))
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (pa *policyAgent) DeletePolicy(ctx context.Context, pr auth.PolicyReq) error {
|
||||
req := &v1.DeleteRelationshipsRequest{
|
||||
RelationshipFilter: &v1.RelationshipFilter{
|
||||
ResourceType: pr.ObjectType,
|
||||
OptionalResourceId: pr.Object,
|
||||
OptionalRelation: pr.Relation,
|
||||
OptionalSubjectFilter: &v1.SubjectFilter{
|
||||
OptionalSubjectId: pr.Subject,
|
||||
SubjectType: pr.SubjectType,
|
||||
OptionalRelation: &v1.SubjectFilter_RelationFilter{
|
||||
Relation: pr.SubjectRelation,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
_, err := pa.permissionClient.DeleteRelationships(ctx, req)
|
||||
if err != nil {
|
||||
return errors.Wrap(errors.ErrMalformedEntity, fmt.Errorf("failed to remove the policy: %w", err))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// RetrieveObjects - Listing of things.
|
||||
func (pa *policyAgent) RetrieveObjects(ctx context.Context, pr auth.PolicyReq, nextPageToken string, limit int32) ([]auth.PolicyRes, string, error) {
|
||||
resourceReq := &v1.LookupResourcesRequest{
|
||||
ResourceObjectType: pr.ObjectType,
|
||||
Permission: pr.Permission,
|
||||
Subject: &v1.SubjectReference{Object: &v1.ObjectReference{ObjectType: pr.SubjectType, ObjectId: pr.Subject}, OptionalRelation: pr.SubjectRelation},
|
||||
OptionalLimit: uint32(limit),
|
||||
}
|
||||
if nextPageToken != "" {
|
||||
resourceReq.OptionalCursor = &v1.Cursor{Token: nextPageToken}
|
||||
}
|
||||
stream, err := pa.permissionClient.LookupResources(ctx, resourceReq)
|
||||
if err != nil {
|
||||
return nil, "", errors.Wrap(errors.ErrMalformedEntity, fmt.Errorf("failed to retrieve policies: %w", err))
|
||||
}
|
||||
resources := []*v1.LookupResourcesResponse{}
|
||||
var retErr error
|
||||
loop:
|
||||
for {
|
||||
resp, err := stream.Recv()
|
||||
switch err {
|
||||
case nil:
|
||||
resources = append(resources, resp)
|
||||
case io.EOF:
|
||||
break loop
|
||||
default:
|
||||
retErr = err
|
||||
break loop
|
||||
}
|
||||
}
|
||||
var token string
|
||||
if len(resources) > 0 {
|
||||
token = resources[len(resources)-1].AfterResultCursor.Token
|
||||
}
|
||||
if retErr != nil {
|
||||
retErr = errors.Wrap(errors.ErrViewEntity, retErr)
|
||||
}
|
||||
return objectsToAuthPolicies(resources), token, retErr
|
||||
}
|
||||
|
||||
func (pa *policyAgent) RetrieveAllObjects(ctx context.Context, pr auth.PolicyReq) ([]auth.PolicyRes, error) {
|
||||
resourceReq := &v1.LookupResourcesRequest{
|
||||
ResourceObjectType: pr.ObjectType,
|
||||
Permission: pr.Permission,
|
||||
Subject: &v1.SubjectReference{Object: &v1.ObjectReference{ObjectType: pr.SubjectType, ObjectId: pr.Subject}, OptionalRelation: pr.SubjectRelation},
|
||||
}
|
||||
stream, err := pa.permissionClient.LookupResources(ctx, resourceReq)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(errors.ErrMalformedEntity, fmt.Errorf("failed to retrieve policies: %w", err))
|
||||
}
|
||||
tuples := []auth.PolicyRes{}
|
||||
for {
|
||||
resp, err := stream.Recv()
|
||||
switch {
|
||||
case errors.Contains(err, io.EOF):
|
||||
return tuples, nil
|
||||
case err != nil:
|
||||
return tuples, err
|
||||
default:
|
||||
tuples = append(tuples, auth.PolicyRes{Object: resp.ResourceObjectId})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (pa *policyAgent) RetrieveAllObjectsCount(ctx context.Context, pr auth.PolicyReq) (int, error) {
|
||||
var count int
|
||||
nextPageToken := ""
|
||||
for {
|
||||
relationTuples, npt, err := pa.RetrieveObjects(ctx, pr, nextPageToken, defRetrieveAllLimit)
|
||||
if err != nil {
|
||||
return count, err
|
||||
}
|
||||
count = count + len(relationTuples)
|
||||
if npt == "" {
|
||||
break
|
||||
}
|
||||
nextPageToken = npt
|
||||
}
|
||||
return count, nil
|
||||
}
|
||||
|
||||
func (pa *policyAgent) RetrieveSubjects(ctx context.Context, pr auth.PolicyReq, nextPageToken string, limit int32) ([]auth.PolicyRes, string, error) {
|
||||
subjectsReq := v1.LookupSubjectsRequest{
|
||||
Resource: &v1.ObjectReference{ObjectType: pr.ObjectType, ObjectId: pr.Object},
|
||||
Permission: pr.Permission,
|
||||
SubjectObjectType: pr.SubjectType,
|
||||
OptionalSubjectRelation: pr.SubjectRelation,
|
||||
OptionalConcreteLimit: uint32(limit),
|
||||
WildcardOption: v1.LookupSubjectsRequest_WILDCARD_OPTION_INCLUDE_WILDCARDS,
|
||||
}
|
||||
if nextPageToken != "" {
|
||||
subjectsReq.OptionalCursor = &v1.Cursor{Token: nextPageToken}
|
||||
}
|
||||
stream, err := pa.permissionClient.LookupSubjects(ctx, &subjectsReq)
|
||||
if err != nil {
|
||||
return nil, "", errors.Wrap(errors.ErrMalformedEntity, fmt.Errorf("failed to retrieve policies: %w", err))
|
||||
}
|
||||
subjects := []*v1.LookupSubjectsResponse{}
|
||||
var retErr error
|
||||
loop:
|
||||
for {
|
||||
resp, err := stream.Recv()
|
||||
|
||||
switch err {
|
||||
case nil:
|
||||
subjects = append(subjects, resp)
|
||||
case io.EOF:
|
||||
break loop
|
||||
default:
|
||||
retErr = err
|
||||
break loop
|
||||
}
|
||||
}
|
||||
if retErr != nil {
|
||||
retErr = errors.Wrap(errors.ErrViewEntity, retErr)
|
||||
}
|
||||
return subjectsToAuthPolicies(subjects), "", retErr
|
||||
}
|
||||
|
||||
func (pa *policyAgent) RetrieveAllSubjects(ctx context.Context, pr auth.PolicyReq) ([]auth.PolicyRes, error) {
|
||||
var tuples []auth.PolicyRes
|
||||
nextPageToken := ""
|
||||
for i := 0; ; i++ {
|
||||
relationTuples, npt, err := pa.RetrieveSubjects(ctx, pr, nextPageToken, defRetrieveAllLimit)
|
||||
if err != nil {
|
||||
return tuples, err
|
||||
}
|
||||
tuples = append(tuples, relationTuples...)
|
||||
if npt == "" || (len(tuples) < defRetrieveAllLimit) {
|
||||
break
|
||||
}
|
||||
nextPageToken = npt
|
||||
}
|
||||
return tuples, nil
|
||||
}
|
||||
|
||||
func (pa *policyAgent) RetrieveAllSubjectsCount(ctx context.Context, pr auth.PolicyReq) (int, error) {
|
||||
var count int
|
||||
nextPageToken := ""
|
||||
for {
|
||||
relationTuples, npt, err := pa.RetrieveSubjects(ctx, pr, nextPageToken, defRetrieveAllLimit)
|
||||
if err != nil {
|
||||
return count, err
|
||||
}
|
||||
count = count + len(relationTuples)
|
||||
if npt == "" {
|
||||
break
|
||||
}
|
||||
nextPageToken = npt
|
||||
}
|
||||
return count, nil
|
||||
}
|
||||
|
||||
func objectsToAuthPolicies(objects []*v1.LookupResourcesResponse) []auth.PolicyRes {
|
||||
var policies []auth.PolicyRes
|
||||
for _, obj := range objects {
|
||||
policies = append(policies, auth.PolicyRes{
|
||||
Object: obj.GetResourceObjectId(),
|
||||
})
|
||||
}
|
||||
return policies
|
||||
}
|
||||
|
||||
func subjectsToAuthPolicies(subjects []*v1.LookupSubjectsResponse) []auth.PolicyRes {
|
||||
var policies []auth.PolicyRes
|
||||
for _, sub := range subjects {
|
||||
policies = append(policies, auth.PolicyRes{
|
||||
Subject: sub.Subject.GetSubjectObjectId(),
|
||||
})
|
||||
}
|
||||
return policies
|
||||
}
|
||||
|
||||
func (pa *policyAgent) Watch(continue_token string) {
|
||||
stream, err := pa.client.WatchServiceClient.Watch(context.Background(), &v1.WatchRequest{
|
||||
OptionalObjectTypes: []string{},
|
||||
OptionalStartCursor: &v1.ZedToken{Token: continue_token},
|
||||
})
|
||||
if err != nil {
|
||||
pa.logger.Error(fmt.Sprintf("got error while watching: %s", err.Error()))
|
||||
}
|
||||
loop:
|
||||
for {
|
||||
watchResp, err := stream.Recv()
|
||||
switch err {
|
||||
case nil:
|
||||
pa.publishToStream(watchResp)
|
||||
case io.EOF:
|
||||
pa.logger.Info("got EOF while watch streaming")
|
||||
break loop
|
||||
default:
|
||||
pa.logger.Error(fmt.Sprintf("got error while watch streaming : %s", err.Error()))
|
||||
break loop
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (pa *policyAgent) publishToStream(resp *v1.WatchResponse) {
|
||||
pa.logger.Info(fmt.Sprintf("Publish next token %s", resp.ChangesThrough.Token))
|
||||
|
||||
for _, update := range resp.Updates {
|
||||
operation := v1.RelationshipUpdate_Operation_name[int32(update.Operation)]
|
||||
objectType := update.Relationship.Resource.ObjectType
|
||||
objectId := update.Relationship.Resource.ObjectId
|
||||
relation := update.Relationship.Relation
|
||||
subjectType := update.Relationship.Subject.Object.ObjectType
|
||||
subjectRelation := update.Relationship.Subject.OptionalRelation
|
||||
subjectId := update.Relationship.Subject.Object.ObjectId
|
||||
|
||||
pa.logger.Info(fmt.Sprintf(`
|
||||
Operation : %s object_type: %s object_id: %s relation: %s subject_type: %s subject_relation: %s subject_id: %s
|
||||
`, operation, objectType, objectId, relation, subjectType, subjectRelation, subjectId))
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
// Copyright (c) Mainflux
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package auth
|
||||
|
||||
// Tokenizer specifies API for encoding and decoding between string and Key.
|
||||
type Tokenizer interface {
|
||||
// Issue converts API Key to its string representation.
|
||||
Issue(key Key) (token string, err error)
|
||||
|
||||
// Parse extracts API Key data from string token.
|
||||
Parse(token string) (key Key, err error)
|
||||
}
|
||||
@@ -0,0 +1,223 @@
|
||||
// Copyright (c) Mainflux
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package tracing
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/mainflux/mainflux/auth"
|
||||
"go.opentelemetry.io/otel/attribute"
|
||||
"go.opentelemetry.io/otel/trace"
|
||||
)
|
||||
|
||||
var _ auth.Service = (*tracingMiddleware)(nil)
|
||||
|
||||
type tracingMiddleware struct {
|
||||
tracer trace.Tracer
|
||||
svc auth.Service
|
||||
}
|
||||
|
||||
// New returns a new group service with tracing capabilities.
|
||||
func New(svc auth.Service, tracer trace.Tracer) auth.Service {
|
||||
return &tracingMiddleware{tracer, svc}
|
||||
}
|
||||
|
||||
func (tm *tracingMiddleware) Issue(ctx context.Context, token string, key auth.Key) (auth.Token, error) {
|
||||
ctx, span := tm.tracer.Start(ctx, "issue", trace.WithAttributes(
|
||||
attribute.String("type", fmt.Sprintf("%d", key.Type)),
|
||||
attribute.String("subject", key.Subject),
|
||||
))
|
||||
defer span.End()
|
||||
|
||||
return tm.svc.Issue(ctx, token, key)
|
||||
}
|
||||
|
||||
func (tm *tracingMiddleware) Revoke(ctx context.Context, token, id string) error {
|
||||
ctx, span := tm.tracer.Start(ctx, "revoke", trace.WithAttributes(
|
||||
attribute.String("id", id),
|
||||
))
|
||||
defer span.End()
|
||||
|
||||
return tm.svc.Revoke(ctx, token, id)
|
||||
}
|
||||
|
||||
func (tm *tracingMiddleware) RetrieveKey(ctx context.Context, token, id string) (auth.Key, error) {
|
||||
ctx, span := tm.tracer.Start(ctx, "retrieve_key", trace.WithAttributes(
|
||||
attribute.String("id", id),
|
||||
))
|
||||
defer span.End()
|
||||
|
||||
return tm.svc.RetrieveKey(ctx, token, id)
|
||||
}
|
||||
|
||||
func (tm *tracingMiddleware) Identify(ctx context.Context, token string) (string, error) {
|
||||
ctx, span := tm.tracer.Start(ctx, "identify")
|
||||
defer span.End()
|
||||
|
||||
return tm.svc.Identify(ctx, token)
|
||||
}
|
||||
|
||||
func (tm *tracingMiddleware) Authorize(ctx context.Context, pr auth.PolicyReq) error {
|
||||
ctx, span := tm.tracer.Start(ctx, "authorize", trace.WithAttributes(
|
||||
attribute.String("subject", pr.Subject),
|
||||
attribute.String("subject_type", pr.SubjectType),
|
||||
attribute.String("subject_relation", pr.SubjectRelation),
|
||||
attribute.String("object", pr.Object),
|
||||
attribute.String("object_type", pr.ObjectType),
|
||||
attribute.String("relation", pr.Relation),
|
||||
attribute.String("permission", pr.Permission),
|
||||
))
|
||||
defer span.End()
|
||||
|
||||
return tm.svc.Authorize(ctx, pr)
|
||||
}
|
||||
|
||||
func (tm *tracingMiddleware) AddPolicy(ctx context.Context, pr auth.PolicyReq) error {
|
||||
ctx, span := tm.tracer.Start(ctx, "add_policy", trace.WithAttributes(
|
||||
attribute.String("subject", pr.Subject),
|
||||
attribute.String("subject_type", pr.SubjectType),
|
||||
attribute.String("subject_relation", pr.SubjectRelation),
|
||||
attribute.String("object", pr.Object),
|
||||
attribute.String("object_type", pr.ObjectType),
|
||||
attribute.String("relation", pr.Relation),
|
||||
attribute.String("permission", pr.Permission),
|
||||
))
|
||||
defer span.End()
|
||||
|
||||
return tm.svc.AddPolicy(ctx, pr)
|
||||
}
|
||||
|
||||
func (tm *tracingMiddleware) AddPolicies(ctx context.Context, token, object string, subjectIDs, relations []string) error {
|
||||
ctx, span := tm.tracer.Start(ctx, "add_policies", trace.WithAttributes(
|
||||
attribute.String("object", object),
|
||||
attribute.StringSlice("subject_ids", subjectIDs),
|
||||
attribute.StringSlice("relations", relations),
|
||||
))
|
||||
defer span.End()
|
||||
|
||||
return tm.svc.AddPolicies(ctx, token, object, subjectIDs, relations)
|
||||
}
|
||||
|
||||
func (tm *tracingMiddleware) DeletePolicy(ctx context.Context, pr auth.PolicyReq) error {
|
||||
ctx, span := tm.tracer.Start(ctx, "delete_policy", trace.WithAttributes(
|
||||
attribute.String("subject", pr.Subject),
|
||||
attribute.String("subject_type", pr.SubjectType),
|
||||
attribute.String("subject_relation", pr.SubjectRelation),
|
||||
attribute.String("object", pr.Object),
|
||||
attribute.String("object_type", pr.ObjectType),
|
||||
attribute.String("relation", pr.Relation),
|
||||
attribute.String("permission", pr.Permission),
|
||||
))
|
||||
defer span.End()
|
||||
|
||||
return tm.svc.DeletePolicy(ctx, pr)
|
||||
}
|
||||
|
||||
func (tm *tracingMiddleware) DeletePolicies(ctx context.Context, token, object string, subjectIDs, relations []string) error {
|
||||
ctx, span := tm.tracer.Start(ctx, "delete_policies", trace.WithAttributes(
|
||||
attribute.String("object", object),
|
||||
attribute.StringSlice("subject_ids", subjectIDs),
|
||||
attribute.StringSlice("relations", relations),
|
||||
))
|
||||
defer span.End()
|
||||
|
||||
return tm.svc.DeletePolicies(ctx, token, object, subjectIDs, relations)
|
||||
}
|
||||
|
||||
func (tm *tracingMiddleware) ListObjects(ctx context.Context, pr auth.PolicyReq, nextPageToken string, limit int32) (auth.PolicyPage, error) {
|
||||
ctx, span := tm.tracer.Start(ctx, "list_objects", trace.WithAttributes(
|
||||
attribute.String("subject", pr.Subject),
|
||||
attribute.String("subject_type", pr.SubjectType),
|
||||
attribute.String("subject_kind", pr.SubjectKind),
|
||||
attribute.String("subject_relation", pr.SubjectRelation),
|
||||
attribute.String("object", pr.Object),
|
||||
attribute.String("object_type", pr.ObjectType),
|
||||
attribute.String("relation", pr.Relation),
|
||||
attribute.String("permission", pr.Permission),
|
||||
))
|
||||
defer span.End()
|
||||
|
||||
return tm.svc.ListObjects(ctx, pr, nextPageToken, limit)
|
||||
}
|
||||
|
||||
func (tm *tracingMiddleware) ListAllObjects(ctx context.Context, pr auth.PolicyReq) (auth.PolicyPage, error) {
|
||||
ctx, span := tm.tracer.Start(ctx, "list_all_objects", trace.WithAttributes(
|
||||
attribute.String("subject", pr.Subject),
|
||||
attribute.String("subject_type", pr.SubjectType),
|
||||
attribute.String("subject_kind", pr.SubjectKind),
|
||||
attribute.String("subject_relation", pr.SubjectRelation),
|
||||
attribute.String("object", pr.Object),
|
||||
attribute.String("object_type", pr.ObjectType),
|
||||
attribute.String("relation", pr.Relation),
|
||||
attribute.String("permission", pr.Permission),
|
||||
))
|
||||
defer span.End()
|
||||
|
||||
return tm.svc.ListAllObjects(ctx, pr)
|
||||
}
|
||||
|
||||
func (tm *tracingMiddleware) CountObjects(ctx context.Context, pr auth.PolicyReq) (int, error) {
|
||||
ctx, span := tm.tracer.Start(ctx, "count_objects", trace.WithAttributes(
|
||||
attribute.String("subject", pr.Subject),
|
||||
attribute.String("subject_type", pr.SubjectType),
|
||||
attribute.String("subject_kind", pr.SubjectKind),
|
||||
attribute.String("subject_relation", pr.SubjectRelation),
|
||||
attribute.String("object", pr.Object),
|
||||
attribute.String("object_type", pr.ObjectType),
|
||||
attribute.String("relation", pr.Relation),
|
||||
attribute.String("permission", pr.Permission),
|
||||
))
|
||||
defer span.End()
|
||||
|
||||
return tm.svc.CountObjects(ctx, pr)
|
||||
}
|
||||
|
||||
func (tm *tracingMiddleware) ListSubjects(ctx context.Context, pr auth.PolicyReq, nextPageToken string, limit int32) (auth.PolicyPage, error) {
|
||||
ctx, span := tm.tracer.Start(ctx, "list_subjects", trace.WithAttributes(
|
||||
attribute.String("subject", pr.Subject),
|
||||
attribute.String("subject_type", pr.SubjectType),
|
||||
attribute.String("subject_kind", pr.SubjectKind),
|
||||
attribute.String("subject_relation", pr.SubjectRelation),
|
||||
attribute.String("object", pr.Object),
|
||||
attribute.String("object_type", pr.ObjectType),
|
||||
attribute.String("relation", pr.Relation),
|
||||
attribute.String("permission", pr.Permission),
|
||||
))
|
||||
defer span.End()
|
||||
|
||||
return tm.svc.ListSubjects(ctx, pr, nextPageToken, limit)
|
||||
}
|
||||
|
||||
func (tm *tracingMiddleware) ListAllSubjects(ctx context.Context, pr auth.PolicyReq) (auth.PolicyPage, error) {
|
||||
ctx, span := tm.tracer.Start(ctx, "list_all_subjects", trace.WithAttributes(
|
||||
attribute.String("subject", pr.Subject),
|
||||
attribute.String("subject_type", pr.SubjectType),
|
||||
attribute.String("subject_kind", pr.SubjectKind),
|
||||
attribute.String("subject_relation", pr.SubjectRelation),
|
||||
attribute.String("object", pr.Object),
|
||||
attribute.String("object_type", pr.ObjectType),
|
||||
attribute.String("relation", pr.Relation),
|
||||
attribute.String("permission", pr.Permission),
|
||||
))
|
||||
defer span.End()
|
||||
|
||||
return tm.svc.ListAllSubjects(ctx, pr)
|
||||
}
|
||||
|
||||
func (tm *tracingMiddleware) CountSubjects(ctx context.Context, pr auth.PolicyReq) (int, error) {
|
||||
ctx, span := tm.tracer.Start(ctx, "count_subjects", trace.WithAttributes(
|
||||
attribute.String("subject", pr.Subject),
|
||||
attribute.String("subject_type", pr.SubjectType),
|
||||
attribute.String("subject_kind", pr.SubjectKind),
|
||||
attribute.String("subject_relation", pr.SubjectRelation),
|
||||
attribute.String("object", pr.Object),
|
||||
attribute.String("object_type", pr.ObjectType),
|
||||
attribute.String("relation", pr.Relation),
|
||||
attribute.String("permission", pr.Permission),
|
||||
))
|
||||
defer span.End()
|
||||
|
||||
return tm.svc.CountSubjects(ctx, pr)
|
||||
}
|
||||
+650
@@ -0,0 +1,650 @@
|
||||
// Copyright (c) Mainflux
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Code generated by protoc-gen-go-grpc. DO NOT EDIT.
|
||||
// versions:
|
||||
// - protoc-gen-go-grpc v1.3.0
|
||||
// - protoc v4.24.3
|
||||
// source: auth.proto
|
||||
|
||||
package mainflux
|
||||
|
||||
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.32.0 or later.
|
||||
const _ = grpc.SupportPackageIsVersion7
|
||||
|
||||
const (
|
||||
AuthzService_Authorize_FullMethodName = "/mainflux.AuthzService/Authorize"
|
||||
)
|
||||
|
||||
// AuthzServiceClient is the client API for AuthzService 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 AuthzServiceClient interface {
|
||||
// Authorize checks if the subject is authorized to perform
|
||||
// the action on the object.
|
||||
Authorize(ctx context.Context, in *AuthorizeReq, opts ...grpc.CallOption) (*AuthorizeRes, error)
|
||||
}
|
||||
|
||||
type authzServiceClient struct {
|
||||
cc grpc.ClientConnInterface
|
||||
}
|
||||
|
||||
func NewAuthzServiceClient(cc grpc.ClientConnInterface) AuthzServiceClient {
|
||||
return &authzServiceClient{cc}
|
||||
}
|
||||
|
||||
func (c *authzServiceClient) Authorize(ctx context.Context, in *AuthorizeReq, opts ...grpc.CallOption) (*AuthorizeRes, error) {
|
||||
out := new(AuthorizeRes)
|
||||
err := c.cc.Invoke(ctx, AuthzService_Authorize_FullMethodName, in, out, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
// AuthzServiceServer is the server API for AuthzService service.
|
||||
// All implementations must embed UnimplementedAuthzServiceServer
|
||||
// for forward compatibility
|
||||
type AuthzServiceServer interface {
|
||||
// Authorize checks if the subject is authorized to perform
|
||||
// the action on the object.
|
||||
Authorize(context.Context, *AuthorizeReq) (*AuthorizeRes, error)
|
||||
mustEmbedUnimplementedAuthzServiceServer()
|
||||
}
|
||||
|
||||
// UnimplementedAuthzServiceServer must be embedded to have forward compatible implementations.
|
||||
type UnimplementedAuthzServiceServer struct {
|
||||
}
|
||||
|
||||
func (UnimplementedAuthzServiceServer) Authorize(context.Context, *AuthorizeReq) (*AuthorizeRes, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method Authorize not implemented")
|
||||
}
|
||||
func (UnimplementedAuthzServiceServer) mustEmbedUnimplementedAuthzServiceServer() {}
|
||||
|
||||
// UnsafeAuthzServiceServer may be embedded to opt out of forward compatibility for this service.
|
||||
// Use of this interface is not recommended, as added methods to AuthzServiceServer will
|
||||
// result in compilation errors.
|
||||
type UnsafeAuthzServiceServer interface {
|
||||
mustEmbedUnimplementedAuthzServiceServer()
|
||||
}
|
||||
|
||||
func RegisterAuthzServiceServer(s grpc.ServiceRegistrar, srv AuthzServiceServer) {
|
||||
s.RegisterService(&AuthzService_ServiceDesc, srv)
|
||||
}
|
||||
|
||||
func _AuthzService_Authorize_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(AuthorizeReq)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(AuthzServiceServer).Authorize(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: AuthzService_Authorize_FullMethodName,
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(AuthzServiceServer).Authorize(ctx, req.(*AuthorizeReq))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
// AuthzService_ServiceDesc is the grpc.ServiceDesc for AuthzService service.
|
||||
// It's only intended for direct use with grpc.RegisterService,
|
||||
// and not to be introspected or modified (even as a copy)
|
||||
var AuthzService_ServiceDesc = grpc.ServiceDesc{
|
||||
ServiceName: "mainflux.AuthzService",
|
||||
HandlerType: (*AuthzServiceServer)(nil),
|
||||
Methods: []grpc.MethodDesc{
|
||||
{
|
||||
MethodName: "Authorize",
|
||||
Handler: _AuthzService_Authorize_Handler,
|
||||
},
|
||||
},
|
||||
Streams: []grpc.StreamDesc{},
|
||||
Metadata: "auth.proto",
|
||||
}
|
||||
|
||||
const (
|
||||
AuthService_Issue_FullMethodName = "/mainflux.AuthService/Issue"
|
||||
AuthService_Login_FullMethodName = "/mainflux.AuthService/Login"
|
||||
AuthService_Refresh_FullMethodName = "/mainflux.AuthService/Refresh"
|
||||
AuthService_Identify_FullMethodName = "/mainflux.AuthService/Identify"
|
||||
AuthService_Authorize_FullMethodName = "/mainflux.AuthService/Authorize"
|
||||
AuthService_AddPolicy_FullMethodName = "/mainflux.AuthService/AddPolicy"
|
||||
AuthService_DeletePolicy_FullMethodName = "/mainflux.AuthService/DeletePolicy"
|
||||
AuthService_ListObjects_FullMethodName = "/mainflux.AuthService/ListObjects"
|
||||
AuthService_ListAllObjects_FullMethodName = "/mainflux.AuthService/ListAllObjects"
|
||||
AuthService_CountObjects_FullMethodName = "/mainflux.AuthService/CountObjects"
|
||||
AuthService_ListSubjects_FullMethodName = "/mainflux.AuthService/ListSubjects"
|
||||
AuthService_ListAllSubjects_FullMethodName = "/mainflux.AuthService/ListAllSubjects"
|
||||
AuthService_CountSubjects_FullMethodName = "/mainflux.AuthService/CountSubjects"
|
||||
)
|
||||
|
||||
// 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.
|
||||
type AuthServiceClient interface {
|
||||
Issue(ctx context.Context, in *IssueReq, opts ...grpc.CallOption) (*Token, error)
|
||||
Login(ctx context.Context, in *LoginReq, opts ...grpc.CallOption) (*Token, error)
|
||||
Refresh(ctx context.Context, in *RefreshReq, opts ...grpc.CallOption) (*Token, error)
|
||||
Identify(ctx context.Context, in *IdentityReq, opts ...grpc.CallOption) (*IdentityRes, error)
|
||||
Authorize(ctx context.Context, in *AuthorizeReq, opts ...grpc.CallOption) (*AuthorizeRes, error)
|
||||
AddPolicy(ctx context.Context, in *AddPolicyReq, opts ...grpc.CallOption) (*AddPolicyRes, error)
|
||||
DeletePolicy(ctx context.Context, in *DeletePolicyReq, opts ...grpc.CallOption) (*DeletePolicyRes, error)
|
||||
ListObjects(ctx context.Context, in *ListObjectsReq, opts ...grpc.CallOption) (*ListObjectsRes, error)
|
||||
ListAllObjects(ctx context.Context, in *ListObjectsReq, opts ...grpc.CallOption) (*ListObjectsRes, error)
|
||||
CountObjects(ctx context.Context, in *CountObjectsReq, opts ...grpc.CallOption) (*CountObjectsRes, error)
|
||||
ListSubjects(ctx context.Context, in *ListSubjectsReq, opts ...grpc.CallOption) (*ListSubjectsRes, error)
|
||||
ListAllSubjects(ctx context.Context, in *ListSubjectsReq, opts ...grpc.CallOption) (*ListSubjectsRes, error)
|
||||
CountSubjects(ctx context.Context, in *CountSubjectsReq, opts ...grpc.CallOption) (*CountSubjectsRes, error)
|
||||
}
|
||||
|
||||
type authServiceClient struct {
|
||||
cc grpc.ClientConnInterface
|
||||
}
|
||||
|
||||
func NewAuthServiceClient(cc grpc.ClientConnInterface) AuthServiceClient {
|
||||
return &authServiceClient{cc}
|
||||
}
|
||||
|
||||
func (c *authServiceClient) Issue(ctx context.Context, in *IssueReq, opts ...grpc.CallOption) (*Token, error) {
|
||||
out := new(Token)
|
||||
err := c.cc.Invoke(ctx, AuthService_Issue_FullMethodName, in, out, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *authServiceClient) Login(ctx context.Context, in *LoginReq, opts ...grpc.CallOption) (*Token, error) {
|
||||
out := new(Token)
|
||||
err := c.cc.Invoke(ctx, AuthService_Login_FullMethodName, in, out, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *authServiceClient) Refresh(ctx context.Context, in *RefreshReq, opts ...grpc.CallOption) (*Token, error) {
|
||||
out := new(Token)
|
||||
err := c.cc.Invoke(ctx, AuthService_Refresh_FullMethodName, in, out, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *authServiceClient) Identify(ctx context.Context, in *IdentityReq, opts ...grpc.CallOption) (*IdentityRes, error) {
|
||||
out := new(IdentityRes)
|
||||
err := c.cc.Invoke(ctx, AuthService_Identify_FullMethodName, in, out, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *authServiceClient) Authorize(ctx context.Context, in *AuthorizeReq, opts ...grpc.CallOption) (*AuthorizeRes, error) {
|
||||
out := new(AuthorizeRes)
|
||||
err := c.cc.Invoke(ctx, AuthService_Authorize_FullMethodName, in, out, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *authServiceClient) AddPolicy(ctx context.Context, in *AddPolicyReq, opts ...grpc.CallOption) (*AddPolicyRes, error) {
|
||||
out := new(AddPolicyRes)
|
||||
err := c.cc.Invoke(ctx, AuthService_AddPolicy_FullMethodName, in, out, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *authServiceClient) DeletePolicy(ctx context.Context, in *DeletePolicyReq, opts ...grpc.CallOption) (*DeletePolicyRes, error) {
|
||||
out := new(DeletePolicyRes)
|
||||
err := c.cc.Invoke(ctx, AuthService_DeletePolicy_FullMethodName, in, out, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *authServiceClient) ListObjects(ctx context.Context, in *ListObjectsReq, opts ...grpc.CallOption) (*ListObjectsRes, error) {
|
||||
out := new(ListObjectsRes)
|
||||
err := c.cc.Invoke(ctx, AuthService_ListObjects_FullMethodName, in, out, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *authServiceClient) ListAllObjects(ctx context.Context, in *ListObjectsReq, opts ...grpc.CallOption) (*ListObjectsRes, error) {
|
||||
out := new(ListObjectsRes)
|
||||
err := c.cc.Invoke(ctx, AuthService_ListAllObjects_FullMethodName, in, out, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *authServiceClient) CountObjects(ctx context.Context, in *CountObjectsReq, opts ...grpc.CallOption) (*CountObjectsRes, error) {
|
||||
out := new(CountObjectsRes)
|
||||
err := c.cc.Invoke(ctx, AuthService_CountObjects_FullMethodName, in, out, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *authServiceClient) ListSubjects(ctx context.Context, in *ListSubjectsReq, opts ...grpc.CallOption) (*ListSubjectsRes, error) {
|
||||
out := new(ListSubjectsRes)
|
||||
err := c.cc.Invoke(ctx, AuthService_ListSubjects_FullMethodName, in, out, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *authServiceClient) ListAllSubjects(ctx context.Context, in *ListSubjectsReq, opts ...grpc.CallOption) (*ListSubjectsRes, error) {
|
||||
out := new(ListSubjectsRes)
|
||||
err := c.cc.Invoke(ctx, AuthService_ListAllSubjects_FullMethodName, in, out, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *authServiceClient) CountSubjects(ctx context.Context, in *CountSubjectsReq, opts ...grpc.CallOption) (*CountSubjectsRes, error) {
|
||||
out := new(CountSubjectsRes)
|
||||
err := c.cc.Invoke(ctx, AuthService_CountSubjects_FullMethodName, in, out, opts...)
|
||||
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
|
||||
type AuthServiceServer interface {
|
||||
Issue(context.Context, *IssueReq) (*Token, error)
|
||||
Login(context.Context, *LoginReq) (*Token, error)
|
||||
Refresh(context.Context, *RefreshReq) (*Token, error)
|
||||
Identify(context.Context, *IdentityReq) (*IdentityRes, error)
|
||||
Authorize(context.Context, *AuthorizeReq) (*AuthorizeRes, error)
|
||||
AddPolicy(context.Context, *AddPolicyReq) (*AddPolicyRes, error)
|
||||
DeletePolicy(context.Context, *DeletePolicyReq) (*DeletePolicyRes, error)
|
||||
ListObjects(context.Context, *ListObjectsReq) (*ListObjectsRes, error)
|
||||
ListAllObjects(context.Context, *ListObjectsReq) (*ListObjectsRes, error)
|
||||
CountObjects(context.Context, *CountObjectsReq) (*CountObjectsRes, error)
|
||||
ListSubjects(context.Context, *ListSubjectsReq) (*ListSubjectsRes, error)
|
||||
ListAllSubjects(context.Context, *ListSubjectsReq) (*ListSubjectsRes, error)
|
||||
CountSubjects(context.Context, *CountSubjectsReq) (*CountSubjectsRes, error)
|
||||
mustEmbedUnimplementedAuthServiceServer()
|
||||
}
|
||||
|
||||
// UnimplementedAuthServiceServer must be embedded to have forward compatible implementations.
|
||||
type UnimplementedAuthServiceServer struct {
|
||||
}
|
||||
|
||||
func (UnimplementedAuthServiceServer) Issue(context.Context, *IssueReq) (*Token, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method Issue not implemented")
|
||||
}
|
||||
func (UnimplementedAuthServiceServer) Login(context.Context, *LoginReq) (*Token, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method Login not implemented")
|
||||
}
|
||||
func (UnimplementedAuthServiceServer) Refresh(context.Context, *RefreshReq) (*Token, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method Refresh not implemented")
|
||||
}
|
||||
func (UnimplementedAuthServiceServer) Identify(context.Context, *IdentityReq) (*IdentityRes, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method Identify not implemented")
|
||||
}
|
||||
func (UnimplementedAuthServiceServer) Authorize(context.Context, *AuthorizeReq) (*AuthorizeRes, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method Authorize not implemented")
|
||||
}
|
||||
func (UnimplementedAuthServiceServer) AddPolicy(context.Context, *AddPolicyReq) (*AddPolicyRes, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method AddPolicy not implemented")
|
||||
}
|
||||
func (UnimplementedAuthServiceServer) DeletePolicy(context.Context, *DeletePolicyReq) (*DeletePolicyRes, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method DeletePolicy not implemented")
|
||||
}
|
||||
func (UnimplementedAuthServiceServer) ListObjects(context.Context, *ListObjectsReq) (*ListObjectsRes, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method ListObjects not implemented")
|
||||
}
|
||||
func (UnimplementedAuthServiceServer) ListAllObjects(context.Context, *ListObjectsReq) (*ListObjectsRes, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method ListAllObjects not implemented")
|
||||
}
|
||||
func (UnimplementedAuthServiceServer) CountObjects(context.Context, *CountObjectsReq) (*CountObjectsRes, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method CountObjects not implemented")
|
||||
}
|
||||
func (UnimplementedAuthServiceServer) ListSubjects(context.Context, *ListSubjectsReq) (*ListSubjectsRes, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method ListSubjects not implemented")
|
||||
}
|
||||
func (UnimplementedAuthServiceServer) ListAllSubjects(context.Context, *ListSubjectsReq) (*ListSubjectsRes, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method ListAllSubjects not implemented")
|
||||
}
|
||||
func (UnimplementedAuthServiceServer) CountSubjects(context.Context, *CountSubjectsReq) (*CountSubjectsRes, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method CountSubjects 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_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.(AuthServiceServer).Issue(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: AuthService_Issue_FullMethodName,
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(AuthServiceServer).Issue(ctx, req.(*IssueReq))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _AuthService_Login_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(LoginReq)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(AuthServiceServer).Login(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: AuthService_Login_FullMethodName,
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(AuthServiceServer).Login(ctx, req.(*LoginReq))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _AuthService_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.(AuthServiceServer).Refresh(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: AuthService_Refresh_FullMethodName,
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(AuthServiceServer).Refresh(ctx, req.(*RefreshReq))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _AuthService_Identify_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(IdentityReq)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(AuthServiceServer).Identify(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: AuthService_Identify_FullMethodName,
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(AuthServiceServer).Identify(ctx, req.(*IdentityReq))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _AuthService_Authorize_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(AuthorizeReq)
|
||||
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.(*AuthorizeReq))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _AuthService_AddPolicy_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(AddPolicyReq)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(AuthServiceServer).AddPolicy(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: AuthService_AddPolicy_FullMethodName,
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(AuthServiceServer).AddPolicy(ctx, req.(*AddPolicyReq))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _AuthService_DeletePolicy_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(DeletePolicyReq)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(AuthServiceServer).DeletePolicy(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: AuthService_DeletePolicy_FullMethodName,
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(AuthServiceServer).DeletePolicy(ctx, req.(*DeletePolicyReq))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _AuthService_ListObjects_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(ListObjectsReq)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(AuthServiceServer).ListObjects(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: AuthService_ListObjects_FullMethodName,
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(AuthServiceServer).ListObjects(ctx, req.(*ListObjectsReq))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _AuthService_ListAllObjects_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(ListObjectsReq)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(AuthServiceServer).ListAllObjects(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: AuthService_ListAllObjects_FullMethodName,
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(AuthServiceServer).ListAllObjects(ctx, req.(*ListObjectsReq))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _AuthService_CountObjects_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(CountObjectsReq)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(AuthServiceServer).CountObjects(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: AuthService_CountObjects_FullMethodName,
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(AuthServiceServer).CountObjects(ctx, req.(*CountObjectsReq))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _AuthService_ListSubjects_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(ListSubjectsReq)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(AuthServiceServer).ListSubjects(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: AuthService_ListSubjects_FullMethodName,
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(AuthServiceServer).ListSubjects(ctx, req.(*ListSubjectsReq))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _AuthService_ListAllSubjects_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(ListSubjectsReq)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(AuthServiceServer).ListAllSubjects(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: AuthService_ListAllSubjects_FullMethodName,
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(AuthServiceServer).ListAllSubjects(ctx, req.(*ListSubjectsReq))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _AuthService_CountSubjects_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(CountSubjectsReq)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(AuthServiceServer).CountSubjects(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: AuthService_CountSubjects_FullMethodName,
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(AuthServiceServer).CountSubjects(ctx, req.(*CountSubjectsReq))
|
||||
}
|
||||
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: "mainflux.AuthService",
|
||||
HandlerType: (*AuthServiceServer)(nil),
|
||||
Methods: []grpc.MethodDesc{
|
||||
{
|
||||
MethodName: "Issue",
|
||||
Handler: _AuthService_Issue_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "Login",
|
||||
Handler: _AuthService_Login_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "Refresh",
|
||||
Handler: _AuthService_Refresh_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "Identify",
|
||||
Handler: _AuthService_Identify_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "Authorize",
|
||||
Handler: _AuthService_Authorize_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "AddPolicy",
|
||||
Handler: _AuthService_AddPolicy_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "DeletePolicy",
|
||||
Handler: _AuthService_DeletePolicy_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "ListObjects",
|
||||
Handler: _AuthService_ListObjects_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "ListAllObjects",
|
||||
Handler: _AuthService_ListAllObjects_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "CountObjects",
|
||||
Handler: _AuthService_CountObjects_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "ListSubjects",
|
||||
Handler: _AuthService_ListSubjects_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "ListAllSubjects",
|
||||
Handler: _AuthService_ListAllSubjects_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "CountSubjects",
|
||||
Handler: _AuthService_CountSubjects_Handler,
|
||||
},
|
||||
},
|
||||
Streams: []grpc.StreamDesc{},
|
||||
Metadata: "auth.proto",
|
||||
}
|
||||
+1305
-1305
File diff suppressed because it is too large
Load Diff
+1080
-1080
File diff suppressed because it is too large
Load Diff
@@ -1,109 +0,0 @@
|
||||
// Copyright (c) Mainflux
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package mocks
|
||||
|
||||
import (
|
||||
"context"
|
||||
"strconv"
|
||||
"sync"
|
||||
|
||||
mfclients "github.com/mainflux/mainflux/pkg/clients"
|
||||
"github.com/mainflux/mainflux/pkg/errors"
|
||||
mfgroups "github.com/mainflux/mainflux/pkg/groups"
|
||||
"github.com/mainflux/mainflux/things/groups"
|
||||
upolicies "github.com/mainflux/mainflux/users/policies"
|
||||
)
|
||||
|
||||
var _ groups.Service = (*mainfluxChannels)(nil)
|
||||
|
||||
type mainfluxChannels struct {
|
||||
mu sync.Mutex
|
||||
counter uint64
|
||||
channels map[string]mfgroups.Group
|
||||
auth upolicies.AuthServiceClient
|
||||
}
|
||||
|
||||
// NewChannelsService returns Mainflux Channels service mock.
|
||||
// Only methods used by SDK are mocked.
|
||||
func NewChannelsService(channels map[string]mfgroups.Group, auth upolicies.AuthServiceClient) groups.Service {
|
||||
return &mainfluxChannels{
|
||||
channels: channels,
|
||||
auth: auth,
|
||||
}
|
||||
}
|
||||
|
||||
func (svc *mainfluxChannels) CreateGroups(ctx context.Context, token string, chs ...mfgroups.Group) ([]mfgroups.Group, error) {
|
||||
svc.mu.Lock()
|
||||
defer svc.mu.Unlock()
|
||||
|
||||
userID, err := svc.auth.Identify(ctx, &upolicies.IdentifyReq{Token: token})
|
||||
if err != nil {
|
||||
return []mfgroups.Group{}, errors.ErrAuthentication
|
||||
}
|
||||
for i := range chs {
|
||||
svc.counter++
|
||||
chs[i].Owner = userID.GetId()
|
||||
chs[i].ID = strconv.FormatUint(svc.counter, 10)
|
||||
svc.channels[chs[i].ID] = chs[i]
|
||||
}
|
||||
|
||||
return chs, nil
|
||||
}
|
||||
|
||||
func (svc *mainfluxChannels) ViewGroup(_ context.Context, owner, id string) (mfgroups.Group, error) {
|
||||
if c, ok := svc.channels[id]; ok {
|
||||
return c, nil
|
||||
}
|
||||
return mfgroups.Group{}, errors.ErrNotFound
|
||||
}
|
||||
|
||||
func (svc *mainfluxChannels) ListGroups(context.Context, string, mfgroups.GroupsPage) (mfgroups.GroupsPage, error) {
|
||||
panic("not implemented")
|
||||
}
|
||||
|
||||
func (svc *mainfluxChannels) ListMemberships(context.Context, string, string, mfgroups.GroupsPage) (mfgroups.MembershipsPage, error) {
|
||||
panic("not implemented")
|
||||
}
|
||||
|
||||
func (svc *mainfluxChannels) UpdateGroup(context.Context, string, mfgroups.Group) (mfgroups.Group, error) {
|
||||
panic("not implemented")
|
||||
}
|
||||
|
||||
func (svc *mainfluxChannels) EnableGroup(ctx context.Context, token, id string) (mfgroups.Group, error) {
|
||||
svc.mu.Lock()
|
||||
defer svc.mu.Unlock()
|
||||
|
||||
userID, err := svc.auth.Identify(ctx, &upolicies.IdentifyReq{Token: token})
|
||||
if err != nil {
|
||||
return mfgroups.Group{}, errors.ErrAuthentication
|
||||
}
|
||||
|
||||
if t, ok := svc.channels[id]; !ok || t.Owner != userID.GetId() {
|
||||
return mfgroups.Group{}, errors.ErrNotFound
|
||||
}
|
||||
if t, ok := svc.channels[id]; ok && t.Owner == userID.GetId() {
|
||||
t.Status = mfclients.EnabledStatus
|
||||
return t, nil
|
||||
}
|
||||
return mfgroups.Group{}, nil
|
||||
}
|
||||
|
||||
func (svc *mainfluxChannels) DisableGroup(ctx context.Context, token, id string) (mfgroups.Group, error) {
|
||||
svc.mu.Lock()
|
||||
defer svc.mu.Unlock()
|
||||
|
||||
userID, err := svc.auth.Identify(ctx, &upolicies.IdentifyReq{Token: token})
|
||||
if err != nil {
|
||||
return mfgroups.Group{}, errors.ErrAuthentication
|
||||
}
|
||||
|
||||
if t, ok := svc.channels[id]; !ok || t.Owner != userID.GetId() {
|
||||
return mfgroups.Group{}, errors.ErrNotFound
|
||||
}
|
||||
if t, ok := svc.channels[id]; ok && t.Owner == userID.GetId() {
|
||||
t.Status = mfclients.DisabledStatus
|
||||
return t, nil
|
||||
}
|
||||
return mfgroups.Group{}, nil
|
||||
}
|
||||
@@ -1,71 +0,0 @@
|
||||
// Copyright (c) Mainflux
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package mocks
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"sync"
|
||||
|
||||
"github.com/mainflux/mainflux/pkg/errors"
|
||||
tpolicies "github.com/mainflux/mainflux/things/policies"
|
||||
upolicies "github.com/mainflux/mainflux/users/policies"
|
||||
)
|
||||
|
||||
var _ tpolicies.Service = (*mainfluxPolicies)(nil)
|
||||
|
||||
type mainfluxPolicies struct {
|
||||
mu sync.Mutex
|
||||
auth upolicies.AuthServiceClient
|
||||
connections map[string]tpolicies.Policy
|
||||
}
|
||||
|
||||
// NewPoliciesService returns Mainflux Things Policies service mock.
|
||||
// Only methods used by SDK are mocked.
|
||||
func NewPoliciesService(auth upolicies.AuthServiceClient) tpolicies.Service {
|
||||
return &mainfluxPolicies{
|
||||
auth: auth,
|
||||
connections: make(map[string]tpolicies.Policy),
|
||||
}
|
||||
}
|
||||
|
||||
func (svc *mainfluxPolicies) AddPolicy(ctx context.Context, token string, external bool, p tpolicies.Policy) (tpolicies.Policy, error) {
|
||||
svc.mu.Lock()
|
||||
defer svc.mu.Unlock()
|
||||
|
||||
if _, err := svc.auth.Identify(ctx, &upolicies.IdentifyReq{Token: token}); err != nil {
|
||||
return tpolicies.Policy{}, errors.ErrAuthentication
|
||||
}
|
||||
svc.connections[fmt.Sprintf("%s:%s", p.Subject, p.Object)] = p
|
||||
|
||||
return p, nil
|
||||
}
|
||||
|
||||
func (svc *mainfluxPolicies) DeletePolicy(ctx context.Context, token string, p tpolicies.Policy) error {
|
||||
svc.mu.Lock()
|
||||
defer svc.mu.Unlock()
|
||||
|
||||
if _, err := svc.auth.Identify(ctx, &upolicies.IdentifyReq{Token: token}); err != nil {
|
||||
return errors.ErrAuthentication
|
||||
}
|
||||
|
||||
for _, pol := range svc.connections {
|
||||
if pol.Subject == p.Subject && pol.Object == p.Object {
|
||||
delete(svc.connections, fmt.Sprintf("%s:%s", p.Subject, p.Object))
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (svc *mainfluxPolicies) UpdatePolicy(context.Context, string, tpolicies.Policy) (tpolicies.Policy, error) {
|
||||
panic("not implemented")
|
||||
}
|
||||
|
||||
func (svc *mainfluxPolicies) Authorize(context.Context, tpolicies.AccessRequest) (tpolicies.Policy, error) {
|
||||
panic("not implemented")
|
||||
}
|
||||
|
||||
func (svc *mainfluxPolicies) ListPolicies(context.Context, string, tpolicies.Page) (tpolicies.PolicyPage, error) {
|
||||
panic("not implemented")
|
||||
}
|
||||
@@ -1,138 +0,0 @@
|
||||
// Copyright (c) Mainflux
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package mocks
|
||||
|
||||
import (
|
||||
"context"
|
||||
"strconv"
|
||||
"sync"
|
||||
|
||||
mfclients "github.com/mainflux/mainflux/pkg/clients"
|
||||
"github.com/mainflux/mainflux/pkg/errors"
|
||||
"github.com/mainflux/mainflux/things/clients"
|
||||
upolicies "github.com/mainflux/mainflux/users/policies"
|
||||
)
|
||||
|
||||
var _ clients.Service = (*mainfluxThings)(nil)
|
||||
|
||||
type mainfluxThings struct {
|
||||
mu sync.Mutex
|
||||
counter uint64
|
||||
things map[string]mfclients.Client
|
||||
auth upolicies.AuthServiceClient
|
||||
}
|
||||
|
||||
// NewThingsService returns Mainflux Things service mock.
|
||||
// Only methods used by SDK are mocked.
|
||||
func NewThingsService(things map[string]mfclients.Client, auth upolicies.AuthServiceClient) clients.Service {
|
||||
return &mainfluxThings{
|
||||
things: things,
|
||||
auth: auth,
|
||||
}
|
||||
}
|
||||
|
||||
func (svc *mainfluxThings) CreateThings(ctx context.Context, token string, ths ...mfclients.Client) ([]mfclients.Client, error) {
|
||||
svc.mu.Lock()
|
||||
defer svc.mu.Unlock()
|
||||
|
||||
userID, err := svc.auth.Identify(ctx, &upolicies.IdentifyReq{Token: token})
|
||||
if err != nil {
|
||||
return []mfclients.Client{}, errors.ErrAuthentication
|
||||
}
|
||||
for i := range ths {
|
||||
svc.counter++
|
||||
ths[i].Owner = userID.GetId()
|
||||
ths[i].ID = strconv.FormatUint(svc.counter, 10)
|
||||
ths[i].Credentials.Secret = ths[i].ID
|
||||
svc.things[ths[i].ID] = ths[i]
|
||||
}
|
||||
|
||||
return ths, nil
|
||||
}
|
||||
|
||||
func (svc *mainfluxThings) ViewClient(ctx context.Context, token, id string) (mfclients.Client, error) {
|
||||
svc.mu.Lock()
|
||||
defer svc.mu.Unlock()
|
||||
|
||||
userID, err := svc.auth.Identify(ctx, &upolicies.IdentifyReq{Token: token})
|
||||
if err != nil {
|
||||
return mfclients.Client{}, errors.ErrAuthentication
|
||||
}
|
||||
|
||||
if t, ok := svc.things[id]; ok && t.Owner == userID.GetId() {
|
||||
return t, nil
|
||||
}
|
||||
|
||||
return mfclients.Client{}, errors.ErrNotFound
|
||||
}
|
||||
|
||||
func (svc *mainfluxThings) EnableClient(ctx context.Context, token, id string) (mfclients.Client, error) {
|
||||
svc.mu.Lock()
|
||||
defer svc.mu.Unlock()
|
||||
|
||||
userID, err := svc.auth.Identify(ctx, &upolicies.IdentifyReq{Token: token})
|
||||
if err != nil {
|
||||
return mfclients.Client{}, errors.ErrAuthentication
|
||||
}
|
||||
|
||||
if t, ok := svc.things[id]; !ok || t.Owner != userID.GetId() {
|
||||
return mfclients.Client{}, errors.ErrNotFound
|
||||
}
|
||||
if t, ok := svc.things[id]; ok && t.Owner == userID.GetId() {
|
||||
t.Status = mfclients.EnabledStatus
|
||||
return t, nil
|
||||
}
|
||||
return mfclients.Client{}, nil
|
||||
}
|
||||
|
||||
func (svc *mainfluxThings) DisableClient(ctx context.Context, token, id string) (mfclients.Client, error) {
|
||||
svc.mu.Lock()
|
||||
defer svc.mu.Unlock()
|
||||
|
||||
userID, err := svc.auth.Identify(ctx, &upolicies.IdentifyReq{Token: token})
|
||||
if err != nil {
|
||||
return mfclients.Client{}, errors.ErrAuthentication
|
||||
}
|
||||
|
||||
if t, ok := svc.things[id]; !ok || t.Owner != userID.GetId() {
|
||||
return mfclients.Client{}, errors.ErrNotFound
|
||||
}
|
||||
if t, ok := svc.things[id]; ok && t.Owner == userID.GetId() {
|
||||
t.Status = mfclients.DisabledStatus
|
||||
return t, nil
|
||||
}
|
||||
return mfclients.Client{}, nil
|
||||
}
|
||||
|
||||
func (svc *mainfluxThings) UpdateClient(context.Context, string, mfclients.Client) (mfclients.Client, error) {
|
||||
panic("not implemented")
|
||||
}
|
||||
|
||||
func (svc *mainfluxThings) UpdateClientSecret(context.Context, string, string, string) (mfclients.Client, error) {
|
||||
panic("not implemented")
|
||||
}
|
||||
|
||||
func (svc *mainfluxThings) UpdateClientOwner(context.Context, string, mfclients.Client) (mfclients.Client, error) {
|
||||
panic("not implemented")
|
||||
}
|
||||
|
||||
func (svc *mainfluxThings) UpdateClientTags(context.Context, string, mfclients.Client) (mfclients.Client, error) {
|
||||
panic("not implemented")
|
||||
}
|
||||
|
||||
func (svc *mainfluxThings) ListClients(context.Context, string, mfclients.Page) (mfclients.ClientsPage, error) {
|
||||
panic("not implemented")
|
||||
}
|
||||
|
||||
func (svc *mainfluxThings) ListClientsByGroup(context.Context, string, string, mfclients.Page) (mfclients.MembersPage, error) {
|
||||
panic("not implemented")
|
||||
}
|
||||
|
||||
func (svc *mainfluxThings) Identify(context.Context, string) (string, error) {
|
||||
panic("not implemented")
|
||||
}
|
||||
|
||||
func (svc *mainfluxThings) ShareClient(ctx context.Context, token, userID, groupID, thingID string, actions []string) error {
|
||||
panic("not implemented")
|
||||
}
|
||||
@@ -1,34 +0,0 @@
|
||||
// Copyright (c) Mainflux
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package mocks
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/mainflux/mainflux/pkg/errors"
|
||||
"github.com/mainflux/mainflux/users/policies"
|
||||
"google.golang.org/grpc"
|
||||
)
|
||||
|
||||
var _ policies.AuthServiceClient = (*serviceMock)(nil)
|
||||
|
||||
type serviceMock struct {
|
||||
users map[string]string
|
||||
}
|
||||
|
||||
// NewAuthClient creates mock of users service.
|
||||
func NewAuthClient(users map[string]string) policies.AuthServiceClient {
|
||||
return &serviceMock{users}
|
||||
}
|
||||
|
||||
func (svc serviceMock) Identify(ctx context.Context, req *policies.IdentifyReq, opts ...grpc.CallOption) (*policies.IdentifyRes, error) {
|
||||
if id, ok := svc.users[req.GetToken()]; ok {
|
||||
return &policies.IdentifyRes{Id: id}, nil
|
||||
}
|
||||
return nil, errors.ErrAuthentication
|
||||
}
|
||||
|
||||
func (svc serviceMock) Authorize(ctx context.Context, req *policies.AuthorizeReq, _ ...grpc.CallOption) (r *policies.AuthorizeRes, err error) {
|
||||
panic("not implemented")
|
||||
}
|
||||
+112
-112
@@ -3,124 +3,124 @@
|
||||
|
||||
package bootstrap_test
|
||||
|
||||
import (
|
||||
"crypto/aes"
|
||||
"crypto/cipher"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"testing"
|
||||
// import (
|
||||
// "crypto/aes"
|
||||
// "crypto/cipher"
|
||||
// "encoding/json"
|
||||
// "fmt"
|
||||
// "net/http"
|
||||
// "testing"
|
||||
|
||||
"github.com/mainflux/mainflux"
|
||||
"github.com/mainflux/mainflux/bootstrap"
|
||||
"github.com/mainflux/mainflux/pkg/errors"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
// "github.com/mainflux/mainflux"
|
||||
// "github.com/mainflux/mainflux/bootstrap"
|
||||
// "github.com/mainflux/mainflux/pkg/errors"
|
||||
// "github.com/stretchr/testify/assert"
|
||||
// )
|
||||
|
||||
type readChan struct {
|
||||
ID string `json:"id"`
|
||||
Name string `json:"name,omitempty"`
|
||||
Metadata interface{} `json:"metadata,omitempty"`
|
||||
}
|
||||
// type readChan struct {
|
||||
// ID string `json:"id"`
|
||||
// Name string `json:"name,omitempty"`
|
||||
// Metadata interface{} `json:"metadata,omitempty"`
|
||||
// }
|
||||
|
||||
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"`
|
||||
}
|
||||
// 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"`
|
||||
// }
|
||||
|
||||
func dec(in []byte) ([]byte, error) {
|
||||
block, err := aes.NewCipher(encKey)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(in) < aes.BlockSize {
|
||||
return nil, errors.ErrMalformedEntity
|
||||
}
|
||||
iv := in[:aes.BlockSize]
|
||||
in = in[aes.BlockSize:]
|
||||
stream := cipher.NewCFBDecrypter(block, iv)
|
||||
stream.XORKeyStream(in, in)
|
||||
return in, nil
|
||||
}
|
||||
// func dec(in []byte) ([]byte, error) {
|
||||
// block, err := aes.NewCipher(encKey)
|
||||
// if err != nil {
|
||||
// return nil, err
|
||||
// }
|
||||
// if len(in) < aes.BlockSize {
|
||||
// return nil, errors.ErrMalformedEntity
|
||||
// }
|
||||
// iv := in[:aes.BlockSize]
|
||||
// in = in[aes.BlockSize:]
|
||||
// stream := cipher.NewCFBDecrypter(block, iv)
|
||||
// stream.XORKeyStream(in, in)
|
||||
// return in, nil
|
||||
// }
|
||||
|
||||
func TestReadConfig(t *testing.T) {
|
||||
cfg := bootstrap.Config{
|
||||
ThingID: "mf_id",
|
||||
ClientCert: "client_cert",
|
||||
ClientKey: "client_key",
|
||||
CACert: "ca_cert",
|
||||
ThingKey: "mf_key",
|
||||
Channels: []bootstrap.Channel{
|
||||
{
|
||||
ID: "mf_id",
|
||||
Name: "mf_name",
|
||||
Metadata: map[string]interface{}{"key": "value}"},
|
||||
},
|
||||
},
|
||||
Content: "content",
|
||||
}
|
||||
ret := readResp{
|
||||
ThingID: "mf_id",
|
||||
ThingKey: "mf_key",
|
||||
Channels: []readChan{
|
||||
{
|
||||
ID: "mf_id",
|
||||
Name: "mf_name",
|
||||
Metadata: map[string]interface{}{"key": "value}"},
|
||||
},
|
||||
},
|
||||
Content: "content",
|
||||
ClientCert: "client_cert",
|
||||
ClientKey: "client_key",
|
||||
CACert: "ca_cert",
|
||||
}
|
||||
// func TestReadConfig(t *testing.T) {
|
||||
// cfg := bootstrap.Config{
|
||||
// ThingID: "mf_id",
|
||||
// ClientCert: "client_cert",
|
||||
// ClientKey: "client_key",
|
||||
// CACert: "ca_cert",
|
||||
// ThingKey: "mf_key",
|
||||
// Channels: []bootstrap.Channel{
|
||||
// {
|
||||
// ID: "mf_id",
|
||||
// Name: "mf_name",
|
||||
// Metadata: map[string]interface{}{"key": "value}"},
|
||||
// },
|
||||
// },
|
||||
// Content: "content",
|
||||
// }
|
||||
// ret := readResp{
|
||||
// ThingID: "mf_id",
|
||||
// ThingKey: "mf_key",
|
||||
// Channels: []readChan{
|
||||
// {
|
||||
// ID: "mf_id",
|
||||
// Name: "mf_name",
|
||||
// Metadata: map[string]interface{}{"key": "value}"},
|
||||
// },
|
||||
// },
|
||||
// Content: "content",
|
||||
// ClientCert: "client_cert",
|
||||
// ClientKey: "client_key",
|
||||
// CACert: "ca_cert",
|
||||
// }
|
||||
|
||||
bin, err := json.Marshal(ret)
|
||||
assert.Nil(t, err, fmt.Sprintf("Marshalling expected to succeed: %s.\n", err))
|
||||
// bin, err := json.Marshal(ret)
|
||||
// assert.Nil(t, err, fmt.Sprintf("Marshalling expected to succeed: %s.\n", err))
|
||||
|
||||
reader := bootstrap.NewConfigReader(encKey)
|
||||
cases := []struct {
|
||||
desc string
|
||||
config bootstrap.Config
|
||||
enc []byte
|
||||
secret bool
|
||||
err error
|
||||
}{
|
||||
{
|
||||
desc: "read a config",
|
||||
config: cfg,
|
||||
enc: bin,
|
||||
secret: false,
|
||||
},
|
||||
{
|
||||
desc: "read encrypted config",
|
||||
config: cfg,
|
||||
enc: bin,
|
||||
secret: true,
|
||||
},
|
||||
}
|
||||
// reader := bootstrap.NewConfigReader(encKey)
|
||||
// cases := []struct {
|
||||
// desc string
|
||||
// config bootstrap.Config
|
||||
// enc []byte
|
||||
// secret bool
|
||||
// err error
|
||||
// }{
|
||||
// {
|
||||
// desc: "read a config",
|
||||
// config: cfg,
|
||||
// enc: bin,
|
||||
// secret: false,
|
||||
// },
|
||||
// {
|
||||
// desc: "read encrypted config",
|
||||
// config: cfg,
|
||||
// enc: bin,
|
||||
// secret: true,
|
||||
// },
|
||||
// }
|
||||
|
||||
for _, tc := range cases {
|
||||
res, err := reader.ReadConfig(tc.config, tc.secret)
|
||||
assert.Nil(t, err, fmt.Sprintf("Reading config to succeed: %s.\n", err))
|
||||
// for _, tc := range cases {
|
||||
// res, err := reader.ReadConfig(tc.config, tc.secret)
|
||||
// assert.Nil(t, err, fmt.Sprintf("Reading config to succeed: %s.\n", err))
|
||||
|
||||
if tc.secret {
|
||||
d, err := dec(res.([]byte))
|
||||
assert.Nil(t, err, fmt.Sprintf("Decrypting expected to succeed: %s.\n", err))
|
||||
assert.Equal(t, tc.enc, d, fmt.Sprintf("%s: expected %s got %s\n", tc.desc, tc.enc, d))
|
||||
continue
|
||||
}
|
||||
b, err := json.Marshal(res)
|
||||
assert.Nil(t, err, fmt.Sprintf("Marshalling expected to succeed: %s.\n", err))
|
||||
assert.Equal(t, tc.enc, b, fmt.Sprintf("%s: expected %s got %s\n", tc.desc, tc.enc, b))
|
||||
resp, ok := res.(mainflux.Response)
|
||||
assert.True(t, ok, "If not encrypted, reader should return response.")
|
||||
assert.False(t, resp.Empty(), fmt.Sprintf("Response should not be empty %s.", err))
|
||||
assert.Equal(t, http.StatusOK, resp.Code(), "Default config response code should be 200.")
|
||||
}
|
||||
}
|
||||
// if tc.secret {
|
||||
// d, err := dec(res.([]byte))
|
||||
// assert.Nil(t, err, fmt.Sprintf("Decrypting expected to succeed: %s.\n", err))
|
||||
// assert.Equal(t, tc.enc, d, fmt.Sprintf("%s: expected %s got %s\n", tc.desc, tc.enc, d))
|
||||
// continue
|
||||
// }
|
||||
// b, err := json.Marshal(res)
|
||||
// assert.Nil(t, err, fmt.Sprintf("Marshalling expected to succeed: %s.\n", err))
|
||||
// assert.Equal(t, tc.enc, b, fmt.Sprintf("%s: expected %s got %s\n", tc.desc, tc.enc, b))
|
||||
// resp, ok := res.(mainflux.Response)
|
||||
// assert.True(t, ok, "If not encrypted, reader should return response.")
|
||||
// assert.False(t, resp.Empty(), fmt.Sprintf("Response should not be empty %s.", err))
|
||||
// assert.Equal(t, http.StatusOK, resp.Code(), "Default config response code should be 200.")
|
||||
// }
|
||||
// }
|
||||
|
||||
+10
-10
@@ -10,9 +10,9 @@ import (
|
||||
"encoding/hex"
|
||||
"time"
|
||||
|
||||
"github.com/mainflux/mainflux"
|
||||
"github.com/mainflux/mainflux/pkg/errors"
|
||||
mfsdk "github.com/mainflux/mainflux/pkg/sdk/go"
|
||||
"github.com/mainflux/mainflux/users/policies"
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -103,14 +103,14 @@ type ConfigReader interface {
|
||||
}
|
||||
|
||||
type bootstrapService struct {
|
||||
auth policies.AuthServiceClient
|
||||
auth mainflux.AuthServiceClient
|
||||
configs ConfigRepository
|
||||
sdk mfsdk.SDK
|
||||
encKey []byte
|
||||
}
|
||||
|
||||
// New returns new Bootstrap service.
|
||||
func New(auth policies.AuthServiceClient, configs ConfigRepository, sdk mfsdk.SDK, encKey []byte) Service {
|
||||
func New(auth mainflux.AuthServiceClient, configs ConfigRepository, sdk mfsdk.SDK, encKey []byte) Service {
|
||||
return &bootstrapService{
|
||||
configs: configs,
|
||||
sdk: sdk,
|
||||
@@ -240,9 +240,9 @@ func (bs bootstrapService) UpdateConnections(ctx context.Context, token, id stri
|
||||
}
|
||||
|
||||
for _, c := range connect {
|
||||
conIDs := mfsdk.ConnectionIDs{
|
||||
ChannelIDs: []string{c},
|
||||
ThingIDs: []string{id},
|
||||
conIDs := mfsdk.Connection{
|
||||
ChannelID: c,
|
||||
ThingID: id,
|
||||
}
|
||||
if err := bs.sdk.Connect(conIDs, token); err != nil {
|
||||
return ErrThings
|
||||
@@ -309,9 +309,9 @@ func (bs bootstrapService) ChangeState(ctx context.Context, token, id string, st
|
||||
switch state {
|
||||
case Active:
|
||||
for _, c := range cfg.Channels {
|
||||
conIDs := mfsdk.ConnectionIDs{
|
||||
ChannelIDs: []string{c.ID},
|
||||
ThingIDs: []string{cfg.ThingID},
|
||||
conIDs := mfsdk.Connection{
|
||||
ChannelID: c.ID,
|
||||
ThingID: cfg.ThingID,
|
||||
}
|
||||
if err := bs.sdk.Connect(conIDs, token); err != nil {
|
||||
return ErrThings
|
||||
@@ -365,7 +365,7 @@ func (bs bootstrapService) identify(ctx context.Context, token string) (string,
|
||||
ctx, cancel := context.WithTimeout(ctx, time.Second)
|
||||
defer cancel()
|
||||
|
||||
res, err := bs.auth.Identify(ctx, &policies.IdentifyReq{Token: token})
|
||||
res, err := bs.auth.Identify(ctx, &mainflux.IdentityReq{Token: token})
|
||||
if err != nil {
|
||||
return "", errors.ErrAuthentication
|
||||
}
|
||||
|
||||
+826
-826
File diff suppressed because it is too large
Load Diff
@@ -1,109 +0,0 @@
|
||||
// Copyright (c) Mainflux
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package mocks
|
||||
|
||||
import (
|
||||
"context"
|
||||
"strconv"
|
||||
"sync"
|
||||
|
||||
mfclients "github.com/mainflux/mainflux/pkg/clients"
|
||||
"github.com/mainflux/mainflux/pkg/errors"
|
||||
mfgroups "github.com/mainflux/mainflux/pkg/groups"
|
||||
"github.com/mainflux/mainflux/things/groups"
|
||||
upolicies "github.com/mainflux/mainflux/users/policies"
|
||||
)
|
||||
|
||||
var _ groups.Service = (*mainfluxChannels)(nil)
|
||||
|
||||
type mainfluxChannels struct {
|
||||
mu sync.Mutex
|
||||
counter uint64
|
||||
channels map[string]mfgroups.Group
|
||||
auth upolicies.AuthServiceClient
|
||||
}
|
||||
|
||||
// NewChannelsService returns Mainflux Channels service mock.
|
||||
// Only methods used by SDK are mocked.
|
||||
func NewChannelsService(channels map[string]mfgroups.Group, auth upolicies.AuthServiceClient) groups.Service {
|
||||
return &mainfluxChannels{
|
||||
channels: channels,
|
||||
auth: auth,
|
||||
}
|
||||
}
|
||||
|
||||
func (svc *mainfluxChannels) CreateGroups(ctx context.Context, token string, chs ...mfgroups.Group) ([]mfgroups.Group, error) {
|
||||
svc.mu.Lock()
|
||||
defer svc.mu.Unlock()
|
||||
|
||||
userID, err := svc.auth.Identify(ctx, &upolicies.IdentifyReq{Token: token})
|
||||
if err != nil {
|
||||
return []mfgroups.Group{}, errors.ErrAuthentication
|
||||
}
|
||||
for i := range chs {
|
||||
svc.counter++
|
||||
chs[i].Owner = userID.GetId()
|
||||
chs[i].ID = strconv.FormatUint(svc.counter, 10)
|
||||
svc.channels[chs[i].ID] = chs[i]
|
||||
}
|
||||
|
||||
return chs, nil
|
||||
}
|
||||
|
||||
func (svc *mainfluxChannels) ViewGroup(_ context.Context, owner, id string) (mfgroups.Group, error) {
|
||||
if c, ok := svc.channels[id]; ok {
|
||||
return c, nil
|
||||
}
|
||||
return mfgroups.Group{}, errors.ErrNotFound
|
||||
}
|
||||
|
||||
func (svc *mainfluxChannels) ListGroups(context.Context, string, mfgroups.GroupsPage) (mfgroups.GroupsPage, error) {
|
||||
panic("not implemented")
|
||||
}
|
||||
|
||||
func (svc *mainfluxChannels) ListMemberships(context.Context, string, string, mfgroups.GroupsPage) (mfgroups.MembershipsPage, error) {
|
||||
panic("not implemented")
|
||||
}
|
||||
|
||||
func (svc *mainfluxChannels) UpdateGroup(context.Context, string, mfgroups.Group) (mfgroups.Group, error) {
|
||||
panic("not implemented")
|
||||
}
|
||||
|
||||
func (svc *mainfluxChannels) EnableGroup(ctx context.Context, token, id string) (mfgroups.Group, error) {
|
||||
svc.mu.Lock()
|
||||
defer svc.mu.Unlock()
|
||||
|
||||
userID, err := svc.auth.Identify(ctx, &upolicies.IdentifyReq{Token: token})
|
||||
if err != nil {
|
||||
return mfgroups.Group{}, errors.ErrAuthentication
|
||||
}
|
||||
|
||||
if t, ok := svc.channels[id]; !ok || t.Owner != userID.GetId() {
|
||||
return mfgroups.Group{}, errors.ErrNotFound
|
||||
}
|
||||
if t, ok := svc.channels[id]; ok && t.Owner == userID.GetId() {
|
||||
t.Status = mfclients.EnabledStatus
|
||||
return t, nil
|
||||
}
|
||||
return mfgroups.Group{}, nil
|
||||
}
|
||||
|
||||
func (svc *mainfluxChannels) DisableGroup(ctx context.Context, token, id string) (mfgroups.Group, error) {
|
||||
svc.mu.Lock()
|
||||
defer svc.mu.Unlock()
|
||||
|
||||
userID, err := svc.auth.Identify(ctx, &upolicies.IdentifyReq{Token: token})
|
||||
if err != nil {
|
||||
return mfgroups.Group{}, errors.ErrAuthentication
|
||||
}
|
||||
|
||||
if t, ok := svc.channels[id]; !ok || t.Owner != userID.GetId() {
|
||||
return mfgroups.Group{}, errors.ErrNotFound
|
||||
}
|
||||
if t, ok := svc.channels[id]; ok && t.Owner == userID.GetId() {
|
||||
t.Status = mfclients.DisabledStatus
|
||||
return t, nil
|
||||
}
|
||||
return mfgroups.Group{}, nil
|
||||
}
|
||||
@@ -1,71 +0,0 @@
|
||||
// Copyright (c) Mainflux
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package mocks
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"sync"
|
||||
|
||||
"github.com/mainflux/mainflux/pkg/errors"
|
||||
tpolicies "github.com/mainflux/mainflux/things/policies"
|
||||
upolicies "github.com/mainflux/mainflux/users/policies"
|
||||
)
|
||||
|
||||
var _ tpolicies.Service = (*mainfluxPolicies)(nil)
|
||||
|
||||
type mainfluxPolicies struct {
|
||||
mu sync.Mutex
|
||||
auth upolicies.AuthServiceClient
|
||||
connections map[string]tpolicies.Policy
|
||||
}
|
||||
|
||||
// NewPoliciesService returns Mainflux Things Policies service mock.
|
||||
// Only methods used by SDK are mocked.
|
||||
func NewPoliciesService(auth upolicies.AuthServiceClient) tpolicies.Service {
|
||||
return &mainfluxPolicies{
|
||||
auth: auth,
|
||||
connections: make(map[string]tpolicies.Policy),
|
||||
}
|
||||
}
|
||||
|
||||
func (svc *mainfluxPolicies) AddPolicy(ctx context.Context, token string, external bool, p tpolicies.Policy) (tpolicies.Policy, error) {
|
||||
svc.mu.Lock()
|
||||
defer svc.mu.Unlock()
|
||||
|
||||
if _, err := svc.auth.Identify(ctx, &upolicies.IdentifyReq{Token: token}); err != nil {
|
||||
return tpolicies.Policy{}, errors.ErrAuthentication
|
||||
}
|
||||
svc.connections[fmt.Sprintf("%s:%s", p.Subject, p.Object)] = p
|
||||
|
||||
return p, nil
|
||||
}
|
||||
|
||||
func (svc *mainfluxPolicies) DeletePolicy(ctx context.Context, token string, p tpolicies.Policy) error {
|
||||
svc.mu.Lock()
|
||||
defer svc.mu.Unlock()
|
||||
|
||||
if _, err := svc.auth.Identify(ctx, &upolicies.IdentifyReq{Token: token}); err != nil {
|
||||
return errors.ErrAuthentication
|
||||
}
|
||||
|
||||
for _, pol := range svc.connections {
|
||||
if pol.Subject == p.Subject && pol.Object == p.Object {
|
||||
delete(svc.connections, fmt.Sprintf("%s:%s", p.Subject, p.Object))
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (svc *mainfluxPolicies) UpdatePolicy(context.Context, string, tpolicies.Policy) (tpolicies.Policy, error) {
|
||||
panic("not implemented")
|
||||
}
|
||||
|
||||
func (svc *mainfluxPolicies) Authorize(context.Context, tpolicies.AccessRequest) (tpolicies.Policy, error) {
|
||||
panic("not implemented")
|
||||
}
|
||||
|
||||
func (svc *mainfluxPolicies) ListPolicies(context.Context, string, tpolicies.Page) (tpolicies.PolicyPage, error) {
|
||||
panic("not implemented")
|
||||
}
|
||||
@@ -1,138 +0,0 @@
|
||||
// Copyright (c) Mainflux
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package mocks
|
||||
|
||||
import (
|
||||
"context"
|
||||
"strconv"
|
||||
"sync"
|
||||
|
||||
mfclients "github.com/mainflux/mainflux/pkg/clients"
|
||||
"github.com/mainflux/mainflux/pkg/errors"
|
||||
"github.com/mainflux/mainflux/things/clients"
|
||||
upolicies "github.com/mainflux/mainflux/users/policies"
|
||||
)
|
||||
|
||||
var _ clients.Service = (*mainfluxThings)(nil)
|
||||
|
||||
type mainfluxThings struct {
|
||||
mu sync.Mutex
|
||||
counter uint64
|
||||
things map[string]mfclients.Client
|
||||
auth upolicies.AuthServiceClient
|
||||
}
|
||||
|
||||
// NewThingsService returns Mainflux Things service mock.
|
||||
// Only methods used by SDK are mocked.
|
||||
func NewThingsService(things map[string]mfclients.Client, auth upolicies.AuthServiceClient) clients.Service {
|
||||
return &mainfluxThings{
|
||||
things: things,
|
||||
auth: auth,
|
||||
}
|
||||
}
|
||||
|
||||
func (svc *mainfluxThings) CreateThings(ctx context.Context, token string, ths ...mfclients.Client) ([]mfclients.Client, error) {
|
||||
svc.mu.Lock()
|
||||
defer svc.mu.Unlock()
|
||||
|
||||
userID, err := svc.auth.Identify(ctx, &upolicies.IdentifyReq{Token: token})
|
||||
if err != nil {
|
||||
return []mfclients.Client{}, errors.ErrAuthentication
|
||||
}
|
||||
for i := range ths {
|
||||
svc.counter++
|
||||
ths[i].Owner = userID.GetId()
|
||||
ths[i].ID = strconv.FormatUint(svc.counter, 10)
|
||||
ths[i].Credentials.Secret = ths[i].ID
|
||||
svc.things[ths[i].ID] = ths[i]
|
||||
}
|
||||
|
||||
return ths, nil
|
||||
}
|
||||
|
||||
func (svc *mainfluxThings) ViewClient(ctx context.Context, token, id string) (mfclients.Client, error) {
|
||||
svc.mu.Lock()
|
||||
defer svc.mu.Unlock()
|
||||
|
||||
userID, err := svc.auth.Identify(ctx, &upolicies.IdentifyReq{Token: token})
|
||||
if err != nil {
|
||||
return mfclients.Client{}, errors.ErrAuthentication
|
||||
}
|
||||
|
||||
if t, ok := svc.things[id]; ok && t.Owner == userID.GetId() {
|
||||
return t, nil
|
||||
}
|
||||
|
||||
return mfclients.Client{}, errors.ErrNotFound
|
||||
}
|
||||
|
||||
func (svc *mainfluxThings) EnableClient(ctx context.Context, token, id string) (mfclients.Client, error) {
|
||||
svc.mu.Lock()
|
||||
defer svc.mu.Unlock()
|
||||
|
||||
userID, err := svc.auth.Identify(ctx, &upolicies.IdentifyReq{Token: token})
|
||||
if err != nil {
|
||||
return mfclients.Client{}, errors.ErrAuthentication
|
||||
}
|
||||
|
||||
if t, ok := svc.things[id]; !ok || t.Owner != userID.GetId() {
|
||||
return mfclients.Client{}, errors.ErrNotFound
|
||||
}
|
||||
if t, ok := svc.things[id]; ok && t.Owner == userID.GetId() {
|
||||
t.Status = mfclients.EnabledStatus
|
||||
return t, nil
|
||||
}
|
||||
return mfclients.Client{}, nil
|
||||
}
|
||||
|
||||
func (svc *mainfluxThings) DisableClient(ctx context.Context, token, id string) (mfclients.Client, error) {
|
||||
svc.mu.Lock()
|
||||
defer svc.mu.Unlock()
|
||||
|
||||
userID, err := svc.auth.Identify(ctx, &upolicies.IdentifyReq{Token: token})
|
||||
if err != nil {
|
||||
return mfclients.Client{}, errors.ErrAuthentication
|
||||
}
|
||||
|
||||
if t, ok := svc.things[id]; !ok || t.Owner != userID.GetId() {
|
||||
return mfclients.Client{}, errors.ErrNotFound
|
||||
}
|
||||
if t, ok := svc.things[id]; ok && t.Owner == userID.GetId() {
|
||||
t.Status = mfclients.DisabledStatus
|
||||
return t, nil
|
||||
}
|
||||
return mfclients.Client{}, nil
|
||||
}
|
||||
|
||||
func (svc *mainfluxThings) UpdateClient(context.Context, string, mfclients.Client) (mfclients.Client, error) {
|
||||
panic("not implemented")
|
||||
}
|
||||
|
||||
func (svc *mainfluxThings) UpdateClientSecret(context.Context, string, string, string) (mfclients.Client, error) {
|
||||
panic("not implemented")
|
||||
}
|
||||
|
||||
func (svc *mainfluxThings) UpdateClientOwner(context.Context, string, mfclients.Client) (mfclients.Client, error) {
|
||||
panic("not implemented")
|
||||
}
|
||||
|
||||
func (svc *mainfluxThings) UpdateClientTags(context.Context, string, mfclients.Client) (mfclients.Client, error) {
|
||||
panic("not implemented")
|
||||
}
|
||||
|
||||
func (svc *mainfluxThings) ListClients(context.Context, string, mfclients.Page) (mfclients.ClientsPage, error) {
|
||||
panic("not implemented")
|
||||
}
|
||||
|
||||
func (svc *mainfluxThings) ListClientsByGroup(context.Context, string, string, mfclients.Page) (mfclients.MembersPage, error) {
|
||||
panic("not implemented")
|
||||
}
|
||||
|
||||
func (svc *mainfluxThings) Identify(context.Context, string) (string, error) {
|
||||
panic("not implemented")
|
||||
}
|
||||
|
||||
func (svc *mainfluxThings) ShareClient(ctx context.Context, token, userID, groupID, thingID string, actions []string) error {
|
||||
panic("not implemented")
|
||||
}
|
||||
+8
-9
@@ -7,10 +7,10 @@ import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"github.com/mainflux/mainflux"
|
||||
"github.com/mainflux/mainflux/certs/pki"
|
||||
"github.com/mainflux/mainflux/pkg/errors"
|
||||
mfsdk "github.com/mainflux/mainflux/pkg/sdk/go"
|
||||
"github.com/mainflux/mainflux/users/policies"
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -45,14 +45,14 @@ type Service interface {
|
||||
}
|
||||
|
||||
type certsService struct {
|
||||
auth policies.AuthServiceClient
|
||||
auth mainflux.AuthServiceClient
|
||||
certsRepo Repository
|
||||
sdk mfsdk.SDK
|
||||
pki pki.Agent
|
||||
}
|
||||
|
||||
// New returns new Certs service.
|
||||
func New(auth policies.AuthServiceClient, certs Repository, sdk mfsdk.SDK, pki pki.Agent) Service {
|
||||
func New(auth mainflux.AuthServiceClient, certs Repository, sdk mfsdk.SDK, pki pki.Agent) Service {
|
||||
return &certsService{
|
||||
certsRepo: certs,
|
||||
sdk: sdk,
|
||||
@@ -80,7 +80,7 @@ type Cert struct {
|
||||
}
|
||||
|
||||
func (cs *certsService) IssueCert(ctx context.Context, token, thingID string, ttl string) (Cert, error) {
|
||||
owner, err := cs.auth.Identify(ctx, &policies.IdentifyReq{Token: token})
|
||||
owner, err := cs.auth.Identify(ctx, &mainflux.IdentityReq{Token: token})
|
||||
if err != nil {
|
||||
return Cert{}, err
|
||||
}
|
||||
@@ -113,7 +113,7 @@ func (cs *certsService) IssueCert(ctx context.Context, token, thingID string, tt
|
||||
|
||||
func (cs *certsService) RevokeCert(ctx context.Context, token, thingID string) (Revoke, error) {
|
||||
var revoke Revoke
|
||||
u, err := cs.auth.Identify(ctx, &policies.IdentifyReq{Token: token})
|
||||
u, err := cs.auth.Identify(ctx, &mainflux.IdentityReq{Token: token})
|
||||
if err != nil {
|
||||
return revoke, err
|
||||
}
|
||||
@@ -122,7 +122,6 @@ func (cs *certsService) RevokeCert(ctx context.Context, token, thingID string) (
|
||||
return revoke, errors.Wrap(ErrFailedCertRevocation, err)
|
||||
}
|
||||
|
||||
// TODO: Replace offset and limit
|
||||
offset, limit := uint64(0), uint64(10000)
|
||||
cp, err := cs.certsRepo.RetrieveByThing(ctx, u.GetId(), thing.ID, offset, limit)
|
||||
if err != nil {
|
||||
@@ -144,7 +143,7 @@ func (cs *certsService) RevokeCert(ctx context.Context, token, thingID string) (
|
||||
}
|
||||
|
||||
func (cs *certsService) ListCerts(ctx context.Context, token, thingID string, offset, limit uint64) (Page, error) {
|
||||
u, err := cs.auth.Identify(ctx, &policies.IdentifyReq{Token: token})
|
||||
u, err := cs.auth.Identify(ctx, &mainflux.IdentityReq{Token: token})
|
||||
if err != nil {
|
||||
return Page{}, err
|
||||
}
|
||||
@@ -167,7 +166,7 @@ func (cs *certsService) ListCerts(ctx context.Context, token, thingID string, of
|
||||
}
|
||||
|
||||
func (cs *certsService) ListSerials(ctx context.Context, token, thingID string, offset, limit uint64) (Page, error) {
|
||||
u, err := cs.auth.Identify(ctx, &policies.IdentifyReq{Token: token})
|
||||
u, err := cs.auth.Identify(ctx, &mainflux.IdentityReq{Token: token})
|
||||
if err != nil {
|
||||
return Page{}, err
|
||||
}
|
||||
@@ -176,7 +175,7 @@ func (cs *certsService) ListSerials(ctx context.Context, token, thingID string,
|
||||
}
|
||||
|
||||
func (cs *certsService) ViewCert(ctx context.Context, token, serialID string) (Cert, error) {
|
||||
u, err := cs.auth.Identify(ctx, &policies.IdentifyReq{Token: token})
|
||||
u, err := cs.auth.Identify(ctx, &mainflux.IdentityReq{Token: token})
|
||||
if err != nil {
|
||||
return Cert{}, err
|
||||
}
|
||||
|
||||
+11
-14
@@ -12,18 +12,17 @@ import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/go-zoo/bone"
|
||||
bsmocks "github.com/mainflux/mainflux/bootstrap/mocks"
|
||||
"github.com/go-chi/chi/v5"
|
||||
"github.com/mainflux/mainflux"
|
||||
authmocks "github.com/mainflux/mainflux/auth/mocks"
|
||||
"github.com/mainflux/mainflux/certs"
|
||||
"github.com/mainflux/mainflux/certs/mocks"
|
||||
"github.com/mainflux/mainflux/logger"
|
||||
mfclients "github.com/mainflux/mainflux/pkg/clients"
|
||||
"github.com/mainflux/mainflux/pkg/errors"
|
||||
mfsdk "github.com/mainflux/mainflux/pkg/sdk/go"
|
||||
"github.com/mainflux/mainflux/things/clients"
|
||||
httpapi "github.com/mainflux/mainflux/things/clients/api"
|
||||
thmocks "github.com/mainflux/mainflux/things/clients/mocks"
|
||||
upolicies "github.com/mainflux/mainflux/users/policies"
|
||||
"github.com/mainflux/mainflux/things"
|
||||
httpapi "github.com/mainflux/mainflux/things/api"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
@@ -49,12 +48,10 @@ const (
|
||||
var adminRelationKeys = []string{"c_update", "c_list", "c_delete", "c_share"}
|
||||
|
||||
func newService(tokens map[string]string) (certs.Service, error) {
|
||||
ac := bsmocks.NewAuthClient(map[string]string{token: email})
|
||||
|
||||
server := newThingsServer(newThingsService(ac))
|
||||
|
||||
policies := []thmocks.MockSubjectSet{{Object: "token", Relation: adminRelationKeys}}
|
||||
auth := thmocks.NewAuthService(tokens, map[string][]thmocks.MockSubjectSet{token: policies})
|
||||
auth := new(authmocks.Service)
|
||||
|
||||
config := mfsdk.Config{
|
||||
ThingsURL: server.URL,
|
||||
@@ -78,7 +75,7 @@ func newService(tokens map[string]string) (certs.Service, error) {
|
||||
return certs.New(auth, repo, sdk, pki), nil
|
||||
}
|
||||
|
||||
func newThingsService(auth upolicies.AuthServiceClient) clients.Service {
|
||||
func newThingsService(auth mainflux.AuthServiceClient) things.Service {
|
||||
ths := make(map[string]mfclients.Client, thingsNum)
|
||||
for i := 0; i < thingsNum; i++ {
|
||||
id := strconv.Itoa(i + 1)
|
||||
@@ -91,7 +88,7 @@ func newThingsService(auth upolicies.AuthServiceClient) clients.Service {
|
||||
}
|
||||
}
|
||||
|
||||
return bsmocks.NewThingsService(ths, auth)
|
||||
return things.NewService(ths, auth)
|
||||
}
|
||||
|
||||
func TestIssueCert(t *testing.T) {
|
||||
@@ -365,9 +362,9 @@ func TestViewCert(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func newThingsServer(svc clients.Service) *httptest.Server {
|
||||
func newThingsServer(svc things.Service) *httptest.Server {
|
||||
logger := logger.NewMock()
|
||||
mux := bone.New()
|
||||
httpapi.MakeHandler(svc, mux, logger, instanceID)
|
||||
mux := chi.NewMux()
|
||||
httpapi.MakeHandler(svc, nil, mux, logger, instanceID)
|
||||
return httptest.NewServer(mux)
|
||||
}
|
||||
|
||||
+33
-22
@@ -95,12 +95,13 @@ var cmdProvision = []cobra.Command{
|
||||
logError(err)
|
||||
return
|
||||
}
|
||||
|
||||
err = sdk.Connect(connIDs, args[1])
|
||||
if err != nil {
|
||||
logError(err)
|
||||
return
|
||||
for _, conn := range connIDs {
|
||||
if err := sdk.Connect(conn, args[1]); err != nil {
|
||||
logError(err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
logOK()
|
||||
},
|
||||
},
|
||||
@@ -179,18 +180,27 @@ var cmdProvision = []cobra.Command{
|
||||
}
|
||||
|
||||
// Connect things to channels - first thing to both channels, second only to first
|
||||
conIDs := mfxsdk.ConnectionIDs{
|
||||
ChannelIDs: []string{channels[0].ID, channels[1].ID},
|
||||
ThingIDs: []string{things[0].ID},
|
||||
conIDs := mfxsdk.Connection{
|
||||
ChannelID: channels[0].ID,
|
||||
ThingID: things[0].ID,
|
||||
}
|
||||
if err := sdk.Connect(conIDs, ut.AccessToken); err != nil {
|
||||
logError(err)
|
||||
return
|
||||
}
|
||||
|
||||
conIDs = mfxsdk.ConnectionIDs{
|
||||
ChannelIDs: []string{channels[0].ID},
|
||||
ThingIDs: []string{things[1].ID},
|
||||
conIDs = mfxsdk.Connection{
|
||||
ChannelID: channels[1].ID,
|
||||
ThingID: things[0].ID,
|
||||
}
|
||||
if err := sdk.Connect(conIDs, ut.AccessToken); err != nil {
|
||||
logError(err)
|
||||
return
|
||||
}
|
||||
|
||||
conIDs = mfxsdk.Connection{
|
||||
ChannelID: channels[0].ID,
|
||||
ThingID: things[1].ID,
|
||||
}
|
||||
if err := sdk.Connect(conIDs, ut.AccessToken); err != nil {
|
||||
logError(err)
|
||||
@@ -311,18 +321,18 @@ func channelsFromFile(path string) ([]mfxsdk.Channel, error) {
|
||||
return channels, nil
|
||||
}
|
||||
|
||||
func connectionsFromFile(path string) (mfxsdk.ConnectionIDs, error) {
|
||||
func connectionsFromFile(path string) ([]mfxsdk.Connection, error) {
|
||||
if _, err := os.Stat(path); os.IsNotExist(err) {
|
||||
return mfxsdk.ConnectionIDs{}, err
|
||||
return []mfxsdk.Connection{}, err
|
||||
}
|
||||
|
||||
file, err := os.OpenFile(path, os.O_RDONLY, os.ModePerm)
|
||||
if err != nil {
|
||||
return mfxsdk.ConnectionIDs{}, err
|
||||
return []mfxsdk.Connection{}, err
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
connections := mfxsdk.ConnectionIDs{}
|
||||
connections := []mfxsdk.Connection{}
|
||||
switch filepath.Ext(path) {
|
||||
case csvExt:
|
||||
reader := csv.NewReader(file)
|
||||
@@ -333,23 +343,24 @@ func connectionsFromFile(path string) (mfxsdk.ConnectionIDs, error) {
|
||||
break
|
||||
}
|
||||
if err != nil {
|
||||
return mfxsdk.ConnectionIDs{}, err
|
||||
return []mfxsdk.Connection{}, err
|
||||
}
|
||||
|
||||
if len(l) < 1 {
|
||||
return mfxsdk.ConnectionIDs{}, errors.New("empty line found in file")
|
||||
return []mfxsdk.Connection{}, errors.New("empty line found in file")
|
||||
}
|
||||
|
||||
connections.ThingIDs = append(connections.ThingIDs, l[0])
|
||||
connections.ChannelIDs = append(connections.ChannelIDs, l[1])
|
||||
connections = append(connections, mfxsdk.Connection{
|
||||
ThingID: l[0],
|
||||
ChannelID: l[1],
|
||||
})
|
||||
}
|
||||
case jsonExt:
|
||||
err := json.NewDecoder(file).Decode(&connections)
|
||||
if err != nil {
|
||||
return mfxsdk.ConnectionIDs{}, err
|
||||
return []mfxsdk.Connection{}, err
|
||||
}
|
||||
default:
|
||||
return mfxsdk.ConnectionIDs{}, err
|
||||
return []mfxsdk.Connection{}, err
|
||||
}
|
||||
|
||||
return connections, nil
|
||||
|
||||
+6
-6
@@ -252,9 +252,9 @@ var cmdThings = []cobra.Command{
|
||||
return
|
||||
}
|
||||
|
||||
connIDs := mfxsdk.ConnectionIDs{
|
||||
ChannelIDs: []string{args[1]},
|
||||
ThingIDs: []string{args[0]},
|
||||
connIDs := mfxsdk.Connection{
|
||||
ChannelID: args[1],
|
||||
ThingID: args[0],
|
||||
}
|
||||
if err := sdk.Connect(connIDs, args[2]); err != nil {
|
||||
logError(err)
|
||||
@@ -276,9 +276,9 @@ var cmdThings = []cobra.Command{
|
||||
return
|
||||
}
|
||||
|
||||
connIDs := mfxsdk.ConnectionIDs{
|
||||
ThingIDs: []string{args[0]},
|
||||
ChannelIDs: []string{args[1]},
|
||||
connIDs := mfxsdk.Connection{
|
||||
ThingID: args[0],
|
||||
ChannelID: args[1],
|
||||
}
|
||||
if err := sdk.Disconnect(connIDs, args[2]); err != nil {
|
||||
logError(err)
|
||||
|
||||
@@ -0,0 +1,221 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
v1 "github.com/authzed/authzed-go/proto/authzed/api/v1"
|
||||
"github.com/authzed/authzed-go/v1"
|
||||
"github.com/authzed/grpcutil"
|
||||
"github.com/jmoiron/sqlx"
|
||||
chclient "github.com/mainflux/callhome/pkg/client"
|
||||
"github.com/mainflux/mainflux"
|
||||
"github.com/mainflux/mainflux/auth"
|
||||
api "github.com/mainflux/mainflux/auth/api"
|
||||
grpcapi "github.com/mainflux/mainflux/auth/api/grpc"
|
||||
httpapi "github.com/mainflux/mainflux/auth/api/http"
|
||||
"github.com/mainflux/mainflux/auth/jwt"
|
||||
apostgres "github.com/mainflux/mainflux/auth/postgres"
|
||||
"github.com/mainflux/mainflux/auth/spicedb"
|
||||
"github.com/mainflux/mainflux/auth/tracing"
|
||||
"github.com/mainflux/mainflux/internal"
|
||||
jaegerclient "github.com/mainflux/mainflux/internal/clients/jaeger"
|
||||
pgclient "github.com/mainflux/mainflux/internal/clients/postgres"
|
||||
"github.com/mainflux/mainflux/internal/postgres"
|
||||
|
||||
"github.com/mainflux/mainflux/internal/env"
|
||||
"github.com/mainflux/mainflux/internal/server"
|
||||
grpcserver "github.com/mainflux/mainflux/internal/server/grpc"
|
||||
httpserver "github.com/mainflux/mainflux/internal/server/http"
|
||||
mflog "github.com/mainflux/mainflux/logger"
|
||||
"github.com/mainflux/mainflux/pkg/uuid"
|
||||
"go.opentelemetry.io/otel/trace"
|
||||
"golang.org/x/sync/errgroup"
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/grpc/credentials/insecure"
|
||||
"google.golang.org/grpc/reflection"
|
||||
)
|
||||
|
||||
const (
|
||||
svcName = "auth"
|
||||
envPrefixHTTP = "MF_AUTH_HTTP_"
|
||||
envPrefixGrpc = "MF_AUTH_GRPC_"
|
||||
envPrefixDB = "MF_AUTH_DB_"
|
||||
defDB = "auth"
|
||||
defSvcHTTPPort = "8180"
|
||||
defSvcGRPCPort = "8181"
|
||||
SpicePreSharedKey = "12345678"
|
||||
)
|
||||
|
||||
type config struct {
|
||||
LogLevel string `env:"MF_AUTH_LOG_LEVEL" envDefault:"info"`
|
||||
SecretKey string `env:"MF_AUTH_SECRET_KEY" envDefault:"secret"`
|
||||
JaegerURL string `env:"MF_JAEGER_URL" envDefault:"http://jaeger:14268/api/traces"`
|
||||
SendTelemetry bool `env:"MF_SEND_TELEMETRY" envDefault:"true"`
|
||||
InstanceID string `env:"MF_AUTH_ADAPTER_INSTANCE_ID" envDefault:""`
|
||||
AccessDuration string `env:"MF_AUTH_ACCESS_TOKEN_DURATION" envDefault:"30m"`
|
||||
RefreshDuration string `env:"MF_AUTH_REFRESH_TOKEN_DURATION" envDefault:"24h"`
|
||||
SpicedbHost string `env:"MF_SPICEDB_HOST" envDefault:"localhost"`
|
||||
SpicedbPort string `env:"MF_SPICEDB_PORT" envDefault:"50051"`
|
||||
SpicedbSchemaFile string `env:"MF_SPICEDB_SCHEMA_FILE" envDefault:"./docker/spicedb/schema.zed"`
|
||||
}
|
||||
|
||||
func main() {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
g, ctx := errgroup.WithContext(ctx)
|
||||
|
||||
cfg := config{}
|
||||
if err := env.Parse(&cfg); err != nil {
|
||||
log.Fatalf("failed to load %s configuration : %s", svcName, err.Error())
|
||||
}
|
||||
|
||||
logger, err := mflog.New(os.Stdout, cfg.LogLevel)
|
||||
if err != nil {
|
||||
logger.Fatal(fmt.Sprintf("failed to init logger: %s", err.Error()))
|
||||
}
|
||||
|
||||
var exitCode int
|
||||
defer mflog.ExitWithError(&exitCode)
|
||||
|
||||
if cfg.InstanceID == "" {
|
||||
if cfg.InstanceID, err = uuid.New().ID(); err != nil {
|
||||
logger.Error(fmt.Sprintf("failed to generate instanceID: %s", err))
|
||||
exitCode = 1
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
dbConfig := pgclient.Config{Name: defDB}
|
||||
if err := dbConfig.LoadEnv(envPrefixDB); err != nil {
|
||||
logger.Fatal(err.Error())
|
||||
}
|
||||
db, err := pgclient.SetupWithConfig(envPrefixDB, *apostgres.Migration(), dbConfig)
|
||||
if err != nil {
|
||||
logger.Error(err.Error())
|
||||
exitCode = 1
|
||||
return
|
||||
}
|
||||
defer db.Close()
|
||||
|
||||
tp, err := jaegerclient.NewProvider(svcName, cfg.JaegerURL, cfg.InstanceID)
|
||||
if err != nil {
|
||||
logger.Error(fmt.Sprintf("failed to init Jaeger: %s", err))
|
||||
exitCode = 1
|
||||
return
|
||||
}
|
||||
defer func() {
|
||||
if err := tp.Shutdown(ctx); err != nil {
|
||||
logger.Error(fmt.Sprintf("error shutting down tracer provider: %v", err))
|
||||
}
|
||||
}()
|
||||
tracer := tp.Tracer(svcName)
|
||||
|
||||
spicedbclient, err := initSpiceDB(cfg)
|
||||
if err != nil {
|
||||
logger.Error(fmt.Sprintf("failed to init spicedb grpc client : %s\n", err.Error()))
|
||||
exitCode = 1
|
||||
return
|
||||
}
|
||||
|
||||
svc := newService(db, tracer, cfg, dbConfig, logger, spicedbclient)
|
||||
|
||||
httpServerConfig := server.Config{Port: defSvcHTTPPort}
|
||||
if err := env.Parse(&httpServerConfig, env.Options{Prefix: envPrefixHTTP}); err != nil {
|
||||
logger.Error(fmt.Sprintf("failed to load %s HTTP server configuration : %s", svcName, err.Error()))
|
||||
exitCode = 1
|
||||
return
|
||||
}
|
||||
hs := httpserver.New(ctx, cancel, svcName, httpServerConfig, httpapi.MakeHandler(svc, logger, cfg.InstanceID), logger)
|
||||
|
||||
grpcServerConfig := server.Config{Port: defSvcGRPCPort}
|
||||
if err := env.Parse(&grpcServerConfig, env.Options{Prefix: envPrefixGrpc}); err != nil {
|
||||
logger.Error(fmt.Sprintf("failed to load %s gRPC server configuration : %s", svcName, err.Error()))
|
||||
exitCode = 1
|
||||
return
|
||||
}
|
||||
registerAuthServiceServer := func(srv *grpc.Server) {
|
||||
reflection.Register(srv)
|
||||
mainflux.RegisterAuthServiceServer(srv, grpcapi.NewServer(svc))
|
||||
}
|
||||
|
||||
gs := grpcserver.New(ctx, cancel, svcName, grpcServerConfig, registerAuthServiceServer, logger)
|
||||
|
||||
if cfg.SendTelemetry {
|
||||
chc := chclient.New(svcName, mainflux.Version, logger, cancel)
|
||||
go chc.CallHome(ctx)
|
||||
}
|
||||
|
||||
g.Go(func() error {
|
||||
return hs.Start()
|
||||
})
|
||||
g.Go(func() error {
|
||||
return gs.Start()
|
||||
})
|
||||
|
||||
g.Go(func() error {
|
||||
return server.StopSignalHandler(ctx, cancel, logger, svcName, hs, gs)
|
||||
})
|
||||
|
||||
if err := g.Wait(); err != nil {
|
||||
logger.Error(fmt.Sprintf("users service terminated: %s", err))
|
||||
}
|
||||
}
|
||||
|
||||
func initSpiceDB(cfg config) (*authzed.Client, error) {
|
||||
client, err := authzed.NewClient(
|
||||
fmt.Sprintf("%s:%s", cfg.SpicedbHost, cfg.SpicedbPort),
|
||||
grpc.WithTransportCredentials(insecure.NewCredentials()),
|
||||
grpcutil.WithInsecureBearerToken(SpicePreSharedKey),
|
||||
)
|
||||
if err != nil {
|
||||
return client, err
|
||||
}
|
||||
|
||||
if err := initSchema(client, cfg.SpicedbSchemaFile); err != nil {
|
||||
return client, err
|
||||
}
|
||||
|
||||
return client, nil
|
||||
}
|
||||
|
||||
func initSchema(client *authzed.Client, schemaFilePath string) error {
|
||||
schemaContent, err := os.ReadFile(schemaFilePath)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to read spice db schema file : %w", err)
|
||||
}
|
||||
|
||||
if _, err = client.SchemaServiceClient.WriteSchema(context.Background(), &v1.WriteSchemaRequest{Schema: string(schemaContent)}); err != nil {
|
||||
return fmt.Errorf("failed to create schema in spicedb : %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func newService(db *sqlx.DB, tracer trace.Tracer, cfg config, dbConfig pgclient.Config, logger mflog.Logger, spicedbClient *authzed.Client) auth.Service {
|
||||
database := postgres.NewDatabase(db, dbConfig, tracer)
|
||||
keysRepo := apostgres.New(database)
|
||||
|
||||
pa := spicedb.NewPolicyAgent(spicedbClient, logger)
|
||||
idProvider := uuid.New()
|
||||
t := jwt.New([]byte(cfg.SecretKey))
|
||||
|
||||
aDuration, err := time.ParseDuration(cfg.AccessDuration)
|
||||
if err != nil {
|
||||
logger.Error(fmt.Sprintf("failed to parse access token duration: %s", err.Error()))
|
||||
}
|
||||
rDuration, err := time.ParseDuration(cfg.RefreshDuration)
|
||||
if err != nil {
|
||||
logger.Error(fmt.Sprintf("failed to parse refresh token duration: %s", err.Error()))
|
||||
}
|
||||
|
||||
svc := auth.New(keysRepo, idProvider, t, pa, aDuration, rDuration)
|
||||
svc = api.LoggingMiddleware(svc, logger)
|
||||
counter, latency := internal.MakeMetrics("groups", "api")
|
||||
svc = api.MetricsMiddleware(svc, counter, latency)
|
||||
svc = tracing.New(svc, tracer)
|
||||
|
||||
return svc
|
||||
}
|
||||
@@ -31,7 +31,6 @@ import (
|
||||
"github.com/mainflux/mainflux/pkg/events/redis"
|
||||
mfsdk "github.com/mainflux/mainflux/pkg/sdk/go"
|
||||
"github.com/mainflux/mainflux/pkg/uuid"
|
||||
"github.com/mainflux/mainflux/users/policies"
|
||||
"go.opentelemetry.io/otel/trace"
|
||||
"golang.org/x/sync/errgroup"
|
||||
)
|
||||
@@ -158,7 +157,7 @@ func main() {
|
||||
}
|
||||
}
|
||||
|
||||
func newService(ctx context.Context, auth policies.AuthServiceClient, db *sqlx.DB, tracer trace.Tracer, logger mflog.Logger, cfg config, dbConfig pgclient.Config) (bootstrap.Service, error) {
|
||||
func newService(ctx context.Context, auth mainflux.AuthServiceClient, db *sqlx.DB, tracer trace.Tracer, logger mflog.Logger, cfg config, dbConfig pgclient.Config) (bootstrap.Service, error) {
|
||||
database := postgres.NewDatabase(db, dbConfig, tracer)
|
||||
|
||||
repoConfig := bootstrappg.NewConfigRepository(database, logger)
|
||||
|
||||
@@ -16,7 +16,6 @@ import (
|
||||
"github.com/mainflux/mainflux/internal"
|
||||
cassandraclient "github.com/mainflux/mainflux/internal/clients/cassandra"
|
||||
authclient "github.com/mainflux/mainflux/internal/clients/grpc/auth"
|
||||
thingsclient "github.com/mainflux/mainflux/internal/clients/grpc/things"
|
||||
"github.com/mainflux/mainflux/internal/env"
|
||||
"github.com/mainflux/mainflux/internal/server"
|
||||
httpserver "github.com/mainflux/mainflux/internal/server/http"
|
||||
@@ -67,17 +66,6 @@ func main() {
|
||||
}
|
||||
}
|
||||
|
||||
// Create new thing grpc client
|
||||
tc, tcHandler, err := thingsclient.Setup()
|
||||
if err != nil {
|
||||
logger.Error(err.Error())
|
||||
exitCode = 1
|
||||
return
|
||||
}
|
||||
defer tcHandler.Close()
|
||||
|
||||
logger.Info("Successfully connected to things grpc server " + tcHandler.Secure())
|
||||
|
||||
// Create new auth grpc client
|
||||
auth, authHandler, err := authclient.Setup(svcName)
|
||||
if err != nil {
|
||||
@@ -108,7 +96,7 @@ func main() {
|
||||
exitCode = 1
|
||||
return
|
||||
}
|
||||
hs := httpserver.New(ctx, cancel, svcName, httpServerConfig, api.MakeHandler(repo, tc, auth, svcName, cfg.InstanceID), logger)
|
||||
hs := httpserver.New(ctx, cancel, svcName, httpServerConfig, api.MakeHandler(repo, auth, svcName, cfg.InstanceID), logger)
|
||||
|
||||
if cfg.SendTelemetry {
|
||||
chc := chclient.New(svcName, mainflux.Version, logger, cancel)
|
||||
|
||||
+1
-2
@@ -29,7 +29,6 @@ import (
|
||||
mflog "github.com/mainflux/mainflux/logger"
|
||||
mfsdk "github.com/mainflux/mainflux/pkg/sdk/go"
|
||||
"github.com/mainflux/mainflux/pkg/uuid"
|
||||
"github.com/mainflux/mainflux/users/policies"
|
||||
"go.opentelemetry.io/otel/trace"
|
||||
"golang.org/x/sync/errgroup"
|
||||
)
|
||||
@@ -162,7 +161,7 @@ func main() {
|
||||
}
|
||||
}
|
||||
|
||||
func newService(auth policies.AuthServiceClient, db *sqlx.DB, tracer trace.Tracer, logger mflog.Logger, cfg config, dbConfig pgclient.Config, pkiAgent vault.Agent) certs.Service {
|
||||
func newService(auth mainflux.AuthServiceClient, db *sqlx.DB, tracer trace.Tracer, logger mflog.Logger, cfg config, dbConfig pgclient.Config, pkiAgent vault.Agent) certs.Service {
|
||||
database := postgres.NewDatabase(db, dbConfig, tracer)
|
||||
certsRepo := certspg.NewRepository(database, logger)
|
||||
config := mfsdk.Config{
|
||||
|
||||
+6
-6
@@ -16,7 +16,7 @@ import (
|
||||
"github.com/mainflux/mainflux/coap/api"
|
||||
"github.com/mainflux/mainflux/coap/tracing"
|
||||
"github.com/mainflux/mainflux/internal"
|
||||
thingsclient "github.com/mainflux/mainflux/internal/clients/grpc/things"
|
||||
authapi "github.com/mainflux/mainflux/internal/clients/grpc/auth"
|
||||
jaegerclient "github.com/mainflux/mainflux/internal/clients/jaeger"
|
||||
"github.com/mainflux/mainflux/internal/env"
|
||||
"github.com/mainflux/mainflux/internal/server"
|
||||
@@ -42,7 +42,7 @@ type config struct {
|
||||
BrokerURL string `env:"MF_BROKER_URL" envDefault:"nats://localhost:4222"`
|
||||
JaegerURL string `env:"MF_JAEGER_URL" envDefault:"http://jaeger:14268/api/traces"`
|
||||
SendTelemetry bool `env:"MF_SEND_TELEMETRY" envDefault:"true"`
|
||||
InstanceID string `env:"MF_COAP_ADAPTER_INSTANCE_ID" envDefault:""`
|
||||
InstanceID string `env:"MF_COAP_ADAPTER_INSTANCE_ID" envDefault:""`
|
||||
}
|
||||
|
||||
func main() {
|
||||
@@ -84,15 +84,15 @@ func main() {
|
||||
return
|
||||
}
|
||||
|
||||
tc, tcHandler, err := thingsclient.Setup()
|
||||
auth, aHandler, err := authapi.SetupAuthz(svcName)
|
||||
if err != nil {
|
||||
logger.Error(err.Error())
|
||||
exitCode = 1
|
||||
return
|
||||
}
|
||||
defer tcHandler.Close()
|
||||
defer aHandler.Close()
|
||||
|
||||
logger.Info("Successfully connected to things grpc server " + tcHandler.Secure())
|
||||
logger.Info("Successfully connected to things grpc server " + aHandler.Secure())
|
||||
|
||||
tp, err := jaegerclient.NewProvider(svcName, cfg.JaegerURL, cfg.InstanceID)
|
||||
if err != nil {
|
||||
@@ -116,7 +116,7 @@ func main() {
|
||||
defer nps.Close()
|
||||
nps = brokerstracing.NewPubSub(coapServerConfig, tracer, nps)
|
||||
|
||||
svc := coap.New(tc, nps)
|
||||
svc := coap.New(auth, nps)
|
||||
|
||||
svc = tracing.New(tracer, svc)
|
||||
|
||||
|
||||
+6
-7
@@ -16,7 +16,7 @@ import (
|
||||
"github.com/mainflux/mainflux/http/api"
|
||||
"github.com/mainflux/mainflux/http/tracing"
|
||||
"github.com/mainflux/mainflux/internal"
|
||||
thingsclient "github.com/mainflux/mainflux/internal/clients/grpc/things"
|
||||
authapi "github.com/mainflux/mainflux/internal/clients/grpc/auth"
|
||||
jaegerclient "github.com/mainflux/mainflux/internal/clients/jaeger"
|
||||
"github.com/mainflux/mainflux/internal/env"
|
||||
"github.com/mainflux/mainflux/internal/server"
|
||||
@@ -26,7 +26,6 @@ import (
|
||||
"github.com/mainflux/mainflux/pkg/messaging/brokers"
|
||||
brokerstracing "github.com/mainflux/mainflux/pkg/messaging/brokers/tracing"
|
||||
"github.com/mainflux/mainflux/pkg/uuid"
|
||||
"github.com/mainflux/mainflux/things/policies"
|
||||
"go.opentelemetry.io/otel/trace"
|
||||
"golang.org/x/sync/errgroup"
|
||||
)
|
||||
@@ -77,15 +76,15 @@ func main() {
|
||||
return
|
||||
}
|
||||
|
||||
tc, tcHandler, err := thingsclient.Setup()
|
||||
auth, aHandler, err := authapi.SetupAuthz("authz")
|
||||
if err != nil {
|
||||
logger.Error(err.Error())
|
||||
exitCode = 1
|
||||
return
|
||||
}
|
||||
defer tcHandler.Close()
|
||||
defer aHandler.Close()
|
||||
|
||||
logger.Info("Successfully connected to things grpc server " + tcHandler.Secure())
|
||||
logger.Info("Successfully connected to things grpc server " + aHandler.Secure())
|
||||
|
||||
tp, err := jaegerclient.NewProvider(svcName, cfg.JaegerURL, cfg.InstanceID)
|
||||
if err != nil {
|
||||
@@ -109,7 +108,7 @@ func main() {
|
||||
defer pub.Close()
|
||||
pub = brokerstracing.NewPublisher(httpServerConfig, tracer, pub)
|
||||
|
||||
svc := newService(pub, tc, logger, tracer)
|
||||
svc := newService(pub, auth, logger, tracer)
|
||||
|
||||
hs := httpserver.New(ctx, cancel, svcName, httpServerConfig, api.MakeHandler(svc, cfg.InstanceID), logger)
|
||||
|
||||
@@ -131,7 +130,7 @@ func main() {
|
||||
}
|
||||
}
|
||||
|
||||
func newService(pub messaging.Publisher, tc policies.AuthServiceClient, logger mflog.Logger, tracer trace.Tracer) adapter.Service {
|
||||
func newService(pub messaging.Publisher, tc mainflux.AuthzServiceClient, logger mflog.Logger, tracer trace.Tracer) adapter.Service {
|
||||
svc := adapter.New(pub, tc)
|
||||
svc = tracing.New(tracer, svc)
|
||||
svc = api.LoggingMiddleware(svc, logger)
|
||||
|
||||
@@ -15,7 +15,6 @@ import (
|
||||
"github.com/mainflux/mainflux"
|
||||
"github.com/mainflux/mainflux/internal"
|
||||
authclient "github.com/mainflux/mainflux/internal/clients/grpc/auth"
|
||||
thingsclient "github.com/mainflux/mainflux/internal/clients/grpc/things"
|
||||
influxdbclient "github.com/mainflux/mainflux/internal/clients/influxdb"
|
||||
"github.com/mainflux/mainflux/internal/env"
|
||||
"github.com/mainflux/mainflux/internal/server"
|
||||
@@ -66,16 +65,6 @@ func main() {
|
||||
}
|
||||
}
|
||||
|
||||
tc, tcHandler, err := thingsclient.Setup()
|
||||
if err != nil {
|
||||
logger.Error(err.Error())
|
||||
exitCode = 1
|
||||
return
|
||||
}
|
||||
defer tcHandler.Close()
|
||||
|
||||
logger.Info("Successfully connected to things grpc server " + tcHandler.Secure())
|
||||
|
||||
auth, authHandler, err := authclient.Setup(svcName)
|
||||
if err != nil {
|
||||
logger.Error(err.Error())
|
||||
@@ -115,7 +104,7 @@ func main() {
|
||||
exitCode = 1
|
||||
return
|
||||
}
|
||||
hs := httpserver.New(ctx, cancel, svcName, httpServerConfig, api.MakeHandler(repo, tc, auth, svcName, cfg.InstanceID), logger)
|
||||
hs := httpserver.New(ctx, cancel, svcName, httpServerConfig, api.MakeHandler(repo, auth, svcName, cfg.InstanceID), logger)
|
||||
|
||||
if cfg.SendTelemetry {
|
||||
chc := chclient.New(svcName, mainflux.Version, logger, cancel)
|
||||
|
||||
@@ -14,7 +14,6 @@ import (
|
||||
"github.com/mainflux/mainflux"
|
||||
"github.com/mainflux/mainflux/internal"
|
||||
authclient "github.com/mainflux/mainflux/internal/clients/grpc/auth"
|
||||
thingsclient "github.com/mainflux/mainflux/internal/clients/grpc/things"
|
||||
mongoclient "github.com/mainflux/mainflux/internal/clients/mongo"
|
||||
"github.com/mainflux/mainflux/internal/env"
|
||||
"github.com/mainflux/mainflux/internal/server"
|
||||
@@ -75,16 +74,6 @@ func main() {
|
||||
|
||||
repo := newService(db, logger)
|
||||
|
||||
tc, tcHandler, err := thingsclient.Setup()
|
||||
if err != nil {
|
||||
logger.Error(err.Error())
|
||||
exitCode = 1
|
||||
return
|
||||
}
|
||||
defer tcHandler.Close()
|
||||
|
||||
logger.Info("Successfully connected to things grpc server " + tcHandler.Secure())
|
||||
|
||||
auth, authHandler, err := authclient.Setup(svcName)
|
||||
if err != nil {
|
||||
logger.Fatal(err.Error())
|
||||
@@ -101,7 +90,7 @@ func main() {
|
||||
exitCode = 1
|
||||
return
|
||||
}
|
||||
hs := httpserver.New(ctx, cancel, svcName, httpServerConfig, api.MakeHandler(repo, tc, auth, svcName, cfg.InstanceID), logger)
|
||||
hs := httpserver.New(ctx, cancel, svcName, httpServerConfig, api.MakeHandler(repo, auth, svcName, cfg.InstanceID), logger)
|
||||
|
||||
if cfg.SendTelemetry {
|
||||
chc := chclient.New(svcName, mainflux.Version, logger, cancel)
|
||||
|
||||
+5
-5
@@ -16,7 +16,7 @@ import (
|
||||
"github.com/cenkalti/backoff/v4"
|
||||
chclient "github.com/mainflux/callhome/pkg/client"
|
||||
"github.com/mainflux/mainflux"
|
||||
thingsclient "github.com/mainflux/mainflux/internal/clients/grpc/things"
|
||||
authapi "github.com/mainflux/mainflux/internal/clients/grpc/auth"
|
||||
jaegerclient "github.com/mainflux/mainflux/internal/clients/jaeger"
|
||||
"github.com/mainflux/mainflux/internal/env"
|
||||
"github.com/mainflux/mainflux/internal/server"
|
||||
@@ -154,17 +154,17 @@ func main() {
|
||||
return
|
||||
}
|
||||
|
||||
tc, tcHandler, err := thingsclient.Setup()
|
||||
auth, aHandler, err := authapi.SetupAuthz("authz")
|
||||
if err != nil {
|
||||
logger.Error(err.Error())
|
||||
exitCode = 1
|
||||
return
|
||||
}
|
||||
defer tcHandler.Close()
|
||||
defer aHandler.Close()
|
||||
|
||||
logger.Info("Successfully connected to things grpc server " + tcHandler.Secure())
|
||||
logger.Info("Successfully connected to things grpc server " + aHandler.Secure())
|
||||
|
||||
h := mqtt.NewHandler([]messaging.Publisher{np}, es, logger, tc)
|
||||
h := mqtt.NewHandler([]messaging.Publisher{np}, es, logger, auth)
|
||||
h = mqtttracing.NewHandler(tracer, h)
|
||||
|
||||
if cfg.SendTelemetry {
|
||||
|
||||
@@ -15,7 +15,6 @@ import (
|
||||
"github.com/mainflux/mainflux"
|
||||
"github.com/mainflux/mainflux/internal"
|
||||
authclient "github.com/mainflux/mainflux/internal/clients/grpc/auth"
|
||||
thingsclient "github.com/mainflux/mainflux/internal/clients/grpc/things"
|
||||
pgclient "github.com/mainflux/mainflux/internal/clients/postgres"
|
||||
"github.com/mainflux/mainflux/internal/env"
|
||||
"github.com/mainflux/mainflux/internal/server"
|
||||
@@ -32,7 +31,7 @@ const (
|
||||
svcName = "postgres-reader"
|
||||
envPrefixDB = "MF_POSTGRES_"
|
||||
envPrefixHTTP = "MF_POSTGRES_READER_HTTP_"
|
||||
defDB = "messages"
|
||||
defDB = "mainflux"
|
||||
defSvcHTTPPort = "9009"
|
||||
)
|
||||
|
||||
@@ -67,7 +66,7 @@ func main() {
|
||||
}
|
||||
}
|
||||
|
||||
dbConfig := pgclient.Config{Name: defDB}
|
||||
dbConfig := pgclient.Config{}
|
||||
if err := dbConfig.LoadEnv(envPrefixDB); err != nil {
|
||||
logger.Error(err.Error())
|
||||
exitCode = 1
|
||||
@@ -81,16 +80,6 @@ func main() {
|
||||
}
|
||||
defer db.Close()
|
||||
|
||||
tc, tcHandler, err := thingsclient.Setup()
|
||||
if err != nil {
|
||||
logger.Error(err.Error())
|
||||
exitCode = 1
|
||||
return
|
||||
}
|
||||
defer tcHandler.Close()
|
||||
|
||||
logger.Info("Successfully connected to things grpc server " + tcHandler.Secure())
|
||||
|
||||
auth, authHandler, err := authclient.Setup(svcName)
|
||||
if err != nil {
|
||||
logger.Error(err.Error())
|
||||
@@ -109,7 +98,7 @@ func main() {
|
||||
exitCode = 1
|
||||
return
|
||||
}
|
||||
hs := httpserver.New(ctx, cancel, svcName, httpServerConfig, api.MakeHandler(repo, tc, auth, svcName, cfg.InstanceID), logger)
|
||||
hs := httpserver.New(ctx, cancel, svcName, httpServerConfig, api.MakeHandler(repo, auth, svcName, cfg.InstanceID), logger)
|
||||
|
||||
if cfg.SendTelemetry {
|
||||
chc := chclient.New(svcName, mainflux.Version, logger, cancel)
|
||||
|
||||
+16
-105
@@ -11,10 +11,10 @@ import (
|
||||
"log"
|
||||
"os"
|
||||
"reflect"
|
||||
"strconv"
|
||||
|
||||
chclient "github.com/mainflux/callhome/pkg/client"
|
||||
"github.com/mainflux/mainflux"
|
||||
"github.com/mainflux/mainflux/internal/env"
|
||||
"github.com/mainflux/mainflux/internal/server"
|
||||
httpserver "github.com/mainflux/mainflux/internal/server/http"
|
||||
mflog "github.com/mainflux/mainflux/logger"
|
||||
@@ -29,74 +29,25 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
svcName = "provision"
|
||||
|
||||
defLogLevel = "error"
|
||||
defConfigFile = "config.toml"
|
||||
defTLS = "false"
|
||||
defServerCert = ""
|
||||
defServerKey = ""
|
||||
defThingsURL = "http://localhost"
|
||||
defUsersURL = "http://localhost"
|
||||
defHTTPPort = "9016"
|
||||
defMfUser = "test@example.com"
|
||||
defMfPass = "test"
|
||||
defMfAPIKey = ""
|
||||
defMfBSURL = "http://localhost:9000/things/configs"
|
||||
defMfWhiteListURL = "http://localhost:9000/things/state"
|
||||
defMfCertsURL = "http://localhost:9019"
|
||||
defProvisionCerts = "false"
|
||||
defProvisionBS = "true"
|
||||
defBSAutoWhitelist = "true"
|
||||
defBSContent = ""
|
||||
defCertsHoursValid = "2400h"
|
||||
defSendTelemetry = "true"
|
||||
defInstanceID = ""
|
||||
|
||||
envConfigFile = "MF_PROVISION_CONFIG_FILE"
|
||||
envLogLevel = "MF_PROVISION_LOG_LEVEL"
|
||||
envHTTPPort = "MF_PROVISION_HTTP_PORT"
|
||||
envTLS = "MF_PROVISION_ENV_CLIENTS_TLS"
|
||||
envServerCert = "MF_PROVISION_SERVER_CERT"
|
||||
envServerKey = "MF_PROVISION_SERVER_KEY"
|
||||
envUsersURL = "MF_PROVISION_USERS_LOCATION"
|
||||
envThingsURL = "MF_PROVISION_THINGS_LOCATION"
|
||||
envMfUser = "MF_PROVISION_USER"
|
||||
envMfPass = "MF_PROVISION_PASS"
|
||||
envMfAPIKey = "MF_PROVISION_API_KEY"
|
||||
envMfCertsURL = "MF_PROVISION_CERTS_SVC_URL"
|
||||
envProvisionCerts = "MF_PROVISION_X509_PROVISIONING"
|
||||
envMfBSURL = "MF_PROVISION_BS_SVC_URL"
|
||||
envMfBSWhiteListURL = "MF_PROVISION_BS_SVC_WHITELIST_URL"
|
||||
envProvisionBS = "MF_PROVISION_BS_CONFIG_PROVISIONING"
|
||||
envBSAutoWhiteList = "MF_PROVISION_BS_AUTO_WHITELIST"
|
||||
envBSContent = "MF_PROVISION_BS_CONTENT"
|
||||
envCertsHoursValid = "MF_PROVISION_CERTS_HOURS_VALID"
|
||||
envSendTelemetry = "MF_SEND_TELEMETRY"
|
||||
envInstanceID = "MF_PROVISION_INSTANCE_ID"
|
||||
|
||||
svcName = "provision"
|
||||
contentType = "application/json"
|
||||
)
|
||||
|
||||
var (
|
||||
errMissingConfigFile = errors.New("missing config file setting")
|
||||
errFailLoadingConfigFile = errors.New("failed to load config from file")
|
||||
errFailGettingAutoWhiteList = errors.New("failed to get auto whitelist setting")
|
||||
errFailGettingCertSettings = errors.New("failed to get certificate file setting")
|
||||
errFailGettingTLSConf = errors.New("failed to get TLS setting")
|
||||
errFailGettingProvBS = errors.New("failed to get BS url setting")
|
||||
errFailedToReadBootstrapContent = errors.New("failed to read bootstrap content from envs")
|
||||
errFailedToSetupCallHome = errors.New("failed to set up callhome")
|
||||
)
|
||||
|
||||
func main() {
|
||||
cfg, err := loadConfig()
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
g, ctx := errgroup.WithContext(ctx)
|
||||
|
||||
cfg, err := loadConfig()
|
||||
if err != nil {
|
||||
log.Fatalf(err.Error())
|
||||
log.Fatalf(fmt.Sprintf("failed to load %s configuration : %s", svcName, err))
|
||||
}
|
||||
|
||||
logger, err := mflog.New(os.Stdout, cfg.Server.LogLevel)
|
||||
if err != nil {
|
||||
log.Fatalf(err.Error())
|
||||
@@ -105,9 +56,8 @@ func main() {
|
||||
var exitCode int
|
||||
defer mflog.ExitWithError(&exitCode)
|
||||
|
||||
instanceID := mainflux.Env(envInstanceID, defInstanceID)
|
||||
if instanceID == "" {
|
||||
if instanceID, err = uuid.New().ID(); err != nil {
|
||||
if cfg.InstanceID == "" {
|
||||
if cfg.InstanceID, err = uuid.New().ID(); err != nil {
|
||||
logger.Error(fmt.Sprintf("failed to generate instanceID: %s", err))
|
||||
exitCode = 1
|
||||
return
|
||||
@@ -137,7 +87,7 @@ func main() {
|
||||
svc = api.NewLoggingMiddleware(svc, logger)
|
||||
|
||||
httpServerConfig := server.Config{Host: "", Port: cfg.Server.HTTPPort, KeyFile: cfg.Server.ServerKey, CertFile: cfg.Server.ServerCert}
|
||||
hs := httpserver.New(ctx, cancel, svcName, httpServerConfig, api.MakeHandler(svc, logger, instanceID), logger)
|
||||
hs := httpserver.New(ctx, cancel, svcName, httpServerConfig, api.MakeHandler(svc, logger, cfg.InstanceID), logger)
|
||||
|
||||
if cfg.SendTelemetry {
|
||||
chc := chclient.New(svcName, mainflux.Version, logger, cancel)
|
||||
@@ -170,60 +120,26 @@ func loadConfigFromFile(file string) (provision.Config, error) {
|
||||
}
|
||||
|
||||
func loadConfig() (provision.Config, error) {
|
||||
tls, err := strconv.ParseBool(mainflux.Env(envTLS, defTLS))
|
||||
if err != nil {
|
||||
return provision.Config{}, errors.Wrap(errFailGettingTLSConf, err)
|
||||
}
|
||||
provisionX509, err := strconv.ParseBool(mainflux.Env(envProvisionCerts, defProvisionCerts))
|
||||
if err != nil {
|
||||
return provision.Config{}, errors.Wrap(errFailGettingCertSettings, err)
|
||||
}
|
||||
provisionBS, err := strconv.ParseBool(mainflux.Env(envProvisionBS, defProvisionBS))
|
||||
if err != nil {
|
||||
return provision.Config{}, errors.Wrap(errFailGettingProvBS, fmt.Errorf(" for %s", envProvisionBS))
|
||||
cfg := provision.Config{}
|
||||
if err := env.Parse(&cfg); err != nil {
|
||||
return provision.Config{}, err
|
||||
}
|
||||
|
||||
autoWhiteList, err := strconv.ParseBool(mainflux.Env(envBSAutoWhiteList, defBSAutoWhitelist))
|
||||
if err != nil {
|
||||
return provision.Config{}, errors.Wrap(errFailGettingAutoWhiteList, fmt.Errorf(" for %s", envBSAutoWhiteList))
|
||||
}
|
||||
if autoWhiteList && !provisionBS {
|
||||
if cfg.Bootstrap.AutoWhiteList && !cfg.Bootstrap.Provision {
|
||||
return provision.Config{}, errors.New("Can't auto whitelist if auto config save is off")
|
||||
}
|
||||
|
||||
var content map[string]interface{}
|
||||
if c := mainflux.Env(envBSContent, defBSContent); c != "" {
|
||||
if err = json.Unmarshal([]byte(c), &content); err != nil {
|
||||
if cfg.BSContent != "" {
|
||||
if err := json.Unmarshal([]byte(cfg.BSContent), &content); err != nil {
|
||||
return provision.Config{}, errFailedToReadBootstrapContent
|
||||
}
|
||||
}
|
||||
|
||||
cfg := provision.Config{
|
||||
Server: provision.ServiceConf{
|
||||
LogLevel: mainflux.Env(envLogLevel, defLogLevel),
|
||||
ServerCert: mainflux.Env(envServerCert, defServerCert),
|
||||
ServerKey: mainflux.Env(envServerKey, defServerKey),
|
||||
HTTPPort: mainflux.Env(envHTTPPort, defHTTPPort),
|
||||
MfBSURL: mainflux.Env(envMfBSURL, defMfBSURL),
|
||||
MfWhiteListURL: mainflux.Env(envMfBSWhiteListURL, defMfWhiteListURL),
|
||||
MfCertsURL: mainflux.Env(envMfCertsURL, defMfCertsURL),
|
||||
MfUser: mainflux.Env(envMfUser, defMfUser),
|
||||
MfPass: mainflux.Env(envMfPass, defMfPass),
|
||||
MfAPIKey: mainflux.Env(envMfAPIKey, defMfAPIKey),
|
||||
ThingsURL: mainflux.Env(envThingsURL, defThingsURL),
|
||||
UsersURL: mainflux.Env(envUsersURL, defUsersURL),
|
||||
TLS: tls,
|
||||
},
|
||||
Cert: provision.Cert{
|
||||
TTL: mainflux.Env(envCertsHoursValid, defCertsHoursValid),
|
||||
},
|
||||
cfg = provision.Config{
|
||||
Bootstrap: provision.Bootstrap{
|
||||
X509Provision: provisionX509,
|
||||
Provision: provisionBS,
|
||||
AutoWhiteList: autoWhiteList,
|
||||
Content: content,
|
||||
Content: content,
|
||||
},
|
||||
|
||||
// This is default conf for provision if there is no config file
|
||||
Channels: []mfgroups.Group{
|
||||
{
|
||||
@@ -242,11 +158,6 @@ func loadConfig() (provision.Config, error) {
|
||||
},
|
||||
}
|
||||
|
||||
cfg.File = mainflux.Env(envConfigFile, defConfigFile)
|
||||
cfg.SendTelemetry, err = strconv.ParseBool(mainflux.Env(envSendTelemetry, defSendTelemetry))
|
||||
if err != nil {
|
||||
return cfg, errors.Wrap(errFailedToSetupCallHome, err)
|
||||
}
|
||||
return cfg, nil
|
||||
}
|
||||
|
||||
|
||||
@@ -31,7 +31,6 @@ import (
|
||||
brokerstracing "github.com/mainflux/mainflux/pkg/messaging/brokers/tracing"
|
||||
"github.com/mainflux/mainflux/pkg/ulid"
|
||||
"github.com/mainflux/mainflux/pkg/uuid"
|
||||
"github.com/mainflux/mainflux/users/policies"
|
||||
"go.opentelemetry.io/otel/trace"
|
||||
"golang.org/x/sync/errgroup"
|
||||
)
|
||||
@@ -158,7 +157,7 @@ func main() {
|
||||
}
|
||||
}
|
||||
|
||||
func newService(db *sqlx.DB, tracer trace.Tracer, auth policies.AuthServiceClient, c config, sc mfsmpp.Config, logger mflog.Logger) notifiers.Service {
|
||||
func newService(db *sqlx.DB, tracer trace.Tracer, auth mainflux.AuthServiceClient, c config, sc mfsmpp.Config, logger mflog.Logger) notifiers.Service {
|
||||
database := notifierpg.NewDatabase(db, tracer)
|
||||
repo := tracing.New(tracer, notifierpg.New(database))
|
||||
idp := ulid.New()
|
||||
|
||||
@@ -32,7 +32,6 @@ import (
|
||||
brokerstracing "github.com/mainflux/mainflux/pkg/messaging/brokers/tracing"
|
||||
"github.com/mainflux/mainflux/pkg/ulid"
|
||||
"github.com/mainflux/mainflux/pkg/uuid"
|
||||
"github.com/mainflux/mainflux/users/policies"
|
||||
"go.opentelemetry.io/otel/trace"
|
||||
"golang.org/x/sync/errgroup"
|
||||
)
|
||||
@@ -168,7 +167,7 @@ func main() {
|
||||
}
|
||||
}
|
||||
|
||||
func newService(db *sqlx.DB, tracer trace.Tracer, auth policies.AuthServiceClient, c config, ec email.Config, logger mflog.Logger) (notifiers.Service, error) {
|
||||
func newService(db *sqlx.DB, tracer trace.Tracer, auth mainflux.AuthServiceClient, c config, ec email.Config, logger mflog.Logger) (notifiers.Service, error) {
|
||||
database := notifierpg.NewDatabase(db, tracer)
|
||||
repo := tracing.New(tracer, notifierpg.New(database))
|
||||
idp := ulid.New()
|
||||
|
||||
+55
-65
@@ -11,10 +11,11 @@ import (
|
||||
"os"
|
||||
"time"
|
||||
|
||||
callhome "github.com/mainflux/callhome/pkg/client"
|
||||
|
||||
"github.com/go-chi/chi/v5"
|
||||
"github.com/go-redis/redis/v8"
|
||||
"github.com/go-zoo/bone"
|
||||
"github.com/jmoiron/sqlx"
|
||||
chclient "github.com/mainflux/callhome/pkg/client"
|
||||
"github.com/mainflux/mainflux"
|
||||
"github.com/mainflux/mainflux/internal"
|
||||
authclient "github.com/mainflux/mainflux/internal/clients/grpc/auth"
|
||||
@@ -22,34 +23,27 @@ import (
|
||||
pgclient "github.com/mainflux/mainflux/internal/clients/postgres"
|
||||
redisclient "github.com/mainflux/mainflux/internal/clients/redis"
|
||||
"github.com/mainflux/mainflux/internal/env"
|
||||
mfgroups "github.com/mainflux/mainflux/internal/groups"
|
||||
gapi "github.com/mainflux/mainflux/internal/groups/api"
|
||||
gpostgres "github.com/mainflux/mainflux/internal/groups/postgres"
|
||||
gtracing "github.com/mainflux/mainflux/internal/groups/tracing"
|
||||
"github.com/mainflux/mainflux/internal/postgres"
|
||||
"github.com/mainflux/mainflux/internal/server"
|
||||
grpcserver "github.com/mainflux/mainflux/internal/server/grpc"
|
||||
httpserver "github.com/mainflux/mainflux/internal/server/http"
|
||||
mflog "github.com/mainflux/mainflux/logger"
|
||||
gpostgres "github.com/mainflux/mainflux/pkg/groups/postgres"
|
||||
"github.com/mainflux/mainflux/pkg/groups"
|
||||
"github.com/mainflux/mainflux/pkg/uuid"
|
||||
"github.com/mainflux/mainflux/things/clients"
|
||||
capi "github.com/mainflux/mainflux/things/clients/api"
|
||||
thcache "github.com/mainflux/mainflux/things/clients/cache"
|
||||
thevents "github.com/mainflux/mainflux/things/clients/events"
|
||||
cpostgres "github.com/mainflux/mainflux/things/clients/postgres"
|
||||
localusers "github.com/mainflux/mainflux/things/clients/standalone"
|
||||
ctracing "github.com/mainflux/mainflux/things/clients/tracing"
|
||||
"github.com/mainflux/mainflux/things/groups"
|
||||
gapi "github.com/mainflux/mainflux/things/groups/api"
|
||||
chevents "github.com/mainflux/mainflux/things/groups/events"
|
||||
gtracing "github.com/mainflux/mainflux/things/groups/tracing"
|
||||
tpolicies "github.com/mainflux/mainflux/things/policies"
|
||||
papi "github.com/mainflux/mainflux/things/policies/api"
|
||||
grpcapi "github.com/mainflux/mainflux/things/policies/api/grpc"
|
||||
httpapi "github.com/mainflux/mainflux/things/policies/api/http"
|
||||
pcache "github.com/mainflux/mainflux/things/policies/cache"
|
||||
pevents "github.com/mainflux/mainflux/things/policies/events"
|
||||
ppostgres "github.com/mainflux/mainflux/things/policies/postgres"
|
||||
ppracing "github.com/mainflux/mainflux/things/policies/tracing"
|
||||
"github.com/mainflux/mainflux/things"
|
||||
"github.com/mainflux/mainflux/things/api"
|
||||
grpcapi "github.com/mainflux/mainflux/things/api/grpc"
|
||||
httpapi "github.com/mainflux/mainflux/things/api/http"
|
||||
thcache "github.com/mainflux/mainflux/things/cache"
|
||||
thevents "github.com/mainflux/mainflux/things/events"
|
||||
thingspg "github.com/mainflux/mainflux/things/postgres"
|
||||
upolicies "github.com/mainflux/mainflux/users/policies"
|
||||
|
||||
localusers "github.com/mainflux/mainflux/things/standalone"
|
||||
ctracing "github.com/mainflux/mainflux/things/tracing"
|
||||
"go.opentelemetry.io/otel/trace"
|
||||
"golang.org/x/sync/errgroup"
|
||||
"google.golang.org/grpc"
|
||||
@@ -60,6 +54,7 @@ const (
|
||||
svcName = "things"
|
||||
envPrefixDB = "MF_THINGS_DB_"
|
||||
envPrefixCache = "MF_THINGS_CACHE_"
|
||||
envPrefixES = "MF_THINGS_ES_"
|
||||
envPrefixHTTP = "MF_THINGS_HTTP_"
|
||||
envPrefixGRPC = "MF_THINGS_AUTH_GRPC_"
|
||||
defDB = "things"
|
||||
@@ -74,7 +69,7 @@ type config struct {
|
||||
JaegerURL string `env:"MF_JAEGER_URL" envDefault:"http://jaeger:14268/api/traces"`
|
||||
CacheKeyDuration string `env:"MF_THINGS_CACHE_KEY_DURATION" envDefault:"10m"`
|
||||
SendTelemetry bool `env:"MF_SEND_TELEMETRY" envDefault:"true"`
|
||||
InstanceID string `env:"MF_THINGS_INSTANCE_ID" envDefault:""`
|
||||
InstanceID string `env:"MF_THINGS_INSTANCE_ID" envDefault:""`
|
||||
ESURL string `env:"MF_THINGS_ES_URL" envDefault:"redis://localhost:6379/0"`
|
||||
}
|
||||
|
||||
@@ -109,7 +104,11 @@ func main() {
|
||||
if err := dbConfig.LoadEnv(envPrefixDB); err != nil {
|
||||
logger.Fatal(err.Error())
|
||||
}
|
||||
db, err := pgclient.SetupWithConfig(envPrefixDB, *thingspg.Migration(), dbConfig)
|
||||
|
||||
tm := thingspg.Migration()
|
||||
gm := gpostgres.Migration()
|
||||
tm.Migrations = append(tm.Migrations, gm.Migrations...)
|
||||
db, err := pgclient.SetupWithConfig(envPrefixDB, *tm, dbConfig)
|
||||
if err != nil {
|
||||
logger.Error(err.Error())
|
||||
exitCode = 1
|
||||
@@ -139,7 +138,17 @@ func main() {
|
||||
}
|
||||
defer cacheclient.Close()
|
||||
|
||||
var auth upolicies.AuthServiceClient
|
||||
// Setup new redis event store client
|
||||
esclient, err := redisclient.Setup(envPrefixES)
|
||||
if err != nil {
|
||||
logger.Error(err.Error())
|
||||
exitCode = 1
|
||||
return
|
||||
}
|
||||
defer esclient.Close()
|
||||
|
||||
var auth mainflux.AuthServiceClient
|
||||
|
||||
switch cfg.StandaloneID != "" && cfg.StandaloneToken != "" {
|
||||
case true:
|
||||
auth = localusers.NewAuthService(cfg.StandaloneID, cfg.StandaloneToken)
|
||||
@@ -156,9 +165,9 @@ func main() {
|
||||
logger.Info("Successfully connected to auth grpc server " + authHandler.Secure())
|
||||
}
|
||||
|
||||
csvc, gsvc, psvc, err := newService(ctx, db, dbConfig, auth, cacheclient, cfg, tracer, logger)
|
||||
csvc, gsvc, err := newService(ctx, db, dbConfig, auth, cacheclient, esclient, cfg.CacheKeyDuration, cfg.ESURL, tracer, logger)
|
||||
if err != nil {
|
||||
logger.Error(fmt.Sprintf("failed to create %s service: %s", svcName, err))
|
||||
logger.Error(fmt.Sprintf("failed to create services: %s", err))
|
||||
exitCode = 1
|
||||
return
|
||||
}
|
||||
@@ -169,10 +178,8 @@ func main() {
|
||||
exitCode = 1
|
||||
return
|
||||
}
|
||||
mux := bone.New()
|
||||
hsp := httpserver.New(ctx, cancel, "things-policies", httpServerConfig, httpapi.MakeHandler(csvc, psvc, mux, logger), logger)
|
||||
hsc := httpserver.New(ctx, cancel, "things-clients", httpServerConfig, capi.MakeHandler(csvc, mux, logger, cfg.InstanceID), logger)
|
||||
hsg := httpserver.New(ctx, cancel, "things-groups", httpServerConfig, gapi.MakeHandler(gsvc, mux, logger), logger)
|
||||
mux := chi.NewRouter()
|
||||
httpSvc := httpserver.New(ctx, cancel, "things-clients", httpServerConfig, httpapi.MakeHandler(csvc, gsvc, mux, logger, cfg.InstanceID), logger)
|
||||
|
||||
grpcServerConfig := server.Config{Port: defSvcAuthGRPCPort}
|
||||
if err := env.Parse(&grpcServerConfig, env.Options{Prefix: envPrefixGRPC}); err != nil {
|
||||
@@ -180,20 +187,20 @@ func main() {
|
||||
exitCode = 1
|
||||
return
|
||||
}
|
||||
registerThingsServiceServer := func(srv *grpc.Server) {
|
||||
regiterAuthzServer := func(srv *grpc.Server) {
|
||||
reflection.Register(srv)
|
||||
tpolicies.RegisterAuthServiceServer(srv, grpcapi.NewServer(csvc, psvc))
|
||||
mainflux.RegisterAuthzServiceServer(srv, grpcapi.NewServer(csvc))
|
||||
}
|
||||
gs := grpcserver.New(ctx, cancel, svcName, grpcServerConfig, registerThingsServiceServer, logger)
|
||||
gs := grpcserver.New(ctx, cancel, svcName, grpcServerConfig, regiterAuthzServer, logger)
|
||||
|
||||
if cfg.SendTelemetry {
|
||||
chc := chclient.New(svcName, mainflux.Version, logger, cancel)
|
||||
chc := callhome.New(svcName, mainflux.Version, logger, cancel)
|
||||
go chc.CallHome(ctx)
|
||||
}
|
||||
|
||||
// Start all servers
|
||||
g.Go(func() error {
|
||||
return hsp.Start()
|
||||
return httpSvc.Start()
|
||||
})
|
||||
|
||||
g.Go(func() error {
|
||||
@@ -201,7 +208,7 @@ func main() {
|
||||
})
|
||||
|
||||
g.Go(func() error {
|
||||
return server.StopSignalHandler(ctx, cancel, logger, svcName, hsc, hsg, hsp, gs)
|
||||
return server.StopSignalHandler(ctx, cancel, logger, svcName, httpSvc)
|
||||
})
|
||||
|
||||
if err := g.Wait(); err != nil {
|
||||
@@ -209,54 +216,37 @@ func main() {
|
||||
}
|
||||
}
|
||||
|
||||
func newService(ctx context.Context, db *sqlx.DB, dbConfig pgclient.Config, auth upolicies.AuthServiceClient, cacheClient *redis.Client, cfg config, tracer trace.Tracer, logger mflog.Logger) (clients.Service, groups.Service, tpolicies.Service, error) {
|
||||
func newService(ctx context.Context, db *sqlx.DB, dbConfig pgclient.Config, auth mainflux.AuthServiceClient, cacheClient *redis.Client, esClient *redis.Client, keyDuration, esURL string, tracer trace.Tracer, logger mflog.Logger) (things.Service, groups.Service, error) {
|
||||
database := postgres.NewDatabase(db, dbConfig, tracer)
|
||||
cRepo := cpostgres.NewRepository(database)
|
||||
cRepo := thingspg.NewRepository(database)
|
||||
gRepo := gpostgres.New(database)
|
||||
pRepo := ppostgres.NewRepository(database)
|
||||
|
||||
idp := uuid.New()
|
||||
|
||||
kDuration, err := time.ParseDuration(cfg.CacheKeyDuration)
|
||||
kDuration, err := time.ParseDuration(keyDuration)
|
||||
if err != nil {
|
||||
logger.Error(fmt.Sprintf("failed to parse cache key duration: %s", err.Error()))
|
||||
}
|
||||
|
||||
policyCache := pcache.NewCache(cacheClient, kDuration)
|
||||
thingCache := thcache.NewCache(cacheClient, kDuration)
|
||||
|
||||
psvc := tpolicies.NewService(auth, pRepo, policyCache, idp)
|
||||
csvc := clients.NewService(auth, psvc, cRepo, gRepo, thingCache, idp)
|
||||
gsvc := groups.NewService(auth, psvc, gRepo, idp)
|
||||
csvc := things.NewService(auth, cRepo, gRepo, thingCache, idp)
|
||||
gsvc := mfgroups.NewService(gRepo, idp, auth)
|
||||
|
||||
csvc, err = thevents.NewEventStoreMiddleware(ctx, csvc, cfg.ESURL)
|
||||
csvc, err = thevents.NewEventStoreMiddleware(ctx, csvc, esURL)
|
||||
if err != nil {
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
|
||||
gsvc, err = chevents.NewEventStoreMiddleware(ctx, gsvc, cfg.ESURL)
|
||||
if err != nil {
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
|
||||
psvc, err = pevents.NewEventStoreMiddleware(ctx, psvc, cfg.ESURL)
|
||||
if err != nil {
|
||||
return nil, nil, nil, err
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
csvc = ctracing.New(csvc, tracer)
|
||||
csvc = capi.LoggingMiddleware(csvc, logger)
|
||||
csvc = api.LoggingMiddleware(csvc, logger)
|
||||
counter, latency := internal.MakeMetrics(svcName, "api")
|
||||
csvc = capi.MetricsMiddleware(csvc, counter, latency)
|
||||
csvc = api.MetricsMiddleware(csvc, counter, latency)
|
||||
|
||||
gsvc = gtracing.New(gsvc, tracer)
|
||||
gsvc = gapi.LoggingMiddleware(gsvc, logger)
|
||||
counter, latency = internal.MakeMetrics(fmt.Sprintf("%s_groups", svcName), "api")
|
||||
gsvc = gapi.MetricsMiddleware(gsvc, counter, latency)
|
||||
psvc = ppracing.New(psvc, tracer)
|
||||
psvc = papi.LoggingMiddleware(psvc, logger)
|
||||
counter, latency = internal.MakeMetrics(fmt.Sprintf("%s_policies", svcName), "api")
|
||||
psvc = papi.MetricsMiddleware(psvc, counter, latency)
|
||||
|
||||
return csvc, gsvc, psvc, nil
|
||||
return csvc, gsvc, err
|
||||
}
|
||||
|
||||
@@ -15,7 +15,6 @@ import (
|
||||
"github.com/mainflux/mainflux"
|
||||
"github.com/mainflux/mainflux/internal"
|
||||
authclient "github.com/mainflux/mainflux/internal/clients/grpc/auth"
|
||||
thingsclient "github.com/mainflux/mainflux/internal/clients/grpc/things"
|
||||
pgclient "github.com/mainflux/mainflux/internal/clients/postgres"
|
||||
"github.com/mainflux/mainflux/internal/env"
|
||||
"github.com/mainflux/mainflux/internal/server"
|
||||
@@ -91,23 +90,13 @@ func main() {
|
||||
|
||||
logger.Info("Successfully connected to auth grpc server " + authHandler.Secure())
|
||||
|
||||
tc, tcHandler, err := thingsclient.Setup()
|
||||
if err != nil {
|
||||
logger.Error(err.Error())
|
||||
exitCode = 1
|
||||
return
|
||||
}
|
||||
defer tcHandler.Close()
|
||||
|
||||
logger.Info("Successfully connected to things grpc server " + tcHandler.Secure())
|
||||
|
||||
httpServerConfig := server.Config{Port: defSvcHTTPPort}
|
||||
if err := env.Parse(&httpServerConfig, env.Options{Prefix: envPrefixHTTP}); err != nil {
|
||||
logger.Error(fmt.Sprintf("failed to load %s HTTP server configuration : %s", svcName, err))
|
||||
exitCode = 1
|
||||
return
|
||||
}
|
||||
hs := httpserver.New(ctx, cancel, svcName, httpServerConfig, api.MakeHandler(repo, tc, auth, svcName, cfg.InstanceID), logger)
|
||||
hs := httpserver.New(ctx, cancel, svcName, httpServerConfig, api.MakeHandler(repo, auth, svcName, cfg.InstanceID), logger)
|
||||
|
||||
if cfg.SendTelemetry {
|
||||
chc := chclient.New(svcName, mainflux.Version, logger, cancel)
|
||||
|
||||
+3
-4
@@ -26,14 +26,13 @@ import (
|
||||
"github.com/mainflux/mainflux/pkg/messaging/brokers"
|
||||
brokerstracing "github.com/mainflux/mainflux/pkg/messaging/brokers/tracing"
|
||||
"github.com/mainflux/mainflux/pkg/uuid"
|
||||
localusers "github.com/mainflux/mainflux/things/clients/standalone"
|
||||
localusers "github.com/mainflux/mainflux/things/standalone"
|
||||
"github.com/mainflux/mainflux/twins"
|
||||
"github.com/mainflux/mainflux/twins/api"
|
||||
twapi "github.com/mainflux/mainflux/twins/api/http"
|
||||
"github.com/mainflux/mainflux/twins/events"
|
||||
twmongodb "github.com/mainflux/mainflux/twins/mongodb"
|
||||
"github.com/mainflux/mainflux/twins/tracing"
|
||||
"github.com/mainflux/mainflux/users/policies"
|
||||
"go.mongodb.org/mongo-driver/mongo"
|
||||
"go.opentelemetry.io/otel/trace"
|
||||
"golang.org/x/sync/errgroup"
|
||||
@@ -120,7 +119,7 @@ func main() {
|
||||
}()
|
||||
tracer := tp.Tracer(svcName)
|
||||
|
||||
var auth policies.AuthServiceClient
|
||||
var auth mainflux.AuthServiceClient
|
||||
switch cfg.StandaloneID != "" && cfg.StandaloneToken != "" {
|
||||
case true:
|
||||
auth = localusers.NewAuthService(cfg.StandaloneID, cfg.StandaloneToken)
|
||||
@@ -172,7 +171,7 @@ func main() {
|
||||
}
|
||||
}
|
||||
|
||||
func newService(ctx context.Context, id string, ps messaging.PubSub, cfg config, users policies.AuthServiceClient, tracer trace.Tracer, db *mongo.Database, cacheclient *redis.Client, logger mflog.Logger) (twins.Service, error) {
|
||||
func newService(ctx context.Context, id string, ps messaging.PubSub, cfg config, users mainflux.AuthServiceClient, tracer trace.Tracer, db *mongo.Database, cacheclient *redis.Client, logger mflog.Logger) (twins.Service, error) {
|
||||
twinRepo := twmongodb.NewTwinRepository(db)
|
||||
twinRepo = tracing.TwinRepositoryMiddleware(tracer, twinRepo)
|
||||
|
||||
|
||||
+46
-76
@@ -12,47 +12,37 @@ import (
|
||||
"regexp"
|
||||
"time"
|
||||
|
||||
"github.com/go-zoo/bone"
|
||||
"github.com/go-chi/chi/v5"
|
||||
"github.com/jmoiron/sqlx"
|
||||
chclient "github.com/mainflux/callhome/pkg/client"
|
||||
"github.com/mainflux/mainflux"
|
||||
"github.com/mainflux/mainflux/internal"
|
||||
authclient "github.com/mainflux/mainflux/internal/clients/grpc/auth"
|
||||
jaegerclient "github.com/mainflux/mainflux/internal/clients/jaeger"
|
||||
pgclient "github.com/mainflux/mainflux/internal/clients/postgres"
|
||||
"github.com/mainflux/mainflux/internal/email"
|
||||
"github.com/mainflux/mainflux/internal/env"
|
||||
mfgroups "github.com/mainflux/mainflux/internal/groups"
|
||||
gapi "github.com/mainflux/mainflux/internal/groups/api"
|
||||
gevents "github.com/mainflux/mainflux/internal/groups/events"
|
||||
gpostgres "github.com/mainflux/mainflux/internal/groups/postgres"
|
||||
gtracing "github.com/mainflux/mainflux/internal/groups/tracing"
|
||||
"github.com/mainflux/mainflux/internal/postgres"
|
||||
"github.com/mainflux/mainflux/internal/server"
|
||||
grpcserver "github.com/mainflux/mainflux/internal/server/grpc"
|
||||
httpserver "github.com/mainflux/mainflux/internal/server/http"
|
||||
mflog "github.com/mainflux/mainflux/logger"
|
||||
mfclients "github.com/mainflux/mainflux/pkg/clients"
|
||||
gpostgres "github.com/mainflux/mainflux/pkg/groups/postgres"
|
||||
"github.com/mainflux/mainflux/pkg/groups"
|
||||
"github.com/mainflux/mainflux/pkg/uuid"
|
||||
"github.com/mainflux/mainflux/users/clients"
|
||||
capi "github.com/mainflux/mainflux/users/clients/api"
|
||||
"github.com/mainflux/mainflux/users/clients/emailer"
|
||||
uevents "github.com/mainflux/mainflux/users/clients/events"
|
||||
uclients "github.com/mainflux/mainflux/users/clients/postgres"
|
||||
ctracing "github.com/mainflux/mainflux/users/clients/tracing"
|
||||
"github.com/mainflux/mainflux/users/groups"
|
||||
gapi "github.com/mainflux/mainflux/users/groups/api"
|
||||
gevents "github.com/mainflux/mainflux/users/groups/events"
|
||||
gtracing "github.com/mainflux/mainflux/users/groups/tracing"
|
||||
"github.com/mainflux/mainflux/users"
|
||||
capi "github.com/mainflux/mainflux/users/api"
|
||||
"github.com/mainflux/mainflux/users/emailer"
|
||||
uevents "github.com/mainflux/mainflux/users/events"
|
||||
"github.com/mainflux/mainflux/users/hasher"
|
||||
"github.com/mainflux/mainflux/users/jwt"
|
||||
"github.com/mainflux/mainflux/users/policies"
|
||||
papi "github.com/mainflux/mainflux/users/policies/api"
|
||||
grpcapi "github.com/mainflux/mainflux/users/policies/api/grpc"
|
||||
httpapi "github.com/mainflux/mainflux/users/policies/api/http"
|
||||
pevents "github.com/mainflux/mainflux/users/policies/events"
|
||||
ppostgres "github.com/mainflux/mainflux/users/policies/postgres"
|
||||
ptracing "github.com/mainflux/mainflux/users/policies/tracing"
|
||||
clientspg "github.com/mainflux/mainflux/users/postgres"
|
||||
ctracing "github.com/mainflux/mainflux/users/tracing"
|
||||
"go.opentelemetry.io/otel/trace"
|
||||
"golang.org/x/sync/errgroup"
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/grpc/reflection"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -120,9 +110,15 @@ func main() {
|
||||
|
||||
dbConfig := pgclient.Config{Name: defDB}
|
||||
if err := dbConfig.LoadEnv(envPrefixDB); err != nil {
|
||||
logger.Fatal(err.Error())
|
||||
logger.Error(fmt.Sprintf("failed to load %s database configuration : %s", svcName, err.Error()))
|
||||
exitCode = 1
|
||||
return
|
||||
}
|
||||
db, err := pgclient.SetupWithConfig(envPrefixDB, *clientspg.Migration(), dbConfig)
|
||||
|
||||
cm := clientspg.Migration()
|
||||
gm := gpostgres.Migration()
|
||||
cm.Migrations = append(cm.Migrations, gm.Migrations...)
|
||||
db, err := pgclient.SetupWithConfig(envPrefixDB, *cm, dbConfig)
|
||||
if err != nil {
|
||||
logger.Error(err.Error())
|
||||
exitCode = 1
|
||||
@@ -143,9 +139,18 @@ func main() {
|
||||
}()
|
||||
tracer := tp.Tracer(svcName)
|
||||
|
||||
csvc, gsvc, psvc, err := newService(ctx, db, dbConfig, tracer, cfg, ec, logger)
|
||||
auth, authHandler, err := authclient.Setup(svcName)
|
||||
if err != nil {
|
||||
logger.Error(fmt.Sprintf("failed to create %s service: %s", svcName, err.Error()))
|
||||
logger.Error(err.Error())
|
||||
exitCode = 1
|
||||
return
|
||||
}
|
||||
defer authHandler.Close()
|
||||
logger.Info("Successfully connected to auth grpc server " + authHandler.Secure())
|
||||
|
||||
csvc, gsvc, err := newService(ctx, auth, db, dbConfig, tracer, cfg, ec, logger)
|
||||
if err != nil {
|
||||
logger.Error(fmt.Sprintf("failed to setup service: %s", err))
|
||||
exitCode = 1
|
||||
return
|
||||
}
|
||||
@@ -156,22 +161,9 @@ func main() {
|
||||
exitCode = 1
|
||||
return
|
||||
}
|
||||
mux := bone.New()
|
||||
hsc := httpserver.New(ctx, cancel, svcName, httpServerConfig, capi.MakeHandler(csvc, mux, logger, cfg.InstanceID), logger)
|
||||
hsg := httpserver.New(ctx, cancel, svcName, httpServerConfig, gapi.MakeHandler(gsvc, mux, logger), logger)
|
||||
hsp := httpserver.New(ctx, cancel, svcName, httpServerConfig, httpapi.MakeHandler(psvc, mux, logger), logger)
|
||||
|
||||
grpcServerConfig := server.Config{Port: defSvcGRPCPort}
|
||||
if err := env.Parse(&grpcServerConfig, env.Options{Prefix: envPrefixGrpc}); err != nil {
|
||||
logger.Error(fmt.Sprintf("failed to load %s gRPC server configuration : %s", svcName, err.Error()))
|
||||
exitCode = 1
|
||||
return
|
||||
}
|
||||
registerAuthServiceServer := func(srv *grpc.Server) {
|
||||
reflection.Register(srv)
|
||||
policies.RegisterAuthServiceServer(srv, grpcapi.NewServer(csvc, psvc))
|
||||
}
|
||||
gs := grpcserver.New(ctx, cancel, svcName, grpcServerConfig, registerAuthServiceServer, logger)
|
||||
mux := chi.NewRouter()
|
||||
httpSrv := httpserver.New(ctx, cancel, svcName, httpServerConfig, capi.MakeHandler(csvc, gsvc, mux, logger, cfg.InstanceID), logger)
|
||||
|
||||
if cfg.SendTelemetry {
|
||||
chc := chclient.New(svcName, mainflux.Version, logger, cancel)
|
||||
@@ -179,14 +171,11 @@ func main() {
|
||||
}
|
||||
|
||||
g.Go(func() error {
|
||||
return hsp.Start()
|
||||
})
|
||||
g.Go(func() error {
|
||||
return gs.Start()
|
||||
return httpSrv.Start()
|
||||
})
|
||||
|
||||
g.Go(func() error {
|
||||
return server.StopSignalHandler(ctx, cancel, logger, svcName, hsc, hsg, hsp, gs)
|
||||
return server.StopSignalHandler(ctx, cancel, logger, svcName, httpSrv)
|
||||
})
|
||||
|
||||
if err := g.Wait(); err != nil {
|
||||
@@ -194,44 +183,29 @@ func main() {
|
||||
}
|
||||
}
|
||||
|
||||
func newService(ctx context.Context, db *sqlx.DB, dbConfig pgclient.Config, tracer trace.Tracer, c config, ec email.Config, logger mflog.Logger) (clients.Service, groups.Service, policies.Service, error) {
|
||||
func newService(ctx context.Context, auth mainflux.AuthServiceClient, db *sqlx.DB, dbConfig pgclient.Config, tracer trace.Tracer, c config, ec email.Config, logger mflog.Logger) (users.Service, groups.Service, error) {
|
||||
database := postgres.NewDatabase(db, dbConfig, tracer)
|
||||
cRepo := uclients.NewRepository(database)
|
||||
cRepo := clientspg.NewRepository(database)
|
||||
gRepo := gpostgres.New(database)
|
||||
pRepo := ppostgres.NewRepository(database)
|
||||
|
||||
idp := uuid.New()
|
||||
hsr := hasher.New()
|
||||
|
||||
aDuration, err := time.ParseDuration(c.AccessDuration)
|
||||
if err != nil {
|
||||
logger.Error(fmt.Sprintf("failed to parse access token duration: %s", err.Error()))
|
||||
}
|
||||
rDuration, err := time.ParseDuration(c.RefreshDuration)
|
||||
if err != nil {
|
||||
logger.Error(fmt.Sprintf("failed to parse refresh token duration: %s", err.Error()))
|
||||
}
|
||||
tokenizer := jwt.NewRepository([]byte(c.SecretKey), aDuration, rDuration)
|
||||
|
||||
emailer, err := emailer.New(c.ResetURL, &ec)
|
||||
if err != nil {
|
||||
logger.Error(fmt.Sprintf("failed to configure e-mailing util: %s", err.Error()))
|
||||
}
|
||||
csvc := clients.NewService(cRepo, pRepo, tokenizer, emailer, hsr, idp, c.PassRegex)
|
||||
gsvc := groups.NewService(gRepo, pRepo, tokenizer, idp)
|
||||
psvc := policies.NewService(pRepo, tokenizer, idp)
|
||||
|
||||
csvc := users.NewService(cRepo, auth, emailer, hsr, idp, c.PassRegex)
|
||||
gsvc := mfgroups.NewService(gRepo, idp, auth)
|
||||
|
||||
csvc, err = uevents.NewEventStoreMiddleware(ctx, csvc, c.ESURL)
|
||||
if err != nil {
|
||||
return nil, nil, nil, err
|
||||
return nil, nil, err
|
||||
}
|
||||
gsvc, err = gevents.NewEventStoreMiddleware(ctx, gsvc, c.ESURL)
|
||||
if err != nil {
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
psvc, err = pevents.NewEventStoreMiddleware(ctx, psvc, c.ESURL)
|
||||
if err != nil {
|
||||
return nil, nil, nil, err
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
csvc = ctracing.New(csvc, tracer)
|
||||
@@ -244,18 +218,14 @@ func newService(ctx context.Context, db *sqlx.DB, dbConfig pgclient.Config, trac
|
||||
counter, latency = internal.MakeMetrics("groups", "api")
|
||||
gsvc = gapi.MetricsMiddleware(gsvc, counter, latency)
|
||||
|
||||
psvc = ptracing.New(psvc, tracer)
|
||||
psvc = papi.LoggingMiddleware(psvc, logger)
|
||||
counter, latency = internal.MakeMetrics("policies", "api")
|
||||
psvc = papi.MetricsMiddleware(psvc, counter, latency)
|
||||
|
||||
if err := createAdmin(ctx, c, cRepo, hsr, csvc); err != nil {
|
||||
logger.Error(fmt.Sprintf("failed to create admin client: %s", err))
|
||||
}
|
||||
return csvc, gsvc, psvc, nil
|
||||
|
||||
return csvc, gsvc, err
|
||||
}
|
||||
|
||||
func createAdmin(ctx context.Context, c config, crepo uclients.Repository, hsr clients.Hasher, svc clients.Service) error {
|
||||
func createAdmin(ctx context.Context, c config, crepo clientspg.Repository, hsr users.Hasher, svc users.Service) error {
|
||||
id, err := uuid.New().ID()
|
||||
if err != nil {
|
||||
return err
|
||||
|
||||
+6
-7
@@ -13,7 +13,7 @@ import (
|
||||
chclient "github.com/mainflux/callhome/pkg/client"
|
||||
"github.com/mainflux/mainflux"
|
||||
"github.com/mainflux/mainflux/internal"
|
||||
thingsclient "github.com/mainflux/mainflux/internal/clients/grpc/things"
|
||||
authapi "github.com/mainflux/mainflux/internal/clients/grpc/auth"
|
||||
jaegerclient "github.com/mainflux/mainflux/internal/clients/jaeger"
|
||||
"github.com/mainflux/mainflux/internal/env"
|
||||
"github.com/mainflux/mainflux/internal/server"
|
||||
@@ -23,7 +23,6 @@ import (
|
||||
"github.com/mainflux/mainflux/pkg/messaging/brokers"
|
||||
brokerstracing "github.com/mainflux/mainflux/pkg/messaging/brokers/tracing"
|
||||
"github.com/mainflux/mainflux/pkg/uuid"
|
||||
"github.com/mainflux/mainflux/things/policies"
|
||||
adapter "github.com/mainflux/mainflux/ws"
|
||||
"github.com/mainflux/mainflux/ws/api"
|
||||
"github.com/mainflux/mainflux/ws/tracing"
|
||||
@@ -77,15 +76,15 @@ func main() {
|
||||
return
|
||||
}
|
||||
|
||||
tc, tcHandler, err := thingsclient.Setup()
|
||||
auth, aHandler, err := authapi.SetupAuthz("authz")
|
||||
if err != nil {
|
||||
logger.Error(err.Error())
|
||||
exitCode = 1
|
||||
return
|
||||
}
|
||||
defer internal.Close(logger, tcHandler)
|
||||
defer aHandler.Close()
|
||||
|
||||
logger.Info("Successfully connected to things grpc server " + tcHandler.Secure())
|
||||
logger.Info("Successfully connected to things grpc server " + aHandler.Secure())
|
||||
|
||||
tp, err := jaegerclient.NewProvider(svcName, cfg.JaegerURL, cfg.InstanceID)
|
||||
if err != nil {
|
||||
@@ -109,7 +108,7 @@ func main() {
|
||||
defer nps.Close()
|
||||
nps = brokerstracing.NewPubSub(httpServerConfig, tracer, nps)
|
||||
|
||||
svc := newService(tc, nps, logger, tracer)
|
||||
svc := newService(auth, nps, logger, tracer)
|
||||
|
||||
hs := httpserver.New(ctx, cancel, svcName, httpServerConfig, api.MakeHandler(ctx, svc, logger, cfg.InstanceID), logger)
|
||||
|
||||
@@ -131,7 +130,7 @@ func main() {
|
||||
}
|
||||
}
|
||||
|
||||
func newService(tc policies.AuthServiceClient, nps messaging.PubSub, logger mflog.Logger, tracer trace.Tracer) adapter.Service {
|
||||
func newService(tc mainflux.AuthzServiceClient, nps messaging.PubSub, logger mflog.Logger, tracer trace.Tracer) adapter.Service {
|
||||
svc := adapter.New(tc, nps)
|
||||
svc = tracing.New(tracer, svc)
|
||||
svc = api.LoggingMiddleware(svc, logger)
|
||||
|
||||
+25
-19
@@ -11,9 +11,9 @@ import (
|
||||
"fmt"
|
||||
"sync"
|
||||
|
||||
"github.com/mainflux/mainflux"
|
||||
"github.com/mainflux/mainflux/pkg/errors"
|
||||
"github.com/mainflux/mainflux/pkg/messaging"
|
||||
"github.com/mainflux/mainflux/things/policies"
|
||||
)
|
||||
|
||||
const chansPrefix = "channels"
|
||||
@@ -39,13 +39,13 @@ var _ Service = (*adapterService)(nil)
|
||||
|
||||
// Observers is a map of maps,.
|
||||
type adapterService struct {
|
||||
auth policies.AuthServiceClient
|
||||
auth mainflux.AuthzServiceClient
|
||||
pubsub messaging.PubSub
|
||||
obsLock sync.Mutex
|
||||
}
|
||||
|
||||
// New instantiates the CoAP adapter implementation.
|
||||
func New(auth policies.AuthServiceClient, pubsub messaging.PubSub) Service {
|
||||
func New(auth mainflux.AuthzServiceClient, pubsub messaging.PubSub) Service {
|
||||
as := &adapterService{
|
||||
auth: auth,
|
||||
pubsub: pubsub,
|
||||
@@ -56,11 +56,13 @@ func New(auth policies.AuthServiceClient, pubsub messaging.PubSub) Service {
|
||||
}
|
||||
|
||||
func (svc *adapterService) Publish(ctx context.Context, key string, msg *messaging.Message) error {
|
||||
ar := &policies.AuthorizeReq{
|
||||
Subject: key,
|
||||
Object: msg.Channel,
|
||||
Action: policies.WriteAction,
|
||||
EntityType: policies.ThingEntityType,
|
||||
ar := &mainflux.AuthorizeReq{
|
||||
Namespace: "",
|
||||
SubjectType: "thing",
|
||||
Permission: "publish",
|
||||
Subject: key,
|
||||
Object: msg.Channel,
|
||||
ObjectType: "group",
|
||||
}
|
||||
res, err := svc.auth.Authorize(ctx, ar)
|
||||
if err != nil {
|
||||
@@ -69,17 +71,19 @@ func (svc *adapterService) Publish(ctx context.Context, key string, msg *messagi
|
||||
if !res.GetAuthorized() {
|
||||
return errors.ErrAuthorization
|
||||
}
|
||||
msg.Publisher = res.GetThingID()
|
||||
msg.Publisher = res.GetId()
|
||||
|
||||
return svc.pubsub.Publish(ctx, msg.Channel, msg)
|
||||
}
|
||||
|
||||
func (svc *adapterService) Subscribe(ctx context.Context, key, chanID, subtopic string, c Client) error {
|
||||
ar := &policies.AuthorizeReq{
|
||||
Subject: key,
|
||||
Object: chanID,
|
||||
Action: policies.ReadAction,
|
||||
EntityType: policies.ThingEntityType,
|
||||
ar := &mainflux.AuthorizeReq{
|
||||
Namespace: "",
|
||||
SubjectType: "thing",
|
||||
Permission: "subscribe",
|
||||
Subject: key,
|
||||
Object: chanID,
|
||||
ObjectType: "group",
|
||||
}
|
||||
res, err := svc.auth.Authorize(ctx, ar)
|
||||
if err != nil {
|
||||
@@ -96,11 +100,13 @@ func (svc *adapterService) Subscribe(ctx context.Context, key, chanID, subtopic
|
||||
}
|
||||
|
||||
func (svc *adapterService) Unsubscribe(ctx context.Context, key, chanID, subtopic, token string) error {
|
||||
ar := &policies.AuthorizeReq{
|
||||
Subject: key,
|
||||
Object: chanID,
|
||||
Action: policies.ReadAction,
|
||||
EntityType: policies.ThingEntityType,
|
||||
ar := &mainflux.AuthorizeReq{
|
||||
Namespace: "",
|
||||
SubjectType: "thing",
|
||||
Permission: "subscribe",
|
||||
Subject: key,
|
||||
Object: chanID,
|
||||
ObjectType: "group",
|
||||
}
|
||||
res, err := svc.auth.Authorize(ctx, ar)
|
||||
if err != nil {
|
||||
|
||||
@@ -13,6 +13,7 @@ import (
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
authmocks "github.com/mainflux/mainflux/auth/mocks"
|
||||
"github.com/mainflux/mainflux/consumers/notifiers"
|
||||
httpapi "github.com/mainflux/mainflux/consumers/notifiers/api"
|
||||
"github.com/mainflux/mainflux/consumers/notifiers/mocks"
|
||||
@@ -65,7 +66,7 @@ func (tr testRequest) make() (*http.Response, error) {
|
||||
}
|
||||
|
||||
func newService(tokens map[string]string) notifiers.Service {
|
||||
auth := mocks.NewAuth(tokens)
|
||||
auth := new(authmocks.Service)
|
||||
repo := mocks.NewRepo(make(map[string]notifiers.Subscription))
|
||||
idp := uuid.NewMock()
|
||||
notif := mocks.NewNotifier()
|
||||
|
||||
@@ -1,34 +0,0 @@
|
||||
// Copyright (c) Mainflux
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package mocks
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/mainflux/mainflux/pkg/errors"
|
||||
"github.com/mainflux/mainflux/users/policies"
|
||||
"google.golang.org/grpc"
|
||||
)
|
||||
|
||||
var _ policies.AuthServiceClient = (*authServiceMock)(nil)
|
||||
|
||||
type authServiceMock struct {
|
||||
users map[string]string
|
||||
}
|
||||
|
||||
// NewAuth creates mock of auth service.
|
||||
func NewAuth(users map[string]string) policies.AuthServiceClient {
|
||||
return &authServiceMock{users}
|
||||
}
|
||||
|
||||
func (svc authServiceMock) Identify(ctx context.Context, req *policies.IdentifyReq, opts ...grpc.CallOption) (*policies.IdentifyRes, error) {
|
||||
if id, ok := svc.users[req.GetToken()]; ok {
|
||||
return &policies.IdentifyRes{Id: id}, nil
|
||||
}
|
||||
return nil, errors.ErrAuthentication
|
||||
}
|
||||
|
||||
func (svc authServiceMock) Authorize(ctx context.Context, req *policies.AuthorizeReq, _ ...grpc.CallOption) (r *policies.AuthorizeRes, err error) {
|
||||
panic("not implemented")
|
||||
}
|
||||
@@ -11,7 +11,6 @@ import (
|
||||
"github.com/mainflux/mainflux/consumers"
|
||||
"github.com/mainflux/mainflux/pkg/errors"
|
||||
"github.com/mainflux/mainflux/pkg/messaging"
|
||||
"github.com/mainflux/mainflux/users/policies"
|
||||
)
|
||||
|
||||
// ErrMessage indicates an error converting a message to Mainflux message.
|
||||
@@ -40,7 +39,7 @@ type Service interface {
|
||||
var _ Service = (*notifierService)(nil)
|
||||
|
||||
type notifierService struct {
|
||||
auth policies.AuthServiceClient
|
||||
auth mainflux.AuthServiceClient
|
||||
subs SubscriptionsRepository
|
||||
idp mainflux.IDProvider
|
||||
notifier Notifier
|
||||
@@ -49,7 +48,7 @@ type notifierService struct {
|
||||
}
|
||||
|
||||
// New instantiates the subscriptions service implementation.
|
||||
func New(auth policies.AuthServiceClient, subs SubscriptionsRepository, idp mainflux.IDProvider, notifier Notifier, from string) Service {
|
||||
func New(auth mainflux.AuthServiceClient, subs SubscriptionsRepository, idp mainflux.IDProvider, notifier Notifier, from string) Service {
|
||||
return ¬ifierService{
|
||||
auth: auth,
|
||||
subs: subs,
|
||||
@@ -61,7 +60,7 @@ func New(auth policies.AuthServiceClient, subs SubscriptionsRepository, idp main
|
||||
}
|
||||
|
||||
func (ns *notifierService) CreateSubscription(ctx context.Context, token string, sub Subscription) (string, error) {
|
||||
res, err := ns.auth.Identify(ctx, &policies.IdentifyReq{Token: token})
|
||||
res, err := ns.auth.Identify(ctx, &mainflux.IdentityReq{Token: token})
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
@@ -75,7 +74,7 @@ func (ns *notifierService) CreateSubscription(ctx context.Context, token string,
|
||||
}
|
||||
|
||||
func (ns *notifierService) ViewSubscription(ctx context.Context, token, id string) (Subscription, error) {
|
||||
if _, err := ns.auth.Identify(ctx, &policies.IdentifyReq{Token: token}); err != nil {
|
||||
if _, err := ns.auth.Identify(ctx, &mainflux.IdentityReq{Token: token}); err != nil {
|
||||
return Subscription{}, err
|
||||
}
|
||||
|
||||
@@ -83,7 +82,7 @@ func (ns *notifierService) ViewSubscription(ctx context.Context, token, id strin
|
||||
}
|
||||
|
||||
func (ns *notifierService) ListSubscriptions(ctx context.Context, token string, pm PageMetadata) (Page, error) {
|
||||
if _, err := ns.auth.Identify(ctx, &policies.IdentifyReq{Token: token}); err != nil {
|
||||
if _, err := ns.auth.Identify(ctx, &mainflux.IdentityReq{Token: token}); err != nil {
|
||||
return Page{}, err
|
||||
}
|
||||
|
||||
@@ -91,7 +90,7 @@ func (ns *notifierService) ListSubscriptions(ctx context.Context, token string,
|
||||
}
|
||||
|
||||
func (ns *notifierService) RemoveSubscription(ctx context.Context, token, id string) error {
|
||||
if _, err := ns.auth.Identify(ctx, &policies.IdentifyReq{Token: token}); err != nil {
|
||||
if _, err := ns.auth.Identify(ctx, &mainflux.IdentityReq{Token: token}); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@ import (
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
authmocks "github.com/mainflux/mainflux/auth/mocks"
|
||||
"github.com/mainflux/mainflux/consumers/notifiers"
|
||||
"github.com/mainflux/mainflux/consumers/notifiers/mocks"
|
||||
"github.com/mainflux/mainflux/pkg/errors"
|
||||
@@ -26,7 +27,7 @@ const (
|
||||
|
||||
func newService() notifiers.Service {
|
||||
repo := mocks.NewRepo(make(map[string]notifiers.Subscription))
|
||||
auth := mocks.NewAuth(map[string]string{exampleUser1: exampleUser1, exampleUser2: exampleUser2, invalidUser: invalidUser})
|
||||
auth := new(authmocks.Service)
|
||||
notifier := mocks.NewNotifier()
|
||||
idp := uuid.NewMock()
|
||||
from := "exampleFrom"
|
||||
|
||||
+27
@@ -44,6 +44,33 @@ MF_SEND_TELEMETRY=true
|
||||
|
||||
## Core Services
|
||||
|
||||
### Auth
|
||||
MF_AUTH_LOG_LEVEL=debug
|
||||
MF_AUTH_HTTP_PORT=8189
|
||||
MF_AUTH_GRPC_PORT=8181
|
||||
MF_AUTH_GRPC_URL=auth:8181
|
||||
MF_AUTH_GRPC_TIMEOUT=300s
|
||||
MF_AUTH_DB_PORT=5432
|
||||
MF_AUTH_DB_USER=mainflux
|
||||
MF_AUTH_DB_PASS=mainflux
|
||||
MF_AUTH_DB=auth
|
||||
MF_AUTH_SECRET=secret
|
||||
MF_AUTH_ACCESS_TOKEN_DURATION="300m"
|
||||
|
||||
|
||||
### SpiceDB Datastore config
|
||||
MF_SPICEDB_DB_USER=mainflux
|
||||
MF_SPICEDB_DB_PASS=mainflux
|
||||
MF_SPICEDB_DB_NAME=spicedb
|
||||
MF_SPICEDB_DB_PORT=5432
|
||||
|
||||
### SpiceDB config
|
||||
MF_SPICEDB_GRPC_PRESHARED_KEY="12345678"
|
||||
MF_SPICEDB_SCHEMA_FILE="/schema.zed"
|
||||
MF_SPICEDB_HOST=mainflux-spicedb
|
||||
MF_SPICEDB_PORT=50051
|
||||
MF_SPICEDB_DATASTORE_ENGINE=postgres
|
||||
|
||||
### Users
|
||||
MF_USERS_LOG_LEVEL=debug
|
||||
MF_USERS_SECRET_KEY=HyE2D4RUt9nnKG6v8zKEqAp6g6ka8hhZsqUpzgKvnwpXrNVQSH
|
||||
|
||||
+126
-26
@@ -2,6 +2,7 @@
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
version: "3.7"
|
||||
name: "mainflux"
|
||||
|
||||
networks:
|
||||
mainflux-base-net:
|
||||
@@ -14,8 +15,101 @@ volumes:
|
||||
mainflux-es-redis-volume:
|
||||
mainflux-mqtt-broker-volume:
|
||||
mainflux-broker-volume:
|
||||
mainflux-spicedb-db-volume:
|
||||
mainflux-auth-db-volume:
|
||||
|
||||
services:
|
||||
spicedb:
|
||||
image: "authzed/spicedb"
|
||||
container_name: mainflux-spicedb
|
||||
command: "serve"
|
||||
restart: "always"
|
||||
networks:
|
||||
- mainflux-base-net
|
||||
ports:
|
||||
- "8080:8080"
|
||||
- "9090:9090"
|
||||
- "50051:50051"
|
||||
environment:
|
||||
SPICEDB_GRPC_PRESHARED_KEY: ${MF_SPICEDB_GRPC_PRESHARED_KEY}
|
||||
SPICEDB_DATASTORE_ENGINE: ${MF_SPICEDB_DATASTORE_ENGINE}
|
||||
SPICEDB_DATASTORE_CONN_URI: "${MF_SPICEDB_DATASTORE_ENGINE}://${MF_SPICEDB_DB_USER}:${MF_SPICEDB_DB_PASS}@spicedb-db:${MF_SPICEDB_DB_PORT}/${MF_SPICEDB_DB_NAME}?sslmode=disable"
|
||||
depends_on:
|
||||
- spicedb-migrate
|
||||
|
||||
spicedb-migrate:
|
||||
image: "authzed/spicedb"
|
||||
container_name: mainflux-spicedb-migrate
|
||||
command: "migrate head"
|
||||
restart: "on-failure"
|
||||
networks:
|
||||
- mainflux-base-net
|
||||
environment:
|
||||
SPICEDB_DATASTORE_ENGINE: ${MF_SPICEDB_DATASTORE_ENGINE}
|
||||
SPICEDB_DATASTORE_CONN_URI: "${MF_SPICEDB_DATASTORE_ENGINE}://${MF_SPICEDB_DB_USER}:${MF_SPICEDB_DB_PASS}@spicedb-db:${MF_SPICEDB_DB_PORT}/${MF_SPICEDB_DB_NAME}?sslmode=disable"
|
||||
depends_on:
|
||||
- spicedb-db
|
||||
|
||||
spicedb-db:
|
||||
image: "postgres:15.3-alpine"
|
||||
container_name: mainflux-spicedb-db
|
||||
networks:
|
||||
- mainflux-base-net
|
||||
ports:
|
||||
- "6010:5432"
|
||||
environment:
|
||||
POSTGRES_USER: ${MF_SPICEDB_DB_USER}
|
||||
POSTGRES_PASSWORD: ${MF_SPICEDB_DB_PASS}
|
||||
POSTGRES_DB: ${MF_SPICEDB_DB_NAME}
|
||||
volumes:
|
||||
- mainflux-spicedb-db-volume:/var/lib/postgresql/data
|
||||
|
||||
auth-db:
|
||||
image: postgres:13.3-alpine
|
||||
container_name: mainflux-auth-db
|
||||
restart: on-failure
|
||||
ports:
|
||||
- 6004:5432
|
||||
environment:
|
||||
POSTGRES_USER: ${MF_AUTH_DB_USER}
|
||||
POSTGRES_PASSWORD: ${MF_AUTH_DB_PASS}
|
||||
POSTGRES_DB: ${MF_AUTH_DB}
|
||||
networks:
|
||||
- mainflux-base-net
|
||||
volumes:
|
||||
- mainflux-auth-db-volume:/var/lib/postgresql/data
|
||||
|
||||
auth:
|
||||
image: mainflux/auth:${MF_RELEASE_TAG}
|
||||
container_name: mainflux-auth
|
||||
depends_on:
|
||||
- auth-db
|
||||
- spicedb
|
||||
expose:
|
||||
- ${MF_AUTH_GRPC_PORT}
|
||||
restart: on-failure
|
||||
environment:
|
||||
MF_SPICEDB_SCHEMA_FILE: ${MF_SPICEDB_SCHEMA_FILE}
|
||||
MF_SPICEDB_HOST: ${MF_SPICEDB_HOST}
|
||||
MF_SPICEDB_PORT: ${MF_SPICEDB_PORT}
|
||||
MF_AUTH_LOG_LEVEL: ${MF_AUTH_LOG_LEVEL}
|
||||
MF_AUTH_DB_HOST: auth-db
|
||||
MF_AUTH_DB_PORT: ${MF_AUTH_DB_PORT}
|
||||
MF_AUTH_DB_USER: ${MF_AUTH_DB_USER}
|
||||
MF_AUTH_DB_PASS: ${MF_AUTH_DB_PASS}
|
||||
MF_AUTH_DB: ${MF_AUTH_DB}
|
||||
MF_AUTH_HTTP_PORT: ${MF_AUTH_HTTP_PORT}
|
||||
MF_AUTH_GRPC_PORT: ${MF_AUTH_GRPC_PORT}
|
||||
MF_AUTH_SECRET: ${MF_AUTH_SECRET}
|
||||
MF_AUTH_ACCESS_TOKEN_DURATION: ${MF_AUTH_ACCESS_TOKEN_DURATION}
|
||||
ports:
|
||||
- ${MF_AUTH_HTTP_PORT}:${MF_AUTH_HTTP_PORT}
|
||||
- ${MF_AUTH_GRPC_PORT}:${MF_AUTH_GRPC_PORT}
|
||||
networks:
|
||||
- mainflux-base-net
|
||||
volumes:
|
||||
- ./spicedb/schema.zed:${MF_SPICEDB_SCHEMA_FILE}
|
||||
|
||||
nginx:
|
||||
image: nginx:1.23.3-alpine
|
||||
container_name: mainflux-nginx
|
||||
@@ -66,11 +160,13 @@ services:
|
||||
POSTGRES_DB: ${MF_THINGS_DB_NAME}
|
||||
networks:
|
||||
- mainflux-base-net
|
||||
ports:
|
||||
- 6006:5432
|
||||
volumes:
|
||||
- mainflux-things-db-volume:/var/lib/postgresql/data
|
||||
|
||||
things-redis:
|
||||
image: redis:7.2.0-alpine
|
||||
image: redis:6.2.2-alpine
|
||||
container_name: mainflux-things-redis
|
||||
restart: on-failure
|
||||
networks:
|
||||
@@ -114,11 +210,11 @@ services:
|
||||
MF_THINGS_DB_SSL_CERT: ${MF_THINGS_DB_SSL_CERT}
|
||||
MF_THINGS_DB_SSL_KEY: ${MF_THINGS_DB_SSL_KEY}
|
||||
MF_THINGS_DB_SSL_ROOT_CERT: ${MF_THINGS_DB_SSL_ROOT_CERT}
|
||||
MF_AUTH_GRPC_URL: ${MF_USERS_GRPC_URL}
|
||||
MF_AUTH_GRPC_TIMEOUT: ${MF_USERS_GRPC_TIMEOUT}
|
||||
MF_AUTH_GRPC_CLIENT_CERT: ${MF_USERS_GRPC_CLIENT_CERT:+/users-grpc-client.crt}
|
||||
MF_AUTH_GRPC_CLIENT_KEY: ${MF_USERS_GRPC_CLIENT_KEY:+/users-grpc-client.key}
|
||||
MF_AUTH_GRPC_SERVER_CA_CERTS: ${MF_USERS_GRPC_SERVER_CA_CERTS:+/users-grpc-server-ca.crt}
|
||||
MF_AUTH_GRPC_URL: ${MF_AUTH_GRPC_URL}
|
||||
MF_AUTH_GRPC_TIMEOUT: ${MF_AUTH_GRPC_TIMEOUT}
|
||||
MF_AUTH_GRPC_CLIENT_CERT: ${MF_AUTH_GRPC_CLIENT_CERT:+/users-grpc-client.crt}
|
||||
MF_AUTH_GRPC_CLIENT_KEY: ${MF_AUTH_GRPC_CLIENT_KEY:+/users-grpc-client.key}
|
||||
MF_AUTH_GRPC_SERVER_CA_CERTS: ${MF_AUTH_GRPC_SERVER_CA_CERTS:+/users-grpc-server-ca.crt}
|
||||
MF_JAEGER_URL: ${MF_JAEGER_URL}
|
||||
MF_SEND_TELEMETRY: ${MF_SEND_TELEMETRY}
|
||||
ports:
|
||||
@@ -173,6 +269,8 @@ services:
|
||||
POSTGRES_USER: ${MF_USERS_DB_USER}
|
||||
POSTGRES_PASSWORD: ${MF_USERS_DB_PASS}
|
||||
POSTGRES_DB: ${MF_USERS_DB_NAME}
|
||||
ports:
|
||||
- 6005:5432
|
||||
networks:
|
||||
- mainflux-base-net
|
||||
volumes:
|
||||
@@ -222,6 +320,8 @@ services:
|
||||
MF_USERS_ES_URL: ${MF_ES_URL}
|
||||
MF_JAEGER_URL: ${MF_JAEGER_URL}
|
||||
MF_SEND_TELEMETRY: ${MF_SEND_TELEMETRY}
|
||||
MF_AUTH_GRPC_URL: ${MF_AUTH_GRPC_URL}
|
||||
MF_AUTH_GRPC_TIMEOUT: ${MF_AUTH_GRPC_TIMEOUT}
|
||||
ports:
|
||||
- ${MF_USERS_HTTP_PORT}:${MF_USERS_HTTP_PORT}
|
||||
- ${MF_USERS_GRPC_PORT}:${MF_USERS_GRPC_PORT}
|
||||
@@ -296,11 +396,11 @@ services:
|
||||
MF_MQTT_ADAPTER_WS_TARGET_PATH: ${MF_MQTT_ADAPTER_WS_TARGET_PATH}
|
||||
MF_MQTT_ADAPTER_INSTANCE: ${MF_MQTT_ADAPTER_INSTANCE}
|
||||
MF_MQTT_ADAPTER_ES_URL: ${MF_ES_URL}
|
||||
MF_THINGS_AUTH_GRPC_URL: ${MF_THINGS_AUTH_GRPC_URL}
|
||||
MF_THINGS_AUTH_GRPC_TIMEOUT: ${MF_THINGS_AUTH_GRPC_TIMEOUT}
|
||||
MF_THINGS_AUTH_GRPC_CLIENT_CERT: ${MF_THINGS_AUTH_GRPC_CLIENT_CERT:+/client.crt}
|
||||
MF_THINGS_AUTH_GRPC_CLIENT_KEY: ${MF_THINGS_AUTH_GRPC_CLIENT_KEY:+/client.key}
|
||||
MF_THINGS_AUTH_GRPC_SERVER_CA_CERTS: ${MF_THINGS_AUTH_GRPC_SERVER_CA_CERTS:+/server_ca.crt}
|
||||
MF_AUTH_GRPC_URL: ${MF_THINGS_AUTH_GRPC_URL}
|
||||
MF_AUTH_GRPC_TIMEOUT: ${MF_THINGS_AUTH_GRPC_TIMEOUT}
|
||||
MF_AUTH_GRPC_CLIENT_CERT: ${MF_THINGS_AUTH_GRPC_CLIENT_CERT:+/client.crt}
|
||||
MF_AUTH_GRPC_CLIENT_KEY: ${MF_THINGS_AUTH_GRPC_CLIENT_KEY:+/client.key}
|
||||
MF_AUTH_GRPC_SERVER_CA_CERTS: ${MF_THINGS_AUTH_GRPC_SERVER_CA_CERTS:+/server_ca.crt}
|
||||
MF_JAEGER_URL: ${MF_JAEGER_URL}
|
||||
MF_BROKER_URL: ${MF_BROKER_URL}
|
||||
MF_SEND_TELEMETRY: ${MF_SEND_TELEMETRY}
|
||||
@@ -337,11 +437,11 @@ services:
|
||||
MF_HTTP_ADAPTER_PORT: ${MF_HTTP_ADAPTER_PORT}
|
||||
MF_HTTP_ADAPTER_SERVER_CERT: ${MF_HTTP_ADAPTER_SERVER_CERT}
|
||||
MF_HTTP_ADAPTER_SERVER_KEY: ${MF_HTTP_ADAPTER_SERVER_KEY}
|
||||
MF_THINGS_AUTH_GRPC_URL: ${MF_THINGS_AUTH_GRPC_URL}
|
||||
MF_THINGS_AUTH_GRPC_TIMEOUT: ${MF_THINGS_AUTH_GRPC_TIMEOUT}
|
||||
MF_THINGS_AUTH_GRPC_CLIENT_CERT: ${MF_THINGS_AUTH_GRPC_CLIENT_CERT:+/client.crt}
|
||||
MF_THINGS_AUTH_GRPC_CLIENT_KEY: ${MF_THINGS_AUTH_GRPC_CLIENT_KEY:+/client.key}
|
||||
MF_THINGS_AUTH_GRPC_SERVER_CA_CERTS: ${MF_THINGS_AUTH_GRPC_SERVER_CA_CERTS:+/server_ca.crt}
|
||||
MF_AUTH_GRPC_URL: ${MF_THINGS_AUTH_GRPC_URL}
|
||||
MF_AUTH_GRPC_TIMEOUT: ${MF_THINGS_AUTH_GRPC_TIMEOUT}
|
||||
MF_AUTH_GRPC_CLIENT_CERT: ${MF_THINGS_AUTH_GRPC_CLIENT_CERT:+/client.crt}
|
||||
MF_AUTH_GRPC_CLIENT_KEY: ${MF_THINGS_AUTH_GRPC_CLIENT_KEY:+/client.key}
|
||||
MF_AUTH_GRPC_SERVER_CA_CERTS: ${MF_THINGS_AUTH_GRPC_SERVER_CA_CERTS:+/server_ca.crt}
|
||||
MF_BROKER_URL: ${MF_BROKER_URL}
|
||||
MF_JAEGER_URL: ${MF_JAEGER_URL}
|
||||
MF_SEND_TELEMETRY: ${MF_SEND_TELEMETRY}
|
||||
@@ -394,11 +494,11 @@ services:
|
||||
MF_COAP_ADAPTER_HTTP_PORT: ${MF_COAP_ADAPTER_HTTP_PORT}
|
||||
MF_COAP_ADAPTER_HTTP_SERVER_CERT: ${MF_COAP_ADAPTER_HTTP_SERVER_CERT}
|
||||
MF_COAP_ADAPTER_HTTP_SERVER_KEY: ${MF_COAP_ADAPTER_HTTP_SERVER_KEY}
|
||||
MF_THINGS_AUTH_GRPC_URL: ${MF_THINGS_AUTH_GRPC_URL}
|
||||
MF_THINGS_AUTH_GRPC_TIMEOUT: ${MF_THINGS_AUTH_GRPC_TIMEOUT}
|
||||
MF_THINGS_AUTH_GRPC_CLIENT_CERT: ${MF_THINGS_AUTH_GRPC_CLIENT_CERT:+/client.crt}
|
||||
MF_THINGS_AUTH_GRPC_CLIENT_KEY: ${MF_THINGS_AUTH_GRPC_CLIENT_KEY:+/client.key}
|
||||
MF_THINGS_AUTH_GRPC_SERVER_CA_CERTS: ${MF_THINGS_AUTH_GRPC_SERVER_CA_CERTS:+/server_ca.crt}
|
||||
MF_AUTH_GRPC_URL: ${MF_THINGS_AUTH_GRPC_URL}
|
||||
MF_AUTH_GRPC_TIMEOUT: ${MF_THINGS_AUTH_GRPC_TIMEOUT}
|
||||
MF_AUTH_GRPC_CLIENT_CERT: ${MF_THINGS_AUTH_GRPC_CLIENT_CERT:+/client.crt}
|
||||
MF_AUTH_GRPC_CLIENT_KEY: ${MF_THINGS_AUTH_GRPC_CLIENT_KEY:+/client.key}
|
||||
MF_AUTH_GRPC_SERVER_CA_CERTS: ${MF_THINGS_AUTH_GRPC_SERVER_CA_CERTS:+/server_ca.crt}
|
||||
MF_BROKER_URL: ${MF_BROKER_URL}
|
||||
MF_JAEGER_URL: ${MF_JAEGER_URL}
|
||||
MF_SEND_TELEMETRY: ${MF_SEND_TELEMETRY}
|
||||
@@ -439,11 +539,11 @@ services:
|
||||
MF_WS_ADAPTER_HTTP_PORT: ${MF_WS_ADAPTER_HTTP_PORT}
|
||||
MF_WS_ADAPTER_HTTP_SERVER_CERT: ${MF_WS_ADAPTER_HTTP_SERVER_CERT}
|
||||
MF_WS_ADAPTER_HTTP_SERVER_KEY: ${MF_WS_ADAPTER_HTTP_SERVER_KEY}
|
||||
MF_THINGS_AUTH_GRPC_URL: ${MF_THINGS_AUTH_GRPC_URL}
|
||||
MF_THINGS_AUTH_GRPC_TIMEOUT: ${MF_THINGS_AUTH_GRPC_TIMEOUT}
|
||||
MF_THINGS_AUTH_GRPC_CLIENT_CERT: ${MF_THINGS_AUTH_GRPC_CLIENT_CERT:+/client.crt}
|
||||
MF_THINGS_AUTH_GRPC_CLIENT_KEY: ${MF_THINGS_AUTH_GRPC_CLIENT_KEY:+/client.key}
|
||||
MF_THINGS_AUTH_GRPC_SERVER_CA_CERTS: ${MF_THINGS_AUTH_GRPC_SERVER_CA_CERTS:+/server_ca.crt}
|
||||
MF_AUTH_GRPC_URL: ${MF_THINGS_AUTH_GRPC_URL}
|
||||
MF_AUTH_GRPC_TIMEOUT: ${MF_THINGS_AUTH_GRPC_TIMEOUT}
|
||||
MF_AUTH_GRPC_CLIENT_CERT: ${MF_THINGS_AUTH_GRPC_CLIENT_CERT:+/client.crt}
|
||||
MF_AUTH_GRPC_CLIENT_KEY: ${MF_THINGS_AUTH_GRPC_CLIENT_KEY:+/client.key}
|
||||
MF_AUTH_GRPC_SERVER_CA_CERTS: ${MF_THINGS_AUTH_GRPC_SERVER_CA_CERTS:+/server_ca.crt}
|
||||
MF_BROKER_URL: ${MF_BROKER_URL}
|
||||
MF_JAEGER_URL: ${MF_JAEGER_URL}
|
||||
MF_SEND_TELEMETRY: ${MF_SEND_TELEMETRY}
|
||||
|
||||
@@ -50,6 +50,31 @@ http {
|
||||
|
||||
server_name localhost;
|
||||
|
||||
# Proxy pass to users & groups id to things service for listing of channels
|
||||
# /users/{userID}/channels - Listing of channels belongs to userID
|
||||
# /groups/{userGroupID}/channels - Listing of channels belongs to userGroupID
|
||||
location ~ ^/(users|groups)/(.+)/channels {
|
||||
include snippets/proxy-headers.conf;
|
||||
add_header Access-Control-Expose-Headers Location;
|
||||
if ($request_method = GET) {
|
||||
proxy_pass http://things:${MF_THINGS_HTTP_PORT};
|
||||
break;
|
||||
}
|
||||
proxy_pass http://users:${MF_USERS_HTTP_PORT};
|
||||
}
|
||||
|
||||
# Proxy pass to channel id to users service for listing of channels
|
||||
# /channels/{channelID}/users - Listing of Users belongs to channelID
|
||||
# /channels/{channelID}/groups - Listing of User Groups belongs to channelID
|
||||
location ~ ^/channels/(.+)/(users|groups) {
|
||||
include snippets/proxy-headers.conf;
|
||||
add_header Access-Control-Expose-Headers Location;
|
||||
if ($request_method = GET) {
|
||||
proxy_pass http://users:${MF_USERS_HTTP_PORT};
|
||||
break;
|
||||
}
|
||||
proxy_pass http://things:${MF_THINGS_HTTP_PORT};
|
||||
}
|
||||
# Proxy pass to users service
|
||||
location ~ ^/(users|groups|password|authorize) {
|
||||
include snippets/proxy-headers.conf;
|
||||
@@ -69,7 +94,7 @@ http {
|
||||
add_header Access-Control-Expose-Headers Location;
|
||||
proxy_pass http://things:${MF_THINGS_HTTP_PORT};
|
||||
}
|
||||
|
||||
|
||||
location ^~ /things/policies {
|
||||
include snippets/proxy-headers.conf;
|
||||
add_header Access-Control-Expose-Headers Location;
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
definition user {
|
||||
relation owner: user
|
||||
}
|
||||
|
||||
definition thing {
|
||||
relation owner: user
|
||||
relation group: group
|
||||
permission delete = owner + group->admin
|
||||
permission edit = owner + group->edit
|
||||
permission view = edit + group->view
|
||||
permission share = edit
|
||||
permission publish = group
|
||||
permission subscribe = group
|
||||
}
|
||||
|
||||
definition role {
|
||||
relation admin: user
|
||||
relation editor: user
|
||||
relation viewer: user
|
||||
}
|
||||
|
||||
definition group {
|
||||
relation owner: user
|
||||
relation admin: user
|
||||
relation editor: user
|
||||
relation viewer: user
|
||||
relation parent_group: group
|
||||
relation role_group: role
|
||||
permission administrator = owner + admin + parent_group->admin + role_group->admin
|
||||
permission delete = administrator
|
||||
permission edit = administrator + editor + parent_group->editor + role_group->editor
|
||||
permission view = viewer + edit + parent_group->view + role_group->viewer + role_group->editor
|
||||
permission share = edit
|
||||
}
|
||||
@@ -1,26 +0,0 @@
|
||||
// Copyright (c) Mainflux
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package mainflux
|
||||
|
||||
import (
|
||||
"os"
|
||||
|
||||
"github.com/subosito/gotenv"
|
||||
)
|
||||
|
||||
// Env reads specified environment variable. If no value has been found,
|
||||
// fallback is returned.
|
||||
func Env(key, fallback string) string {
|
||||
if v := os.Getenv(key); v != "" {
|
||||
return v
|
||||
}
|
||||
|
||||
return fallback
|
||||
}
|
||||
|
||||
// LoadEnvFile loads environment variables defined in an .env formatted file.
|
||||
func LoadEnvFile(envfilepath string) error {
|
||||
err := gotenv.Load(envfilepath)
|
||||
return err
|
||||
}
|
||||
@@ -1,90 +1,103 @@
|
||||
module github.com/mainflux/mainflux
|
||||
|
||||
go 1.20
|
||||
go 1.21
|
||||
|
||||
require (
|
||||
github.com/authzed/authzed-go v0.10.0
|
||||
github.com/authzed/grpcutil v0.0.0-20230908193239-4286bb1d6403
|
||||
github.com/caarlos0/env/v7 v7.1.0
|
||||
github.com/cenkalti/backoff/v4 v4.2.1
|
||||
github.com/docker/docker v24.0.2+incompatible
|
||||
github.com/eclipse/paho.mqtt.golang v1.4.2
|
||||
github.com/docker/docker v24.0.6+incompatible
|
||||
github.com/eclipse/paho.mqtt.golang v1.4.3
|
||||
github.com/fatih/color v1.15.0
|
||||
github.com/fiorix/go-smpp v0.0.0-20210403173735-2894b96e70ba
|
||||
github.com/go-kit/kit v0.12.0
|
||||
github.com/go-chi/chi/v5 v5.0.10
|
||||
github.com/go-kit/kit v0.13.0
|
||||
github.com/go-kit/log v0.2.1
|
||||
github.com/go-redis/redis/v8 v8.11.5
|
||||
github.com/go-zoo/bone v1.3.0
|
||||
github.com/gocql/gocql v1.5.2
|
||||
github.com/gocql/gocql v1.6.0
|
||||
github.com/gofrs/uuid v4.4.0+incompatible
|
||||
github.com/gookit/color v1.5.3
|
||||
github.com/gookit/color v1.5.4
|
||||
github.com/goombaio/namegenerator v0.0.0-20181006234301-989e774b106e
|
||||
github.com/gopcua/opcua v0.1.6
|
||||
github.com/gorilla/websocket v1.5.0
|
||||
github.com/hashicorp/vault/api v1.9.2
|
||||
github.com/hashicorp/vault/api v1.10.0
|
||||
github.com/hokaccha/go-prettyjson v0.0.0-20211117102719-0474bc63780f
|
||||
github.com/influxdata/influxdb-client-go/v2 v2.12.3
|
||||
github.com/ivanpirog/coloredcobra v1.0.1
|
||||
github.com/jackc/pgerrcode v0.0.0-20220416144525-469b46aa5efa
|
||||
github.com/jackc/pgtype v1.14.0
|
||||
github.com/jackc/pgx/v5 v5.4.1
|
||||
github.com/jackc/pgx/v5 v5.4.3
|
||||
github.com/jmoiron/sqlx v1.3.5
|
||||
github.com/lestrrat-go/jwx/v2 v2.0.11
|
||||
github.com/mainflux/callhome v0.0.0-20230626140149-b03b1f4c46f2
|
||||
github.com/lestrrat-go/jwx/v2 v2.0.13
|
||||
github.com/mainflux/callhome v0.0.0-20230920140432-33c5663382ce
|
||||
github.com/mainflux/mproxy v0.3.1-0.20230822124450-4b4dfe600cc2
|
||||
github.com/mainflux/senml v1.5.0
|
||||
github.com/mitchellh/mapstructure v1.5.0
|
||||
github.com/nats-io/nats.go v1.27.1
|
||||
github.com/nats-io/nats.go v1.30.2
|
||||
github.com/oklog/ulid/v2 v2.1.0
|
||||
github.com/ory/dockertest/v3 v3.10.0
|
||||
github.com/pelletier/go-toml v1.9.5
|
||||
github.com/plgd-dev/go-coap/v2 v2.6.0
|
||||
github.com/prometheus/client_golang v1.16.0
|
||||
github.com/rabbitmq/amqp091-go v1.8.1
|
||||
github.com/rubenv/sql-migrate v1.5.1
|
||||
github.com/prometheus/client_golang v1.17.0
|
||||
github.com/rabbitmq/amqp091-go v1.9.0
|
||||
github.com/rubenv/sql-migrate v1.5.2
|
||||
github.com/spf13/cobra v1.7.0
|
||||
github.com/spf13/viper v1.16.0
|
||||
github.com/spf13/viper v1.17.0
|
||||
github.com/stretchr/testify v1.8.4
|
||||
github.com/subosito/gotenv v1.4.2
|
||||
go.mongodb.org/mongo-driver v1.12.0
|
||||
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.42.0
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.42.0
|
||||
go.opentelemetry.io/contrib/propagators/jaeger v1.17.0
|
||||
go.opentelemetry.io/otel v1.16.0
|
||||
go.opentelemetry.io/otel/exporters/jaeger v1.16.0
|
||||
go.opentelemetry.io/otel/sdk v1.16.0
|
||||
go.opentelemetry.io/otel/trace v1.16.0
|
||||
golang.org/x/crypto v0.11.0
|
||||
golang.org/x/exp v0.0.0-20230321023759-10a507213a29
|
||||
golang.org/x/net v0.12.0
|
||||
golang.org/x/sync v0.3.0
|
||||
gonum.org/v1/gonum v0.13.0
|
||||
google.golang.org/grpc v1.56.1
|
||||
go.mongodb.org/mongo-driver v1.12.1
|
||||
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.45.0
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.45.0
|
||||
go.opentelemetry.io/otel v1.19.0
|
||||
go.opentelemetry.io/otel/exporters/jaeger v1.17.0
|
||||
go.opentelemetry.io/otel/sdk v1.19.0
|
||||
go.opentelemetry.io/otel/trace v1.19.0
|
||||
golang.org/x/crypto v0.14.0
|
||||
golang.org/x/exp v0.0.0-20231006140011-7918f672742d
|
||||
golang.org/x/net v0.17.0
|
||||
golang.org/x/sync v0.4.0
|
||||
gonum.org/v1/gonum v0.14.0
|
||||
google.golang.org/grpc v1.58.3
|
||||
google.golang.org/protobuf v1.31.0
|
||||
gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 // indirect
|
||||
github.com/BurntSushi/toml v1.3.2 // indirect
|
||||
github.com/CloudyKit/fastprinter v0.0.0-20200109182630-33d98a066a53 // indirect
|
||||
github.com/CloudyKit/jet/v6 v6.2.0 // indirect
|
||||
github.com/Joker/jade v1.1.3 // indirect
|
||||
github.com/Microsoft/go-winio v0.6.1 // indirect
|
||||
github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5 // indirect
|
||||
github.com/Shopify/goreferrer v0.0.0-20220729165902-8cddb4f5de06 // indirect
|
||||
github.com/andybalholm/brotli v1.0.5 // indirect
|
||||
github.com/apapsch/go-jsonmerge/v2 v2.0.0 // indirect
|
||||
github.com/aymerick/douceur v0.2.0 // indirect
|
||||
github.com/beorn7/perks v1.0.1 // indirect
|
||||
github.com/bytedance/sonic v1.9.2 // indirect
|
||||
github.com/bytedance/sonic v1.10.2 // indirect
|
||||
github.com/cenkalti/backoff/v3 v3.2.2 // indirect
|
||||
github.com/certifi/gocertifi v0.0.0-20210507211836-431795d63e8d // indirect
|
||||
github.com/cespare/xxhash/v2 v2.2.0 // indirect
|
||||
github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 // indirect
|
||||
github.com/containerd/continuity v0.4.1 // indirect
|
||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||
github.com/chenzhuoyu/base64x v0.0.0-20230717121745-296ad89f973d // indirect
|
||||
github.com/chenzhuoyu/iasm v0.9.0 // indirect
|
||||
github.com/containerd/continuity v0.4.2 // indirect
|
||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
|
||||
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0 // indirect
|
||||
github.com/deepmap/oapi-codegen v1.13.0 // indirect
|
||||
github.com/deepmap/oapi-codegen v1.15.0 // indirect
|
||||
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
|
||||
github.com/docker/cli v24.0.2+incompatible // indirect
|
||||
github.com/docker/cli v24.0.6+incompatible // indirect
|
||||
github.com/docker/go-connections v0.4.0 // indirect
|
||||
github.com/docker/go-units v0.5.0 // indirect
|
||||
github.com/dsnet/golib/memfile v1.0.0 // indirect
|
||||
github.com/envoyproxy/protoc-gen-validate v1.0.2 // indirect
|
||||
github.com/fatih/structs v1.1.0 // indirect
|
||||
github.com/felixge/httpsnoop v1.0.3 // indirect
|
||||
github.com/flosch/pongo2/v4 v4.0.2 // indirect
|
||||
github.com/fsnotify/fsnotify v1.6.0 // indirect
|
||||
github.com/fxamacker/cbor/v2 v2.4.0 // indirect
|
||||
github.com/gabriel-vasile/mimetype v1.4.2 // indirect
|
||||
github.com/fxamacker/cbor/v2 v2.5.0 // indirect
|
||||
github.com/gabriel-vasile/mimetype v1.4.3 // indirect
|
||||
github.com/gin-contrib/sse v0.1.0 // indirect
|
||||
github.com/gin-gonic/gin v1.9.1 // indirect
|
||||
github.com/go-gorp/gorp/v3 v3.1.0 // indirect
|
||||
@@ -94,77 +107,101 @@ require (
|
||||
github.com/go-logr/stdr v1.2.2 // indirect
|
||||
github.com/go-playground/locales v0.14.1 // indirect
|
||||
github.com/go-playground/universal-translator v0.18.1 // indirect
|
||||
github.com/go-playground/validator/v10 v10.14.1 // indirect
|
||||
github.com/go-playground/validator/v10 v10.15.5 // indirect
|
||||
github.com/goccy/go-json v0.10.2 // indirect
|
||||
github.com/gogo/protobuf v1.3.2 // indirect
|
||||
github.com/golang/protobuf v1.5.3 // indirect
|
||||
github.com/golang/snappy v0.0.4 // indirect
|
||||
github.com/gomarkdown/markdown v0.0.0-20230922112808-5421fefb8386 // indirect
|
||||
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect
|
||||
github.com/google/uuid v1.3.0 // indirect
|
||||
github.com/google/uuid v1.3.1 // indirect
|
||||
github.com/gorilla/css v1.0.0 // indirect
|
||||
github.com/grpc-ecosystem/go-grpc-middleware v1.4.0 // indirect
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.18.0 // indirect
|
||||
github.com/hailocab/go-hostpool v0.0.0-20160125115350-e80d13ce29ed // indirect
|
||||
github.com/hashicorp/errwrap v1.1.0 // indirect
|
||||
github.com/hashicorp/go-cleanhttp v0.5.2 // indirect
|
||||
github.com/hashicorp/go-hclog v1.5.0 // indirect
|
||||
github.com/hashicorp/go-multierror v1.1.1 // indirect
|
||||
github.com/hashicorp/go-retryablehttp v0.7.4 // indirect
|
||||
github.com/hashicorp/go-rootcerts v1.0.2 // indirect
|
||||
github.com/hashicorp/go-secure-stdlib/parseutil v0.1.7 // indirect
|
||||
github.com/hashicorp/go-secure-stdlib/strutil v0.1.2 // indirect
|
||||
github.com/hashicorp/go-sockaddr v1.0.2 // indirect
|
||||
github.com/hashicorp/go-sockaddr v1.0.5 // indirect
|
||||
github.com/hashicorp/hcl v1.0.0 // indirect
|
||||
github.com/imdario/mergo v0.3.13 // indirect
|
||||
github.com/imdario/mergo v0.3.16 // indirect
|
||||
github.com/inconshreveable/mousetrap v1.1.0 // indirect
|
||||
github.com/influxdata/line-protocol v0.0.0-20210922203350-b1ad95c89adf // indirect
|
||||
github.com/iris-contrib/schema v0.0.6 // indirect
|
||||
github.com/jackc/pgio v1.0.0 // indirect
|
||||
github.com/jackc/pgpassfile v1.0.0 // indirect
|
||||
github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a // indirect
|
||||
github.com/josharian/intern v1.0.0 // indirect
|
||||
github.com/json-iterator/go v1.1.12 // indirect
|
||||
github.com/klauspost/compress v1.16.7 // indirect
|
||||
github.com/jzelinskie/stringz v0.0.2 // indirect
|
||||
github.com/kataras/blocks v0.0.8 // indirect
|
||||
github.com/kataras/golog v0.1.9 // indirect
|
||||
github.com/kataras/iris/v12 v12.2.7 // indirect
|
||||
github.com/kataras/pio v0.0.12 // indirect
|
||||
github.com/kataras/sitemap v0.0.6 // indirect
|
||||
github.com/kataras/tunnel v0.0.4 // indirect
|
||||
github.com/klauspost/compress v1.17.0 // indirect
|
||||
github.com/klauspost/cpuid/v2 v2.2.5 // indirect
|
||||
github.com/labstack/echo/v4 v4.10.2 // indirect
|
||||
github.com/labstack/echo/v4 v4.11.2 // indirect
|
||||
github.com/labstack/gommon v0.4.0 // indirect
|
||||
github.com/leodido/go-urn v1.2.4 // indirect
|
||||
github.com/lestrrat-go/blackmagic v1.0.1 // indirect
|
||||
github.com/lestrrat-go/blackmagic v1.0.2 // indirect
|
||||
github.com/lestrrat-go/httpcc v1.0.1 // indirect
|
||||
github.com/lestrrat-go/httprc v1.0.4 // indirect
|
||||
github.com/lestrrat-go/iter v1.0.2 // indirect
|
||||
github.com/lestrrat-go/option v1.0.1 // indirect
|
||||
github.com/magiconair/properties v1.8.7 // indirect
|
||||
github.com/mailgun/raymond/v2 v2.0.48 // indirect
|
||||
github.com/mailru/easyjson v0.7.7 // indirect
|
||||
github.com/mattn/go-colorable v0.1.13 // indirect
|
||||
github.com/mattn/go-isatty v0.0.19 // indirect
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect
|
||||
github.com/microcosm-cc/bluemonday v1.0.26 // indirect
|
||||
github.com/mitchellh/go-homedir v1.1.0 // indirect
|
||||
github.com/moby/term v0.5.0 // indirect
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||
github.com/modern-go/reflect2 v1.0.2 // indirect
|
||||
github.com/montanaflynn/stats v0.7.1 // indirect
|
||||
github.com/nats-io/nkeys v0.4.4 // indirect
|
||||
github.com/nats-io/nkeys v0.4.5 // indirect
|
||||
github.com/nats-io/nuid v1.0.1 // indirect
|
||||
github.com/opencontainers/go-digest v1.0.0 // indirect
|
||||
github.com/opencontainers/image-spec v1.0.2 // indirect
|
||||
github.com/opencontainers/runc v1.1.7 // indirect
|
||||
github.com/pelletier/go-toml/v2 v2.0.8 // indirect
|
||||
github.com/opencontainers/runc v1.1.9 // indirect
|
||||
github.com/pelletier/go-toml/v2 v2.1.0 // indirect
|
||||
github.com/pion/dtls/v2 v2.2.7 // indirect
|
||||
github.com/pion/logging v0.2.2 // indirect
|
||||
github.com/pion/transport/v2 v2.2.1 // indirect
|
||||
github.com/pion/transport/v2 v2.2.4 // indirect
|
||||
github.com/pkg/errors v0.9.1 // indirect
|
||||
github.com/plgd-dev/kit/v2 v2.0.0-20211006190727-057b33161b90 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||
github.com/prometheus/client_model v0.4.0 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
|
||||
github.com/prometheus/client_model v0.5.0 // indirect
|
||||
github.com/prometheus/common v0.44.0 // indirect
|
||||
github.com/prometheus/procfs v0.11.0 // indirect
|
||||
github.com/prometheus/procfs v0.12.0 // indirect
|
||||
github.com/russross/blackfriday/v2 v2.1.0 // indirect
|
||||
github.com/ryanuber/go-glob v1.0.0 // indirect
|
||||
github.com/sagikazarmark/locafero v0.3.0 // indirect
|
||||
github.com/sagikazarmark/slog-shim v0.1.0 // indirect
|
||||
github.com/schollz/closestmatch v2.1.0+incompatible // indirect
|
||||
github.com/segmentio/asm v1.2.0 // indirect
|
||||
github.com/sirupsen/logrus v1.9.3 // indirect
|
||||
github.com/spf13/afero v1.9.5 // indirect
|
||||
github.com/sourcegraph/conc v0.3.0 // indirect
|
||||
github.com/spf13/afero v1.10.0 // indirect
|
||||
github.com/spf13/cast v1.5.1 // indirect
|
||||
github.com/spf13/jwalterweatherman v1.1.0 // indirect
|
||||
github.com/spf13/pflag v1.0.5 // indirect
|
||||
github.com/stretchr/objx v0.5.0 // indirect
|
||||
github.com/stretchr/objx v0.5.1 // indirect
|
||||
github.com/subosito/gotenv v1.6.0 // indirect
|
||||
github.com/tdewolff/minify/v2 v2.12.9 // indirect
|
||||
github.com/tdewolff/parse/v2 v2.6.8 // indirect
|
||||
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
|
||||
github.com/ugorji/go/codec v1.2.11 // indirect
|
||||
github.com/valyala/bytebufferpool v1.0.0 // indirect
|
||||
github.com/valyala/fasttemplate v1.2.2 // indirect
|
||||
github.com/vmihailenco/msgpack/v5 v5.4.0 // indirect
|
||||
github.com/vmihailenco/tagparser/v2 v2.0.0 // indirect
|
||||
github.com/x448/float16 v0.8.4 // indirect
|
||||
github.com/xdg-go/pbkdf2 v1.0.0 // indirect
|
||||
github.com/xdg-go/scram v1.1.2 // indirect
|
||||
@@ -173,16 +210,20 @@ require (
|
||||
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect
|
||||
github.com/xeipuuv/gojsonschema v1.2.0 // indirect
|
||||
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect
|
||||
github.com/yosssi/ace v0.0.5 // indirect
|
||||
github.com/youmark/pkcs8 v0.0.0-20201027041543-1326539a0a0a // indirect
|
||||
go.opentelemetry.io/otel/metric v1.16.0 // indirect
|
||||
go.opentelemetry.io/otel/metric v1.19.0 // indirect
|
||||
go.uber.org/atomic v1.11.0 // indirect
|
||||
golang.org/x/arch v0.4.0 // indirect
|
||||
golang.org/x/mod v0.12.0 // indirect
|
||||
golang.org/x/sys v0.10.0 // indirect
|
||||
golang.org/x/text v0.11.0 // indirect
|
||||
go.uber.org/multierr v1.11.0 // indirect
|
||||
golang.org/x/arch v0.5.0 // indirect
|
||||
golang.org/x/mod v0.13.0 // indirect
|
||||
golang.org/x/sys v0.13.0 // indirect
|
||||
golang.org/x/text v0.13.0 // indirect
|
||||
golang.org/x/time v0.3.0 // indirect
|
||||
golang.org/x/tools v0.11.0 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20230629202037-9506855d4529 // indirect
|
||||
golang.org/x/tools v0.14.0 // indirect
|
||||
google.golang.org/genproto v0.0.0-20231012201019-e917dd12ba7a // indirect
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20231012201019-e917dd12ba7a // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20231012201019-e917dd12ba7a // indirect
|
||||
gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc // indirect
|
||||
gopkg.in/inf.v0 v0.9.1 // indirect
|
||||
gopkg.in/ini.v1 v1.67.0 // indirect
|
||||
|
||||
@@ -17,15 +17,17 @@ cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHOb
|
||||
cloud.google.com/go v0.72.0/go.mod h1:M+5Vjvlc2wnp6tjzE102Dw08nGShTscUx2nZMufOKPI=
|
||||
cloud.google.com/go v0.74.0/go.mod h1:VV1xSbzvo+9QJOxLDaJfTjx5e+MePCpCWwvftOeQmWk=
|
||||
cloud.google.com/go v0.75.0/go.mod h1:VGuuCn7PG0dwsd5XPVm2Mm3wlh3EL55/79EKB6hlPTY=
|
||||
cloud.google.com/go v0.110.0 h1:Zc8gqp3+a9/Eyph2KDmcGaPtbKRIoqq4YTlL4NMD0Ys=
|
||||
cloud.google.com/go v0.110.8 h1:tyNdfIxjzaWctIiLYOTalaLKZ17SI44SKFW26QbOhME=
|
||||
cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o=
|
||||
cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE=
|
||||
cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc=
|
||||
cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg=
|
||||
cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc=
|
||||
cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ=
|
||||
cloud.google.com/go/compute v1.19.1 h1:am86mquDUgjGNWxiGn+5PGLbmgiWXlE/yNWpIpNvuXY=
|
||||
cloud.google.com/go/compute v1.23.0 h1:tP41Zoavr8ptEqaW6j+LQOnyBBhO7OkOMAGrgLopTwY=
|
||||
cloud.google.com/go/compute v1.23.0/go.mod h1:4tCnrn48xsqlwSAiLf1HXMQk8CONslYbdiEZc9FEIbM=
|
||||
cloud.google.com/go/compute/metadata v0.2.3 h1:mg4jlk7mCAj6xXp9UJ4fjI9VUI5rubuGBW5aJ7UnBMY=
|
||||
cloud.google.com/go/compute/metadata v0.2.3/go.mod h1:VAV5nSsACxMJvgaAuX6Pk2AawlZn8kiOGuCv6gTkwuA=
|
||||
cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE=
|
||||
cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk=
|
||||
cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I=
|
||||
@@ -42,17 +44,41 @@ dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7
|
||||
github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 h1:L/gRVlceqvL25UVaW/CKtUDjefjrs0SPonmDGUVOYP0=
|
||||
github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E=
|
||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||
github.com/BurntSushi/toml v1.3.2 h1:o7IhLm0Msx3BaB+n3Ag7L8EVlByGnpq14C4YWiu/gL8=
|
||||
github.com/BurntSushi/toml v1.3.2/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=
|
||||
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
|
||||
github.com/CloudyKit/fastprinter v0.0.0-20200109182630-33d98a066a53 h1:sR+/8Yb4slttB4vD+b9btVEnWgL3Q00OBTzVT8B9C0c=
|
||||
github.com/CloudyKit/fastprinter v0.0.0-20200109182630-33d98a066a53/go.mod h1:+3IMCy2vIlbG1XG/0ggNQv0SvxCAIpPM5b1nCz56Xno=
|
||||
github.com/CloudyKit/jet/v6 v6.2.0 h1:EpcZ6SR9n28BUGtNJSvlBqf90IpjeFr36Tizxhn/oME=
|
||||
github.com/CloudyKit/jet/v6 v6.2.0/go.mod h1:d3ypHeIRNo2+XyqnGA8s+aphtcVpjP5hPwP/Lzo7Ro4=
|
||||
github.com/Joker/hpp v1.0.0 h1:65+iuJYdRXv/XyN62C1uEmmOx3432rNG/rKlX6V7Kkc=
|
||||
github.com/Joker/hpp v1.0.0/go.mod h1:8x5n+M1Hp5hC0g8okX3sR3vFQwynaX/UgSOM9MeBKzY=
|
||||
github.com/Joker/jade v1.1.3 h1:Qbeh12Vq6BxURXT1qZBRHsDxeURB8ztcL6f3EXSGeHk=
|
||||
github.com/Joker/jade v1.1.3/go.mod h1:T+2WLyt7VH6Lp0TRxQrUYEs64nRc83wkMQrfeIQKduM=
|
||||
github.com/Masterminds/semver/v3 v3.1.1/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs=
|
||||
github.com/Microsoft/go-winio v0.6.1 h1:9/kr64B9VUZrLm5YYwbGtUJnMgqWVOdUAXu6Migciow=
|
||||
github.com/Microsoft/go-winio v0.6.1/go.mod h1:LRdKpFKfdobln8UmuiYcKPot9D2v6svN5+sAH+4kjUM=
|
||||
github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5 h1:TngWCqHvy9oXAN6lEVMRuU21PR1EtLVZJmdB18Gu3Rw=
|
||||
github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5/go.mod h1:lmUJ/7eu/Q8D7ML55dXQrVaamCz2vxCfdQBasLZfHKk=
|
||||
github.com/RaveNoX/go-jsoncommentstrip v1.0.0/go.mod h1:78ihd09MekBnJnxpICcwzCMzGrKSKYe4AqU6PDYYpjk=
|
||||
github.com/Shopify/goreferrer v0.0.0-20220729165902-8cddb4f5de06 h1:KkH3I3sJuOLP3TjA/dfr4NAY8bghDwnXiU7cTKxQqo0=
|
||||
github.com/Shopify/goreferrer v0.0.0-20220729165902-8cddb4f5de06/go.mod h1:7erjKLwalezA0k99cWs5L11HWOAPNjdUZ6RxH1BXbbM=
|
||||
github.com/VividCortex/gohistogram v1.0.0 h1:6+hBz+qvs0JOrrNhhmR7lFxo5sINxBCGXrdtl/UvroE=
|
||||
github.com/VividCortex/gohistogram v1.0.0/go.mod h1:Pf5mBqqDxYaXu3hDrrU+w6nw50o/4+TcAqDqk/vUH7g=
|
||||
github.com/ajg/form v1.5.1 h1:t9c7v8JUKu/XxOGBU0yjNpaMloxGEJhUkqFRq0ibGeU=
|
||||
github.com/ajg/form v1.5.1/go.mod h1:uL1WgH+h2mgNtvBq0339dVnzXdBETtL2LeUXaIv25UY=
|
||||
github.com/andybalholm/brotli v1.0.5 h1:8uQZIdzKmjc/iuPu7O2ioW48L81FgatrcpfFmiq/cCs=
|
||||
github.com/andybalholm/brotli v1.0.5/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig=
|
||||
github.com/apapsch/go-jsonmerge/v2 v2.0.0 h1:axGnT1gRIfimI7gJifB699GoE/oq+F2MU7Dml6nw9rQ=
|
||||
github.com/apapsch/go-jsonmerge/v2 v2.0.0/go.mod h1:lvDnEdqiQrp0O42VQGgmlKpxL1AP2+08jFMw88y4klk=
|
||||
github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
|
||||
github.com/authzed/authzed-go v0.10.0 h1:vbY7qOLxElZXcJbTJRwaath/dFF4CI1Iy9dCfVKFfEc=
|
||||
github.com/authzed/authzed-go v0.10.0/go.mod h1:9Pl5jDQJHrjbMDuCrsa+Q6Tqmi1f2pDdIn/qNGI++vA=
|
||||
github.com/authzed/grpcutil v0.0.0-20230908193239-4286bb1d6403 h1:bQeIwWWRI9bl93poTqpix4sYHi+gnXUPK7N6bMtXzBE=
|
||||
github.com/authzed/grpcutil v0.0.0-20230908193239-4286bb1d6403/go.mod h1:s3qC7V7XIbiNWERv7Lfljy/Lx25/V1Qlexb0WJuA8uQ=
|
||||
github.com/aymerick/douceur v0.2.0 h1:Mv+mAeH1Q+n9Fr+oyamOlAkUNPWPlA8PPGR0QAaYuPk=
|
||||
github.com/aymerick/douceur v0.2.0/go.mod h1:wlT5vV2O3h55X9m7iVYN0TBM0NH/MmbLnd30/FjWUq4=
|
||||
github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA=
|
||||
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
|
||||
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
|
||||
github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
|
||||
@@ -62,8 +88,9 @@ github.com/bmatcuk/doublestar v1.1.1/go.mod h1:UD6OnuiIn0yFxxA2le/rnRU1G4RaI4UvF
|
||||
github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869 h1:DDGfHa7BWjL4YnC6+E63dPcxHo2sUxDIu8g3QgEJdRY=
|
||||
github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869/go.mod h1:Ekp36dRnpXw/yCqJaO+ZrUyxD+3VXMFFr56k5XYrpB4=
|
||||
github.com/bytedance/sonic v1.5.0/go.mod h1:ED5hyg4y6t3/9Ku1R6dU/4KyJ48DZ4jPhfY1O2AihPM=
|
||||
github.com/bytedance/sonic v1.9.2 h1:GDaNjuWSGu09guE9Oql0MSTNhNCLlWwO8y/xM5BzcbM=
|
||||
github.com/bytedance/sonic v1.9.2/go.mod h1:i736AoUSYt75HyZLoJW9ERYxcy6eaN6h4BZXU064P/U=
|
||||
github.com/bytedance/sonic v1.10.0-rc/go.mod h1:ElCzW+ufi8qKqNW0FY314xriJhyJhuoJ3gFZdAHF7NM=
|
||||
github.com/bytedance/sonic v1.10.2 h1:GQebETVBxYB7JGWJtLBi07OVzWwt+8dWA00gEVW2ZFE=
|
||||
github.com/bytedance/sonic v1.10.2/go.mod h1:iZcSUejdk5aukTND/Eu/ivjQuEL0Cu9/rf50Hi0u/g4=
|
||||
github.com/caarlos0/env/v7 v7.1.0 h1:9lzTF5amyQeWHZzuZeKlCb5FWSUxpG1js43mhbY8ozg=
|
||||
github.com/caarlos0/env/v7 v7.1.0/go.mod h1:LPPWniDUq4JaO6Q41vtlyikhMknqymCLBw0eX4dcH1E=
|
||||
github.com/cenkalti/backoff v2.2.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM=
|
||||
@@ -72,11 +99,16 @@ github.com/cenkalti/backoff/v3 v3.2.2/go.mod h1:cIeZDE3IrqwwJl6VUwCN6trj1oXrTS4r
|
||||
github.com/cenkalti/backoff/v4 v4.2.1 h1:y4OZtCnogmCPw98Zjyt5a6+QwPLGkiQsYW5oUqylYbM=
|
||||
github.com/cenkalti/backoff/v4 v4.2.1/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE=
|
||||
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
||||
github.com/certifi/gocertifi v0.0.0-20210507211836-431795d63e8d h1:S2NE3iHSwP0XV47EEXL8mWmRdEfGscSJ+7EgePNgt0s=
|
||||
github.com/certifi/gocertifi v0.0.0-20210507211836-431795d63e8d/go.mod h1:sGbDF6GwGcLpkNXPUTkMRoywsNa/ol15pxFe6ERfguA=
|
||||
github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44=
|
||||
github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||
github.com/chenzhuoyu/base64x v0.0.0-20211019084208-fb5309c8db06/go.mod h1:DH46F32mSOjUmXrMHnKwZdA8wcEefY7UVqBKYGjpdQY=
|
||||
github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 h1:qSGYFH7+jGhDF8vLC+iwCD4WpbV1EBDSzWkJODFLams=
|
||||
github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311/go.mod h1:b583jCggY9gE99b6G5LEC39OIiVsWj+R97kbl5odCEk=
|
||||
github.com/chenzhuoyu/base64x v0.0.0-20230717121745-296ad89f973d h1:77cEq6EriyTZ0g/qfRdp61a3Uu/AWrgIq2s0ClJV1g0=
|
||||
github.com/chenzhuoyu/base64x v0.0.0-20230717121745-296ad89f973d/go.mod h1:8EPpVsBuRksnlj1mLy4AWzRNQYxauNi62uWcE3to6eA=
|
||||
github.com/chenzhuoyu/iasm v0.9.0 h1:9fhXjVzq5hUy2gkhhgHl95zG2cEAhw9OSGs8toWWAwo=
|
||||
github.com/chenzhuoyu/iasm v0.9.0/go.mod h1:Xjy2NpN3h7aUqeqM+woSuuvxmIe6+DDsiNLIrkAmYog=
|
||||
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
|
||||
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
|
||||
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
|
||||
@@ -85,9 +117,10 @@ github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGX
|
||||
github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
|
||||
github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
|
||||
github.com/cncf/xds/go v0.0.0-20230607035331-e9ce68804cb4 h1:/inchEIKaYC1Akx+H+gqO04wryn5h75LSazbRlnya1k=
|
||||
github.com/cncf/xds/go v0.0.0-20230607035331-e9ce68804cb4/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
|
||||
github.com/cockroachdb/apd v1.1.0/go.mod h1:8Sl8LxpKi29FqWXR16WEFZRNSz3SoPzUzeMeY4+DwBQ=
|
||||
github.com/containerd/continuity v0.4.1 h1:wQnVrjIyQ8vhU2sgOiL5T07jo+ouqc2bnKsv5/EqGhU=
|
||||
github.com/containerd/continuity v0.4.1/go.mod h1:F6PTNCKepoxEaXLQp3wDAjygEnImnZ/7o4JzpodfroQ=
|
||||
github.com/containerd/continuity v0.4.2 h1:v3y/4Yz5jwnvqPKJJ+7Wf93fyWoCB3F5EclWG023MDM=
|
||||
github.com/containerd/continuity v0.4.2/go.mod h1:F6PTNCKepoxEaXLQp3wDAjygEnImnZ/7o4JzpodfroQ=
|
||||
github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
|
||||
github.com/coreos/go-systemd v0.0.0-20190719114852-fd7a80b32e1f/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
|
||||
@@ -95,21 +128,23 @@ github.com/cpuguy83/go-md2man/v2 v2.0.1/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46t
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
|
||||
github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY=
|
||||
github.com/creack/pty v1.1.18 h1:n56/Zwd5o6whRC5PMGretI4IdRLlmBXYNjScPaBgsbY=
|
||||
github.com/creack/pty v1.1.18/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
|
||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/decred/dcrd/crypto/blake256 v1.0.1/go.mod h1:2OfgNZ5wDpcsFmHmCK5gZTPcCXqlm2ArzUIkw9czNJo=
|
||||
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0 h1:8UrgZ3GkP4i/CLijOJx79Yu+etlyjdBU4sfcs2WYQMs=
|
||||
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0/go.mod h1:v57UDF4pDQJcEfFUCRop3lJL149eHGSe9Jvczhzjo/0=
|
||||
github.com/deepmap/oapi-codegen v1.13.0 h1:cnFHelhsRQbYvanCUAbRSn/ZpkUb1HPRlQcu8YqSORQ=
|
||||
github.com/deepmap/oapi-codegen v1.13.0/go.mod h1:Amy7tbubKY9qkZOXqymI3Z6xSbndmu+atMJheLdyg44=
|
||||
github.com/deepmap/oapi-codegen v1.15.0 h1:SQqViaeb4k2vMul8gx12oDOIadEtoRqTdLkxjzqtQ90=
|
||||
github.com/deepmap/oapi-codegen v1.15.0/go.mod h1:a6KoHV7lMRwsPoEg2C6NDHiXYV3EQfiFocOlJ8dgJQE=
|
||||
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
|
||||
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78=
|
||||
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc=
|
||||
github.com/docker/cli v24.0.2+incompatible h1:QdqR7znue1mtkXIJ+ruQMGQhpw2JzMJLRXp6zpzF6tM=
|
||||
github.com/docker/cli v24.0.2+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8=
|
||||
github.com/docker/docker v24.0.2+incompatible h1:eATx+oLz9WdNVkQrr0qjQ8HvRJ4bOOxfzEo8R+dA3cg=
|
||||
github.com/docker/docker v24.0.2+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
|
||||
github.com/docker/cli v24.0.6+incompatible h1:fF+XCQCgJjjQNIMjzaSmiKJSCcfcXb3TWTcc7GAneOY=
|
||||
github.com/docker/cli v24.0.6+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8=
|
||||
github.com/docker/docker v24.0.6+incompatible h1:hceabKCtUgDqPu+qm0NgsaXf28Ljf4/pWFL7xjWWDgE=
|
||||
github.com/docker/docker v24.0.6+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
|
||||
github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ=
|
||||
github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec=
|
||||
github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4=
|
||||
@@ -118,37 +153,45 @@ github.com/dsnet/golib/memfile v0.0.0-20190531212259-571cdbcff553/go.mod h1:tXGN
|
||||
github.com/dsnet/golib/memfile v0.0.0-20200723050859-c110804dfa93/go.mod h1:tXGNW9q3RwvWt1VV2qrRKlSSz0npnh12yftCSCy2T64=
|
||||
github.com/dsnet/golib/memfile v1.0.0 h1:J9pUspY2bDCbF9o+YGwcf3uG6MdyITfh/Fk3/CaEiFs=
|
||||
github.com/dsnet/golib/memfile v1.0.0/go.mod h1:tXGNW9q3RwvWt1VV2qrRKlSSz0npnh12yftCSCy2T64=
|
||||
github.com/eclipse/paho.mqtt.golang v1.4.2 h1:66wOzfUHSSI1zamx7jR6yMEI5EuHnT1G6rNA5PM12m4=
|
||||
github.com/eclipse/paho.mqtt.golang v1.4.2/go.mod h1:JGt0RsEwEX+Xa/agj90YJ9d9DH2b7upDZMK9HRbFvCA=
|
||||
github.com/eclipse/paho.mqtt.golang v1.4.3 h1:2kwcUGn8seMUfWndX0hGbvH8r7crgcJguQNCyp70xik=
|
||||
github.com/eclipse/paho.mqtt.golang v1.4.3/go.mod h1:CSYvoAlsMkhYOXh/oKyxa8EcBci6dVkLCbo5tTC1RIE=
|
||||
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
|
||||
github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po=
|
||||
github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
|
||||
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
|
||||
github.com/envoyproxy/protoc-gen-validate v0.10.1 h1:c0g45+xCJhdgFGw7a5QAfdS4byAbud7miNWJ1WwEVf8=
|
||||
github.com/envoyproxy/protoc-gen-validate v1.0.2 h1:QkIBuU5k+x7/QXPvPPnWXWlCdaBFApVqftFV6k087DA=
|
||||
github.com/envoyproxy/protoc-gen-validate v1.0.2/go.mod h1:GpiZQP3dDbg4JouG/NNS7QWXpgx6x8QiMKdmN72jogE=
|
||||
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
|
||||
github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk=
|
||||
github.com/fatih/color v1.15.0 h1:kOqh6YHBtK8aywxGerMG2Eq3H6Qgoqeo13Bk2Mv/nBs=
|
||||
github.com/fatih/color v1.15.0/go.mod h1:0h5ZqXfHYED7Bhv2ZJamyIOUej9KtShiJESRwBDUSsw=
|
||||
github.com/fatih/structs v1.1.0 h1:Q7juDM0QtcnhCpeyLGQKyg4TOIghuNXrkL32pHAUMxo=
|
||||
github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M=
|
||||
github.com/felixge/httpsnoop v1.0.3 h1:s/nj+GCswXYzN5v2DpNMuMQYe+0DDwt5WVCU6CWBdXk=
|
||||
github.com/felixge/httpsnoop v1.0.3/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
|
||||
github.com/fiorix/go-smpp v0.0.0-20210403173735-2894b96e70ba h1:vBqABUa2HUSc6tj22Tw+ZMVGHuBzKtljM38kbRanmrM=
|
||||
github.com/fiorix/go-smpp v0.0.0-20210403173735-2894b96e70ba/go.mod h1:VfKFK7fGeCP81xEhbrOqUEh45n73Yy6jaPWwTVbxprI=
|
||||
github.com/flosch/pongo2/v4 v4.0.2 h1:gv+5Pe3vaSVmiJvh/BZa82b7/00YUGm0PIyVVLop0Hw=
|
||||
github.com/flosch/pongo2/v4 v4.0.2/go.mod h1:B5ObFANs/36VwxxlgKpdchIJHMvHB562PW+BWPhwZD8=
|
||||
github.com/frankban/quicktest v1.14.4 h1:g2rn0vABPOOXmZUj+vbmUp0lPoXEMuhTpIluN0XL9UY=
|
||||
github.com/frankban/quicktest v1.14.4/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0=
|
||||
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
|
||||
github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY=
|
||||
github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw=
|
||||
github.com/fxamacker/cbor/v2 v2.2.0/go.mod h1:TA1xS00nchWmaBnEIxPSE5oHLuJBAVvqrtAnWBwBCVo=
|
||||
github.com/fxamacker/cbor/v2 v2.4.0 h1:ri0ArlOR+5XunOP8CRUowT0pSJOwhW098ZCUyskZD88=
|
||||
github.com/fxamacker/cbor/v2 v2.4.0/go.mod h1:TA1xS00nchWmaBnEIxPSE5oHLuJBAVvqrtAnWBwBCVo=
|
||||
github.com/gabriel-vasile/mimetype v1.4.2 h1:w5qFW6JKBz9Y393Y4q372O9A7cUSequkh1Q7OhCmWKU=
|
||||
github.com/gabriel-vasile/mimetype v1.4.2/go.mod h1:zApsH/mKG4w07erKIaJPFiX0Tsq9BFQgN3qGY5GnNgA=
|
||||
github.com/fxamacker/cbor/v2 v2.5.0 h1:oHsG0V/Q6E/wqTS2O1Cozzsy69nqCiguo5Q1a1ADivE=
|
||||
github.com/fxamacker/cbor/v2 v2.5.0/go.mod h1:TA1xS00nchWmaBnEIxPSE5oHLuJBAVvqrtAnWBwBCVo=
|
||||
github.com/gabriel-vasile/mimetype v1.4.3 h1:in2uUcidCuFcDKtdcBxlR0rJ1+fsokWf+uqxgUFjbI0=
|
||||
github.com/gabriel-vasile/mimetype v1.4.3/go.mod h1:d8uq/6HKRL6CGdk+aubisF/M5GcPfT7nKyLpA0lbSSk=
|
||||
github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE=
|
||||
github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=
|
||||
github.com/gin-gonic/gin v1.9.1 h1:4idEAncQnU5cB7BeOkPtxjfCSye0AAm1R0RVIqJ+Jmg=
|
||||
github.com/gin-gonic/gin v1.9.1/go.mod h1:hPrL7YrpYKXt5YId3A/Tnip5kqbEAP+KLuI3SUcPTeU=
|
||||
github.com/go-acme/lego v2.7.2+incompatible/go.mod h1:yzMNe9CasVUhkquNvti5nAtPmG94USbYxYrZfTkIn0M=
|
||||
github.com/go-chi/chi/v5 v5.0.10 h1:rLz5avzKpjqxrYwXNfmjkrYYXOyLJd37pz53UFHC6vk=
|
||||
github.com/go-chi/chi/v5 v5.0.10/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8=
|
||||
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
|
||||
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
|
||||
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
|
||||
@@ -157,8 +200,8 @@ github.com/go-gorp/gorp/v3 v3.1.0/go.mod h1:dLEjIyyRNiXvNZ8PSmzpt1GsWAUK8kjVhEpj
|
||||
github.com/go-jose/go-jose/v3 v3.0.0 h1:s6rrhirfEP/CGIoc6p+PZAeogN2SxKav6Wp7+dyMWVo=
|
||||
github.com/go-jose/go-jose/v3 v3.0.0/go.mod h1:RNkWWRld676jZEYoV3+XK8L2ZnNSvIsxFMht0mSX+u8=
|
||||
github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
|
||||
github.com/go-kit/kit v0.12.0 h1:e4o3o3IsBfAKQh5Qbbiqyfu97Ku7jrO/JbohvztANh4=
|
||||
github.com/go-kit/kit v0.12.0/go.mod h1:lHd+EkCZPIwYItmGDDRdhinkzX2A1sj+M9biaEaizzs=
|
||||
github.com/go-kit/kit v0.13.0 h1:OoneCcHKHQ03LfBpoQCUfCluwd2Vt3ohz+kvbJneZAU=
|
||||
github.com/go-kit/kit v0.13.0/go.mod h1:phqEHMMUbyrCFCTgH48JueqrM3md2HcAZ8N3XE4FKDg=
|
||||
github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY=
|
||||
github.com/go-kit/log v0.2.1 h1:MRVx0/zhvdseW+Gza6N9rVzU/IVzaeE1SFI4raAhmBU=
|
||||
github.com/go-kit/log v0.2.1/go.mod h1:NwTd00d/i8cPZ3xOwwiv2PO5MOcx78fFErGNcVmBjv0=
|
||||
@@ -174,27 +217,34 @@ github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre
|
||||
github.com/go-ocf/go-coap/v2 v2.0.4-0.20200728125043-f38b86f047a7/go.mod h1:X9wVKcaOSx7wBxKcvrWgMQq1R2DNeA7NBLW2osIb8TM=
|
||||
github.com/go-ocf/kit v0.0.0-20200728130040-4aebdb6982bc/go.mod h1:TIsoMT/iB7t9P6ahkcOnsmvS83SIJsv9qXRfz/yLf6M=
|
||||
github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s=
|
||||
github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
|
||||
github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA=
|
||||
github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY=
|
||||
github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY=
|
||||
github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
|
||||
github.com/go-playground/validator/v10 v10.14.1 h1:9c50NUPC30zyuKprjL3vNZ0m5oG+jU0zvx4AqHGnv4k=
|
||||
github.com/go-playground/validator/v10 v10.14.1/go.mod h1:9iXMNT7sEkjXb0I+enO7QXmzG6QCsPWY4zveKFVRSyU=
|
||||
github.com/go-playground/validator/v10 v10.15.5 h1:LEBecTWb/1j5TNY1YYG2RcOUN3R7NLylN+x8TTueE24=
|
||||
github.com/go-playground/validator/v10 v10.15.5/go.mod h1:9iXMNT7sEkjXb0I+enO7QXmzG6QCsPWY4zveKFVRSyU=
|
||||
github.com/go-redis/redis/v8 v8.11.5 h1:AcZZR7igkdvfVmQTPnu9WE37LRrO/YrBH5zWyjDC0oI=
|
||||
github.com/go-redis/redis/v8 v8.11.5/go.mod h1:gREzHqY1hg6oD9ngVRbLStwAWKhA0FEgq8Jd4h5lpwo=
|
||||
github.com/go-sql-driver/mysql v1.6.0 h1:BCTh4TKNUYmOmMUcQ3IipzF5prigylS7XXjEkfCHuOE=
|
||||
github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
|
||||
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
|
||||
github.com/go-test/deep v1.0.2 h1:onZX1rnHT3Wv6cqNgYyFOOlgVKJrksuCMCRvJStbMYw=
|
||||
github.com/go-test/deep v1.0.2/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA=
|
||||
github.com/go-zoo/bone v1.3.0 h1:PY6sHq37FnQhj+4ZyqFIzJQHvrrGx0GEc3vTZZC/OsI=
|
||||
github.com/go-zoo/bone v1.3.0/go.mod h1:HI3Lhb7G3UQcAwEhOJ2WyNcsFtQX1WYHa0Hl4OBbhW8=
|
||||
github.com/gobuffalo/logger v1.0.6 h1:nnZNpxYo0zx+Aj9RfMPBm+x9zAU2OayFh/xrAWi34HU=
|
||||
github.com/gobuffalo/logger v1.0.6/go.mod h1:J31TBEHR1QLV2683OXTAItYIg8pv2JMHnF/quuAbMjs=
|
||||
github.com/gobuffalo/packd v1.0.1 h1:U2wXfRr4E9DH8IdsDLlRFwTZTK7hLfq9qT/QHXGVe/0=
|
||||
github.com/gobuffalo/packd v1.0.1/go.mod h1:PP2POP3p3RXGz7Jh6eYEf93S7vA2za6xM7QT85L4+VY=
|
||||
github.com/gobuffalo/packr/v2 v2.8.3 h1:xE1yzvnO56cUC0sTpKR3DIbxZgB54AftTFMhB2XEWlY=
|
||||
github.com/gobuffalo/packr/v2 v2.8.3/go.mod h1:0SahksCVcx4IMnigTjiFuyldmTrdTctXsOdiU5KwbKc=
|
||||
github.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y=
|
||||
github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8=
|
||||
github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU=
|
||||
github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
|
||||
github.com/gocql/gocql v1.5.2 h1:WnKf8xRQImcT/KLaEWG2pjEeryDB7K0qQN9mPs1C58Q=
|
||||
github.com/gocql/gocql v1.5.2/go.mod h1:3gM2c4D3AnkISwBxGnMMsS8Oy4y2lhbPRsH4xnJrHG8=
|
||||
github.com/gocql/gocql v1.6.0 h1:IdFdOTbnpbd0pDhl4REKQDM+Q0SzKXQ1Yh+YZZ8T/qU=
|
||||
github.com/gocql/gocql v1.6.0/go.mod h1:3gM2c4D3AnkISwBxGnMMsS8Oy4y2lhbPRsH4xnJrHG8=
|
||||
github.com/gofrs/uuid v3.3.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
|
||||
github.com/gofrs/uuid v4.0.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
|
||||
github.com/gofrs/uuid v4.4.0+incompatible h1:3qXRTX8/NbyulANqlc0lchS1gqAVxRgsuW1YrTJupqA=
|
||||
@@ -204,6 +254,8 @@ github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXP
|
||||
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
|
||||
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
|
||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
||||
github.com/golang/glog v1.1.1 h1:jxpi2eWoU84wbX9iIEyAeeoac3FLuifZpY9tcNUD9kw=
|
||||
github.com/golang/glog v1.1.1/go.mod h1:zR+okUeTbrL6EL3xHUDxZuEtGv04p5shwip1+mL/rLQ=
|
||||
github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
@@ -235,6 +287,8 @@ github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEW
|
||||
github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
||||
github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM=
|
||||
github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
||||
github.com/gomarkdown/markdown v0.0.0-20230922112808-5421fefb8386 h1:EcQR3gusLHN46TAD+G+EbaaqJArt5vHhNpXAa12PQf4=
|
||||
github.com/gomarkdown/markdown v0.0.0-20230922112808-5421fefb8386/go.mod h1:JDGcbDT52eL4fju3sZ4TeHGsQwhG9nbDV21aMyhwPoA=
|
||||
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
||||
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
||||
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
||||
@@ -248,6 +302,9 @@ github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/
|
||||
github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
|
||||
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8=
|
||||
github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU=
|
||||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
|
||||
github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=
|
||||
@@ -266,22 +323,27 @@ github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm4
|
||||
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaUGG7oYTSPP8MxqL4YI3kZKwcP4=
|
||||
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ=
|
||||
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
|
||||
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/google/uuid v1.3.1 h1:KjJaJ9iWZ3jOFZIf1Lqf4laDRCasjl0BCmnEGxkdLb4=
|
||||
github.com/google/uuid v1.3.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
|
||||
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
|
||||
github.com/googleapis/google-cloud-go-testing v0.0.0-20200911160855-bcd43fbb19e8/go.mod h1:dvDLG8qkwmyD9a/MJJN3XJcT3xFxOKAvTZGvuZmac9g=
|
||||
github.com/gookit/color v1.5.3 h1:twfIhZs4QLCtimkP7MOxlF3A0U/5cDPseRT9M/+2SCE=
|
||||
github.com/gookit/color v1.5.3/go.mod h1:NUzwzeehUfl7GIb36pqId+UGmRfQcU/WiiyTTeNjHtE=
|
||||
github.com/gookit/color v1.5.4 h1:FZmqs7XOyGgCAxmWyPslpiok1k05wmY3SJTytgvYFs0=
|
||||
github.com/gookit/color v1.5.4/go.mod h1:pZJOeOS8DM43rXbp4AZo1n9zCU2qjpcRko0b6/QJi9w=
|
||||
github.com/goombaio/namegenerator v0.0.0-20181006234301-989e774b106e h1:XmA6L9IPRdUr28a+SK/oMchGgQy159wvzXA5tJ7l+40=
|
||||
github.com/goombaio/namegenerator v0.0.0-20181006234301-989e774b106e/go.mod h1:AFIo+02s+12CEg8Gzz9kzhCbmbq6JcKNrhHffCGA9z4=
|
||||
github.com/gopcua/opcua v0.1.6 h1:B9SVRKQGzcWcwP2QPYN93Uku32+3wL+v5cgzBxE6V5I=
|
||||
github.com/gopcua/opcua v0.1.6/go.mod h1:INwnDoRxmNWAt7+tzqxuGqQkSF2c1C69VAL0c2q6AcY=
|
||||
github.com/gorilla/css v1.0.0 h1:BQqNyPTi50JCFMTw/b67hByjMVXZRwGha6wxVGkeihY=
|
||||
github.com/gorilla/css v1.0.0/go.mod h1:Dn721qIggHpt4+EFCcTLTU/vk5ySda2ReITrtgBl60c=
|
||||
github.com/gorilla/mux v1.7.4/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So=
|
||||
github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||
github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc=
|
||||
github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||
github.com/grpc-ecosystem/go-grpc-middleware v1.2.0/go.mod h1:mJzapYve32yjrKlk9GbyCZHuPgZsrbyIbyKhSzOpg6s=
|
||||
github.com/grpc-ecosystem/go-grpc-middleware v1.4.0 h1:UH//fgunKIs4JdUbpDl1VZCDaL56wXCB/5+wF6uHfaI=
|
||||
github.com/grpc-ecosystem/go-grpc-middleware v1.4.0/go.mod h1:g5qyo/la0ALbONm6Vbp88Yd8NsDy6rZz+RcrMPxvld8=
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.18.0 h1:RtRsiaGvWxcwd8y3BiRZxsylPT8hLWZ5SPcfI+3IDNk=
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.18.0/go.mod h1:TzP6duP4Py2pHLVPPQp42aoYI92+PCrVotyR5e8Vqlk=
|
||||
github.com/hailocab/go-hostpool v0.0.0-20160125115350-e80d13ce29ed h1:5upAirOpQc1Q53c0bnx2ufif5kANL7bfZWcc6VJWJd8=
|
||||
github.com/hailocab/go-hostpool v0.0.0-20160125115350-e80d13ce29ed/go.mod h1:tMWxXQ9wFIaZeTI9F+hmhFiGpFmhOHzyShyFUhRm0H4=
|
||||
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
|
||||
@@ -304,20 +366,23 @@ github.com/hashicorp/go-secure-stdlib/parseutil v0.1.7/go.mod h1:QmrqtbKuxxSWTN3
|
||||
github.com/hashicorp/go-secure-stdlib/strutil v0.1.1/go.mod h1:gKOamz3EwoIoJq7mlMIRBpVTAUn8qPCrEclOKKWhD3U=
|
||||
github.com/hashicorp/go-secure-stdlib/strutil v0.1.2 h1:kes8mmyCpxJsI7FTwtzRqEy9CdjCtrXrXGuOpxEA7Ts=
|
||||
github.com/hashicorp/go-secure-stdlib/strutil v0.1.2/go.mod h1:Gou2R9+il93BqX25LAKCLuM+y9U2T4hlwvT1yprcna4=
|
||||
github.com/hashicorp/go-sockaddr v1.0.2 h1:ztczhD1jLxIRjVejw8gFomI1BQZOe2WoVOu0SyteCQc=
|
||||
github.com/hashicorp/go-sockaddr v1.0.2/go.mod h1:rB4wwRAUzs07qva3c5SdrY/NEtAUjGlgmH/UkBUC97A=
|
||||
github.com/hashicorp/go-sockaddr v1.0.5 h1:dvk7TIXCZpmfOlM+9mlcrWmWjw/wlKT+VDq2wMvfPJU=
|
||||
github.com/hashicorp/go-sockaddr v1.0.5/go.mod h1:uoUUmtwU7n9Dv3O4SNLeFvg0SxQ3lyjsj6+CCykpaxI=
|
||||
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
||||
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
||||
github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
|
||||
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
|
||||
github.com/hashicorp/vault/api v1.9.2 h1:YjkZLJ7K3inKgMZ0wzCU9OHqc+UqMQyXsPXnf3Cl2as=
|
||||
github.com/hashicorp/vault/api v1.9.2/go.mod h1:jo5Y/ET+hNyz+JnKDt8XLAdKs+AM0G5W0Vp1IrFI8N8=
|
||||
github.com/hashicorp/vault/api v1.10.0 h1:/US7sIjWN6Imp4o/Rj1Ce2Nr5bki/AXi9vAW3p2tOJQ=
|
||||
github.com/hashicorp/vault/api v1.10.0/go.mod h1:jo5Y/ET+hNyz+JnKDt8XLAdKs+AM0G5W0Vp1IrFI8N8=
|
||||
github.com/hokaccha/go-prettyjson v0.0.0-20211117102719-0474bc63780f h1:7LYC+Yfkj3CTRcShK0KOL/w6iTiKyqqBA9a41Wnggw8=
|
||||
github.com/hokaccha/go-prettyjson v0.0.0-20211117102719-0474bc63780f/go.mod h1:pFlLw2CfqZiIBOx6BuCeRLCrfxBJipTY0nIOF/VbGcI=
|
||||
github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
|
||||
github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
|
||||
github.com/imdario/mergo v0.3.13 h1:lFzP57bqS/wsqKssCGmtLAb8A0wKjLGrve2q3PPVcBk=
|
||||
github.com/imdario/mergo v0.3.13/go.mod h1:4lJ1jqUDcsbIECGy0RUJAXNIhg+6ocWgb1ALK2O4oXg=
|
||||
github.com/imdario/mergo v0.3.16 h1:wwQJbIsHYGMUyLSPrEq1CT16AhnhNJQ51+4fdHUnCl4=
|
||||
github.com/imdario/mergo v0.3.16/go.mod h1:WBLT9ZmE3lPoWsEzCh9LPo3TiwVN+ZKEjmz+hD27ysY=
|
||||
github.com/imkira/go-interpol v1.1.0 h1:KIiKr0VSG2CUW1hl1jpiyuzuJeKUUpC8iM1AIE7N1Vk=
|
||||
github.com/imkira/go-interpol v1.1.0/go.mod h1:z0h2/2T3XF8kyEPpRgJ3kmNv+C43p+I/CoI+jC3w2iA=
|
||||
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
|
||||
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
|
||||
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
|
||||
@@ -325,6 +390,10 @@ github.com/influxdata/influxdb-client-go/v2 v2.12.3 h1:28nRlNMRIV4QbtIUvxhWqaxn0
|
||||
github.com/influxdata/influxdb-client-go/v2 v2.12.3/go.mod h1:IrrLUbCjjfkmRuaCiGQg4m2GbkaeJDcuWoxiWdQEbA0=
|
||||
github.com/influxdata/line-protocol v0.0.0-20210922203350-b1ad95c89adf h1:7JTmneyiNEwVBOHSjoMxiWAqB992atOeepeFYegn5RU=
|
||||
github.com/influxdata/line-protocol v0.0.0-20210922203350-b1ad95c89adf/go.mod h1:xaLFMmpvUxqXtVkUJfg9QmT88cDaCJ3ZKgdZ78oO8Qo=
|
||||
github.com/iris-contrib/httpexpect/v2 v2.15.2 h1:T9THsdP1woyAqKHwjkEsbCnMefsAFvk8iJJKokcJ3Go=
|
||||
github.com/iris-contrib/httpexpect/v2 v2.15.2/go.mod h1:JLDgIqnFy5loDSUv1OA2j0mb6p/rDhiCqigP22Uq9xE=
|
||||
github.com/iris-contrib/schema v0.0.6 h1:CPSBLyx2e91H2yJzPuhGuifVRnZBBJ3pCOMbOvPZaTw=
|
||||
github.com/iris-contrib/schema v0.0.6/go.mod h1:iYszG0IOsuIsfzjymw1kMzTL8YQcCWlm65f3wX8J5iA=
|
||||
github.com/ivanpirog/coloredcobra v1.0.1 h1:aURSdEmlR90/tSiWS0dMjdwOvCVUeYLfltLfbgNxrN4=
|
||||
github.com/ivanpirog/coloredcobra v1.0.1/go.mod h1:iho4nEKcnwZFiniGSdcgdvRgZNjxm+h20acv8vqmN6Q=
|
||||
github.com/jackc/chunkreader v1.0.0 h1:4s39bBR8ByfqH+DKm8rQA3E1LHZWB9XWcrz8fqaZbe0=
|
||||
@@ -339,6 +408,7 @@ github.com/jackc/pgconn v1.8.0/go.mod h1:1C2Pb36bGIP9QHGBYCjnyhqu7Rv3sGshaQUvmfG
|
||||
github.com/jackc/pgconn v1.9.0/go.mod h1:YctiPyvzfU11JFxoXokUOOKQXQmDMoJL9vJzHH8/2JY=
|
||||
github.com/jackc/pgconn v1.9.1-0.20210724152538-d89c8390a530/go.mod h1:4z2w8XhRbP1hYxkpTuBjTS3ne3J48K83+u0zoyvg2pI=
|
||||
github.com/jackc/pgconn v1.14.0 h1:vrbA9Ud87g6JdFWkHTJXppVce58qPIdP7N8y0Ml/A7Q=
|
||||
github.com/jackc/pgconn v1.14.0/go.mod h1:9mBNlny0UvkgJdCDvdVHYSjI+8tD2rnKK69Wz8ti++E=
|
||||
github.com/jackc/pgerrcode v0.0.0-20220416144525-469b46aa5efa h1:s+4MhCQ6YrzisK6hFJUX53drDT4UsSW3DEhKn0ifuHw=
|
||||
github.com/jackc/pgerrcode v0.0.0-20220416144525-469b46aa5efa/go.mod h1:a/s9Lp5W7n/DD0VrVoyJ00FbP2ytTPDVOivvn2bMlds=
|
||||
github.com/jackc/pgio v1.0.0 h1:g12B9UwVnzGhueNavwioyEEpAmqMe1E/BN9ES+8ovkE=
|
||||
@@ -357,6 +427,7 @@ github.com/jackc/pgproto3/v2 v2.0.0-rc3.0.20190831210041-4c03ce451f29/go.mod h1:
|
||||
github.com/jackc/pgproto3/v2 v2.0.6/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA=
|
||||
github.com/jackc/pgproto3/v2 v2.1.1/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA=
|
||||
github.com/jackc/pgproto3/v2 v2.3.2 h1:7eY55bdBeCz1F2fTzSz69QC+pG46jYq9/jtSPiJ5nn0=
|
||||
github.com/jackc/pgproto3/v2 v2.3.2/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA=
|
||||
github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b/go.mod h1:vsD4gTJCa9TptPL8sPkXrLZ+hDuNrZCnj29CQpr4X1E=
|
||||
github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a h1:bbPeKD0xmW/Y25WS6cokEszi5g+S0QxI/d45PkRi7Nk=
|
||||
github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM=
|
||||
@@ -371,20 +442,37 @@ github.com/jackc/pgx/v4 v4.0.0-20190421002000-1b8f0016e912/go.mod h1:no/Y67Jkk/9
|
||||
github.com/jackc/pgx/v4 v4.0.0-pre1.0.20190824185557-6972a5742186/go.mod h1:X+GQnOEnf1dqHGpw7JmHqHc1NxDoalibchSk9/RWuDc=
|
||||
github.com/jackc/pgx/v4 v4.12.1-0.20210724153913-640aa07df17c h1:Dznn52SgVIVst9UyOT9brctYUgxs+CvVfPaC3jKrA50=
|
||||
github.com/jackc/pgx/v4 v4.12.1-0.20210724153913-640aa07df17c/go.mod h1:1QD0+tgSXP7iUjYm9C1NxKhny7lq6ee99u/z+IHFcgs=
|
||||
github.com/jackc/pgx/v5 v5.4.1 h1:oKfB/FhuVtit1bBM3zNRRsZ925ZkMN3HXL+LgLUM9lE=
|
||||
github.com/jackc/pgx/v5 v5.4.1/go.mod h1:q6iHT8uDNXWiFNOlRqJzBTaSH3+2xCXkokxHZC5qWFY=
|
||||
github.com/jackc/pgx/v5 v5.4.3 h1:cxFyXhxlvAifxnkKKdlxv8XqUf59tDlYjnV5YYfsJJY=
|
||||
github.com/jackc/pgx/v5 v5.4.3/go.mod h1:Ig06C2Vu0t5qXC60W8sqIthScaEnFvojjj9dSljmHRA=
|
||||
github.com/jackc/puddle v0.0.0-20190413234325-e4ced69a3a2b/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
|
||||
github.com/jackc/puddle v0.0.0-20190608224051-11cab39313c9/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
|
||||
github.com/jackc/puddle v1.1.3/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
|
||||
github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
|
||||
github.com/jmoiron/sqlx v1.3.5 h1:vFFPA71p1o5gAeqtEAwLU4dnX2napprKtHr7PYIcN3g=
|
||||
github.com/jmoiron/sqlx v1.3.5/go.mod h1:nRVWtLre0KfCLJvgxzCsLVMogSvQ1zNJtpYr2Ccp0mQ=
|
||||
github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
|
||||
github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
|
||||
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
|
||||
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
|
||||
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
|
||||
github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk=
|
||||
github.com/juju/gnuflag v0.0.0-20171113085948-2ce1bb71843d/go.mod h1:2PavIy+JPciBPrBUjwbNvtwB6RQlve+hkpll6QSNmOE=
|
||||
github.com/jzelinskie/stringz v0.0.2 h1:OSjMEYvz8tjhovgZ/6cGcPID736ubeukr35mu6RYAmg=
|
||||
github.com/jzelinskie/stringz v0.0.2/go.mod h1:hHYbgxJuNLRw91CmpuFsYEOyQqpDVFg8pvEh23vy4P0=
|
||||
github.com/karrick/godirwalk v1.16.1 h1:DynhcF+bztK8gooS0+NDJFrdNZjJ3gzVzC545UNA9iw=
|
||||
github.com/karrick/godirwalk v1.16.1/go.mod h1:j4mkqPuvaLI8mp1DroR3P6ad7cyYd4c1qeJ3RV7ULlk=
|
||||
github.com/kataras/blocks v0.0.8 h1:MrpVhoFTCR2v1iOOfGng5VJSILKeZZI+7NGfxEh3SUM=
|
||||
github.com/kataras/blocks v0.0.8/go.mod h1:9Jm5zx6BB+06NwA+OhTbHW1xkMOYxahnqTN5DveZ2Yg=
|
||||
github.com/kataras/golog v0.1.9 h1:vLvSDpP7kihFGKFAvBSofYo7qZNULYSHOH2D7rPTKJk=
|
||||
github.com/kataras/golog v0.1.9/go.mod h1:jlpk/bOaYCyqDqH18pgDHdaJab72yBE6i0O3s30hpWY=
|
||||
github.com/kataras/iris/v12 v12.2.7 h1:C9KWZmZT5pB5f2ot1XYWDBdi5XeTz0CGweHRXCDARZg=
|
||||
github.com/kataras/iris/v12 v12.2.7/go.mod h1:mD76k/tIBFy8pHTFIgUPrVrkI4lTKvFbIcfbStJSBnA=
|
||||
github.com/kataras/pio v0.0.12 h1:o52SfVYauS3J5X08fNjlGS5arXHjW/ItLkyLcKjoH6w=
|
||||
github.com/kataras/pio v0.0.12/go.mod h1:ODK/8XBhhQ5WqrAhKy+9lTPS7sBf6O3KcLhc9klfRcY=
|
||||
github.com/kataras/sitemap v0.0.6 h1:w71CRMMKYMJh6LR2wTgnk5hSgjVNB9KL60n5e2KHvLY=
|
||||
github.com/kataras/sitemap v0.0.6/go.mod h1:dW4dOCNs896OR1HmG+dMLdT7JjDk7mYBzoIRwuj5jA4=
|
||||
github.com/kataras/tunnel v0.0.4 h1:sCAqWuJV7nPzGrlb0os3j49lk2JhILT0rID38NHNLpA=
|
||||
github.com/kataras/tunnel v0.0.4/go.mod h1:9FkU4LaeifdMWqZu7o20ojmW4B7hdhv2CMLwfnHGpYw=
|
||||
github.com/kelseyhightower/envconfig v1.4.0/go.mod h1:cccZRl6mQpaq41TPp5QxidR+Sa3axMbJDNb//FQX6Gg=
|
||||
github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=
|
||||
github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00=
|
||||
@@ -392,29 +480,32 @@ github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI
|
||||
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
||||
github.com/klauspost/compress v1.10.4/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs=
|
||||
github.com/klauspost/compress v1.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk=
|
||||
github.com/klauspost/compress v1.16.7 h1:2mk3MPGNzKyxErAw8YaohYh69+pa4sIQSC0fPGCFR9I=
|
||||
github.com/klauspost/compress v1.16.7/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE=
|
||||
github.com/klauspost/compress v1.17.0 h1:Rnbp4K9EjcDuVuHtd0dgA4qNuv9yKDYKK1ulpJwgrqM=
|
||||
github.com/klauspost/compress v1.17.0/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE=
|
||||
github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
|
||||
github.com/klauspost/cpuid/v2 v2.2.5 h1:0E5MSMDEoAulmXNFquVs//DdoomxaoTY1kUhbc/qbZg=
|
||||
github.com/klauspost/cpuid/v2 v2.2.5/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws=
|
||||
github.com/knz/go-libedit v1.10.1/go.mod h1:MZTVkCWyz0oBc7JOWP3wNAzd002ZbM/5hgShxwh4x8M=
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||
github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg=
|
||||
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
|
||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
||||
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
|
||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/pty v1.1.8/go.mod h1:O1sed60cT9XZ5uDucP5qwvh+TE3NnUj51EiZO/lmSfw=
|
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||
github.com/labstack/echo/v4 v4.10.2 h1:n1jAhnq/elIFTHr1EYpiYtyKgx4RW9ccVgkqByZaN2M=
|
||||
github.com/labstack/echo/v4 v4.10.2/go.mod h1:OEyqf2//K1DFdE57vw2DRgWY0M7s65IVQO2FzvI4J5k=
|
||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||
github.com/labstack/echo/v4 v4.11.2 h1:T+cTLQxWCDfqDEoydYm5kCobjmHwOwcv4OJAPHilmdE=
|
||||
github.com/labstack/echo/v4 v4.11.2/go.mod h1:UcGuQ8V6ZNRmSweBIJkPvGfwCMIlFmiqrPqiEBfPYws=
|
||||
github.com/labstack/gommon v0.4.0 h1:y7cvthEAEbU0yHOf4axH8ZG2NH8knB9iNSoTO8dyIk8=
|
||||
github.com/labstack/gommon v0.4.0/go.mod h1:uW6kP17uPlLJsD3ijUYn3/M5bAxtlZhMI6m3MFxTMTM=
|
||||
github.com/leodido/go-urn v1.2.4 h1:XlAE/cm/ms7TE/VMVoduSpNBoyc2dOxHs5MZSwAN63Q=
|
||||
github.com/leodido/go-urn v1.2.4/go.mod h1:7ZrI8mTSeBSHl/UaRyKQW1qZeMgak41ANeCNaVckg+4=
|
||||
github.com/lestrrat-go/blackmagic v1.0.1 h1:lS5Zts+5HIC/8og6cGHb0uCcNCa3OUt1ygh3Qz2Fe80=
|
||||
github.com/lestrrat-go/blackmagic v1.0.1/go.mod h1:UrEqBzIR2U6CnzVyUtfM6oZNMt/7O7Vohk2J0OGSAtU=
|
||||
github.com/lestrrat-go/blackmagic v1.0.2 h1:Cg2gVSc9h7sz9NOByczrbUvLopQmXrfFx//N+AkAr5k=
|
||||
github.com/lestrrat-go/blackmagic v1.0.2/go.mod h1:UrEqBzIR2U6CnzVyUtfM6oZNMt/7O7Vohk2J0OGSAtU=
|
||||
github.com/lestrrat-go/httpcc v1.0.1 h1:ydWCStUeJLkpYyjLDHihupbn2tYmZ7m22BGkcvZZrIE=
|
||||
github.com/lestrrat-go/httpcc v1.0.1/go.mod h1:qiltp3Mt56+55GPVCbTdM9MlqhvzyuL6W/NMDA8vA5E=
|
||||
github.com/lestrrat-go/httprc v1.0.4 h1:bAZymwoZQb+Oq8MEbyipag7iSq6YIga8Wj6GOiJGdI8=
|
||||
@@ -423,8 +514,8 @@ github.com/lestrrat-go/iter v0.0.0-20200422075355-fc1769541911/go.mod h1:zIdgO1m
|
||||
github.com/lestrrat-go/iter v1.0.2 h1:gMXo1q4c2pHmC3dn8LzRhJfP1ceCbgSiT9lUydIzltI=
|
||||
github.com/lestrrat-go/iter v1.0.2/go.mod h1:Momfcq3AnRlRjI5b5O8/G5/BvpzrhoFTZcn06fEOPt4=
|
||||
github.com/lestrrat-go/jwx v1.0.2/go.mod h1:TPF17WiSFegZo+c20fdpw49QD+/7n4/IsGvEmCSWwT0=
|
||||
github.com/lestrrat-go/jwx/v2 v2.0.11 h1:ViHMnaMeaO0qV16RZWBHM7GTrAnX2aFLVKofc7FuKLQ=
|
||||
github.com/lestrrat-go/jwx/v2 v2.0.11/go.mod h1:ZtPtMFlrfDrH2Y0iwfa3dRFn8VzwBrB+cyrm3IBWdDg=
|
||||
github.com/lestrrat-go/jwx/v2 v2.0.13 h1:XdxzJbudGaHEoNmyJACAT8aFCB+DmviiaiMoZwuJoUo=
|
||||
github.com/lestrrat-go/jwx/v2 v2.0.13/go.mod h1:UzXMzcV99p9/xe1JsIb336NJDGXLsleR+Qj3ucEDtfI=
|
||||
github.com/lestrrat-go/option v1.0.0/go.mod h1:5ZHFbivi4xwXxhxY9XHDe2FHo6/Z7WWmtT7T5nBBp3I=
|
||||
github.com/lestrrat-go/option v1.0.1 h1:oAzP2fvZGQKWkvHa1/SAcFolBEca1oN+mQ7eooNBEYU=
|
||||
github.com/lestrrat-go/option v1.0.1/go.mod h1:5ZHFbivi4xwXxhxY9XHDe2FHo6/Z7WWmtT7T5nBBp3I=
|
||||
@@ -434,23 +525,30 @@ github.com/lib/pq v1.1.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
|
||||
github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
|
||||
github.com/lib/pq v1.10.2/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
|
||||
github.com/lib/pq v1.10.7 h1:p7ZhMD+KsSRozJr34udlUrhboJwWAgCg34+/ZZNvZZw=
|
||||
github.com/lib/pq v1.10.7/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
|
||||
github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY=
|
||||
github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0=
|
||||
github.com/mainflux/callhome v0.0.0-20230626140149-b03b1f4c46f2 h1:QN+yhU6Twwwwz8Mu9u12f2TbPsmM/zIvndAhH1dIdWU=
|
||||
github.com/mainflux/callhome v0.0.0-20230626140149-b03b1f4c46f2/go.mod h1:q4cTH8I3Y6kDyocJh5dBppuv4dY9drb/2kVdB6FP124=
|
||||
github.com/mailgun/raymond/v2 v2.0.48 h1:5dmlB680ZkFG2RN/0lvTAghrSxIESeu9/2aeDqACtjw=
|
||||
github.com/mailgun/raymond/v2 v2.0.48/go.mod h1:lsgvL50kgt1ylcFJYZiULi5fjPBkkhNfj4KA0W54Z18=
|
||||
github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0=
|
||||
github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
|
||||
github.com/mainflux/callhome v0.0.0-20230920140432-33c5663382ce h1:BX3a91DXaewIBd+3zp9GpUZCpFfZX2FRLUJ2Mpas7jU=
|
||||
github.com/mainflux/callhome v0.0.0-20230920140432-33c5663382ce/go.mod h1:a8I+g3fXeefHGoGBG69Ndj1Dg1nrahaM2OWdUc9K4EI=
|
||||
github.com/mainflux/mproxy v0.3.1-0.20230822124450-4b4dfe600cc2 h1:D5Ofrffx/4FWehczvJbmzD8lfcOkxcIS4XZE/fwl4mo=
|
||||
github.com/mainflux/mproxy v0.3.1-0.20230822124450-4b4dfe600cc2/go.mod h1:nG9MP2YbS8ax26Z8mvJOYohhi3ebYwSlOmePzbfv2ew=
|
||||
github.com/mainflux/senml v1.5.0 h1:GAd1y1eMohfa6sVYcr2iQfVfkkh9l/q7B1TWF5L68xs=
|
||||
github.com/mainflux/senml v1.5.0/go.mod h1:SMX76mM5yenjLVjZOM27+njCGkP+AA64O46nRQiBRlE=
|
||||
github.com/markbates/errx v1.1.0 h1:QDFeR+UP95dO12JgW+tgi2UVfo0V8YBHiUIOaeBPiEI=
|
||||
github.com/markbates/errx v1.1.0/go.mod h1:PLa46Oex9KNbVDZhKel8v1OT7hD5JZ2eI7AHhA0wswc=
|
||||
github.com/markbates/oncer v1.0.0 h1:E83IaVAHygyndzPimgUYJjbshhDTALZyXxvk9FOlQRY=
|
||||
github.com/markbates/oncer v1.0.0/go.mod h1:Z59JA581E9GP6w96jai+TGqafHPW+cPfRxz2aSZ0mcI=
|
||||
github.com/markbates/safe v1.0.1 h1:yjZkbvRM6IzKj9tlu/zMJLS0n/V351OZWRnF3QfaUxI=
|
||||
github.com/markbates/safe v1.0.1/go.mod h1:nAqgmRi7cY2nqMc92/bSEeQA+R4OheNU2T1kNSCBdG0=
|
||||
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
|
||||
github.com/mattn/go-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcnceauSikq3lYCQ=
|
||||
github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
|
||||
github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
|
||||
github.com/mattn/go-colorable v0.1.11/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4=
|
||||
github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4=
|
||||
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
|
||||
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
|
||||
github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
|
||||
@@ -463,14 +561,20 @@ github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APP
|
||||
github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||
github.com/mattn/go-sqlite3 v1.14.6/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU=
|
||||
github.com/mattn/go-sqlite3 v1.14.15 h1:vfoHhTN1af61xCRSWzFIWzx2YskyMTwHLrExkBOjvxI=
|
||||
github.com/mattn/go-sqlite3 v1.14.15/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg=
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo=
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4=
|
||||
github.com/microcosm-cc/bluemonday v1.0.26 h1:xbqSvqzQMeEHCqMi64VAs4d8uy6Mequs3rQ0k/Khz58=
|
||||
github.com/microcosm-cc/bluemonday v1.0.26/go.mod h1:JyzOCs9gkyQyjs+6h10UEVSe02CGwkhd72Xdqh78TWs=
|
||||
github.com/miekg/dns v1.1.29/go.mod h1:KNUDUusw/aVsxyTYZM1oqvCicbwhgbNgztCETuNZ7xM=
|
||||
github.com/minio/highwayhash v1.0.2 h1:Aak5U0nElisjDCfPSG79Tgzkn2gl66NxOMspRrKnA/g=
|
||||
github.com/minio/highwayhash v1.0.2/go.mod h1:BQskDq+xkJ12lmlUUi7U0M5Swg3EWR+dLTk+kldvVxY=
|
||||
github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc=
|
||||
github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y=
|
||||
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
|
||||
github.com/mitchellh/go-wordwrap v1.0.0/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo=
|
||||
github.com/mitchellh/go-wordwrap v1.0.1 h1:TLuKupo69TCn6TQSyGxwI1EblZZEsQ0vMlAFQflz0v0=
|
||||
github.com/mitchellh/go-wordwrap v1.0.1/go.mod h1:R62XHJLzvMFRBbcrT7m7WgmE1eOyTSsCt+hzestvNj0=
|
||||
github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
|
||||
github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
|
||||
github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
|
||||
@@ -484,25 +588,31 @@ github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjY
|
||||
github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe/go.mod h1:wL8QJuTMNUDYhXwkmfOly8iTdp5TEcJFWZD2D7SIkUc=
|
||||
github.com/montanaflynn/stats v0.7.1 h1:etflOAAHORrCC44V+aR6Ftzort912ZU+YLiSTuV8eaE=
|
||||
github.com/montanaflynn/stats v0.7.1/go.mod h1:etXPPgVO6n31NxCd9KQUMvCM+ve0ruNzt6R8Bnaayow=
|
||||
github.com/nats-io/jwt/v2 v2.0.3 h1:i/O6cmIsjpcQyWDYNcq2JyZ3/VTF8SJ4JWluI5OhpvI=
|
||||
github.com/nats-io/nats-server/v2 v2.5.0 h1:wsnVaaXH9VRSg+A2MVg5Q727/CqxnmPLGFQ3YZYKTQg=
|
||||
github.com/nats-io/nats.go v1.27.1 h1:OuYnal9aKVSnOzLQIzf7554OXMCG7KbaTkCSBHRcSoo=
|
||||
github.com/nats-io/nats.go v1.27.1/go.mod h1:XpbWUlOElGwTYbMR7imivs7jJj9GtK7ypv321Wp6pjc=
|
||||
github.com/nats-io/nkeys v0.4.4 h1:xvBJ8d69TznjcQl9t6//Q5xXuVhyYiSos6RPtvQNTwA=
|
||||
github.com/nats-io/nkeys v0.4.4/go.mod h1:XUkxdLPTufzlihbamfzQ7mw/VGx6ObUs+0bN5sNvt64=
|
||||
github.com/nats-io/jwt/v2 v2.5.0 h1:WQQ40AAlqqfx+f6ku+i0pOVm+ASirD4fUh+oQsiE9Ak=
|
||||
github.com/nats-io/jwt/v2 v2.5.0/go.mod h1:24BeQtRwxRV8ruvC4CojXlx/WQ/VjuwlYiH+vu/+ibI=
|
||||
github.com/nats-io/nats-server/v2 v2.8.4 h1:0jQzze1T9mECg8YZEl8+WYUXb9JKluJfCBriPUtluB4=
|
||||
github.com/nats-io/nats-server/v2 v2.8.4/go.mod h1:8zZa+Al3WsESfmgSs98Fi06dRWLH5Bnq90m5bKD/eT4=
|
||||
github.com/nats-io/nats.go v1.30.2 h1:aloM0TGpPorZKQhbAkdCzYDj+ZmsJDyeo3Gkbr72NuY=
|
||||
github.com/nats-io/nats.go v1.30.2/go.mod h1:dcfhUgmQNN4GJEfIb2f9R7Fow+gzBF4emzDHrVBd5qM=
|
||||
github.com/nats-io/nkeys v0.4.5 h1:Zdz2BUlFm4fJlierwvGK+yl20IAKUm7eV6AAZXEhkPk=
|
||||
github.com/nats-io/nkeys v0.4.5/go.mod h1:XUkxdLPTufzlihbamfzQ7mw/VGx6ObUs+0bN5sNvt64=
|
||||
github.com/nats-io/nuid v1.0.1 h1:5iA8DT8V7q8WK2EScv2padNa/rTESc1KdnPw4TC2paw=
|
||||
github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c=
|
||||
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
|
||||
github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE=
|
||||
github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU=
|
||||
github.com/oklog/ulid/v2 v2.1.0 h1:+9lhoxAP56we25tyYETBBY1YLA2SaoLvUFgrP2miPJU=
|
||||
github.com/oklog/ulid/v2 v2.1.0/go.mod h1:rcEKHmBBKfef9DhnvX7y1HZBYxjXb0cP5ExxNsTT1QQ=
|
||||
github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE=
|
||||
github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU=
|
||||
github.com/onsi/gomega v1.18.1 h1:M1GfJqGRrBrrGGsbxzV5dqM2U2ApXefZCQpkukxYRLE=
|
||||
github.com/onsi/gomega v1.18.1/go.mod h1:0q+aL8jAiMXy9hbwj2mr5GziHiwhAIQpFmmtT5hitRs=
|
||||
github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U=
|
||||
github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM=
|
||||
github.com/opencontainers/image-spec v1.0.2 h1:9yCKha/T5XdGtO0q9Q9a6T5NUCsTn/DrBg0D7ufOcFM=
|
||||
github.com/opencontainers/image-spec v1.0.2/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0=
|
||||
github.com/opencontainers/runc v1.1.7 h1:y2EZDS8sNng4Ksf0GUYNhKbTShZJPJg1FiXJNH/uoCk=
|
||||
github.com/opencontainers/runc v1.1.7/go.mod h1:CbUumNnWCuTGFukNXahoo/RFBZvDAgRh/smNYNOhA50=
|
||||
github.com/opencontainers/runc v1.1.9 h1:XR0VIHTGce5eWPkaPesqTBrhW2yAcaraWfsEalNwQLM=
|
||||
github.com/opencontainers/runc v1.1.9/go.mod h1:CbUumNnWCuTGFukNXahoo/RFBZvDAgRh/smNYNOhA50=
|
||||
github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o=
|
||||
github.com/ory/dockertest/v3 v3.10.0 h1:4K3z2VMe8Woe++invjaTB7VRyQXQy5UY+loujO4aNE4=
|
||||
github.com/ory/dockertest/v3 v3.10.0/go.mod h1:nr57ZbRWMqfsdGdFNLHz5jjNdDb7VVFnzAeW1n5N1Lg=
|
||||
@@ -513,8 +623,8 @@ github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTK
|
||||
github.com/pborman/getopt v0.0.0-20170112200414-7148bc3a4c30/go.mod h1:85jBQOZwpVEaDAr341tbn15RS4fCAsIst0qp7i8ex1o=
|
||||
github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3ve8=
|
||||
github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c=
|
||||
github.com/pelletier/go-toml/v2 v2.0.8 h1:0ctb6s9mE31h0/lhu+J6OPmVeDxJn+kYnJc2jZR9tGQ=
|
||||
github.com/pelletier/go-toml/v2 v2.0.8/go.mod h1:vuYfssBdrU2XDZ9bYydBu6t+6a6PYNcZljzZR9VXg+4=
|
||||
github.com/pelletier/go-toml/v2 v2.1.0 h1:FnwAJ4oYMvbT/34k9zzHuZNrhlz48GB3/s6at6/MHO4=
|
||||
github.com/pelletier/go-toml/v2 v2.1.0/go.mod h1:tJU2Z3ZkXwnxa4DPO899bsyIoywizdUvyaeZurnPPDc=
|
||||
github.com/pion/dtls/v2 v2.0.1-0.20200503085337-8e86b3a7d585/go.mod h1:/GahSOC8ZY/+17zkaGJIG4OUkSGAcZu/N/g3roBOCkM=
|
||||
github.com/pion/dtls/v2 v2.0.10-0.20210502094952-3dc563b9aede/go.mod h1:86wv5dgx2J/z871nUR+5fTTY9tISLUlo+C5Gm86r1Hs=
|
||||
github.com/pion/dtls/v2 v2.2.7 h1:cSUBsETxepsCSFSxC3mc/aDo14qQLMSL+O6IjG28yV8=
|
||||
@@ -524,8 +634,9 @@ github.com/pion/logging v0.2.2/go.mod h1:k0/tDVsRCX2Mb2ZEmTqNa7CWsQPc+YYCB7Q+5pa
|
||||
github.com/pion/transport v0.10.0/go.mod h1:BnHnUipd0rZQyTVB2SBGojFHT9CBt5C5TcsJSQGkvSE=
|
||||
github.com/pion/transport v0.12.2/go.mod h1:N3+vZQD9HlDP5GWkZ85LohxNsDcNgofQmyL6ojX5d8Q=
|
||||
github.com/pion/transport v0.12.3/go.mod h1:OViWW9SP2peE/HbwBvARicmAVnesphkNkCVZIWJ6q9A=
|
||||
github.com/pion/transport/v2 v2.2.1 h1:7qYnCBlpgSJNYMbLCKuSY9KbQdBFoETvPNETv0y4N7c=
|
||||
github.com/pion/transport/v2 v2.2.1/go.mod h1:cXXWavvCnFF6McHTft3DWS9iic2Mftcz1Aq29pGcU5g=
|
||||
github.com/pion/transport/v2 v2.2.4 h1:41JJK6DZQYSeVLxILA2+F4ZkKb4Xd/tFJZRFZQ9QAlo=
|
||||
github.com/pion/transport/v2 v2.2.4/go.mod h1:q2U/tf9FEfnSBGSW6w5Qp5PFWRLRj3NjLhCCgpRK4p0=
|
||||
github.com/pion/udp v0.1.1/go.mod h1:6AFo+CMdKQm7UiA0eUPA8/eVCTx8jBIITLZHc9DWX5M=
|
||||
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||
@@ -538,63 +649,79 @@ github.com/plgd-dev/go-coap/v2 v2.6.0/go.mod h1:wm9fcL58Ky442Krix74S9Y54rCo36u59
|
||||
github.com/plgd-dev/kit v0.0.0-20200819113605-d5fcf3e94f63/go.mod h1:Yl9zisyXfPdtP9hTWlJqjJYXmgU/jtSDKttz9/CeD90=
|
||||
github.com/plgd-dev/kit/v2 v2.0.0-20211006190727-057b33161b90 h1:TC1HJ/UbyflJFPvaOdGmNZ5TeFGex1/dyr9urNGLy7M=
|
||||
github.com/plgd-dev/kit/v2 v2.0.0-20211006190727-057b33161b90/go.mod h1:Z7oKFLSGQjdi8eInxwFCs0tSApuEM1o0qNck+sJYp4M=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
|
||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI=
|
||||
github.com/poy/onpar v1.1.2 h1:QaNrNiZx0+Nar5dLgTVp5mXkyoVFIbepjyEoGSnhbAY=
|
||||
github.com/prometheus/client_golang v1.16.0 h1:yk/hx9hDbrGHovbci4BY+pRMfSuuat626eFsHb7tmT8=
|
||||
github.com/prometheus/client_golang v1.16.0/go.mod h1:Zsulrv/L9oM40tJ7T815tM89lFEugiJ9HzIqaAx4LKc=
|
||||
github.com/poy/onpar v1.1.2/go.mod h1:6X8FLNoxyr9kkmnlqpK6LSoiOtrO6MICtWwEuWkLjzg=
|
||||
github.com/prometheus/client_golang v1.17.0 h1:rl2sfwZMtSthVU752MqfjQozy7blglC+1SOtjMAMh+Q=
|
||||
github.com/prometheus/client_golang v1.17.0/go.mod h1:VeL+gMmOAxkS2IqfCq0ZmHSL+LjWfWDUmp1mBz9JgUY=
|
||||
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||
github.com/prometheus/client_model v0.4.0 h1:5lQXD3cAg1OXBf4Wq03gTrXHeaV0TQvGfUooCfx1yqY=
|
||||
github.com/prometheus/client_model v0.4.0/go.mod h1:oMQmHW1/JoDwqLtg57MGgP/Fb1CJEYF2imWWhWtMkYU=
|
||||
github.com/prometheus/client_model v0.5.0 h1:VQw1hfvPvk3Uv6Qf29VrPF32JB6rtbgI6cYPYQjL0Qw=
|
||||
github.com/prometheus/client_model v0.5.0/go.mod h1:dTiFglRmd66nLR9Pv9f0mZi7B7fk5Pm3gvsjB5tr+kI=
|
||||
github.com/prometheus/common v0.44.0 h1:+5BrQJwiBB9xsMygAB3TNvpQKOwlkc25LbISbrdOOfY=
|
||||
github.com/prometheus/common v0.44.0/go.mod h1:ofAIvZbQ1e/nugmZGz4/qCb9Ap1VoSTIO7x0VV9VvuY=
|
||||
github.com/prometheus/procfs v0.11.0 h1:5EAgkfkMl659uZPbe9AS2N68a7Cc1TJbPEuGzFuRbyk=
|
||||
github.com/prometheus/procfs v0.11.0/go.mod h1:nwNm2aOCAYw8uTR/9bWRREkZFxAUcWzPHWJq+XBB/FM=
|
||||
github.com/rabbitmq/amqp091-go v1.8.1 h1:RejT1SBUim5doqcL6s7iN6SBmsQqyTgXb1xMlH0h1hA=
|
||||
github.com/rabbitmq/amqp091-go v1.8.1/go.mod h1:+jPrT9iY2eLjRaMSRHUhc3z14E/l85kv/f+6luSD3pc=
|
||||
github.com/prometheus/procfs v0.12.0 h1:jluTpSng7V9hY0O2R9DzzJHYb2xULk9VTR1V1R/k6Bo=
|
||||
github.com/prometheus/procfs v0.12.0/go.mod h1:pcuDEFsWDnvcgNzo4EEweacyhjeA9Zk3cnaOZAZEfOo=
|
||||
github.com/rabbitmq/amqp091-go v1.9.0 h1:qrQtyzB4H8BQgEuJwhmVQqVHB9O4+MNDJCCAcpc3Aoo=
|
||||
github.com/rabbitmq/amqp091-go v1.9.0/go.mod h1:+jPrT9iY2eLjRaMSRHUhc3z14E/l85kv/f+6luSD3pc=
|
||||
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
|
||||
github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ=
|
||||
github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog=
|
||||
github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ=
|
||||
github.com/rs/zerolog v1.13.0/go.mod h1:YbFCdg8HfsridGWAh22vktObvhZbQsZXe4/zB0OKkWU=
|
||||
github.com/rs/zerolog v1.15.0/go.mod h1:xYTKnLHcpfU2225ny5qZjxnj9NvkumZYjJHlAThCjNc=
|
||||
github.com/rubenv/sql-migrate v1.5.1 h1:WsZo4jPQfjmddDTh/suANP2aKPA7/ekN0LzuuajgQEo=
|
||||
github.com/rubenv/sql-migrate v1.5.1/go.mod h1:H38GW8Vqf8F0Su5XignRyaRcbXbJunSWxs+kmzlg0Is=
|
||||
github.com/rubenv/sql-migrate v1.5.2 h1:bMDqOnrJVV/6JQgQ/MxOpU+AdO8uzYYA/TxFUBzFtS0=
|
||||
github.com/rubenv/sql-migrate v1.5.2/go.mod h1:H38GW8Vqf8F0Su5XignRyaRcbXbJunSWxs+kmzlg0Is=
|
||||
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||
github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
|
||||
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||
github.com/ryanuber/columnize v2.1.0+incompatible/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
|
||||
github.com/ryanuber/go-glob v1.0.0 h1:iQh3xXAumdQ+4Ufa5b25cRpC5TYKlno6hsv6Cb3pkBk=
|
||||
github.com/ryanuber/go-glob v1.0.0/go.mod h1:807d1WSdnB0XRJzKNil9Om6lcp/3a0v4qIHxIXzX/Yc=
|
||||
github.com/sagikazarmark/locafero v0.3.0 h1:zT7VEGWC2DTflmccN/5T1etyKvxSxpHsjb9cJvm4SvQ=
|
||||
github.com/sagikazarmark/locafero v0.3.0/go.mod h1:w+v7UsPNFwzF1cHuOajOOzoq4U7v/ig1mpRjqV+Bu1U=
|
||||
github.com/sagikazarmark/slog-shim v0.1.0 h1:diDBnUNK9N/354PgrxMywXnAwEr1QZcOr6gto+ugjYE=
|
||||
github.com/sagikazarmark/slog-shim v0.1.0/go.mod h1:SrcSrq8aKtyuqEI1uvTDTK1arOWRIczQRv+GVI1AkeQ=
|
||||
github.com/sanity-io/litter v1.5.5 h1:iE+sBxPBzoK6uaEP5Lt3fHNgpKcHXc/A2HGETy0uJQo=
|
||||
github.com/sanity-io/litter v1.5.5/go.mod h1:9gzJgR2i4ZpjZHsKvUXIRQVk7P+yM3e+jAF7bU2UI5U=
|
||||
github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0=
|
||||
github.com/schollz/closestmatch v2.1.0+incompatible h1:Uel2GXEpJqOWBrlyI+oY9LTiyyjYS17cCYRqP13/SHk=
|
||||
github.com/schollz/closestmatch v2.1.0+incompatible/go.mod h1:RtP1ddjLong6gTkbtmuhtR2uUrrJOpYzYRvbcPAid+g=
|
||||
github.com/segmentio/asm v1.2.0 h1:9BQrFxC+YOHJlTlHGkTrFWf59nbL3XnCoFLTwDCI7ys=
|
||||
github.com/segmentio/asm v1.2.0/go.mod h1:BqMnlJP91P8d+4ibuonYZw9mfnzI9HfxselHZr5aAcs=
|
||||
github.com/sergi/go-diff v1.0.0 h1:Kpca3qRNrduNnOQeazBd0ysaKrUJiIuISHxogkT9RPQ=
|
||||
github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=
|
||||
github.com/shopspring/decimal v0.0.0-20180709203117-cd690d0c9e24/go.mod h1:M+9NzErvs504Cn4c5DxATwIqPbtswREoFCre64PpcG4=
|
||||
github.com/shopspring/decimal v1.2.0/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o=
|
||||
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
|
||||
github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q=
|
||||
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
|
||||
github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
|
||||
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
|
||||
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
|
||||
github.com/spf13/afero v1.9.5 h1:stMpOSZFs//0Lv29HduCmli3GUfpFoF3Y1Q/aXj/wVM=
|
||||
github.com/spf13/afero v1.9.5/go.mod h1:UBogFpq8E9Hx+xc5CNTTEpTnuHVmXDwZcZcE1eb/UhQ=
|
||||
github.com/sourcegraph/conc v0.3.0 h1:OQTbbt6P72L20UqAkXXuLOj79LfEanQ+YQFNpLA9ySo=
|
||||
github.com/sourcegraph/conc v0.3.0/go.mod h1:Sdozi7LEKbFPqYX2/J+iBAM6HpqSLTASQIKqDmF7Mt0=
|
||||
github.com/spf13/afero v1.10.0 h1:EaGW2JJh15aKOejeuJ+wpFSHnbd7GE6Wvp3TsNhb6LY=
|
||||
github.com/spf13/afero v1.10.0/go.mod h1:UBogFpq8E9Hx+xc5CNTTEpTnuHVmXDwZcZcE1eb/UhQ=
|
||||
github.com/spf13/cast v1.5.1 h1:R+kOtfhWQE6TVQzY+4D7wJLBgkdVasCEFxSUBYBYIlA=
|
||||
github.com/spf13/cast v1.5.1/go.mod h1:b9PdjNptOpzXr7Rq1q9gJML/2cdGQAo69NKzQ10KN48=
|
||||
github.com/spf13/cobra v1.4.0/go.mod h1:Wo4iy3BUC+X2Fybo0PDqwJIv3dNRiZLHQymsfxlB84g=
|
||||
github.com/spf13/cobra v1.7.0 h1:hyqWnYt1ZQShIddO5kBpj3vu05/++x6tJ6dg8EC572I=
|
||||
github.com/spf13/cobra v1.7.0/go.mod h1:uLxZILRyS/50WlhOIKD7W6V5bgeIt+4sICxh6uRMrb0=
|
||||
github.com/spf13/jwalterweatherman v1.1.0 h1:ue6voC5bR5F8YxI5S67j9i582FU4Qvo2bmqnqMYADFk=
|
||||
github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo=
|
||||
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
|
||||
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||
github.com/spf13/viper v1.16.0 h1:rGGH0XDZhdUOryiDWjmIvUSWpbNqisK8Wk0Vyefw8hc=
|
||||
github.com/spf13/viper v1.16.0/go.mod h1:yg78JgCJcbrQOvV9YLXgkLaZqUidkY9K+Dd1FofRzQg=
|
||||
github.com/spf13/viper v1.17.0 h1:I5txKw7MJasPL/BrfkbA0Jyo/oELqVmux4pR/UxOMfI=
|
||||
github.com/spf13/viper v1.17.0/go.mod h1:BmMMMLQXSbcHK6KAOiFLz0l5JHrU89OdIRHvsk0+yVI=
|
||||
github.com/spkg/bom v0.0.0-20160624110644-59b7046e48ad/go.mod h1:qLr4V1qq6nMqFKkMo8ZTx3f+BZEkzsRUY10Xsm2mwU0=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE=
|
||||
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
||||
github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c=
|
||||
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
||||
github.com/stretchr/objx v0.5.1 h1:4VhoImhV/Bm0ToFkXFi8hXNXwpDRZ/ynw3amt82mzq0=
|
||||
github.com/stretchr/objx v0.5.1/go.mod h1:/iHQpkQwBD6DLUmQ4pE+s1TXdob1mORJ4/UFdrifcy0=
|
||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||
@@ -602,15 +729,20 @@ github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5
|
||||
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals=
|
||||
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||
github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||
github.com/stretchr/testify v1.8.3/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
||||
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
|
||||
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
||||
github.com/subosito/gotenv v1.4.2 h1:X1TuBLAMDFbaTAChgCBLu3DU3UPyELpnF2jjJ2cz/S8=
|
||||
github.com/subosito/gotenv v1.4.2/go.mod h1:ayKnFf/c6rvx/2iiLrJUk1e6plDbT3edrFNGqEflhK0=
|
||||
github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8=
|
||||
github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU=
|
||||
github.com/tdewolff/minify/v2 v2.12.9 h1:dvn5MtmuQ/DFMwqf5j8QhEVpPX6fi3WGImhv8RUB4zA=
|
||||
github.com/tdewolff/minify/v2 v2.12.9/go.mod h1:qOqdlDfL+7v0/fyymB+OP497nIxJYSvX4MQWA8OoiXU=
|
||||
github.com/tdewolff/parse/v2 v2.6.8 h1:mhNZXYCx//xG7Yq2e/kVLNZw4YfYmeHbhx+Zc0OvFMA=
|
||||
github.com/tdewolff/parse/v2 v2.6.8/go.mod h1:XHDhaU6IBgsryfdnpzUXBlT6leW/l25yrFBTEb4eIyM=
|
||||
github.com/tdewolff/test v1.0.9 h1:SswqJCmeN4B+9gEAi/5uqT0qpi1y2/2O47V/1hhGZT0=
|
||||
github.com/tdewolff/test v1.0.9/go.mod h1:6DAvZliBAAnD7rhVgwaM7DE5/d9NMOAJ09SqYqeK4QE=
|
||||
github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI=
|
||||
github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08=
|
||||
github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw=
|
||||
@@ -625,6 +757,10 @@ github.com/valyala/fasttemplate v1.2.1/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+
|
||||
github.com/valyala/fasttemplate v1.2.2 h1:lxLXG0uE3Qnshl9QyaK6XJxMXlQZELvChBOCmQD0Loo=
|
||||
github.com/valyala/fasttemplate v1.2.2/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ=
|
||||
github.com/valyala/tcplisten v0.0.0-20161114210144-ceec8f93295a/go.mod h1:v3UYOV9WzVtRmSR+PDvWpU/qWl4Wa5LApYYX4ZtKbio=
|
||||
github.com/vmihailenco/msgpack/v5 v5.4.0 h1:hRM0digJwyR6vll33NNAwCFguy5JuBD6jxDmQP3l608=
|
||||
github.com/vmihailenco/msgpack/v5 v5.4.0/go.mod h1:GaZTsDaehaPpQVyxrf5mtQlH+pc21PIudVV/E3rRQok=
|
||||
github.com/vmihailenco/tagparser/v2 v2.0.0 h1:y09buUbR+b5aycVFQs/g70pqKVZNBmxwAhO7/IwNM9g=
|
||||
github.com/vmihailenco/tagparser/v2 v2.0.0/go.mod h1:Wri+At7QHww0WTrCBeu4J6bNtoV6mEfg5OIWRZA9qds=
|
||||
github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM=
|
||||
github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg=
|
||||
github.com/xdg-go/pbkdf2 v1.0.0 h1:Su7DPu48wXMwC3bs7MCNG+z4FhcyEuz5dlvchbq0B0c=
|
||||
@@ -642,58 +778,71 @@ github.com/xeipuuv/gojsonschema v1.2.0 h1:LhYJRs+L4fBtjZUfuSZIKGeVu0QRy8e5Xi7D17
|
||||
github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y=
|
||||
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e h1:JVG44RsyaB9T2KIHavMF/ppJZNG9ZpyihvCd0w101no=
|
||||
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e/go.mod h1:RbqR21r5mrJuqunuUZ/Dhy/avygyECGrLceyNeo4LiM=
|
||||
github.com/yalp/jsonpath v0.0.0-20180802001716-5cc68e5049a0 h1:6fRhSjgLCkTD3JnJxvaJ4Sj+TYblw757bqYgZaOq5ZY=
|
||||
github.com/yalp/jsonpath v0.0.0-20180802001716-5cc68e5049a0/go.mod h1:/LWChgwKmvncFJFHJ7Gvn9wZArjbV5/FppcK2fKk/tI=
|
||||
github.com/yosssi/ace v0.0.5 h1:tUkIP/BLdKqrlrPwcmH0shwEEhTRHoGnc1wFIWmaBUA=
|
||||
github.com/yosssi/ace v0.0.5/go.mod h1:ALfIzm2vT7t5ZE7uoIZqF3TQ7SAOyupFZnkrF5id+K0=
|
||||
github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d/go.mod h1:rHwXgn7JulP+udvsHwJoVG1YGAP6VLg4y9I5dyZdqmA=
|
||||
github.com/youmark/pkcs8 v0.0.0-20201027041543-1326539a0a0a h1:fZHgsYlfvtyqToslyjUt3VOPF4J7aK/3MPcK7xp3PDk=
|
||||
github.com/youmark/pkcs8 v0.0.0-20201027041543-1326539a0a0a/go.mod h1:ul22v+Nro/R083muKhosV54bj5niojjWZvU8xrevuH4=
|
||||
github.com/yudai/gojsondiff v1.0.0 h1:27cbfqXLVEJ1o8I6v3y9lg8Ydm53EKqHXAOMxEGlCOA=
|
||||
github.com/yudai/gojsondiff v1.0.0/go.mod h1:AY32+k2cwILAkW1fbgxQ5mUmMiZFgLIV+FBNExI05xg=
|
||||
github.com/yudai/golcs v0.0.0-20170316035057-ecda9a501e82 h1:BHyfKlQyqbsFN5p3IfnEUduWvb9is428/nNb5L3U01M=
|
||||
github.com/yudai/golcs v0.0.0-20170316035057-ecda9a501e82/go.mod h1:lgjkn3NuSvDfVJdfcVVdX+jpBxNmX4rDAzaS45IcYoM=
|
||||
github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
|
||||
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
||||
github.com/zenazn/goji v0.9.0/go.mod h1:7S9M489iMyHBNxwZnk9/EHS098H4/F6TATF2mIxtB1Q=
|
||||
go.mongodb.org/mongo-driver v1.12.0 h1:aPx33jmn/rQuJXPQLZQ8NtfPQG8CaqgLThFtqRb0PiE=
|
||||
go.mongodb.org/mongo-driver v1.12.0/go.mod h1:AZkxhPnFJUoH7kZlFkVKucV20K387miPfm7oimrSmK0=
|
||||
go.mongodb.org/mongo-driver v1.12.1 h1:nLkghSU8fQNaK7oUmDhQFsnrtcoNy7Z6LVFKsEecqgE=
|
||||
go.mongodb.org/mongo-driver v1.12.1/go.mod h1:/rGBTebI3XYboVmgz+Wv3Bcbl3aD0QF9zl6kDDw18rQ=
|
||||
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
|
||||
go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
|
||||
go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
||||
go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
||||
go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
||||
go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk=
|
||||
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.42.0 h1:ZOLJc06r4CB42laIXg/7udr0pbZyuAihN10A/XuiQRY=
|
||||
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.42.0/go.mod h1:5z+/ZWJQKXa9YT34fQNx5K8Hd1EoIhvtUygUQPqEOgQ=
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.42.0 h1:pginetY7+onl4qN1vl0xW/V/v6OBZ0vVdH+esuJgvmM=
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.42.0/go.mod h1:XiYsayHc36K3EByOO6nbAXnAWbrUxdjUROCEeeROOH8=
|
||||
go.opentelemetry.io/contrib/propagators/jaeger v1.17.0 h1:Zbpbmwav32Ea5jSotpmkWEl3a6Xvd4tw/3xxGO1i05Y=
|
||||
go.opentelemetry.io/contrib/propagators/jaeger v1.17.0/go.mod h1:tcTUAlmO8nuInPDSBVfG+CP6Mzjy5+gNV4mPxMbL0IA=
|
||||
go.opentelemetry.io/otel v1.16.0 h1:Z7GVAX/UkAXPKsy94IU+i6thsQS4nb7LviLpnaNeW8s=
|
||||
go.opentelemetry.io/otel v1.16.0/go.mod h1:vl0h9NUa1D5s1nv3A5vZOYWn8av4K8Ml6JDeHrT/bx4=
|
||||
go.opentelemetry.io/otel/exporters/jaeger v1.16.0 h1:YhxxmXZ011C0aDZKoNw+juVWAmEfv/0W2XBOv9aHTaA=
|
||||
go.opentelemetry.io/otel/exporters/jaeger v1.16.0/go.mod h1:grYbBo/5afWlPpdPZYhyn78Bk04hnvxn2+hvxQhKIQM=
|
||||
go.opentelemetry.io/otel/metric v1.16.0 h1:RbrpwVG1Hfv85LgnZ7+txXioPDoh6EdbZHo26Q3hqOo=
|
||||
go.opentelemetry.io/otel/metric v1.16.0/go.mod h1:QE47cpOmkwipPiefDwo2wDzwJrlfxxNYodqc4xnGCo4=
|
||||
go.opentelemetry.io/otel/sdk v1.16.0 h1:Z1Ok1YsijYL0CSJpHt4cS3wDDh7p572grzNrBMiMWgE=
|
||||
go.opentelemetry.io/otel/sdk v1.16.0/go.mod h1:tMsIuKXuuIWPBAOrH+eHtvhTL+SntFtXF9QD68aP6p4=
|
||||
go.opentelemetry.io/otel/trace v1.16.0 h1:8JRpaObFoW0pxuVPapkgH8UhHQj+bJW8jJsCZEu5MQs=
|
||||
go.opentelemetry.io/otel/trace v1.16.0/go.mod h1:Yt9vYq1SdNz3xdjZZK7wcXv1qv2pwLkqr2QVwea0ef0=
|
||||
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.45.0 h1:RsQi0qJ2imFfCvZabqzM9cNXBG8k6gXMv1A0cXRmH6A=
|
||||
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.45.0/go.mod h1:vsh3ySueQCiKPxFLvjWC4Z135gIa34TQ/NSqkDTZYUM=
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.45.0 h1:x8Z78aZx8cOF0+Kkazoc7lwUNMGy0LrzEMxTm4BbTxg=
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.45.0/go.mod h1:62CPTSry9QZtOaSsE3tOzhx6LzDhHnXJ6xHeMNNiM6Q=
|
||||
go.opentelemetry.io/otel v1.19.0 h1:MuS/TNf4/j4IXsZuJegVzI1cwut7Qc00344rgH7p8bs=
|
||||
go.opentelemetry.io/otel v1.19.0/go.mod h1:i0QyjOq3UPoTzff0PJB2N66fb4S0+rSbSB15/oyH9fY=
|
||||
go.opentelemetry.io/otel/exporters/jaeger v1.17.0 h1:D7UpUy2Xc2wsi1Ras6V40q806WM07rqoCWzXu7Sqy+4=
|
||||
go.opentelemetry.io/otel/exporters/jaeger v1.17.0/go.mod h1:nPCqOnEH9rNLKqH/+rrUjiMzHJdV1BlpKcTwRTyKkKI=
|
||||
go.opentelemetry.io/otel/metric v1.19.0 h1:aTzpGtV0ar9wlV4Sna9sdJyII5jTVJEvKETPiOKwvpE=
|
||||
go.opentelemetry.io/otel/metric v1.19.0/go.mod h1:L5rUsV9kM1IxCj1MmSdS+JQAcVm319EUrDVLrt7jqt8=
|
||||
go.opentelemetry.io/otel/sdk v1.19.0 h1:6USY6zH+L8uMH8L3t1enZPR3WFEmSTADlqldyHtJi3o=
|
||||
go.opentelemetry.io/otel/sdk v1.19.0/go.mod h1:NedEbbS4w3C6zElbLdPJKOpJQOrGUJ+GfzpjUvI0v1A=
|
||||
go.opentelemetry.io/otel/trace v1.19.0 h1:DFVQmlVbfVeOuBRrwdtaehRrWiL1JoVs9CPIQ1Dzxpg=
|
||||
go.opentelemetry.io/otel/trace v1.19.0/go.mod h1:mfaSyvGyEJEI0nyV2I4qhNQnbBOUUmYZpYojqMnX2vo=
|
||||
go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
|
||||
go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
|
||||
go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ=
|
||||
go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ=
|
||||
go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
|
||||
go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE=
|
||||
go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0=
|
||||
go.uber.org/goleak v1.1.10/go.mod h1:8a7PlsEVH3e/a/GLqe5IIrQx6GzcnRmZEufDUTk4A7A=
|
||||
go.uber.org/goleak v1.2.1 h1:NBol2c7O1ZokfZ0LEU9K6Whx/KnwvepVetCUhtKja4A=
|
||||
go.uber.org/goleak v1.2.1/go.mod h1:qlT2yGI9QafXHhZZLxlSuNsMw3FFLxBr+tBRlmO1xH4=
|
||||
go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
|
||||
go.uber.org/multierr v1.3.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4=
|
||||
go.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU=
|
||||
go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU=
|
||||
go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
|
||||
go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
|
||||
go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA=
|
||||
go.uber.org/zap v1.9.1/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
|
||||
go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
|
||||
go.uber.org/zap v1.13.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM=
|
||||
go.uber.org/zap v1.15.0/go.mod h1:Mb2vm2krFEG5DV0W9qcHBYFtp/Wku1cvYaqPsS/WYfc=
|
||||
go.uber.org/zap v1.18.1/go.mod h1:xg/QME4nWcxGxrpdeYfq7UvYrLh66cuVKdrbD1XF/NI=
|
||||
golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
|
||||
golang.org/x/arch v0.4.0 h1:A8WCeEWhLwPBKNbFi5Wv5UTCBx5zzubnXDlMOFAzFMc=
|
||||
golang.org/x/arch v0.4.0/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
|
||||
golang.org/x/arch v0.5.0 h1:jpGode6huXQxcskEIpOCvrU+tzo81b6+oFLUYXWtH/Y=
|
||||
golang.org/x/arch v0.5.0/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20190411191339-88737f569e3a/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE=
|
||||
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
@@ -713,9 +862,10 @@ golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5y
|
||||
golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||
golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||
golang.org/x/crypto v0.8.0/go.mod h1:mRqEX+O9/h5TFCrQhkgjo2yKi0yYA+9ecGkdQoHrywE=
|
||||
golang.org/x/crypto v0.9.0/go.mod h1:yrmDGqONDYtNj3tH8X9dzUun2m2lzPa9ngI6/RUPGR0=
|
||||
golang.org/x/crypto v0.11.0 h1:6Ewdq3tDic1mg5xRO4milcWCfMVQhI4NkqWWvqejpuA=
|
||||
golang.org/x/crypto v0.11.0/go.mod h1:xgJhtzW8F9jGdVFWZESrid1U1bjeNy4zgy5cRr/CIio=
|
||||
golang.org/x/crypto v0.12.0/go.mod h1:NF0Gs7EO5K4qLn+Ylc+fih8BSTeIjAP05siRnAh98yw=
|
||||
golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc=
|
||||
golang.org/x/crypto v0.14.0 h1:wBqGXzWJW6m1XrIKlAH0Hs1JJ7+9KBwnIO8v66Q9cHc=
|
||||
golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4=
|
||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
|
||||
@@ -726,8 +876,8 @@ golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u0
|
||||
golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
|
||||
golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM=
|
||||
golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU=
|
||||
golang.org/x/exp v0.0.0-20230321023759-10a507213a29 h1:ooxPy7fPvB4kwsA2h+iBNHkAbp/4JxTSwCmvdjEYmug=
|
||||
golang.org/x/exp v0.0.0-20230321023759-10a507213a29/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc=
|
||||
golang.org/x/exp v0.0.0-20231006140011-7918f672742d h1:jtJma62tbqLibJ5sFQz8bKtEM8rJBtfilJ2qTU199MI=
|
||||
golang.org/x/exp v0.0.0-20231006140011-7918f672742d/go.mod h1:ldy0pHrwJyGW56pPQzzkH36rKxoZW1tw7ZJpeKx+hdo=
|
||||
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
|
||||
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
|
||||
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||
@@ -751,15 +901,17 @@ golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.5.1/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro=
|
||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
||||
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||
golang.org/x/mod v0.12.0 h1:rmsUpXtvNzj340zd98LZ4KntptpfRHwpFOHG188oHXc=
|
||||
golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||
golang.org/x/mod v0.13.0 h1:I/DsJXRlw/8l/0c24sM9yb0T4z9liZTduXvdAWYiysY=
|
||||
golang.org/x/mod v0.13.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
|
||||
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190327091125-710a502c58a2/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
@@ -776,7 +928,6 @@ golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLL
|
||||
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||
golang.org/x/net v0.0.0-20200425230154-ff2c4b7c35a0/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||
golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||
golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||
golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||
@@ -792,13 +943,15 @@ golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v
|
||||
golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20210502030024-e5908800b52b/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk=
|
||||
golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
||||
golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns=
|
||||
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
|
||||
golang.org/x/net v0.12.0 h1:cfawfvKITfUsFCeJIHJrbSxpeu/E81khclypR0GVT50=
|
||||
golang.org/x/net v0.12.0/go.mod h1:zEVYFnQC7m/vmpQFELhcD1EWkZlX69l4oqgmer6hfKA=
|
||||
golang.org/x/net v0.14.0/go.mod h1:PpSgVXXLK0OxS0F31C1/tv6XNguvCrnXIDrFMspZIUI=
|
||||
golang.org/x/net v0.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM=
|
||||
golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
@@ -808,7 +961,8 @@ golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43/go.mod h1:KelEdhl1UZF7XfJ
|
||||
golang.org/x/oauth2 v0.0.0-20201109201403-9fd604954f58/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
|
||||
golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
|
||||
golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
|
||||
golang.org/x/oauth2 v0.8.0 h1:6dkIjl3j3LtZ/O3sTgZTMsLKSftL/B8Zgq4huOIIUu8=
|
||||
golang.org/x/oauth2 v0.12.0 h1:smVPGxink+n1ZI5pkQa8y6fZT0RW0MgCO5bFpepy4B4=
|
||||
golang.org/x/oauth2 v0.12.0/go.mod h1:A74bZ3aGXgCY0qaIC9Ahg6Lglin4AMAco8cIv9baba4=
|
||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
@@ -822,8 +976,8 @@ golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJ
|
||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.3.0 h1:ftCYgMx6zT/asHUrPw8BLLscYtGznsLAnjq5RH9P66E=
|
||||
golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
|
||||
golang.org/x/sync v0.4.0 h1:zxkM55ReGkDlKSM+Fu41A+zmbZuaPVbGMzvvdUPznYQ=
|
||||
golang.org/x/sync v0.4.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
|
||||
golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
@@ -873,9 +1027,10 @@ golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBc
|
||||
golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20211025201205-69cdffdb9359/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20211103235746-7861aae1554b/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
@@ -885,15 +1040,20 @@ golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.10.0 h1:SqMFp9UcQJZa+pmYuAKjd9xq1f0j5rLcDIk0mj4qAsA=
|
||||
golang.org/x/sys v0.10.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE=
|
||||
golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
|
||||
golang.org/x/term v0.7.0/go.mod h1:P32HKFT3hSsZrRxla30E9HqToFYAQPCMs/zFMBUFqPY=
|
||||
golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo=
|
||||
golang.org/x/term v0.10.0 h1:3R7pNqamzBraeqj/Tj8qt1aQ2HpmlC+Cx/qL/7hn4/c=
|
||||
golang.org/x/term v0.11.0/go.mod h1:zC9APTIj3jG3FdV/Ons+XE1riIZXG4aZ4GTHiPZJPIU=
|
||||
golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU=
|
||||
golang.org/x/term v0.13.0 h1:bb+I9cTfFazGW51MZqBVmZy7+JEJMouUHTUSKVQLBek=
|
||||
golang.org/x/term v0.13.0/go.mod h1:LTmsnFJwVN6bCy1rVCoS+qHT1HhALEFxKncY3WNNh4U=
|
||||
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
@@ -905,8 +1065,9 @@ golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||
golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ=
|
||||
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
|
||||
golang.org/x/text v0.11.0 h1:LAntKIrcmeSKERyiOh0XMV39LXS8IE9UL2yP7+f5ij4=
|
||||
golang.org/x/text v0.11.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
|
||||
golang.org/x/text v0.12.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
|
||||
golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k=
|
||||
golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
|
||||
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
@@ -934,6 +1095,7 @@ golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtn
|
||||
golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191108193012-7d206e10da11/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
@@ -971,18 +1133,19 @@ golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4f
|
||||
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||
golang.org/x/tools v0.0.0-20210108195828-e2f9c7f1fc8e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||
golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0=
|
||||
golang.org/x/tools v0.1.9/go.mod h1:nABZi5QlRsZVlzPpHl034qft6wpY4eDcsTt5AaioBiU=
|
||||
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
||||
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
|
||||
golang.org/x/tools v0.11.0 h1:EMCa6U9S2LtZXLAMoWiR/R8dAQFRqbAitmbJ2UKhoi8=
|
||||
golang.org/x/tools v0.11.0/go.mod h1:anzJrxPjNtfgiYQYirP2CPGzGLxrH2u2QBhn6Bf3qY8=
|
||||
golang.org/x/tools v0.14.0 h1:jvNa2pY0M4r62jkRQ6RwEZZyPcymeL9XZMLBbV7U2nc=
|
||||
golang.org/x/tools v0.14.0/go.mod h1:uYBEerGOWcJyEORxN+Ek8+TT266gXkNlHdJBwexUsBg=
|
||||
golang.org/x/xerrors v0.0.0-20190410155217-1f06c39b4373/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20190513163551-3ee3066db522/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
gonum.org/v1/gonum v0.13.0 h1:a0T3bh+7fhRyqeNbiC3qVHYmkiQgit3wnNan/2c0HMM=
|
||||
gonum.org/v1/gonum v0.13.0/go.mod h1:/WPYRckkfWrhWefxyYTfrTtQR0KH4iyHNuzxqXAKyAU=
|
||||
gonum.org/v1/gonum v0.14.0 h1:2NiG67LD1tEH0D7kM+ps2V+fXmsAnpUeec7n8tcr4S0=
|
||||
gonum.org/v1/gonum v0.14.0/go.mod h1:AoWeoz0becf9QMWtE8iWXNXc27fK4fNeHNf/oMejGfU=
|
||||
google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=
|
||||
google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M=
|
||||
google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
|
||||
@@ -1031,6 +1194,7 @@ google.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfG
|
||||
google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||
google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||
google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||
google.golang.org/genproto v0.0.0-20200423170343-7949de9c1215/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||
google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||
google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||
google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U=
|
||||
@@ -1046,8 +1210,12 @@ google.golang.org/genproto v0.0.0-20201210142538-e3217bee35cc/go.mod h1:FWY/as6D
|
||||
google.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
google.golang.org/genproto v0.0.0-20210108203827-ffc7fda8c3d7/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
google.golang.org/genproto v0.0.0-20210226172003-ab064af71705/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20230629202037-9506855d4529 h1:DEH99RbiLZhMxrpEJCZ0A+wdTe0EOgou/poSLx9vWf4=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20230629202037-9506855d4529/go.mod h1:66JfowdXAEgad5O9NnYcsNPLCPZJD++2L9X0PCMODrA=
|
||||
google.golang.org/genproto v0.0.0-20231012201019-e917dd12ba7a h1:fwgW9j3vHirt4ObdHoYNwuO24BEZjSzbh+zPaNWoiY8=
|
||||
google.golang.org/genproto v0.0.0-20231012201019-e917dd12ba7a/go.mod h1:EMfReVxb80Dq1hhioy0sOsY9jCE46YDgHlJ7fWVUWRE=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20231012201019-e917dd12ba7a h1:myvhA4is3vrit1a6NZCWBIwN0kNEnX21DJOJX/NvIfI=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20231012201019-e917dd12ba7a/go.mod h1:SUBoKXbI1Efip18FClrQVGjWcyd0QZd8KkvdP34t7ww=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20231012201019-e917dd12ba7a h1:a2MQQVoTo96JC9PMGtGBymLp7+/RzpFc2yX/9WfFg1c=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20231012201019-e917dd12ba7a/go.mod h1:4cYg8o5yUbm77w8ZX00LhMVNl/YVBFJRYWDc0uYWMs0=
|
||||
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
||||
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
|
||||
google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
|
||||
@@ -1064,8 +1232,8 @@ google.golang.org/grpc v1.31.1/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM
|
||||
google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc=
|
||||
google.golang.org/grpc v1.34.0/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA51WJ8=
|
||||
google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
|
||||
google.golang.org/grpc v1.56.1 h1:z0dNfjIl0VpaZ9iSVjA6daGatAYwPGstTjt5vkRMFkQ=
|
||||
google.golang.org/grpc v1.56.1/go.mod h1:I9bI3vqKfayGqPUAwGdOSu7kt6oIJLixfffKrpXqQ9s=
|
||||
google.golang.org/grpc v1.58.3 h1:BjnpXut1btbtgN/6sp+brB2Kbm2LjNXnidYujAVbSoQ=
|
||||
google.golang.org/grpc v1.58.3/go.mod h1:tgX3ZQDlNJGU96V6yHh1T/JeoBQ2TXdr43YbYSsCJk0=
|
||||
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
|
||||
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
|
||||
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
|
||||
@@ -1084,7 +1252,9 @@ gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc h1:2gG
|
||||
gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc/go.mod h1:m7x9LTH6d71AHyAX77c9yqWCCa3UKHcVEj9y7hAtKDk=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
||||
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
|
||||
gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df h1:n7WqCuqOuCbNr617RXOY0AWRXxgwEyPp2z+p0+hgMuE=
|
||||
gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df/go.mod h1:LRQQ+SO6ZHR7tOkpBDuZnXENFzX8qRjMDMyPD6BRkCw=
|
||||
@@ -1095,16 +1265,18 @@ gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA=
|
||||
gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
||||
gopkg.in/square/go-jose.v2 v2.5.1/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI=
|
||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
|
||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
|
||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.7/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
|
||||
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.0/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gotest.tools/v3 v3.3.0 h1:MfDY1b1/0xN1CyMlQDac0ziEy9zJQd9CXBRRDHw2jJo=
|
||||
gotest.tools/v3 v3.3.0/go.mod h1:Mcr9QNxkg0uMvy/YElmo4SpXgJKWgQvYrT7Kw5RzJ1A=
|
||||
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
@@ -1112,6 +1284,9 @@ honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWh
|
||||
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
|
||||
honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
|
||||
honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
|
||||
moul.io/http2curl/v2 v2.3.0 h1:9r3JfDzWPcbIklMOs2TnIFzDYvfAZvjeavG6EzP7jYs=
|
||||
moul.io/http2curl/v2 v2.3.0/go.mod h1:RW4hyBjTWSYDOxapodpNEtX0g5Eb16sxklBqmd2RHcE=
|
||||
nullprogram.com/x/optparse v1.0.0/go.mod h1:KdyPE+Igbe0jQUrVfMqDMeJQIJZEuyV7pjYmp6pbG50=
|
||||
rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
|
||||
rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4=
|
||||
rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=
|
||||
|
||||
+14
-11
@@ -8,9 +8,9 @@ package http
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/mainflux/mainflux"
|
||||
"github.com/mainflux/mainflux/pkg/errors"
|
||||
"github.com/mainflux/mainflux/pkg/messaging"
|
||||
"github.com/mainflux/mainflux/things/policies"
|
||||
)
|
||||
|
||||
// Service specifies coap service API.
|
||||
@@ -23,32 +23,35 @@ var _ Service = (*adapterService)(nil)
|
||||
|
||||
type adapterService struct {
|
||||
publisher messaging.Publisher
|
||||
things policies.AuthServiceClient
|
||||
auth mainflux.AuthzServiceClient
|
||||
}
|
||||
|
||||
// New instantiates the HTTP adapter implementation.
|
||||
func New(publisher messaging.Publisher, things policies.AuthServiceClient) Service {
|
||||
func New(publisher messaging.Publisher, auth mainflux.AuthzServiceClient) Service {
|
||||
return &adapterService{
|
||||
publisher: publisher,
|
||||
things: things,
|
||||
auth: auth,
|
||||
}
|
||||
}
|
||||
|
||||
func (as *adapterService) Publish(ctx context.Context, token string, msg *messaging.Message) error {
|
||||
ar := &policies.AuthorizeReq{
|
||||
Subject: token,
|
||||
Object: msg.Channel,
|
||||
Action: policies.WriteAction,
|
||||
EntityType: policies.ThingEntityType,
|
||||
ar := &mainflux.AuthorizeReq{
|
||||
Namespace: "",
|
||||
SubjectType: "thing",
|
||||
Permission: "publish",
|
||||
Subject: token,
|
||||
Object: msg.Channel,
|
||||
ObjectType: "group",
|
||||
}
|
||||
res, err := as.things.Authorize(ctx, ar)
|
||||
|
||||
res, err := as.auth.Authorize(ctx, ar)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !res.GetAuthorized() {
|
||||
return errors.ErrAuthorization
|
||||
}
|
||||
msg.Publisher = res.GetThingID()
|
||||
msg.Publisher = res.GetId()
|
||||
|
||||
return as.publisher.Publish(ctx, msg.Channel, msg)
|
||||
}
|
||||
|
||||
@@ -11,19 +11,20 @@ import (
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
authmocks "github.com/mainflux/mainflux/auth/mocks"
|
||||
server "github.com/mainflux/mainflux/http"
|
||||
"github.com/mainflux/mainflux/http/api"
|
||||
"github.com/mainflux/mainflux/http/mocks"
|
||||
"github.com/mainflux/mainflux/internal/apiutil"
|
||||
"github.com/mainflux/mainflux/things/policies"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
const instanceID = "5de9b29a-feb9-11ed-be56-0242ac120002"
|
||||
|
||||
func newService(cc policies.AuthServiceClient) server.Service {
|
||||
func newService() server.Service {
|
||||
auth := new(authmocks.Service)
|
||||
pub := mocks.NewPublisher()
|
||||
return server.New(pub, cc)
|
||||
return server.New(pub, auth)
|
||||
}
|
||||
|
||||
func newHTTPServer(svc server.Service) *httptest.Server {
|
||||
@@ -69,8 +70,7 @@ func TestPublish(t *testing.T) {
|
||||
msg := `[{"n":"current","t":-1,"v":1.6}]`
|
||||
msgJSON := `{"field1":"val1","field2":"val2"}`
|
||||
msgCBOR := `81A3616E6763757272656E746174206176FB3FF999999999999A`
|
||||
thingsClient := mocks.NewThingsClient(map[string]string{thingKey: chanID})
|
||||
svc := newService(thingsClient)
|
||||
svc := newService()
|
||||
ts := newHTTPServer(svc)
|
||||
defer ts.Close()
|
||||
|
||||
@@ -151,8 +151,8 @@ func TestPublish(t *testing.T) {
|
||||
chanID: chanID,
|
||||
msg: msg,
|
||||
contentType: ctSenmlJSON,
|
||||
key: mocks.ServiceErrToken,
|
||||
status: http.StatusInternalServerError,
|
||||
// key: mocks.ServiceErrToken,
|
||||
status: http.StatusInternalServerError,
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
@@ -17,6 +17,5 @@ func (req publishReq) validate() error {
|
||||
if req.token == "" {
|
||||
return apiutil.ErrBearerKey
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -1,53 +0,0 @@
|
||||
// Copyright (c) Mainflux
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package mocks
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/mainflux/mainflux/pkg/errors"
|
||||
"github.com/mainflux/mainflux/things/policies"
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/grpc/codes"
|
||||
"google.golang.org/grpc/status"
|
||||
)
|
||||
|
||||
var _ policies.AuthServiceClient = (*thingsClient)(nil)
|
||||
|
||||
// ServiceErrToken is used to simulate internal server error.
|
||||
const ServiceErrToken = "unavailable"
|
||||
|
||||
type thingsClient struct {
|
||||
things map[string]string
|
||||
}
|
||||
|
||||
// NewThingsClient returns mock implementation of things service client.
|
||||
func NewThingsClient(data map[string]string) policies.AuthServiceClient {
|
||||
return &thingsClient{data}
|
||||
}
|
||||
|
||||
func (tc thingsClient) Authorize(ctx context.Context, req *policies.AuthorizeReq, opts ...grpc.CallOption) (*policies.AuthorizeRes, error) {
|
||||
secret := req.GetSubject()
|
||||
|
||||
// Since there is no appropriate way to simulate internal server error,
|
||||
// we had to use this obscure approach. ErrorToken simulates gRPC
|
||||
// call which returns internal server error.
|
||||
if secret == ServiceErrToken {
|
||||
return &policies.AuthorizeRes{ThingID: "", Authorized: false}, status.Error(codes.Internal, "internal server error")
|
||||
}
|
||||
|
||||
if secret == "" {
|
||||
return &policies.AuthorizeRes{ThingID: "", Authorized: false}, errors.ErrAuthentication
|
||||
}
|
||||
|
||||
id, ok := tc.things[secret]
|
||||
if !ok {
|
||||
return &policies.AuthorizeRes{ThingID: "", Authorized: false}, status.Error(codes.Unauthenticated, "invalid credentials provided")
|
||||
}
|
||||
return &policies.AuthorizeRes{ThingID: id, Authorized: true}, nil
|
||||
}
|
||||
|
||||
func (tc thingsClient) Identify(ctx context.Context, req *policies.IdentifyReq, opts ...grpc.CallOption) (*policies.IdentifyRes, error) {
|
||||
panic("not implemented")
|
||||
}
|
||||
@@ -17,6 +17,9 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
MemberKindKey = "member_kind"
|
||||
PermissionKey = "permission"
|
||||
RelationKey = "relation"
|
||||
StatusKey = "status"
|
||||
OffsetKey = "offset"
|
||||
LimitKey = "limit"
|
||||
@@ -38,6 +41,7 @@ const (
|
||||
VisibilityKey = "visibility"
|
||||
SharedByKey = "shared_by"
|
||||
TokenKey = "token"
|
||||
DefPermission = "view"
|
||||
DefTotal = uint64(100)
|
||||
DefOffset = 0
|
||||
DefLimit = 10
|
||||
@@ -101,6 +105,7 @@ func EncodeError(_ context.Context, err error, w http.ResponseWriter) {
|
||||
errors.Contains(err, apiutil.ErrMissingID),
|
||||
errors.Contains(err, apiutil.ErrEmptyList),
|
||||
errors.Contains(err, apiutil.ErrMissingMemberType),
|
||||
errors.Contains(err, apiutil.ErrMissingMemberKind),
|
||||
errors.Contains(err, apiutil.ErrNameSize):
|
||||
w.WriteHeader(http.StatusBadRequest)
|
||||
case errors.Contains(err, errors.ErrAuthentication):
|
||||
|
||||
@@ -51,6 +51,9 @@ var (
|
||||
// ErrInvalidDirection indicates an invalid list direction.
|
||||
ErrInvalidDirection = errors.New("invalid list direction provided")
|
||||
|
||||
// ErrInvalidMemberKind indicates an invalid member kind.
|
||||
ErrInvalidMemberKind = errors.New("invalid member kind")
|
||||
|
||||
// ErrEmptyList indicates that entity data is empty.
|
||||
ErrEmptyList = errors.New("empty list provided")
|
||||
|
||||
@@ -99,6 +102,12 @@ var (
|
||||
// ErrMissingMemberType indicates missing group member type.
|
||||
ErrMissingMemberType = errors.New("missing group member type")
|
||||
|
||||
// ErrMissingMemberKind indicates missing group member kind.
|
||||
ErrMissingMemberKind = errors.New("missing group member kind")
|
||||
|
||||
// ErrMissingRelation indicates missing relation.
|
||||
ErrMissingRelation = errors.New("missing relation")
|
||||
|
||||
// ErrInvalidAPIKey indicates an invalid API key type.
|
||||
ErrInvalidAPIKey = errors.New("invalid api key type")
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user