From 10081bfbf0ba52cb6276ced801e5c5e46ebb57e9 Mon Sep 17 00:00:00 2001 From: Arvindh Date: Tue, 7 May 2024 14:58:12 +0530 Subject: [PATCH 01/24] init Signed-off-by: Arvindh --- auth/keys.go | 51 +++++++++++++++++++++++++++++++++++++ docker/nginx/nginx-key.conf | 2 +- 2 files changed, 52 insertions(+), 1 deletion(-) diff --git a/auth/keys.go b/auth/keys.go index aa21ee48e0..ddc69fbfcc 100644 --- a/auth/keys.go +++ b/auth/keys.go @@ -49,6 +49,56 @@ func (kt KeyType) String() string { } } +type ScopeOperation uint32 + +const ( + CreateOp ScopeOperation = iota + ReadOp + ListOp + UpdateOp + DeleteOp +) + +type ScopeEntityType uint32 + +const ( + DomainManagementScope ScopeEntityType = iota + GroupScope + ChannelScope + ThingScope +) + +type ScopeValue interface { + string | []string +} + +type KeyScopes map[ScopeEntityType]map[ScopeOperation]map[string]struct{} + +func NewKeyScopes() KeyScopes { + return make(KeyScopes) +} + +func (ks KeyScopes) Add(entityType ScopeEntityType, operation ScopeOperation, entity string) { + if ks[entityType] == nil { + ks[entityType] = make(map[ScopeOperation]map[string]struct{}) + } + if ks[entityType][operation] == nil { + ks[entityType][operation] = make(map[string]struct{}) + } + ks[entityType][operation][entity] = struct{}{} +} + +func (ks KeyScopes) Contains(entityType ScopeEntityType, operation ScopeOperation, entity string) bool { + if scopOperations, ok := ks[entityType]; ok { + if entities, ok := scopOperations[operation]; ok { + if _, ok := entities[entity]; ok { + return ok + } + } + } + return false +} + // Key represents API key. type Key struct { ID string `json:"id,omitempty"` @@ -57,6 +107,7 @@ type Key struct { Subject string `json:"subject,omitempty"` // user ID User string `json:"user,omitempty"` Domain string `json:"domain,omitempty"` // domain user ID + Scopes KeyScopes `json:"scopes,omitempty"` IssuedAt time.Time `json:"issued_at,omitempty"` ExpiresAt time.Time `json:"expires_at,omitempty"` } diff --git a/docker/nginx/nginx-key.conf b/docker/nginx/nginx-key.conf index 153a7b7a42..b9a4eee838 100644 --- a/docker/nginx/nginx-key.conf +++ b/docker/nginx/nginx-key.conf @@ -114,7 +114,7 @@ http { # Proxy pass to auth service - location ~ ^/(domains) { + location ~ ^/(domains|keys) { include snippets/proxy-headers.conf; add_header Access-Control-Expose-Headers Location; proxy_pass http://auth:${MG_AUTH_HTTP_PORT}; From e96c46711293e03ebf8bd94a371c7e56d94ab53b Mon Sep 17 00:00:00 2001 From: Arvindh Date: Mon, 17 Jun 2024 14:50:05 +0530 Subject: [PATCH 02/24] revert keys Signed-off-by: Arvindh --- auth/keys.go | 51 --------------------------------------------------- 1 file changed, 51 deletions(-) diff --git a/auth/keys.go b/auth/keys.go index ddc69fbfcc..aa21ee48e0 100644 --- a/auth/keys.go +++ b/auth/keys.go @@ -49,56 +49,6 @@ func (kt KeyType) String() string { } } -type ScopeOperation uint32 - -const ( - CreateOp ScopeOperation = iota - ReadOp - ListOp - UpdateOp - DeleteOp -) - -type ScopeEntityType uint32 - -const ( - DomainManagementScope ScopeEntityType = iota - GroupScope - ChannelScope - ThingScope -) - -type ScopeValue interface { - string | []string -} - -type KeyScopes map[ScopeEntityType]map[ScopeOperation]map[string]struct{} - -func NewKeyScopes() KeyScopes { - return make(KeyScopes) -} - -func (ks KeyScopes) Add(entityType ScopeEntityType, operation ScopeOperation, entity string) { - if ks[entityType] == nil { - ks[entityType] = make(map[ScopeOperation]map[string]struct{}) - } - if ks[entityType][operation] == nil { - ks[entityType][operation] = make(map[string]struct{}) - } - ks[entityType][operation][entity] = struct{}{} -} - -func (ks KeyScopes) Contains(entityType ScopeEntityType, operation ScopeOperation, entity string) bool { - if scopOperations, ok := ks[entityType]; ok { - if entities, ok := scopOperations[operation]; ok { - if _, ok := entities[entity]; ok { - return ok - } - } - } - return false -} - // Key represents API key. type Key struct { ID string `json:"id,omitempty"` @@ -107,7 +57,6 @@ type Key struct { Subject string `json:"subject,omitempty"` // user ID User string `json:"user,omitempty"` Domain string `json:"domain,omitempty"` // domain user ID - Scopes KeyScopes `json:"scopes,omitempty"` IssuedAt time.Time `json:"issued_at,omitempty"` ExpiresAt time.Time `json:"expires_at,omitempty"` } From b9a2a0e346b286fdd70d26d4c9964f97f1ccb42d Mon Sep 17 00:00:00 2001 From: Arvindh Date: Mon, 17 Jun 2024 14:50:17 +0530 Subject: [PATCH 03/24] add pat Signed-off-by: Arvindh --- auth/pat.go | 243 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 243 insertions(+) create mode 100644 auth/pat.go diff --git a/auth/pat.go b/auth/pat.go new file mode 100644 index 0000000000..1009a3eb4a --- /dev/null +++ b/auth/pat.go @@ -0,0 +1,243 @@ +// Copyright (c) Abstract Machines +// SPDX-License-Identifier: Apache-2.0 + +package auth + +import ( + "context" + "encoding/json" + "fmt" + "time" +) + +// Define OperationType. +type OperationType uint32 + +const ( + CreateOp OperationType = iota + ReadOp + ListOp + UpdateOp + DeleteOp +) + +func (ot OperationType) String() string { + switch ot { + case CreateOp: + return "create" + case ReadOp: + return "read" + case ListOp: + return "list" + case UpdateOp: + return "update" + case DeleteOp: + return "delete" + default: + return fmt.Sprintf("unknown operation type %d", ot) + } +} + +func (ot OperationType) MarshalJSON() ([]byte, error) { + return []byte(ot.String()), nil +} +func (ot OperationType) MarshalText() (text []byte, err error) { + return []byte(ot.String()), nil +} + +// Define EntityType. +type EntityType uint32 + +const ( + DomainsScope EntityType = iota + GroupsScope + ChannelsScope + ThingsScope +) + +func (et EntityType) String() string { + switch et { + case DomainsScope: + return "domains" + case GroupsScope: + return "groups" + case ChannelsScope: + return "channels" + case ThingsScope: + return "things" + default: + return fmt.Sprintf("unknown entity type %d", et) + } +} + +func (et EntityType) MarshalJSON() ([]byte, error) { + return []byte(et.String()), nil +} +func (et EntityType) MarshalText() (text []byte, err error) { + return []byte(et.String()), nil +} + +// ScopeValue interface for Any entity ids or for sets of entity ids. +type ScopeValue interface { + Contains(id string) bool +} + +// AnyIDs implements ScopeValue for any entity id value. +type AnyIDs struct{} + +func (s AnyIDs) Contains(id string) bool { return true } + +// SelectedIDs implements ScopeValue for sets of entity ids. +type SelectedIDs map[string]struct{} + +func (s SelectedIDs) Contains(id string) bool { _, ok := s[id]; return ok } + +// OperationRegistry contains map of OperationType with value of AnyIDs or SelectedIDs. +type OperationRegistry[T ScopeValue] struct { + Operations map[OperationType]T `json:"operations,omitempty"` +} + +// Define EntityRegistry contains map of Entity types with all its related operations registry. +type EntityRegistry struct { + Entities map[EntityType]OperationRegistry[ScopeValue] `json:"entities,omitempty"` +} + +// Add adds entry in Registry. +func (er *EntityRegistry) Add(entityType EntityType, operation OperationType, entityIDs ...string) { + var value ScopeValue + + switch { + case len(entityIDs) == 0, len(entityIDs) == 1 && entityIDs[0] == "*": + value = AnyIDs{} + + default: + var sids SelectedIDs + for _, entityID := range entityIDs { + if sids == nil { + sids = make(SelectedIDs) + } + sids[entityID] = struct{}{} + } + value = sids + } + + if er.Entities == nil { + er.Entities = make(map[EntityType]OperationRegistry[ScopeValue]) + } + if _, exists := er.Entities[entityType]; !exists { + er.Entities[entityType] = OperationRegistry[ScopeValue]{ + Operations: make(map[OperationType]ScopeValue), + } + } + er.Entities[entityType].Operations[operation] = value +} + +func (er *EntityRegistry) Delete(entityType EntityType, operation OperationType, entityIDs ...string) error { + if er.Entities == nil { + return nil + } + + opReg, exists := er.Entities[entityType] + if !exists { + return nil + } + + opEntityIDs, exists := opReg.Operations[operation] + if !exists { + return nil + } + + if len(entityIDs) == 0 { + delete(opReg.Operations, operation) + return nil + } + + switch eIDs := any(opEntityIDs).(type) { + case AnyIDs: + delete(opReg.Operations, operation) + case SelectedIDs: + for _, entityID := range entityIDs { + if !eIDs.Contains(entityID) { + return fmt.Errorf("invalid entity ID in list") + } + } + for _, entityID := range entityIDs { + delete(eIDs, entityID) + if len(eIDs) == 0 { + delete(opReg.Operations, operation) + } + } + } + return nil +} + +// Check entry in Registry. +func (er *EntityRegistry) Check(entityType EntityType, operation OperationType, id string) bool { + if er.Entities == nil { + return false + } + operations, exists := er.Entities[entityType] + if !exists { + return false + } + scopeValue, exists := operations.Operations[operation] + if !exists { + return false + } + return scopeValue.Contains(id) +} + +func (er *EntityRegistry) String() string { + str, _ := json.MarshalIndent(er, "", " ") + return string(str) +} + +// PAT in Domain Level +// +// { +// "DomainManagement" : { +// "CreateOp": "All", +// "UpdateOp" : ["domain_1", "domain2"], +// "DeleteOp" : ["domain_3"] +// }, +// "Group" :{ +// "CreateOp" : "All", +// "ReadOp" : ["group_1","group_2"], +// "UpdateOp" : ["group_4"], +// "DeleteOp" : ["group_6"], +// } +// }`` + +// PAT represents Personal Access Token. +type PAT struct { + ID string `json:"id,omitempty"` + User string `json:"user,omitempty"` + Scopes EntityRegistry `json:"scopes,omitempty"` + IssuedAt time.Time `json:"issued_at,omitempty"` + ExpiresAt time.Time `json:"expires_at,omitempty"` +} + +func (pat PAT) String() string { + str, _ := json.MarshalIndent(pat, "", " ") + return string(str) +} + +// Expired verifies if the key is expired. +func (pat PAT) Expired() bool { + return pat.ExpiresAt.UTC().Before(time.Now().UTC()) +} + +// KeyRepository specifies Key persistence API. +// +//go:generate mockery --name KeyRepository --output=./mocks --filename keys.go --quiet --note "Copyright (c) Abstract Machines" +type PATRepository 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 +} From 937790dce7043322b3c07186056b2c7effedcfc6 Mon Sep 17 00:00:00 2001 From: Arvindh Date: Mon, 17 Jun 2024 14:58:39 +0530 Subject: [PATCH 04/24] add pat Signed-off-by: Arvindh --- auth/pat.go | 66 ++++++++++++++++++++++++++++++++++++++++------------- 1 file changed, 50 insertions(+), 16 deletions(-) diff --git a/auth/pat.go b/auth/pat.go index 1009a3eb4a..b5d07fa3ec 100644 --- a/auth/pat.go +++ b/auth/pat.go @@ -97,7 +97,26 @@ type OperationRegistry[T ScopeValue] struct { Operations map[OperationType]T `json:"operations,omitempty"` } -// Define EntityRegistry contains map of Entity types with all its related operations registry. +// `EntityRegistry` contains map of Entity types with all its related operations registry. +// Example Visualization of `EntityRegistry`. +// +// { +// "entities": { +// "domains": { +// "operations": { +// "create": {} +// } +// }, +// "groups": { +// "operations": { +// "read": { +// "group1": {}, +// "group2": {} +// } +// } +// } +// } +// } type EntityRegistry struct { Entities map[EntityType]OperationRegistry[ScopeValue] `json:"entities,omitempty"` } @@ -188,27 +207,39 @@ func (er *EntityRegistry) Check(entityType EntityType, operation OperationType, } func (er *EntityRegistry) String() string { - str, _ := json.MarshalIndent(er, "", " ") + str, err := json.MarshalIndent(er, "", " ") + if err != nil { + return fmt.Sprintf("failed to convert scope/entity_registry to string: json marshal error :%s", err.Error()) + } return string(str) } -// PAT in Domain Level +// PAT represents Personal Access Token. +// Example Visualization of PAT. // // { -// "DomainManagement" : { -// "CreateOp": "All", -// "UpdateOp" : ["domain_1", "domain2"], -// "DeleteOp" : ["domain_3"] +// "id": "new id", +// "user": "user 1", +// "scopes": { +// "entities": { +// "domains": { +// "operations": { +// "create": {} +// } // }, -// "Group" :{ -// "CreateOp" : "All", -// "ReadOp" : ["group_1","group_2"], -// "UpdateOp" : ["group_4"], -// "DeleteOp" : ["group_6"], +// "groups": { +// "operations": { +// "read": { +// "group1": {}, +// "group2": {} +// } +// } // } -// }`` - -// PAT represents Personal Access Token. +// } +// }, +// "issued_at": "2024-06-17T14:52:22.670691615+05:30", +// "expires_at": "2024-06-20T14:52:22.670691708+05:30" +// } type PAT struct { ID string `json:"id,omitempty"` User string `json:"user,omitempty"` @@ -218,7 +249,10 @@ type PAT struct { } func (pat PAT) String() string { - str, _ := json.MarshalIndent(pat, "", " ") + str, err := json.MarshalIndent(pat, "", " ") + if err != nil { + return fmt.Sprintf("failed to convert PAT to string: json marshal error :%s", err.Error()) + } return string(str) } From 2bd0c1ebc3c7c5d41382674f68bd5889f0c2a7d3 Mon Sep 17 00:00:00 2001 From: Arvindh Date: Tue, 18 Jun 2024 17:34:36 +0530 Subject: [PATCH 05/24] update PAT Signed-off-by: Arvindh --- auth/pat.go | 438 ++++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 323 insertions(+), 115 deletions(-) diff --git a/auth/pat.go b/auth/pat.go index b5d07fa3ec..1a9dac8ea1 100644 --- a/auth/pat.go +++ b/auth/pat.go @@ -45,36 +45,63 @@ func (ot OperationType) MarshalText() (text []byte, err error) { return []byte(ot.String()), nil } -// Define EntityType. -type EntityType uint32 +// Define DomainEntityType. +type DomainEntityType uint32 const ( - DomainsScope EntityType = iota - GroupsScope - ChannelsScope - ThingsScope + DomainManagementScope DomainEntityType = iota + DomainGroupsScope + DomainChannelsScope + DomainThingsScope + DomainNullScope ) -func (et EntityType) String() string { - switch et { - case DomainsScope: - return "domains" - case GroupsScope: +func (det DomainEntityType) String() string { + switch det { + case DomainManagementScope: + return "domain_management" + case DomainGroupsScope: return "groups" - case ChannelsScope: + case DomainChannelsScope: return "channels" - case ThingsScope: + case DomainThingsScope: return "things" default: - return fmt.Sprintf("unknown entity type %d", et) + return fmt.Sprintf("unknown domain entity type %d", det) } } -func (et EntityType) MarshalJSON() ([]byte, error) { - return []byte(et.String()), nil +func (det DomainEntityType) MarshalJSON() ([]byte, error) { + return []byte(det.String()), nil } -func (et EntityType) MarshalText() (text []byte, err error) { - return []byte(et.String()), nil +func (det DomainEntityType) MarshalText() (text []byte, err error) { + return []byte(det.String()), nil +} + +// Define DomainEntityType. +type PlatformEntityType uint32 + +const ( + PlatformUsersScope PlatformEntityType = iota + PlatformDomainsScope +) + +func (pet PlatformEntityType) String() string { + switch pet { + case PlatformUsersScope: + return "users" + case PlatformDomainsScope: + return "domains" + default: + return fmt.Sprintf("unknown platform entity type %d", pet) + } +} + +func (pet PlatformEntityType) MarshalJSON() ([]byte, error) { + return []byte(pet.String()), nil +} +func (pet PlatformEntityType) MarshalText() (text []byte, err error) { + return []byte(pet.String()), nil } // ScopeValue interface for Any entity ids or for sets of entity ids. @@ -92,46 +119,30 @@ type SelectedIDs map[string]struct{} func (s SelectedIDs) Contains(id string) bool { _, ok := s[id]; return ok } -// OperationRegistry contains map of OperationType with value of AnyIDs or SelectedIDs. -type OperationRegistry[T ScopeValue] struct { - Operations map[OperationType]T `json:"operations,omitempty"` +// OperationScope contains map of OperationType with value of AnyIDs or SelectedIDs. +type OperationScope struct { + Operations map[OperationType]ScopeValue `json:"operations,omitempty"` } -// `EntityRegistry` contains map of Entity types with all its related operations registry. -// Example Visualization of `EntityRegistry`. -// -// { -// "entities": { -// "domains": { -// "operations": { -// "create": {} -// } -// }, -// "groups": { -// "operations": { -// "read": { -// "group1": {}, -// "group2": {} -// } -// } -// } -// } -// } -type EntityRegistry struct { - Entities map[EntityType]OperationRegistry[ScopeValue] `json:"entities,omitempty"` -} - -// Add adds entry in Registry. -func (er *EntityRegistry) Add(entityType EntityType, operation OperationType, entityIDs ...string) { +func (os *OperationScope) Add(operation OperationType, entityIDs ...string) error { var value ScopeValue + if os == nil || os.Operations == nil { + os.Operations = make(map[OperationType]ScopeValue) + } + + if len(entityIDs) == 0 { + return fmt.Errorf("entity ID is missing") + } switch { - case len(entityIDs) == 0, len(entityIDs) == 1 && entityIDs[0] == "*": + case len(entityIDs) == 1 && entityIDs[0] == "*": value = AnyIDs{} - default: var sids SelectedIDs for _, entityID := range entityIDs { + if entityID == "*" { + return fmt.Errorf("list contains wildcard") + } if sids == nil { sids = make(SelectedIDs) } @@ -139,113 +150,302 @@ func (er *EntityRegistry) Add(entityType EntityType, operation OperationType, en } value = sids } - - if er.Entities == nil { - er.Entities = make(map[EntityType]OperationRegistry[ScopeValue]) - } - if _, exists := er.Entities[entityType]; !exists { - er.Entities[entityType] = OperationRegistry[ScopeValue]{ - Operations: make(map[OperationType]ScopeValue), - } - } - er.Entities[entityType].Operations[operation] = value + os.Operations[operation] = value + return nil } -func (er *EntityRegistry) Delete(entityType EntityType, operation OperationType, entityIDs ...string) error { - if er.Entities == nil { - return nil - } - - opReg, exists := er.Entities[entityType] - if !exists { +func (os *OperationScope) Delete(operation OperationType, entityIDs ...string) error { + if os == nil || os.Operations == nil { return nil } - opEntityIDs, exists := opReg.Operations[operation] + opEntityIDs, exists := os.Operations[operation] if !exists { return nil } if len(entityIDs) == 0 { - delete(opReg.Operations, operation) - return nil + return fmt.Errorf("failed to delete operation %s: entity ID is missing", operation.String()) } - switch eIDs := any(opEntityIDs).(type) { + switch eIDs := opEntityIDs.(type) { case AnyIDs: - delete(opReg.Operations, operation) + if !(len(entityIDs) == 1 && entityIDs[0] == "*") { + return fmt.Errorf("failed to delete operation %s: invalid list", operation.String()) + } + delete(os.Operations, operation) + return nil case SelectedIDs: for _, entityID := range entityIDs { if !eIDs.Contains(entityID) { - return fmt.Errorf("invalid entity ID in list") + return fmt.Errorf("failed to delete operation %s: invalid entity ID in list", operation.String()) } } for _, entityID := range entityIDs { delete(eIDs, entityID) if len(eIDs) == 0 { - delete(opReg.Operations, operation) + delete(os.Operations, operation) } } + return nil + default: + return fmt.Errorf("failed to delete operation: invalid entity id type %d", operation) } - return nil } -// Check entry in Registry. -func (er *EntityRegistry) Check(entityType EntityType, operation OperationType, id string) bool { - if er.Entities == nil { +func (os *OperationScope) Check(operation OperationType, entityIDs ...string) bool { + if os == nil || os.Operations == nil { return false } - operations, exists := er.Entities[entityType] + + if scopeValue, ok := os.Operations[operation]; ok { + if len(entityIDs) == 0 { + _, ok := scopeValue.(AnyIDs) + return ok + } + for _, entityID := range entityIDs { + if !scopeValue.Contains(entityID) { + return false + } + } + return true + } + + return false +} + +type DomainScope struct { + DomainManagement OperationScope `json:"domain_management,omitempty"` + Entities map[DomainEntityType]OperationScope `json:"entities,omitempty"` +} + +// Add entry in Domain scope. +func (ds *DomainScope) Add(domainEntityType DomainEntityType, operation OperationType, entityIDs ...string) error { + if ds == nil { + return fmt.Errorf("failed to add domain %s scope: domain_scope is nil and not initialized", domainEntityType) + } + + if domainEntityType < DomainManagementScope || domainEntityType > DomainThingsScope { + return fmt.Errorf("failed to add domain %d scope: invalid domain entity type", domainEntityType) + } + if domainEntityType == DomainManagementScope { + if err := ds.DomainManagement.Add(operation, entityIDs...); err != nil { + return fmt.Errorf("failed to delete domain management scope: %w", err) + } + } + + if ds.Entities == nil { + ds.Entities = make(map[DomainEntityType]OperationScope) + } + + opReg, ok := ds.Entities[domainEntityType] + if !ok { + opReg = OperationScope{} + } + + if err := opReg.Add(operation, entityIDs...); err != nil { + return fmt.Errorf("failed to add domain %s scope: %w ", domainEntityType.String(), err) + } + ds.Entities[domainEntityType] = opReg + return nil +} + +// Delete entry in Domain scope. +func (ds *DomainScope) Delete(domainEntityType DomainEntityType, operation OperationType, entityIDs ...string) error { + if ds == nil { + return nil + } + + if domainEntityType < DomainManagementScope || domainEntityType > DomainThingsScope { + return fmt.Errorf("failed to delete domain %d scope: invalid domain entity type", domainEntityType) + } + if ds.Entities == nil { + return nil + } + + if domainEntityType == DomainManagementScope { + if err := ds.DomainManagement.Delete(operation, entityIDs...); err != nil { + return fmt.Errorf("failed to delete domain management scope: %w", err) + } + } + + os, exists := ds.Entities[domainEntityType] if !exists { + return nil + } + + if err := os.Delete(operation, entityIDs...); err != nil { + return fmt.Errorf("failed to delete domain %s scope: %w", domainEntityType.String(), err) + } + + if len(os.Operations) == 0 { + delete(ds.Entities, domainEntityType) + } + return nil +} + +// Check entry in Domain scope. +func (ds *DomainScope) Check(domainEntityType DomainEntityType, operation OperationType, ids ...string) bool { + if ds.Entities == nil { + return false + } + if domainEntityType < DomainManagementScope || domainEntityType > DomainThingsScope { return false } - scopeValue, exists := operations.Operations[operation] + if domainEntityType == DomainManagementScope { + return ds.DomainManagement.Check(operation, ids...) + } + os, exists := ds.Entities[domainEntityType] if !exists { return false } - return scopeValue.Contains(id) + + return os.Check(operation, ids...) } -func (er *EntityRegistry) String() string { - str, err := json.MarshalIndent(er, "", " ") +func (ds *DomainScope) String() string { + str, err := json.MarshalIndent(ds, "", " ") if err != nil { - return fmt.Sprintf("failed to convert scope/entity_registry to string: json marshal error :%s", err.Error()) + return fmt.Sprintf("failed to convert domain_scope to string: json marshal error :%s", err.Error()) } return string(str) } -// PAT represents Personal Access Token. -// Example Visualization of PAT. +// Example Scope as JSON // // { -// "id": "new id", -// "user": "user 1", -// "scopes": { -// "entities": { -// "domains": { -// "operations": { -// "create": {} -// } -// }, -// "groups": { -// "operations": { -// "read": { -// "group1": {}, -// "group2": {} -// } -// } -// } -// } -// }, -// "issued_at": "2024-06-17T14:52:22.670691615+05:30", -// "expires_at": "2024-06-20T14:52:22.670691708+05:30" -// } +// "platform": { +// "users": { +// "create": {}, +// "read": {}, +// "list": {}, +// "update": {}, +// "delete": {} +// } +// }, +// "domains": { +// "domain_1": { +// "entities": { +// "groups": { +// "create": {}, // this for all groups in domain +// }, +// "channels": { +// // for particular channel in domain +// "delete": { +// "channel1": {}, +// "channel2":{} +// } +// }, +// "things": { +// "update": {} // this for all things in domain +// } +// } +// } +// } +// } +type Scope struct { + Users OperationScope `json:"users,omitempty"` + Domains map[string]DomainScope `json:"domains,omitempty"` +} + +// Add entry in Domain scope. +func (s *Scope) Add(platformEntityType PlatformEntityType, optionalDomainID string, optionalDomainEntityType DomainEntityType, operation OperationType, entityIDs ...string) error { + if s == nil { + return fmt.Errorf("failed to add platform %s scope: scope is nil and not initialized", platformEntityType.String()) + } + switch platformEntityType { + case PlatformUsersScope: + if err := s.Users.Add(operation, entityIDs...); err != nil { + return fmt.Errorf("failed to add platform %s scope: %w", platformEntityType.String(), err) + } + case PlatformDomainsScope: + if optionalDomainID == "" { + return fmt.Errorf("failed to add platform %s scope: invalid domain id", platformEntityType.String()) + } + if s.Domains == nil || len(s.Domains) == 0 { + s.Domains = make(map[string]DomainScope) + } + + ds, ok := s.Domains[optionalDomainID] + if !ok { + ds = DomainScope{} + } + if err := ds.Add(optionalDomainEntityType, operation, entityIDs...); err != nil { + return fmt.Errorf("failed to add platform %s id %s scope : %w", platformEntityType.String(), optionalDomainID, err) + } + s.Domains[optionalDomainID] = ds + default: + return fmt.Errorf("failed to add platform %d scope: invalid platform entity type ", platformEntityType) + } + return nil +} + +// Delete entry in Domain scope. +func (s *Scope) Delete(platformEntityType PlatformEntityType, optionalDomainID string, optionalDomainEntityType DomainEntityType, operation OperationType, entityIDs ...string) error { + if s == nil { + return nil + } + switch platformEntityType { + case PlatformUsersScope: + if err := s.Users.Delete(operation, entityIDs...); err != nil { + return fmt.Errorf("failed to delete platform %s scope: %w", platformEntityType.String(), err) + } + case PlatformDomainsScope: + if optionalDomainID == "" { + return fmt.Errorf("failed to delete platform %s scope: invalid domain id", platformEntityType.String()) + } + ds, ok := s.Domains[optionalDomainID] + if !ok { + return nil + } + if err := ds.Delete(optionalDomainEntityType, operation, entityIDs...); err != nil { + return fmt.Errorf("failed to delete platform %s id %s scope : %w", platformEntityType.String(), optionalDomainID, err) + } + default: + return fmt.Errorf("failed to add platform %d scope: invalid platform entity type ", platformEntityType) + } + return nil +} + +// Check entry in Domain scope. +func (s *Scope) Check(platformEntityType PlatformEntityType, optionalDomainID string, optionalDomainEntityType DomainEntityType, operation OperationType, entityIDs ...string) bool { + if s == nil { + return false + } + switch platformEntityType { + case PlatformUsersScope: + return s.Users.Check(operation, entityIDs...) + case PlatformDomainsScope: + ds, ok := s.Domains[optionalDomainID] + if !ok { + return false + } + return ds.Check(optionalDomainEntityType, operation, entityIDs...) + default: + return false + } +} + +func (s *Scope) String() string { + str, err := json.MarshalIndent(s, "", " ") + if err != nil { + return fmt.Sprintf("failed to convert scope to string: json marshal error :%s", err.Error()) + } + return string(str) +} + +// PAT represents Personal Access Token. type PAT struct { - ID string `json:"id,omitempty"` - User string `json:"user,omitempty"` - Scopes EntityRegistry `json:"scopes,omitempty"` - IssuedAt time.Time `json:"issued_at,omitempty"` - ExpiresAt time.Time `json:"expires_at,omitempty"` + ID string `json:"id,omitempty"` + User string `json:"user,omitempty"` + Name string `json:"name,omitempty"` + Scope Scope `json:"scope,omitempty"` + IssuedAt time.Time `json:"issued_at,omitempty"` + ExpiresAt time.Time `json:"expires_at,omitempty"` + UpdatedAt time.Time `json:"updated_at,omitempty"` + LastUsedAt time.Time `json:"last_used_at,omitempty"` + Revoked bool `json:"revoked,omitempty"` + RevokedAt time.Time `json:"revoked_at,omitempty"` } func (pat PAT) String() string { @@ -261,17 +461,25 @@ func (pat PAT) Expired() bool { return pat.ExpiresAt.UTC().Before(time.Now().UTC()) } +type PATService interface { + Create(ctx context.Context, token string, scope Scope) (PAT, error) + Retrieve(ctx context.Context, token string, patID string) (PAT, error) + List(ctx context.Context, token string) (PAT, error) + Revoke(ctx context.Context, token string, paToken string) error + Delete(ctx context.Context, token string, paToken string) error +} + // KeyRepository specifies Key persistence API. // //go:generate mockery --name KeyRepository --output=./mocks --filename keys.go --quiet --note "Copyright (c) Abstract Machines" type PATRepository 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) + Save(ctx context.Context, pat PAT) (id string, err error) // Retrieve retrieves Key by its unique identifier. - Retrieve(ctx context.Context, issuer string, id string) (key Key, err error) + Retrieve(ctx context.Context, id string) (pat PAT, err error) // Remove removes Key with provided ID. - Remove(ctx context.Context, issuer string, id string) error + Remove(ctx context.Context, id string) error } From 72bb8fc4377a1df440e68f86b63a3e344e95d774 Mon Sep 17 00:00:00 2001 From: Arvindh Date: Sat, 22 Jun 2024 09:42:23 +0530 Subject: [PATCH 06/24] poc pat Signed-off-by: Arvindh --- auth/bolt/doc.go | 6 + auth/bolt/init.go | 6 + auth/bolt/pat.go | 68 ++++++++ auth/keys.go | 4 + auth/pat.go | 212 ++++++++++++++++++++---- auth/service.go | 152 +++++++++++++++++- badgergob/badgergob_test.go | 55 +++++++ badgergob/bgob.go | 187 +++++++++++++++++++++ cmd/test/badger/main.go | 205 +++++++++++++++++++++++ cmd/test/badgergob/benchmark.go | 54 +++++++ cmd/test/badgergob/main.go | 187 +++++++++++++++++++++ cmd/test/bbolt/main.go | 170 ++++++++++++++++++++ cmd/test/bboltgob/main.go | 166 +++++++++++++++++++ cmd/test/patunmarshal/main.go | 172 ++++++++++++++++++++ cmd/test/patunmarshal/temp.json | 71 ++++++++ cmd/test/redis/main.go | 277 ++++++++++++++++++++++++++++++++ cmd/test/redisgob/main.go | 143 +++++++++++++++++ cmd/test/second/main.go | 98 +++++++++++ cmd/test/test.go | 76 +++++++++ go.mod | 9 ++ go.sum | 38 +++++ 21 files changed, 2319 insertions(+), 37 deletions(-) create mode 100644 auth/bolt/doc.go create mode 100644 auth/bolt/init.go create mode 100644 auth/bolt/pat.go create mode 100644 badgergob/badgergob_test.go create mode 100644 badgergob/bgob.go create mode 100644 cmd/test/badger/main.go create mode 100644 cmd/test/badgergob/benchmark.go create mode 100644 cmd/test/badgergob/main.go create mode 100644 cmd/test/bbolt/main.go create mode 100644 cmd/test/bboltgob/main.go create mode 100644 cmd/test/patunmarshal/main.go create mode 100644 cmd/test/patunmarshal/temp.json create mode 100644 cmd/test/redis/main.go create mode 100644 cmd/test/redisgob/main.go create mode 100644 cmd/test/second/main.go create mode 100644 cmd/test/test.go diff --git a/auth/bolt/doc.go b/auth/bolt/doc.go new file mode 100644 index 0000000000..dcd06ac566 --- /dev/null +++ b/auth/bolt/doc.go @@ -0,0 +1,6 @@ +// Copyright (c) Abstract Machines +// SPDX-License-Identifier: Apache-2.0 + +// Package bolt contains PAT repository implementations using +// bolt as the underlying database. +package bolt diff --git a/auth/bolt/init.go b/auth/bolt/init.go new file mode 100644 index 0000000000..dcd06ac566 --- /dev/null +++ b/auth/bolt/init.go @@ -0,0 +1,6 @@ +// Copyright (c) Abstract Machines +// SPDX-License-Identifier: Apache-2.0 + +// Package bolt contains PAT repository implementations using +// bolt as the underlying database. +package bolt diff --git a/auth/bolt/pat.go b/auth/bolt/pat.go new file mode 100644 index 0000000000..ed32c5c796 --- /dev/null +++ b/auth/bolt/pat.go @@ -0,0 +1,68 @@ +// Copyright (c) Abstract Machines +// SPDX-License-Identifier: Apache-2.0 + +package bolt + +import ( + "context" + "time" + + "github.com/absmach/magistrala/auth" + "go.etcd.io/bbolt" +) + +type patRepo struct { + db *bbolt.DB +} + +// NewPATRepository instantiates a bbolt +// implementation of PAT repository. +func NewDomainRepository(db *bbolt.DB) auth.PATSRepository { + return &patRepo{ + db: db, + } +} + +func (pr *patRepo) Save(ctx context.Context, pat auth.PAT) (id string, err error) { + return "", nil +} + +func (pr *patRepo) Retrieve(ctx context.Context, userID, patID string) (pat auth.PAT, err error) { + return auth.PAT{}, nil +} + +func (pr *patRepo) UpdateName(ctx context.Context, userID, patID, name string) (auth.PAT, error) { + return auth.PAT{}, nil +} + +func (pr *patRepo) UpdateDescription(ctx context.Context, userID, patID, description string) (auth.PAT, error) { + return auth.PAT{}, nil +} + +func (pr *patRepo) UpdateTokenHash(ctx context.Context, userID, patID, tokenHash string, expiryAt time.Time) (auth.PAT, error) { + return auth.PAT{}, nil +} + +func (pr *patRepo) RetrieveAll(ctx context.Context, userID string) (pats auth.PATSPage, err error) { + return auth.PATSPage{}, nil +} + +func (pr *patRepo) Revoke(ctx context.Context, userID, patID string) error { + return nil +} + +func (pr *patRepo) Remove(ctx context.Context, userID, patID string) error { + return nil +} + +func (pr *patRepo) AddScopeEntry(ctx context.Context, userID, patID string, platformEntityType auth.PlatformEntityType, optionalDomainID string, optionalDomainEntityType auth.DomainEntityType, operation auth.OperationType, entityIDs ...string) (auth.Scope, error) { + return auth.Scope{}, nil +} + +func (pr *patRepo) RemoveScopeEntry(ctx context.Context, userID, patID string, platformEntityType auth.PlatformEntityType, optionalDomainID string, optionalDomainEntityType auth.DomainEntityType, operation auth.OperationType, entityIDs ...string) (auth.Scope, error) { + return auth.Scope{}, nil +} + +func (pr *patRepo) RemoveAllScopeEntry(ctx context.Context, userID, patID string) error { + return nil +} diff --git a/auth/keys.go b/auth/keys.go index aa21ee48e0..e273119014 100644 --- a/auth/keys.go +++ b/auth/keys.go @@ -30,6 +30,8 @@ const ( RecoveryKey // APIKey enables the one to act on behalf of the user. APIKey + // PersonalAccessToken represents token generated by user for automation. + PersonalAccessToken // InvitationKey is a key for inviting new users. InvitationKey ) @@ -44,6 +46,8 @@ func (kt KeyType) String() string { return "recovery" case APIKey: return "API" + case PersonalAccessToken: + return "pat" default: return "unknown" } diff --git a/auth/pat.go b/auth/pat.go index 1a9dac8ea1..fc48851da3 100644 --- a/auth/pat.go +++ b/auth/pat.go @@ -38,6 +38,23 @@ func (ot OperationType) String() string { } } +func ParseOperationType(ot string) (OperationType, error) { + switch ot { + case "create": + return CreateOp, nil + case "read": + return ReadOp, nil + case "list": + return ListOp, nil + case "update": + return UpdateOp, nil + case "delete": + return DeleteOp, nil + default: + return 0, fmt.Errorf("unknown operation type %s", ot) + } +} + func (ot OperationType) MarshalJSON() ([]byte, error) { return []byte(ot.String()), nil } @@ -45,6 +62,11 @@ func (ot OperationType) MarshalText() (text []byte, err error) { return []byte(ot.String()), nil } +func (ot *OperationType) UnmarshalText(data []byte) (err error) { + *ot, err = ParseOperationType(string(data)) + return err +} + // Define DomainEntityType. type DomainEntityType uint32 @@ -71,13 +93,33 @@ func (det DomainEntityType) String() string { } } +func ParseDomainEntityType(det string) (DomainEntityType, error) { + switch det { + case "domain_management": + return DomainManagementScope, nil + case "groups": + return DomainGroupsScope, nil + case "channels": + return DomainChannelsScope, nil + case "things": + return DomainThingsScope, nil + default: + return 0, fmt.Errorf("unknown domain entity type %s", det) + } +} + func (det DomainEntityType) MarshalJSON() ([]byte, error) { return []byte(det.String()), nil } -func (det DomainEntityType) MarshalText() (text []byte, err error) { +func (det DomainEntityType) MarshalText() ([]byte, error) { return []byte(det.String()), nil } +func (det *DomainEntityType) UnmarshalText(data []byte) (err error) { + *det, err = ParseDomainEntityType(string(data)) + return err +} + // Define DomainEntityType. type PlatformEntityType uint32 @@ -97,6 +139,17 @@ func (pet PlatformEntityType) String() string { } } +func ParsePlatformEntityType(pet string) (PlatformEntityType, error) { + switch pet { + case "users": + return PlatformUsersScope, nil + case "domains": + return PlatformDomainsScope, nil + default: + return 0, fmt.Errorf("unknown platform entity type %s", pet) + } +} + func (pet PlatformEntityType) MarshalJSON() ([]byte, error) { return []byte(pet.String()), nil } @@ -104,6 +157,11 @@ func (pet PlatformEntityType) MarshalText() (text []byte, err error) { return []byte(pet.String()), nil } +func (pet *PlatformEntityType) UnmarshalText(data []byte) (err error) { + *pet, err = ParsePlatformEntityType(string(data)) + return err +} + // ScopeValue interface for Any entity ids or for sets of entity ids. type ScopeValue interface { Contains(id string) bool @@ -124,6 +182,50 @@ type OperationScope struct { Operations map[OperationType]ScopeValue `json:"operations,omitempty"` } +func (os *OperationScope) UnmarshalJSON(data []byte) error { + type tempOperationScope struct { + Operations map[OperationType]json.RawMessage `json:"operations"` + } + + var tempScope tempOperationScope + if err := json.Unmarshal(data, &tempScope); err != nil { + return err + } + // Initialize the Operations map + os.Operations = make(map[OperationType]ScopeValue) + + for opType, rawMessage := range tempScope.Operations { + var stringValue string + var stringArrayValue []string + + // Try to unmarshal as string + if err := json.Unmarshal(rawMessage, &stringValue); err == nil { + switch { + case stringValue == "*": + os.Operations[opType] = AnyIDs{} + default: + os.Operations[opType] = SelectedIDs{stringValue: {}} + } + continue + } + + // Try to unmarshal as []string + if err := json.Unmarshal(rawMessage, &stringArrayValue); err == nil { + sids := make(SelectedIDs) + for _, stringVal := range stringArrayValue { + sids[stringVal] = struct{}{} + } + os.Operations[opType] = sids + continue + } + + // If neither unmarshalling succeeded, return an error + return fmt.Errorf("invalid ScopeValue for OperationType %v", opType) + } + + return nil +} + func (os *OperationScope) Add(operation OperationType, entityIDs ...string) error { var value ScopeValue @@ -303,14 +405,6 @@ func (ds *DomainScope) Check(domainEntityType DomainEntityType, operation Operat return os.Check(operation, ids...) } -func (ds *DomainScope) String() string { - str, err := json.MarshalIndent(ds, "", " ") - if err != nil { - return fmt.Sprintf("failed to convert domain_scope to string: json marshal error :%s", err.Error()) - } - return string(str) -} - // Example Scope as JSON // // { @@ -427,7 +521,7 @@ func (s *Scope) Check(platformEntityType PlatformEntityType, optionalDomainID st } func (s *Scope) String() string { - str, err := json.MarshalIndent(s, "", " ") + str, err := json.Marshal(s) // , "", " ") if err != nil { return fmt.Sprintf("failed to convert scope to string: json marshal error :%s", err.Error()) } @@ -436,19 +530,28 @@ func (s *Scope) String() string { // PAT represents Personal Access Token. type PAT struct { - ID string `json:"id,omitempty"` - User string `json:"user,omitempty"` - Name string `json:"name,omitempty"` - Scope Scope `json:"scope,omitempty"` - IssuedAt time.Time `json:"issued_at,omitempty"` - ExpiresAt time.Time `json:"expires_at,omitempty"` - UpdatedAt time.Time `json:"updated_at,omitempty"` - LastUsedAt time.Time `json:"last_used_at,omitempty"` - Revoked bool `json:"revoked,omitempty"` - RevokedAt time.Time `json:"revoked_at,omitempty"` -} - -func (pat PAT) String() string { + ID string `json:"id,omitempty"` + User string `json:"user,omitempty"` + Name string `json:"name,omitempty"` + Description string `json:"Description,omitempty"` + Token string `json:"Token,omitempty"` + Scope Scope `json:"scope,omitempty"` + IssuedAt time.Time `json:"issued_at,omitempty"` + ExpiresAt time.Time `json:"expires_at,omitempty"` + UpdatedAt time.Time `json:"updated_at,omitempty"` + LastUsedAt time.Time `json:"last_used_at,omitempty"` + Revoked bool `json:"revoked,omitempty"` + RevokedAt time.Time `json:"revoked_at,omitempty"` +} + +type PATSPage struct { + Total uint64 `json:"total"` + Offset uint64 `json:"offset"` + Limit uint64 `json:"limit"` + PATS []PAT `json:"pats"` +} + +func (pat *PAT) String() string { str, err := json.MarshalIndent(pat, "", " ") if err != nil { return fmt.Sprintf("failed to convert PAT to string: json marshal error :%s", err.Error()) @@ -461,25 +564,64 @@ func (pat PAT) Expired() bool { return pat.ExpiresAt.UTC().Before(time.Now().UTC()) } -type PATService interface { - Create(ctx context.Context, token string, scope Scope) (PAT, error) +type PATS interface { + Create(ctx context.Context, token, name, description string, duration time.Duration, scope Scope) (PAT, error) + + UpdateName(ctx context.Context, token, patID, name string) (PAT, error) + UpdateDescription(ctx context.Context, token, patID, description string) (PAT, error) + Retrieve(ctx context.Context, token string, patID string) (PAT, error) - List(ctx context.Context, token string) (PAT, error) - Revoke(ctx context.Context, token string, paToken string) error - Delete(ctx context.Context, token string, paToken string) error + List(ctx context.Context, token string) (PATSPage, error) + Delete(ctx context.Context, token string, patID string) error + + ResetToken(ctx context.Context, token, patID string, duration time.Duration) (PAT, error) + RevokeToken(ctx context.Context, token string, patID string) error + + AddScope(ctx context.Context, token, patID string, platformEntityType PlatformEntityType, optionalDomainID string, optionalDomainEntityType DomainEntityType, operation OperationType, entityIDs ...string) error + RemoveScope(ctx context.Context, token, patID string, platformEntityType PlatformEntityType, optionalDomainID string, optionalDomainEntityType DomainEntityType, operation OperationType, entityIDs ...string) error + ClearAllScope(ctx context.Context, token, patID string) error + + // This will be removed during PR merge. + TestCheckScope(ctx context.Context, token, patID string, platformEntityType PlatformEntityType, optionalDomainID string, optionalDomainEntityType DomainEntityType, operation OperationType, entityIDs ...string) error + + IdentifyPAT(ctx context.Context, paToken string) (PAT, error) + AuthorizationPAT(ctx context.Context, paToken string) (PAT, error) } -// KeyRepository specifies Key persistence API. +// PATSRepository specifies Key persistence API. // //go:generate mockery --name KeyRepository --output=./mocks --filename keys.go --quiet --note "Copyright (c) Abstract Machines" -type PATRepository interface { - // Save persists the Key. A non-nil error is returned to indicate - // operation failure +type PATSRepository interface { + // Save persists the PAT Save(ctx context.Context, pat PAT) (id string, err error) - // Retrieve retrieves Key by its unique identifier. - Retrieve(ctx context.Context, id string) (pat PAT, err error) + // Retrieve retrieves users PAT by its unique identifier. + Retrieve(ctx context.Context, userID, patID string) (pat PAT, err error) + + // UpdateName updates the name of a PAT. + UpdateName(ctx context.Context, userID, patID, name string) (PAT, error) + + // UpdateDescription updates the description of a PAT. + UpdateDescription(ctx context.Context, userID, patID, description string) (PAT, error) + + // UpdateTokenHash updates the token hash of a PAT. + UpdateTokenHash(ctx context.Context, userID, patID, tokenHash string, expiryAt time.Time) (PAT, error) + + // UpdateLastUsed updates the last used details of a PAT. + UpdateLastUsed(ctx context.Context, token, patID, description string) (PAT, error) + + // RetrieveAll retrieves all PATs belongs to userID. + RetrieveAll(ctx context.Context, userID string) (pats PATSPage, err error) + + // Revoke PAT with provided ID. + Revoke(ctx context.Context, userID, patID string) error // Remove removes Key with provided ID. - Remove(ctx context.Context, id string) error + Remove(ctx context.Context, userID, patID string) error + + AddScopeEntry(ctx context.Context, userID, patID string, platformEntityType PlatformEntityType, optionalDomainID string, optionalDomainEntityType DomainEntityType, operation OperationType, entityIDs ...string) (Scope, error) + + RemoveScopeEntry(ctx context.Context, userID, patID string, platformEntityType PlatformEntityType, optionalDomainID string, optionalDomainEntityType DomainEntityType, operation OperationType, entityIDs ...string) (Scope, error) + + RemoveAllScopeEntry(ctx context.Context, userID, patID string) error } diff --git a/auth/service.go b/auth/service.go index 8bb5f1342d..3a587e9c38 100644 --- a/auth/service.go +++ b/auth/service.go @@ -71,6 +71,12 @@ var ( AdminPermission, MembershipPermission, } + + errCreatePAT = errors.New("failed to create PAT") + errUpdatePAT = errors.New("failed to update PAT") + errRetrievePAT = errors.New("failed to retrieve PAT") + errDeletePAT = errors.New("failed to delete PAT") + errRevokePAT = errors.New("failed to revoke PAT") ) // Authn specifies an API that must be fulfilled by the domain service @@ -105,6 +111,7 @@ type Service interface { Authn Authz Domains + PATS } var _ Service = (*service)(nil) @@ -112,6 +119,7 @@ var _ Service = (*service)(nil) type service struct { keys KeyRepository domains DomainsRepository + pats PATSRepository idProvider magistrala.IDProvider agent PolicyAgent tokenizer Tokenizer @@ -208,10 +216,14 @@ func (svc service) Authorize(ctx context.Context, pr PolicyReq) error { return errors.Wrap(svcerr.ErrAuthentication, err) } if key.Subject == "" { - if pr.ObjectType == GroupType || pr.ObjectType == ThingType || pr.ObjectType == DomainType { + switch { + case pr.ObjectType == GroupType || pr.ObjectType == ThingType || pr.ObjectType == DomainType: return svcerr.ErrDomainAuthorization + case pr.ObjectType == UserType: + key.Subject = key.User + default: + return svcerr.ErrAuthentication } - return svcerr.ErrAuthentication } pr.Subject = key.Subject pr.Domain = key.Domain @@ -1066,3 +1078,139 @@ func (svc service) DeleteEntityPolicies(ctx context.Context, entityType, id stri return errInvalidEntityType } } + +func (svc service) Create(ctx context.Context, token, name, description string, duration time.Duration, scope Scope) (PAT, error) { + key, err := svc.Identify(ctx, token) + if err != nil { + return PAT{}, err + } + + pat := PAT{ + User: key.User, + } + id, err := svc.pats.Save(ctx, pat) + if err != nil { + return PAT{}, errors.Wrap(errCreatePAT, err) + } + pat.ID = id + return pat, nil +} +func (svc service) UpdateName(ctx context.Context, token, patID, name string) (PAT, error) { + key, err := svc.Identify(ctx, token) + if err != nil { + return PAT{}, err + } + pat, err := svc.pats.UpdateName(ctx, key.User, patID, name) + if err != nil { + return PAT{}, errors.Wrap(errUpdatePAT, err) + } + return pat, nil +} +func (svc service) UpdateDescription(ctx context.Context, token, patID, description string) (PAT, error) { + key, err := svc.Identify(ctx, token) + if err != nil { + return PAT{}, err + } + pat, err := svc.pats.UpdateDescription(ctx, key.User, patID, description) + if err != nil { + return PAT{}, errors.Wrap(errUpdatePAT, err) + } + return pat, nil +} +func (svc service) Retrieve(ctx context.Context, token, patID string) (PAT, error) { + key, err := svc.Identify(ctx, token) + if err != nil { + return PAT{}, err + } + + pat, err := svc.pats.Retrieve(ctx, key.User, patID) + if err != nil { + return PAT{}, errors.Wrap(errRetrievePAT, err) + } + return pat, nil +} +func (svc service) List(ctx context.Context, token string) (PATSPage, error) { + key, err := svc.Identify(ctx, token) + if err != nil { + return PATSPage{}, err + } + patsPage, err := svc.pats.RetrieveAll(ctx, key.User) + if err != nil { + return PATSPage{}, errors.Wrap(errRetrievePAT, err) + } + return patsPage, nil +} +func (svc service) Delete(ctx context.Context, token, patID string) error { + key, err := svc.Identify(ctx, token) + if err != nil { + return err + } + if err := svc.pats.Remove(ctx, key.User, patID); err != nil { + return errors.Wrap(errDeletePAT, err) + } + return nil +} +func (svc service) ResetToken(ctx context.Context, token, patID string, duration time.Duration) (PAT, error) { + key, err := svc.Identify(ctx, token) + if err != nil { + return PAT{}, err + } + + var newTokenHash string + var newExpiry time.Time + // Generate new HashToken take place here + + pat, err := svc.pats.UpdateTokenHash(ctx, key.User, patID, newTokenHash, newExpiry) + if err != nil { + return PAT{}, errors.Wrap(errUpdatePAT, err) + } + return pat, nil +} +func (svc service) RevokeToken(ctx context.Context, token, patID string) error { + key, err := svc.Identify(ctx, token) + if err != nil { + return err + } + + if err := svc.pats.Revoke(ctx, key.User, patID); err != nil { + return errors.Wrap(errRevokePAT, err) + } + return nil +} +func (svc service) AddScope(ctx context.Context, token, patID string, platformEntityType PlatformEntityType, optionalDomainID string, optionalDomainEntityType DomainEntityType, operation OperationType, entityIDs ...string) (Scope, error) { + key, err := svc.Identify(ctx, token) + if err != nil { + return Scope{}, err + } + if err := svc.pats.AddScope(ctx, key.User, patID); err != nil { + return errors.Wrap(errRevokePAT, err) + } + return nil +} +func (svc service) RemoveScope(ctx context.Context, token, patID string, platformEntityType PlatformEntityType, optionalDomainID string, optionalDomainEntityType DomainEntityType, operation OperationType, entityIDs ...string) (Scope, error) { + key, err := svc.Identify(ctx, token) + if err != nil { + return Scope{}, err + } + return nil +} +func (svc service) ClearAllScope(ctx context.Context, token, patID string) error { + key, err := svc.Identify(ctx, token) + if err != nil { + return err + } + return nil +} +func (svc service) TestCheckScope(ctx context.Context, token, patID string, platformEntityType PlatformEntityType, optionalDomainID string, optionalDomainEntityType DomainEntityType, operation OperationType, entityIDs ...string) error { + key, err := svc.Identify(ctx, token) + if err != nil { + return PAT{}, err + } + return nil +} +func (svc service) IdentifyPAT(ctx context.Context, paToken string) (PAT, error) { + return PAT{}, nil +} +func (svc service) AuthorizationPAT(ctx context.Context, paToken string) (PAT, error) { + return PAT{}, nil +} diff --git a/badgergob/badgergob_test.go b/badgergob/badgergob_test.go new file mode 100644 index 0000000000..4439ca0391 --- /dev/null +++ b/badgergob/badgergob_test.go @@ -0,0 +1,55 @@ +package badgergob_test + +import ( + "log" + "testing" + "time" + + "github.com/absmach/magistrala/auth" + "github.com/absmach/magistrala/badgergob" + badger "github.com/dgraph-io/badger/v4" + "github.com/google/uuid" +) + +func generateTestPAT() auth.PAT { + return auth.PAT{ + ID: uuid.New().String(), + User: "user123", + Name: "Test Token", + IssuedAt: time.Now(), + ExpiresAt: time.Now().Add(24 * time.Hour), + Scope: auth.Scope{ + Users: auth.OperationScope{ + Operations: map[auth.OperationType]auth.ScopeValue{ + auth.CreateOp: auth.SelectedIDs{"entity1": {}, "entity2": {}}, + }, + }, + }, + } +} + +func BenchmarkGetPAT(b *testing.B) { + // Open Badger database + opts := badger.DefaultOptions("./badger") + opts.Logger = nil + db, err := badger.Open(opts) + if err != nil { + log.Fatal(err) + } + defer db.Close() + + // Store a PAT to retrieve + pat := generateTestPAT() + err = badgergob.StorePAT(db, pat) + if err != nil { + log.Fatal(err) + } + + b.ResetTimer() + for i := 0; i < b.N; i++ { + _, err := badgergob.GetPAT(db, pat.ID) + if err != nil { + log.Fatal(err) + } + } +} diff --git a/badgergob/bgob.go b/badgergob/bgob.go new file mode 100644 index 0000000000..4727d84a94 --- /dev/null +++ b/badgergob/bgob.go @@ -0,0 +1,187 @@ +package badgergob + +import ( + "bytes" + "encoding/gob" + "fmt" + "log" + "strings" + "time" + + "github.com/absmach/magistrala/auth" + badger "github.com/dgraph-io/badger/v4" + "github.com/google/uuid" +) + +const timeLayout = "2006-01-02 15:04:05.999999999 -0700 MST" + +func init() { + gob.Register(auth.SelectedIDs{}) + gob.Register(auth.AnyIDs{}) +} +func main() { + // Open Badger database + db, err := badger.Open(badger.DefaultOptions("./badger")) + if err != nil { + log.Fatal(err) + } + defer db.Close() + + // Create a new PAT + pat := auth.PAT{ + ID: uuid.New().String(), + User: "user123", + Name: "Test Token", + IssuedAt: time.Now(), + ExpiresAt: time.Now().Add(24 * time.Hour), + } + + // Set scope + pat.Scope.Users = auth.OperationScope{ + Operations: map[auth.OperationType]auth.ScopeValue{ + auth.CreateOp: auth.SelectedIDs{"entity1": {}, "entity2": {}}, + }, + } + + // Store PAT + if err := StorePAT(db, pat); err != nil { + log.Fatal(err) + } + + // Retrieve PAT + retrievedPAT, err := GetPAT(db, pat.ID) + if err != nil { + log.Fatal(err) + } + fmt.Printf("Retrieved PAT: %+v\n", retrievedPAT) +} + +func StorePAT(db *badger.DB, pat auth.PAT) error { + return db.Update(func(txn *badger.Txn) error { + if err := txn.Set([]byte(fmt.Sprintf("pat:%s:id", pat.ID)), []byte(pat.ID)); err != nil { + return err + } + if err := txn.Set([]byte(fmt.Sprintf("pat:%s:user", pat.ID)), []byte(pat.User)); err != nil { + return err + } + if err := txn.Set([]byte(fmt.Sprintf("pat:%s:name", pat.ID)), []byte(pat.Name)); err != nil { + return err + } + if err := txn.Set([]byte(fmt.Sprintf("pat:%s:issued_at", pat.ID)), []byte(pat.IssuedAt.String())); err != nil { + return err + } + if err := txn.Set([]byte(fmt.Sprintf("pat:%s:expires_at", pat.ID)), []byte(pat.ExpiresAt.String())); err != nil { + return err + } + // Store scope + scopeBytes, err := EncodeScopeToGob(pat.Scope) + if err != nil { + return err + } + if err := txn.Set([]byte(fmt.Sprintf("pat:%s:scope", pat.ID)), scopeBytes); err != nil { + return err + } + return nil + }) +} + +func GetPAT(db *badger.DB, id string) (auth.PAT, error) { + var pat auth.PAT + err := db.View(func(txn *badger.Txn) error { + patID, err := txn.Get([]byte(fmt.Sprintf("pat:%s:id", id))) + if err != nil { + return err + } + err = patID.Value(func(val []byte) error { + pat.ID = string(val) + return nil + }) + if err != nil { + return err + } + + user, err := txn.Get([]byte(fmt.Sprintf("pat:%s:user", id))) + if err != nil { + return err + } + err = user.Value(func(val []byte) error { + pat.User = string(val) + return nil + }) + if err != nil { + return err + } + + name, err := txn.Get([]byte(fmt.Sprintf("pat:%s:name", id))) + if err != nil { + return err + } + err = name.Value(func(val []byte) error { + pat.Name = string(val) + return nil + }) + if err != nil { + return err + } + + issuedAt, err := txn.Get([]byte(fmt.Sprintf("pat:%s:issued_at", id))) + if err != nil { + return err + } + err = issuedAt.Value(func(val []byte) error { + pat.IssuedAt, err = time.Parse(timeLayout, strings.Split(string(val), " m=")[0]) + return err + }) + if err != nil { + return err + } + + expiresAt, err := txn.Get([]byte(fmt.Sprintf("pat:%s:expires_at", id))) + if err != nil { + return err + } + err = expiresAt.Value(func(val []byte) error { + pat.ExpiresAt, err = time.Parse(timeLayout, strings.Split(string(val), " m=")[0]) + return err + }) + if err != nil { + return err + } + + // Retrieve scope + scope, err := txn.Get([]byte(fmt.Sprintf("pat:%s:scope", id))) + if err != nil { + return err + } + + err = scope.Value(func(val []byte) error { + pat.Scope, err = DecodeGobToScope(val) + return err + }) + if err != nil { + return err + } + + return nil + }) + return pat, err +} + +func EncodeScopeToGob(scope auth.Scope) ([]byte, error) { + buf := bytes.NewBuffer([]byte{}) + enc := gob.NewEncoder(buf) + if err := enc.Encode(scope); err != nil { + return nil, err + } + return buf.Bytes(), nil +} + +func DecodeGobToScope(scopeBytes []byte) (auth.Scope, error) { + buf := bytes.NewBuffer(scopeBytes) + var scope auth.Scope + dec := gob.NewDecoder(buf) + if err := dec.Decode(&scope); err != nil { + return auth.Scope{}, err + } + return scope, nil +} diff --git a/cmd/test/badger/main.go b/cmd/test/badger/main.go new file mode 100644 index 0000000000..f9d18d3a2b --- /dev/null +++ b/cmd/test/badger/main.go @@ -0,0 +1,205 @@ +package main + +import ( + "fmt" + "log" + "strings" + "time" + + "github.com/absmach/magistrala/auth" + badger "github.com/dgraph-io/badger/v4" + "github.com/google/uuid" +) + +const timeLayout = "2006-01-02 15:04:05.999999999 -0700 MST" + +func main() { + // Open Badger database + db, err := badger.Open(badger.DefaultOptions("./badger")) + if err != nil { + log.Fatal(err) + } + defer db.Close() + + // Create a new PAT + pat := auth.PAT{ + ID: uuid.New().String(), + User: "user123", + Name: "Test Token", + IssuedAt: time.Now(), + ExpiresAt: time.Now().Add(24 * time.Hour), + } + + // Set scope + pat.Scope.Users = auth.OperationScope{ + Operations: map[auth.OperationType]auth.ScopeValue{ + auth.CreateOp: auth.SelectedIDs{"entity1": {}, "entity2": {}}, + }, + } + + // Store PAT + if err := storePAT(db, pat); err != nil { + log.Fatal(err) + } + + // Retrieve PAT + retrievedPAT, err := getPAT(db, pat.ID) + if err != nil { + log.Fatal(err) + } + fmt.Printf("Retrieved PAT: %+v\n", retrievedPAT) +} + +func storePAT(db *badger.DB, pat auth.PAT) error { + return db.Update(func(txn *badger.Txn) error { + if err := txn.Set([]byte(fmt.Sprintf("pat:%s:id", pat.ID)), []byte(pat.ID)); err != nil { + return err + } + if err := txn.Set([]byte(fmt.Sprintf("pat:%s:user", pat.ID)), []byte(pat.User)); err != nil { + return err + } + if err := txn.Set([]byte(fmt.Sprintf("pat:%s:name", pat.ID)), []byte(pat.Name)); err != nil { + return err + } + if err := txn.Set([]byte(fmt.Sprintf("pat:%s:issued_at", pat.ID)), []byte(pat.IssuedAt.String())); err != nil { + return err + } + if err := txn.Set([]byte(fmt.Sprintf("pat:%s:expires_at", pat.ID)), []byte(pat.ExpiresAt.String())); err != nil { + return err + } + // Store scope + scopeKeyPrefix := fmt.Sprintf("pat:%s:scope", pat.ID) + if err := storeOperationScope(txn, scopeKeyPrefix, pat.Scope.Users); err != nil { + return err + } + return nil + }) +} + +func storeOperationScope(txn *badger.Txn, keyPrefix string, os auth.OperationScope) error { + for opType, scopeValue := range os.Operations { + scopeKey := fmt.Sprintf("%s:%d", keyPrefix, opType) + switch v := scopeValue.(type) { + case auth.AnyIDs: + if err := txn.Set([]byte(scopeKey), []byte("*")); err != nil { + return err + } + case auth.SelectedIDs: + for id := range v { + if err := txn.Set([]byte(fmt.Sprintf("%s:%s", scopeKey, id)), []byte(id)); err != nil { + return err + } + } + } + } + return nil +} + +func getPAT(db *badger.DB, id string) (auth.PAT, error) { + var pat auth.PAT + err := db.View(func(txn *badger.Txn) error { + patID, err := txn.Get([]byte(fmt.Sprintf("pat:%s:id", id))) + if err != nil { + return err + } + err = patID.Value(func(val []byte) error { + pat.ID = string(val) + return nil + }) + if err != nil { + return err + } + + user, err := txn.Get([]byte(fmt.Sprintf("pat:%s:user", id))) + if err != nil { + return err + } + err = user.Value(func(val []byte) error { + pat.User = string(val) + return nil + }) + if err != nil { + return err + } + + name, err := txn.Get([]byte(fmt.Sprintf("pat:%s:name", id))) + if err != nil { + return err + } + err = name.Value(func(val []byte) error { + pat.Name = string(val) + return nil + }) + if err != nil { + return err + } + + issuedAt, err := txn.Get([]byte(fmt.Sprintf("pat:%s:issued_at", id))) + if err != nil { + return err + } + err = issuedAt.Value(func(val []byte) error { + pat.IssuedAt, err = time.Parse(timeLayout, strings.Split(string(val), " m=")[0]) + return err + }) + if err != nil { + return err + } + + expiresAt, err := txn.Get([]byte(fmt.Sprintf("pat:%s:expires_at", id))) + if err != nil { + return err + } + err = expiresAt.Value(func(val []byte) error { + pat.ExpiresAt, err = time.Parse(timeLayout, strings.Split(string(val), " m=")[0]) + return err + }) + if err != nil { + return err + } + + // Retrieve scope + scopeKeyPrefix := fmt.Sprintf("pat:%s:scope", id) + scope, err := getOperationScope(txn, scopeKeyPrefix) + if err != nil { + return err + } + pat.Scope.Users = scope + + return nil + }) + return pat, err +} + +func getOperationScope(txn *badger.Txn, keyPrefix string) (auth.OperationScope, error) { + os := auth.OperationScope{Operations: make(map[auth.OperationType]auth.ScopeValue)} + opt := badger.DefaultIteratorOptions + opt.Prefix = []byte(keyPrefix) + it := txn.NewIterator(opt) + defer it.Close() + + for it.Rewind(); it.Valid(); it.Next() { + item := it.Item() + k := item.Key() + opTypeStr := string(k[len(keyPrefix)+1:]) + var opType auth.OperationType + _, err := fmt.Sscanf(opTypeStr, "%d", &opType) + if err != nil { + return os, err + } + + item.Value(func(val []byte) error { + if string(val) == "*" { + os.Operations[opType] = auth.AnyIDs{} + } else { + if os.Operations[opType] == nil { + os.Operations[opType] = auth.SelectedIDs{} + } + os.Operations[opType].(auth.SelectedIDs)[string(val)] = struct{}{} + } + return nil + }) + } + + return os, nil +} diff --git a/cmd/test/badgergob/benchmark.go b/cmd/test/badgergob/benchmark.go new file mode 100644 index 0000000000..596afe14a8 --- /dev/null +++ b/cmd/test/badgergob/benchmark.go @@ -0,0 +1,54 @@ +package badgergob + +import ( + "log" + "testing" + "time" + + "github.com/absmach/magistrala/auth" + badger "github.com/dgraph-io/badger/v4" + "github.com/google/uuid" +) + +var num = 1000 + +func generateTestPAT() auth.PAT { + return auth.PAT{ + ID: uuid.New().String(), + User: "user123", + Name: "Test Token", + IssuedAt: time.Now(), + ExpiresAt: time.Now().Add(24 * time.Hour), + Scope: auth.Scope{ + Users: auth.OperationScope{ + Operations: map[auth.OperationType]auth.ScopeValue{ + auth.CreateOp: auth.SelectedIDs{"entity1": {}, "entity2": {}}, + }, + }, + }, + } +} + +func BenchmarkGetPAT(b *testing.B) { + // Open Badger database + db, err := badger.Open(badger.DefaultOptions("./badger")) + if err != nil { + log.Fatal(err) + } + defer db.Close() + + // Store a PAT to retrieve + pat := generateTestPAT() + err = StorePAT(db, pat) + if err != nil { + log.Fatal(err) + } + + b.ResetTimer() + for i := 0; i < b.N; i++ { + _, err := GetPAT(db, pat.ID) + if err != nil { + log.Fatal(err) + } + } +} diff --git a/cmd/test/badgergob/main.go b/cmd/test/badgergob/main.go new file mode 100644 index 0000000000..4727d84a94 --- /dev/null +++ b/cmd/test/badgergob/main.go @@ -0,0 +1,187 @@ +package badgergob + +import ( + "bytes" + "encoding/gob" + "fmt" + "log" + "strings" + "time" + + "github.com/absmach/magistrala/auth" + badger "github.com/dgraph-io/badger/v4" + "github.com/google/uuid" +) + +const timeLayout = "2006-01-02 15:04:05.999999999 -0700 MST" + +func init() { + gob.Register(auth.SelectedIDs{}) + gob.Register(auth.AnyIDs{}) +} +func main() { + // Open Badger database + db, err := badger.Open(badger.DefaultOptions("./badger")) + if err != nil { + log.Fatal(err) + } + defer db.Close() + + // Create a new PAT + pat := auth.PAT{ + ID: uuid.New().String(), + User: "user123", + Name: "Test Token", + IssuedAt: time.Now(), + ExpiresAt: time.Now().Add(24 * time.Hour), + } + + // Set scope + pat.Scope.Users = auth.OperationScope{ + Operations: map[auth.OperationType]auth.ScopeValue{ + auth.CreateOp: auth.SelectedIDs{"entity1": {}, "entity2": {}}, + }, + } + + // Store PAT + if err := StorePAT(db, pat); err != nil { + log.Fatal(err) + } + + // Retrieve PAT + retrievedPAT, err := GetPAT(db, pat.ID) + if err != nil { + log.Fatal(err) + } + fmt.Printf("Retrieved PAT: %+v\n", retrievedPAT) +} + +func StorePAT(db *badger.DB, pat auth.PAT) error { + return db.Update(func(txn *badger.Txn) error { + if err := txn.Set([]byte(fmt.Sprintf("pat:%s:id", pat.ID)), []byte(pat.ID)); err != nil { + return err + } + if err := txn.Set([]byte(fmt.Sprintf("pat:%s:user", pat.ID)), []byte(pat.User)); err != nil { + return err + } + if err := txn.Set([]byte(fmt.Sprintf("pat:%s:name", pat.ID)), []byte(pat.Name)); err != nil { + return err + } + if err := txn.Set([]byte(fmt.Sprintf("pat:%s:issued_at", pat.ID)), []byte(pat.IssuedAt.String())); err != nil { + return err + } + if err := txn.Set([]byte(fmt.Sprintf("pat:%s:expires_at", pat.ID)), []byte(pat.ExpiresAt.String())); err != nil { + return err + } + // Store scope + scopeBytes, err := EncodeScopeToGob(pat.Scope) + if err != nil { + return err + } + if err := txn.Set([]byte(fmt.Sprintf("pat:%s:scope", pat.ID)), scopeBytes); err != nil { + return err + } + return nil + }) +} + +func GetPAT(db *badger.DB, id string) (auth.PAT, error) { + var pat auth.PAT + err := db.View(func(txn *badger.Txn) error { + patID, err := txn.Get([]byte(fmt.Sprintf("pat:%s:id", id))) + if err != nil { + return err + } + err = patID.Value(func(val []byte) error { + pat.ID = string(val) + return nil + }) + if err != nil { + return err + } + + user, err := txn.Get([]byte(fmt.Sprintf("pat:%s:user", id))) + if err != nil { + return err + } + err = user.Value(func(val []byte) error { + pat.User = string(val) + return nil + }) + if err != nil { + return err + } + + name, err := txn.Get([]byte(fmt.Sprintf("pat:%s:name", id))) + if err != nil { + return err + } + err = name.Value(func(val []byte) error { + pat.Name = string(val) + return nil + }) + if err != nil { + return err + } + + issuedAt, err := txn.Get([]byte(fmt.Sprintf("pat:%s:issued_at", id))) + if err != nil { + return err + } + err = issuedAt.Value(func(val []byte) error { + pat.IssuedAt, err = time.Parse(timeLayout, strings.Split(string(val), " m=")[0]) + return err + }) + if err != nil { + return err + } + + expiresAt, err := txn.Get([]byte(fmt.Sprintf("pat:%s:expires_at", id))) + if err != nil { + return err + } + err = expiresAt.Value(func(val []byte) error { + pat.ExpiresAt, err = time.Parse(timeLayout, strings.Split(string(val), " m=")[0]) + return err + }) + if err != nil { + return err + } + + // Retrieve scope + scope, err := txn.Get([]byte(fmt.Sprintf("pat:%s:scope", id))) + if err != nil { + return err + } + + err = scope.Value(func(val []byte) error { + pat.Scope, err = DecodeGobToScope(val) + return err + }) + if err != nil { + return err + } + + return nil + }) + return pat, err +} + +func EncodeScopeToGob(scope auth.Scope) ([]byte, error) { + buf := bytes.NewBuffer([]byte{}) + enc := gob.NewEncoder(buf) + if err := enc.Encode(scope); err != nil { + return nil, err + } + return buf.Bytes(), nil +} + +func DecodeGobToScope(scopeBytes []byte) (auth.Scope, error) { + buf := bytes.NewBuffer(scopeBytes) + var scope auth.Scope + dec := gob.NewDecoder(buf) + if err := dec.Decode(&scope); err != nil { + return auth.Scope{}, err + } + return scope, nil +} diff --git a/cmd/test/bbolt/main.go b/cmd/test/bbolt/main.go new file mode 100644 index 0000000000..52351dbd52 --- /dev/null +++ b/cmd/test/bbolt/main.go @@ -0,0 +1,170 @@ +package main + +import ( + "fmt" + "log" + "time" + + "github.com/absmach/magistrala/auth" + "github.com/google/uuid" + "go.etcd.io/bbolt" +) + +func main() { + // Open bbolt database + db, err := bbolt.Open("./bbolt.db", 0600, nil) + if err != nil { + log.Fatal(err) + } + defer db.Close() + + // Create a new PAT + pat := auth.PAT{ + ID: uuid.New().String(), + User: "user123", + Name: "Test Token", + IssuedAt: time.Now(), + ExpiresAt: time.Now().Add(24 * time.Hour), + } + + // Set scope + pat.Scope.Users = auth.OperationScope{ + Operations: map[auth.OperationType]auth.ScopeValue{ + auth.CreateOp: auth.SelectedIDs{"entity1": {}, "entity2": {}}, + }, + } + + // Store PAT + if err := storePAT(db, pat); err != nil { + log.Fatal(err) + } + + // Retrieve PAT + retrievedPAT, err := getPAT(db, pat.ID) + if err != nil { + log.Fatal(err) + } + fmt.Printf("Retrieved PAT: %+v\n", retrievedPAT) +} + +func storePAT(db *bbolt.DB, pat auth.PAT) error { + return db.Update(func(tx *bbolt.Tx) error { + bucket, err := tx.CreateBucketIfNotExists([]byte("pats")) + if err != nil { + return err + } + + if err := bucket.Put([]byte(fmt.Sprintf("pat:%s:id", pat.ID)), []byte(pat.ID)); err != nil { + return err + } + if err := bucket.Put([]byte(fmt.Sprintf("pat:%s:user", pat.ID)), []byte(pat.User)); err != nil { + return err + } + if err := bucket.Put([]byte(fmt.Sprintf("pat:%s:name", pat.ID)), []byte(pat.Name)); err != nil { + return err + } + if err := bucket.Put([]byte(fmt.Sprintf("pat:%s:issued_at", pat.ID)), []byte(pat.IssuedAt.String())); err != nil { + return err + } + if err := bucket.Put([]byte(fmt.Sprintf("pat:%s:expires_at", pat.ID)), []byte(pat.ExpiresAt.String())); err != nil { + return err + } + // Store scope + scopeKeyPrefix := fmt.Sprintf("pat:%s:scope", pat.ID) + if err := storeOperationScope(bucket, scopeKeyPrefix, pat.Scope.Users); err != nil { + return err + } + return nil + }) +} + +func storeOperationScope(bucket *bbolt.Bucket, keyPrefix string, os auth.OperationScope) error { + for opType, scopeValue := range os.Operations { + scopeKey := []byte(fmt.Sprintf("%s:%d", keyPrefix, opType)) + switch v := scopeValue.(type) { + case auth.AnyIDs: + if err := bucket.Put(scopeKey, []byte("*")); err != nil { + return err + } + case auth.SelectedIDs: + for id := range v { + if err := bucket.Put([]byte(fmt.Sprintf("%s:%s", scopeKey, id)), []byte(id)); err != nil { + return err + } + } + } + } + return nil +} + +func getPAT(db *bbolt.DB, id string) (auth.PAT, error) { + var pat auth.PAT + err := db.View(func(tx *bbolt.Tx) error { + bucket := tx.Bucket([]byte("pats")) + if bucket == nil { + return fmt.Errorf("bucket not found") + } + + patID := bucket.Get([]byte(fmt.Sprintf("pat:%s:id", id))) + if patID == nil { + return fmt.Errorf("pat with ID %s not found", id) + } + pat.ID = string(patID) + + user := bucket.Get([]byte(fmt.Sprintf("pat:%s:user", id))) + if user != nil { + pat.User = string(user) + } + + name := bucket.Get([]byte(fmt.Sprintf("pat:%s:name", id))) + if name != nil { + pat.Name = string(name) + } + + issuedAt := bucket.Get([]byte(fmt.Sprintf("pat:%s:issued_at", id))) + if issuedAt != nil { + pat.IssuedAt, _ = time.Parse(time.RFC3339, string(issuedAt)) + } + + expiresAt := bucket.Get([]byte(fmt.Sprintf("pat:%s:expires_at", id))) + if expiresAt != nil { + pat.ExpiresAt, _ = time.Parse(time.RFC3339, string(expiresAt)) + } + + // Retrieve scope + scopeKeyPrefix := fmt.Sprintf("pat:%s:scope", id) + scope, err := getOperationScope(bucket, scopeKeyPrefix) + if err != nil { + return err + } + pat.Scope.Users = scope + + return nil + }) + return pat, err +} + +func getOperationScope(bucket *bbolt.Bucket, keyPrefix string) (auth.OperationScope, error) { + os := auth.OperationScope{Operations: make(map[auth.OperationType]auth.ScopeValue)} + c := bucket.Cursor() + prefix := []byte(keyPrefix) + for k, v := c.Seek(prefix); k != nil && len(k) > len(prefix) && string(k[:len(prefix)]) == keyPrefix; k, v = c.Next() { + opTypeStr := string(k[len(prefix)+1:]) + var opType auth.OperationType + _, err := fmt.Sscanf(opTypeStr, "%d", &opType) + if err != nil { + return os, err + } + + if string(v) == "*" { + os.Operations[opType] = auth.AnyIDs{} + } else { + if os.Operations[opType] == nil { + os.Operations[opType] = auth.SelectedIDs{} + } + os.Operations[opType].(auth.SelectedIDs)[string(v)] = struct{}{} + } + } + + return os, nil +} diff --git a/cmd/test/bboltgob/main.go b/cmd/test/bboltgob/main.go new file mode 100644 index 0000000000..e440876c65 --- /dev/null +++ b/cmd/test/bboltgob/main.go @@ -0,0 +1,166 @@ +package main + +import ( + "bytes" + "encoding/gob" + "fmt" + "log" + "strings" + "time" + + "github.com/absmach/magistrala/auth" + "github.com/google/uuid" + "go.etcd.io/bbolt" +) + +const timeLayout = "2006-01-02 15:04:05.999999999 -0700 MST" + +func init() { + gob.Register(auth.SelectedIDs{}) + gob.Register(auth.AnyIDs{}) +} +func main() { + // Open bbolt database + db, err := bbolt.Open("./bbolt.db", 0600, nil) + if err != nil { + log.Fatal(err) + } + defer db.Close() + + // Create a new PAT + pat := auth.PAT{ + ID: uuid.New().String(), + User: "user123", + Name: "Test Token", + IssuedAt: time.Now(), + ExpiresAt: time.Now().Add(24 * time.Hour), + } + + // Set scope + pat.Scope.Users = auth.OperationScope{ + Operations: map[auth.OperationType]auth.ScopeValue{ + auth.CreateOp: auth.SelectedIDs{"entity1": {}, "entity2": {}}, + }, + } + + // Store PAT + if err := storePATGob(db, pat); err != nil { + log.Fatal(err) + } + + // Retrieve PAT + retrievedPAT, err := getPATGob(db, pat.ID) + if err != nil { + log.Fatal(err) + } + fmt.Printf("Retrieved PAT: %+v\n", retrievedPAT) +} + +func storePATGob(db *bbolt.DB, pat auth.PAT) error { + return db.Update(func(tx *bbolt.Tx) error { + bucket, err := tx.CreateBucketIfNotExists([]byte("pats")) + if err != nil { + return err + } + + if err := bucket.Put([]byte(fmt.Sprintf("pat:%s:id", pat.ID)), []byte(pat.ID)); err != nil { + return err + } + if err := bucket.Put([]byte(fmt.Sprintf("pat:%s:user", pat.ID)), []byte(pat.User)); err != nil { + return err + } + if err := bucket.Put([]byte(fmt.Sprintf("pat:%s:name", pat.ID)), []byte(pat.Name)); err != nil { + return err + } + if err := bucket.Put([]byte(fmt.Sprintf("pat:%s:issued_at", pat.ID)), []byte(pat.IssuedAt.String())); err != nil { + return err + } + if err := bucket.Put([]byte(fmt.Sprintf("pat:%s:expires_at", pat.ID)), []byte(pat.ExpiresAt.String())); err != nil { + return err + } + // Store scope + scopeBytes, err := EncodeScopeToGob(pat.Scope) + if err != nil { + return err + } + if err := bucket.Put([]byte(fmt.Sprintf("pat:%s:scope", pat.ID)), scopeBytes); err != nil { + return err + } + + return nil + }) +} + +func getPATGob(db *bbolt.DB, id string) (auth.PAT, error) { + var pat auth.PAT + err := db.View(func(tx *bbolt.Tx) (err error) { + bucket := tx.Bucket([]byte("pats")) + if bucket == nil { + return fmt.Errorf("bucket not found") + } + + patID := bucket.Get([]byte(fmt.Sprintf("pat:%s:id", id))) + if patID == nil { + return fmt.Errorf("pat with ID %s not found", id) + } + pat.ID = string(patID) + + user := bucket.Get([]byte(fmt.Sprintf("pat:%s:user", id))) + if user != nil { + pat.User = string(user) + } + + name := bucket.Get([]byte(fmt.Sprintf("pat:%s:name", id))) + if name != nil { + pat.Name = string(name) + } + + issuedAt := bucket.Get([]byte(fmt.Sprintf("pat:%s:issued_at", id))) + if issuedAt != nil { + pat.IssuedAt, err = time.Parse(timeLayout, strings.Split(string(issuedAt), " m=")[0]) + if err != nil { + return err + } + } + + expiresAt := bucket.Get([]byte(fmt.Sprintf("pat:%s:expires_at", id))) + if expiresAt != nil { + pat.ExpiresAt, err = time.Parse(timeLayout, strings.Split(string(expiresAt), " m=")[0]) + if err != nil { + return err + } + } + + // Retrieve scope + scopeBytes := bucket.Get([]byte(fmt.Sprintf("pat:%s:scope", id))) + if scopeBytes != nil { + scope, err := DecodeGobToScope(scopeBytes) + if err != nil { + return err + } + pat.Scope = scope + } + + return nil + }) + return pat, err +} + +func EncodeScopeToGob(scope auth.Scope) ([]byte, error) { + buf := bytes.NewBuffer([]byte{}) + enc := gob.NewEncoder(buf) + if err := enc.Encode(scope); err != nil { + return nil, err + } + return buf.Bytes(), nil +} + +func DecodeGobToScope(scopeBytes []byte) (auth.Scope, error) { + buf := bytes.NewBuffer(scopeBytes) + var scope auth.Scope + dec := gob.NewDecoder(buf) + if err := dec.Decode(&scope); err != nil { + return auth.Scope{}, err + } + return scope, nil +} diff --git a/cmd/test/patunmarshal/main.go b/cmd/test/patunmarshal/main.go new file mode 100644 index 0000000000..352f34cacf --- /dev/null +++ b/cmd/test/patunmarshal/main.go @@ -0,0 +1,172 @@ +package main + +import ( + "encoding/json" + "fmt" + + "github.com/absmach/magistrala/auth" +) + +func main() { + var pat auth.PAT + + jsonData := ` + { + "id": "id_1", + "user": "user_1", + "name": "user 1 PAT", + "Description": "user 1 pat 1 description", + "Token": "hashed token", + "scope": { + "users": { + "operations": { + "update": [ + "123", + "123123" + ], + "create": "123", + "read": "*" + } + }, + "domains": { + "domain_1": { + "domain_management": { + "operations": { + "update": [ + "123", + "123123" + ], + "create": "123", + "read": "*" + } + }, + "entities": { + "groups": { + "operations": { + "update": [ + "123", + "123123" + ], + "create": "123", + "read": "*" + } + }, + "things": { + "operations": { + "update": [ + "123", + "123123" + ], + "create": "123", + "read": "*" + } + }, + "channels": { + "operations": { + "update": [ + "123", + "123123" + ], + "create": "123", + "read": "*" + } + } + } + } + } + }, + "issued_at": "2024-01-01T00:00:00Z", + "expires_at": "2024-01-04T00:00:00Z", + "updated_at": "2024-01-02T00:00:00Z", + "last_used_at": "2024-01-02T00:00:00Z", + "revoked": true, + "revoked_at": "2024-01-03T00:00:00Z" + } + ` + + if err := json.Unmarshal([]byte(jsonData), &pat); err != nil { + fmt.Println("Error:", err) + return + } + + fmt.Printf("pat: %s\n", pat.String()) + + scopeByte := ` + { + "domain_management": { + "operations": { + "update": [ + "123", + "123123" + ], + "create": "123", + "read": "*" + } + }, + "entities": { + "groups": { + "operations": { + "update": [ + "123", + "123123" + ], + "create": "123", + "read": "*" + } + }, + "things": { + "operations": { + "update": [ + "123", + "123123" + ], + "create": "123", + "read": "*" + } + }, + "channels": { + "operations": { + "update": [ + "123", + "123123" + ], + "create": "123", + "read": "*" + } + } + } +} + ` + var domainscope auth.DomainScope + + if err := json.Unmarshal([]byte(scopeByte), &domainscope); err != nil { + panic(err) + } + + domBytes, _ := json.MarshalIndent(domainscope, "", " ") + fmt.Println("\n\n", string(domBytes)) + + operationsByte := ` + { + "operations": { + "update": [ + "123", + "123123" + ], + "create": "123", + "read": "*" + } + } + + ` + + var operation auth.OperationScope + + if err := json.Unmarshal([]byte(operationsByte), &operation); err != nil { + panic(err) + } + + opbytes, _ := json.MarshalIndent(operation, "", " ") + fmt.Println("\n\n", string(opbytes)) + +} diff --git a/cmd/test/patunmarshal/temp.json b/cmd/test/patunmarshal/temp.json new file mode 100644 index 0000000000..ed45c5ebf7 --- /dev/null +++ b/cmd/test/patunmarshal/temp.json @@ -0,0 +1,71 @@ +{ + "id": "id_1", + "user": "user_1", + "name": "user 1 PAT", + "Description": "user 1 pat 1 description", + "Token": "hashed token", + "scope": { + "users": { + "operations": { + "update": [ + "123", + "123123" + ], + "create": "123", + "read": "*" + } + }, + "domains": { + "domain_1": { + "domain_management": { + "operations": { + "update": [ + "123", + "123123" + ], + "create": "123", + "read": "*" + } + }, + "entites": { + "groups": { + "operations": { + "update": [ + "123", + "123123" + ], + "create": "123", + "read": "*" + } + }, + "things": { + "operations": { + "update": [ + "123", + "123123" + ], + "create": "123", + "read": "*" + } + }, + "channels": { + "operations": { + "update": [ + "123", + "123123" + ], + "create": "123", + "read": "*" + } + } + } + } + } + }, + "issued_at": "2024-01-01T00:00:00Z", + "expires_at": "2024-01-04T00:00:00Z", + "updated_at": "2024-01-02T00:00:00Z", + "last_used_at": "2024-01-02T00:00:00Z", + "revoked": true, + "revoked_at": "2024-01-03T00:00:00Z" +} diff --git a/cmd/test/redis/main.go b/cmd/test/redis/main.go new file mode 100644 index 0000000000..8ac4450f98 --- /dev/null +++ b/cmd/test/redis/main.go @@ -0,0 +1,277 @@ +package main + +import ( + "context" + "fmt" + "strconv" + "time" + + "github.com/absmach/magistrala/auth" + "github.com/go-redis/redis/v8" + "github.com/google/uuid" +) + +var ctx = context.Background() + +func main() { + rdb := redis.NewClient(&redis.Options{ + Addr: "localhost:6379", + Password: "", // no password set + DB: 0, // use default DB + }) + // Create a new PAT + pat := auth.PAT{ + ID: uuid.New().String(), + User: "user123", + Name: "Test Token", + IssuedAt: time.Now(), + ExpiresAt: time.Now().Add(24 * time.Hour), + } + + // Set scope + pat.Scope.Users = auth.OperationScope{ + Operations: map[auth.OperationType]auth.ScopeValue{ + auth.CreateOp: auth.SelectedIDs{"entity1": {}, "entity2": {}}, + }, + } + + if err := StorePAT(rdb, pat); err != nil { + panic(err) + } + rPAT, err := RetrievePAT(rdb, pat.ID) + if err != nil { + panic(err) + } + + fmt.Println(rPAT.String()) +} + +func StorePAT(client *redis.Client, pat auth.PAT) error { + // Create a hash to store PAT fields + key := "pat:" + pat.ID + + // Convert time.Time fields to Unix timestamps + issuedAt := strconv.FormatInt(pat.IssuedAt.Unix(), 10) + expiresAt := strconv.FormatInt(pat.ExpiresAt.Unix(), 10) + updatedAt := strconv.FormatInt(pat.UpdatedAt.Unix(), 10) + lastUsedAt := strconv.FormatInt(pat.LastUsedAt.Unix(), 10) + revokedAt := strconv.FormatInt(pat.RevokedAt.Unix(), 10) + + // Store basic PAT fields + err := client.HMSet(ctx, key, map[string]interface{}{ + "user": pat.User, + "name": pat.Name, + "issued_at": issuedAt, + "expires_at": expiresAt, + "updated_at": updatedAt, + "last_used_at": lastUsedAt, + "revoked": pat.Revoked, + "revoked_at": revokedAt, + }).Err() + if err != nil { + return err + } + + // Store Scope + err = StoreScope(client, key+":scope", pat.Scope) + if err != nil { + return err + } + + return nil +} + +func StoreScope(client *redis.Client, key string, scope auth.Scope) error { + // Store Users OperationScope + err := StoreOperationScope(client, key+":users", scope.Users) + if err != nil { + return err + } + + // Store Domains + for domainID, domainScope := range scope.Domains { + domainKey := key + ":domains:" + domainID + err = StoreDomainScope(client, domainKey, domainScope) + if err != nil { + return err + } + } + + return nil +} + +func StoreOperationScope(client *redis.Client, key string, os auth.OperationScope) error { + for operation, scopeValue := range os.Operations { + operationKey := key + ":" + operation.String() + switch value := scopeValue.(type) { + case auth.AnyIDs: + err := client.Set(ctx, operationKey, "*", 0).Err() + if err != nil { + return err + } + case auth.SelectedIDs: + for id := range value { + err := client.HSet(ctx, operationKey, id, "").Err() + if err != nil { + return err + } + } + } + } + return nil +} + +func StoreDomainScope(client *redis.Client, key string, ds auth.DomainScope) error { + // Store DomainManagement OperationScope + err := StoreOperationScope(client, key+":domain_management", ds.DomainManagement) + if err != nil { + return err + } + + // Store Entities + for entityType, operationScope := range ds.Entities { + entityKey := key + ":entities:" + entityType.String() + err = StoreOperationScope(client, entityKey, operationScope) + if err != nil { + return err + } + } + + return nil +} + +func RetrievePAT(client *redis.Client, patID string) (*auth.PAT, error) { + key := "pat:" + patID + + fields, err := client.HGetAll(ctx, key).Result() + if err != nil { + return nil, err + } + + issuedAt, _ := strconv.ParseInt(fields["issued_at"], 10, 64) + expiresAt, _ := strconv.ParseInt(fields["expires_at"], 10, 64) + updatedAt, _ := strconv.ParseInt(fields["updated_at"], 10, 64) + lastUsedAt, _ := strconv.ParseInt(fields["last_used_at"], 10, 64) + revokedAt, _ := strconv.ParseInt(fields["revoked_at"], 10, 64) + + pat := &auth.PAT{ + ID: patID, + User: fields["user"], + Name: fields["name"], + IssuedAt: time.Unix(issuedAt, 0), + ExpiresAt: time.Unix(expiresAt, 0), + UpdatedAt: time.Unix(updatedAt, 0), + LastUsedAt: time.Unix(lastUsedAt, 0), + Revoked: fields["revoked"] == "true", + RevokedAt: time.Unix(revokedAt, 0), + } + + // Retrieve Scope + scope, err := RetrieveScope(client, key+":scope") + if err != nil { + return nil, err + } + pat.Scope = *scope + + return pat, nil +} + +func RetrieveScope(client *redis.Client, key string) (*auth.Scope, error) { + scope := &auth.Scope{} + + // Retrieve Users OperationScope + users, err := RetrieveOperationScope(client, key+":users") + if err != nil { + return nil, err + } + scope.Users = *users + + // Retrieve Domains + domainKeys, err := client.Keys(ctx, key+":domains:*").Result() + if err != nil { + return nil, err + } + + scope.Domains = make(map[string]auth.DomainScope) + for _, domainKey := range domainKeys { + domainID := domainKey[len(key+":domains:"):] + domainScope, err := RetrieveDomainScope(client, domainKey) + if err != nil { + return nil, err + } + scope.Domains[domainID] = *domainScope + } + + return scope, nil +} + +func RetrieveOperationScope(client *redis.Client, key string) (*auth.OperationScope, error) { + os := &auth.OperationScope{ + Operations: make(map[auth.OperationType]auth.ScopeValue), + } + + operationKeys, err := client.Keys(ctx, key+":*").Result() + if err != nil { + return nil, err + } + + for _, operationKey := range operationKeys { + operationStr := operationKey[len(key+":"):] + operation, err := auth.ParseOperationType(operationStr) // You'll need to implement this function to convert string to OperationType + if err != nil { + return nil, err + } + + if wildcard, err := client.Get(ctx, operationKey).Result(); err == nil && wildcard == "*" { + os.Operations[operation] = auth.AnyIDs{} + } else { + ids, err := client.HKeys(ctx, operationKey).Result() + if err != nil { + return nil, err + } + + selectedIDs := auth.SelectedIDs{} + for _, id := range ids { + selectedIDs[id] = struct{}{} + } + os.Operations[operation] = selectedIDs + } + } + + return os, nil +} + +func RetrieveDomainScope(client *redis.Client, key string) (*auth.DomainScope, error) { + ds := &auth.DomainScope{ + Entities: make(map[auth.DomainEntityType]auth.OperationScope), + } + + // Retrieve DomainManagement OperationScope + domainManagement, err := RetrieveOperationScope(client, key+":domain_management") + if err != nil { + return nil, err + } + ds.DomainManagement = *domainManagement + + // Retrieve Entities + entityKeys, err := client.Keys(ctx, key+":entities:*").Result() + if err != nil { + return nil, err + } + + for _, entityKey := range entityKeys { + entityTypeStr := entityKey[len(key+":entities:")] + entityType, err := auth.ParseDomainEntityType(string(entityTypeStr)) // You'll need to implement this function to convert string to DomainEntityType + if err != nil { + return nil, err + } + + operationScope, err := RetrieveOperationScope(client, entityKey) + if err != nil { + return nil, err + } + ds.Entities[entityType] = *operationScope + } + + return ds, nil +} diff --git a/cmd/test/redisgob/main.go b/cmd/test/redisgob/main.go new file mode 100644 index 0000000000..ce11d51bff --- /dev/null +++ b/cmd/test/redisgob/main.go @@ -0,0 +1,143 @@ +package main + +import ( + "bytes" + "context" + "encoding/gob" + "fmt" + "strconv" + "time" + + "github.com/absmach/magistrala/auth" + "github.com/go-redis/redis/v8" + "github.com/google/uuid" +) + +var ctx = context.Background() + +func init() { + gob.Register(auth.SelectedIDs{}) + gob.Register(auth.AnyIDs{}) +} +func main() { + rdb := redis.NewClient(&redis.Options{ + Addr: "localhost:6379", + Password: "", // no password set + DB: 0, // use default DB + }) + // Create a new PAT + pat := auth.PAT{ + ID: uuid.New().String(), + User: "user123", + Name: "Test Token", + IssuedAt: time.Now(), + ExpiresAt: time.Now().Add(24 * time.Hour), + } + + // Set scope + pat.Scope.Users = auth.OperationScope{ + Operations: map[auth.OperationType]auth.ScopeValue{ + auth.CreateOp: auth.SelectedIDs{"entity1": {}, "entity2": {}}, + }, + } + + if err := StorePATGob(rdb, pat); err != nil { + panic(err) + } + rPAT, err := RetrievePATGob(rdb, pat.ID) + if err != nil { + panic(err) + } + + fmt.Println(rPAT.String()) +} + +func StorePATGob(client *redis.Client, pat auth.PAT) error { + scopeByte, err := EncodeScopeToGob(pat.Scope) + if err != nil { + return err + } + // Create a hash to store PAT fields + key := "pat:" + pat.ID + + // Convert time.Time fields to Unix timestamps + issuedAt := strconv.FormatInt(pat.IssuedAt.Unix(), 10) + expiresAt := strconv.FormatInt(pat.ExpiresAt.Unix(), 10) + updatedAt := strconv.FormatInt(pat.UpdatedAt.Unix(), 10) + lastUsedAt := strconv.FormatInt(pat.LastUsedAt.Unix(), 10) + revokedAt := strconv.FormatInt(pat.RevokedAt.Unix(), 10) + + // Store basic PAT fields + err = client.HMSet(ctx, key, map[string]interface{}{ + "user": pat.User, + "name": pat.Name, + "issued_at": issuedAt, + "expires_at": expiresAt, + "updated_at": updatedAt, + "last_used_at": lastUsedAt, + "revoked": pat.Revoked, + "revoked_at": revokedAt, + "scope": scopeByte, + }).Err() + if err != nil { + return err + } + return nil +} + +func RetrievePATGob(client *redis.Client, patID string) (*auth.PAT, error) { + key := "pat:" + patID + + fields, err := client.HGetAll(ctx, key).Result() + if err != nil { + return nil, err + } + + issuedAt, _ := strconv.ParseInt(fields["issued_at"], 10, 64) + expiresAt, _ := strconv.ParseInt(fields["expires_at"], 10, 64) + updatedAt, _ := strconv.ParseInt(fields["updated_at"], 10, 64) + lastUsedAt, _ := strconv.ParseInt(fields["last_used_at"], 10, 64) + revokedAt, _ := strconv.ParseInt(fields["revoked_at"], 10, 64) + + // Decode scope from bytes + scopeBytes := []byte(fields["scope"]) + + scope, err := DecodeGobToScope(scopeBytes) + if err != nil { + return nil, err + } + + pat := &auth.PAT{ + ID: patID, + User: fields["user"], + Name: fields["name"], + IssuedAt: time.Unix(issuedAt, 0), + ExpiresAt: time.Unix(expiresAt, 0), + UpdatedAt: time.Unix(updatedAt, 0), + LastUsedAt: time.Unix(lastUsedAt, 0), + Revoked: fields["revoked"] == "true", + RevokedAt: time.Unix(revokedAt, 0), + Scope: scope, // Assign decoded scope to PAT's Scope field + } + + return pat, nil +} + +func EncodeScopeToGob(scope auth.Scope) ([]byte, error) { + buf := bytes.NewBuffer([]byte{}) + enc := gob.NewEncoder(buf) + if err := enc.Encode(scope); err != nil { + return nil, err + } + return buf.Bytes(), nil +} + +func DecodeGobToScope(scopeBytes []byte) (auth.Scope, error) { + buf := bytes.NewBuffer(scopeBytes) + var scope auth.Scope + dec := gob.NewDecoder(buf) + if err := dec.Decode(&scope); err != nil { + return auth.Scope{}, err + } + return scope, nil +} diff --git a/cmd/test/second/main.go b/cmd/test/second/main.go new file mode 100644 index 0000000000..e84af018d6 --- /dev/null +++ b/cmd/test/second/main.go @@ -0,0 +1,98 @@ +package main + +import ( + "encoding/json" + "fmt" +) + +type OperationType uint32 + +const ( + CreateOp OperationType = iota + ReadOp + ListOp + UpdateOp + DeleteOp +) + +func (ot OperationType) String() string { + switch ot { + case CreateOp: + return "create" + case ReadOp: + return "read" + case ListOp: + return "list" + case UpdateOp: + return "update" + case DeleteOp: + return "delete" + default: + return fmt.Sprintf("unknown operation type %d", ot) + } +} + +func (ot OperationType) MarshalText() ([]byte, error) { + return []byte(ot.String()), nil +} + +type OperationScope struct { + Operations map[OperationType]string `json:"operations,omitempty"` +} + +type DomainEntityType uint32 + +const ( + DomainManagementScope DomainEntityType = iota + DomainGroupsScope + DomainChannelsScope + DomainThingsScope + DomainNullScope +) + +func (det DomainEntityType) String() string { + switch det { + case DomainManagementScope: + return "domain_management" + case DomainGroupsScope: + return "groups" + case DomainChannelsScope: + return "channels" + case DomainThingsScope: + return "things" + case DomainNullScope: + return "null" + default: + return fmt.Sprintf("unknown domain entity type %d", det) + } +} + +func (det DomainEntityType) MarshalText() ([]byte, error) { + return []byte(det.String()), nil +} + +type DomainScope struct { + Entities map[DomainEntityType]string `json:"entities,omitempty"` +} + +func main() { + // OperationScope works because map keys are encoded correctly + os := &OperationScope{ + Operations: map[OperationType]string{ + CreateOp: "allowed", + ReadOp: "allowed", + }, + } + osJSON, _ := json.MarshalIndent(os, "", " ") + fmt.Println("OperationScope:", string(osJSON)) + + // DomainScope does not work as intended for map keys + ds := &DomainScope{ + Entities: map[DomainEntityType]string{ + DomainManagementScope: "allowed", + DomainGroupsScope: "allowed", + }, + } + dsJSON, _ := json.MarshalIndent(ds, "", " ") + fmt.Println("DomainScope (incorrect):", string(dsJSON)) +} diff --git a/cmd/test/test.go b/cmd/test/test.go new file mode 100644 index 0000000000..773d69f5a8 --- /dev/null +++ b/cmd/test/test.go @@ -0,0 +1,76 @@ +package main + +import ( + "fmt" + "log" + + "github.com/dgraph-io/badger/v4" +) + +func main() { + // Open the Badger database located in the /tmp/badger directory. + // It will be created if it doesn't exist. + opts := badger.DefaultOptions("./badger") + db, err := badger.Open(opts) + if err != nil { + log.Fatal(err) + } + defer db.Close() + + // Insert some data into the database + // err = db.Update(func(txn *badger.Txn) error { + // for i := 0; i < 10; i++ { + // key := fmt.Sprintf("key%d", i) + // value := fmt.Sprintf("value%d", i) + // err := txn.Set([]byte(key), []byte(value)) + // if err != nil { + // return err + // } + // } + // return nil + // }) + // if err != nil { + // log.Fatal(err) + // } + + // Read and filter the data + // err = db.View(func(txn *badger.Txn) error { + // it := txn.NewIterator(badger.DefaultIteratorOptions) + // defer it.Close() + // for it.Rewind(); it.Valid(); it.Next() { + // item := it.Item() + // k := item.Key() + // err := item.Value(func(v []byte) error { + // // Filter: Only print keys with even numbers + // if k[len(k)-1]%2 == 0 { + // fmt.Printf("key=%s, value=%s\n", k, v) + // } + // return nil + // }) + // if err != nil { + // return err + // } + // } + // return nil + // }) + + err = db.View(func(txn *badger.Txn) error { + item, err := txn.Get([]byte("pat:b24bff46-07f2-4d33-a5f0-08447cfc20ca:scope")) + if err != nil { + log.Fatal(err) + } + err = item.Value(func(val []byte) error { + fmt.Printf("The answer is: %s\n", val) + return nil + }) + if err != nil { + log.Fatal(err) + } + + return nil + }) + + if err != nil { + log.Fatal(err) + } +} diff --git a/go.mod b/go.mod index 2e367d7ed6..07be4cf3da 100644 --- a/go.mod +++ b/go.mod @@ -80,12 +80,15 @@ require ( github.com/containerd/continuity v0.4.3 // indirect github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/decred/dcrd/dcrec/secp256k1/v4 v4.3.0 // indirect + github.com/dgraph-io/badger/v4 v4.2.0 // indirect + github.com/dgraph-io/ristretto v0.1.1 // indirect github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect github.com/docker/cli v26.0.0+incompatible // indirect github.com/docker/docker v26.0.2+incompatible // indirect github.com/docker/go-connections v0.5.0 // indirect github.com/docker/go-units v0.5.0 // indirect github.com/dsnet/golib/memfile v1.0.0 // indirect + github.com/dustin/go-humanize v1.0.0 // indirect github.com/envoyproxy/protoc-gen-validate v1.0.4 // indirect github.com/felixge/httpsnoop v1.0.4 // indirect github.com/fsnotify/fsnotify v1.7.0 // indirect @@ -98,7 +101,11 @@ require ( github.com/go-logr/stdr v1.2.2 // indirect github.com/goccy/go-json v0.10.2 // indirect github.com/gogo/protobuf v1.3.2 // indirect + github.com/golang/glog v1.2.0 // indirect + github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect + github.com/golang/protobuf v1.5.4 // indirect github.com/golang/snappy v0.0.4 // indirect + github.com/google/flatbuffers v1.12.1 // indirect github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect github.com/google/uuid v1.6.0 // indirect github.com/grpc-ecosystem/go-grpc-middleware v1.4.0 // indirect @@ -173,6 +180,8 @@ require ( github.com/xeipuuv/gojsonschema v1.2.0 // indirect github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect github.com/youmark/pkcs8 v0.0.0-20201027041543-1326539a0a0a // indirect + go.etcd.io/bbolt v1.3.10 // indirect + go.opencensus.io v0.24.0 // indirect go.opentelemetry.io/otel/metric v1.28.0 // indirect go.opentelemetry.io/proto/otlp v1.2.0 // indirect go.uber.org/atomic v1.11.0 // indirect diff --git a/go.sum b/go.sum index bb37ab1192..199cb2e239 100644 --- a/go.sum +++ b/go.sum @@ -50,6 +50,7 @@ github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyY 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.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= @@ -71,6 +72,12 @@ github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1 github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/decred/dcrd/dcrec/secp256k1/v4 v4.3.0 h1:rpfIENRNNilwHwZeG5+P150SMrnNEcHYvcCuK6dPZSg= github.com/decred/dcrd/dcrec/secp256k1/v4 v4.3.0/go.mod h1:v57UDF4pDQJcEfFUCRop3lJL149eHGSe9Jvczhzjo/0= +github.com/dgraph-io/badger/v4 v4.2.0 h1:kJrlajbXXL9DFTNuhhu9yCx7JJa4qpYWxtE8BzuWsEs= +github.com/dgraph-io/badger/v4 v4.2.0/go.mod h1:qfCqhPoWDFJRx1gp5QwwyGo8xk1lbHUxvK9nK0OGAak= +github.com/dgraph-io/ristretto v0.1.1 h1:6CWw5tJNgpegArSHpNHJKldNeq03FQCwYvfMVWajOK8= +github.com/dgraph-io/ristretto v0.1.1/go.mod h1:S1GPSBCYCIhmVNfcth17y2zZtQT6wzkzgwUve0VDWWA= +github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= +github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw= 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 v26.0.0+incompatible h1:90BKrx1a1HKYpSnnBFR6AgDq/FqkHxwlUyzJVPxD30I= @@ -83,6 +90,8 @@ github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4 github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= 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/dustin/go-humanize v1.0.0 h1:VSnTsYCnlFHaM2/igO1h6X3HA71jcobQuxemgkq4zYo= +github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= 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= @@ -141,21 +150,42 @@ github.com/gofrs/uuid v4.4.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRx 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.2.0 h1:uCdmnmatrKCgMBlM4rMuJZWOkPDqdbZPnrMXDY4gI68= +github.com/golang/glog v1.2.0/go.mod h1:6AhwSGph0fcJtXVM/PEHPqZlFeoLxhs7/t5UDAwmO+w= +github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE= +github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= +github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= +github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= +github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= +github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= +github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= +github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= +github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= 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/google/flatbuffers v1.12.1 h1:MVlul7pQNoDzWRLTw5imwYsl+usrS1TXG2H4jg6ImGw= +github.com/google/flatbuffers v1.12.1/go.mod h1:1AeVuKshWv4vARoZatz6mlQ0JxURH0Kv5+zNeJKJCa8= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= +github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= 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.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/gookit/color v1.5.4 h1:FZmqs7XOyGgCAxmWyPslpiok1k05wmY3SJTytgvYFs0= @@ -482,8 +512,12 @@ github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9de github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= 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.etcd.io/bbolt v1.3.10 h1:+BqfJTcCzTItrop8mq/lbzL8wSGtj94UO/3U31shqG0= +go.etcd.io/bbolt v1.3.10/go.mod h1:bK3UQLPJZly7IlNmV7uVHJDxfe5aK9Ll93e/74Y9oEQ= go.mongodb.org/mongo-driver v1.15.0 h1:rJCKC8eEliewXjZGf0ddURtl7tTVy1TK3bfl0gkUSLc= go.mongodb.org/mongo-driver v1.15.0/go.mod h1:Vzb0Mk/pa7e6cWw85R4F/endUC3u0U9jGcNU603k65c= +go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0= +go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo= go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.53.0 h1:9G6E0TXzGFVfTnawRzrPl83iHOAV7L8NJiR8RSGYV1g= go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.53.0/go.mod h1:azvtTADFQJA8mX80jIH/akaE7h+dbm/sVuaHqN13w74= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.53.0 h1:4K4tsIXefpVJtvA/8srF4V4y0akAoPHkIslgAkjixJA= @@ -564,6 +598,9 @@ golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLL golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20201201195509-5d6afe98e0b7/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +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-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= @@ -607,6 +644,7 @@ golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20221010170243-090e33056c14/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 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.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= From 0c7e4f05d7686ac2177d87250d7e4ad4aebbe941 Mon Sep 17 00:00:00 2001 From: Arvindh M Date: Mon, 24 Jun 2024 10:03:42 +0530 Subject: [PATCH 07/24] update pat Signed-off-by: Arvindh --- auth/pat.go | 6 ++-- auth/service.go | 38 ++++++++++++++------ auth/tracing/tracing.go | 80 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 112 insertions(+), 12 deletions(-) diff --git a/auth/pat.go b/auth/pat.go index fc48851da3..32d47454c3 100644 --- a/auth/pat.go +++ b/auth/pat.go @@ -577,8 +577,8 @@ type PATS interface { ResetToken(ctx context.Context, token, patID string, duration time.Duration) (PAT, error) RevokeToken(ctx context.Context, token string, patID string) error - AddScope(ctx context.Context, token, patID string, platformEntityType PlatformEntityType, optionalDomainID string, optionalDomainEntityType DomainEntityType, operation OperationType, entityIDs ...string) error - RemoveScope(ctx context.Context, token, patID string, platformEntityType PlatformEntityType, optionalDomainID string, optionalDomainEntityType DomainEntityType, operation OperationType, entityIDs ...string) error + AddScope(ctx context.Context, token, patID string, platformEntityType PlatformEntityType, optionalDomainID string, optionalDomainEntityType DomainEntityType, operation OperationType, entityIDs ...string) (Scope, error) + RemoveScope(ctx context.Context, token, patID string, platformEntityType PlatformEntityType, optionalDomainID string, optionalDomainEntityType DomainEntityType, operation OperationType, entityIDs ...string) (Scope, error) ClearAllScope(ctx context.Context, token, patID string) error // This will be removed during PR merge. @@ -623,5 +623,7 @@ type PATSRepository interface { RemoveScopeEntry(ctx context.Context, userID, patID string, platformEntityType PlatformEntityType, optionalDomainID string, optionalDomainEntityType DomainEntityType, operation OperationType, entityIDs ...string) (Scope, error) + CheckScopeEntry(ctx context.Context, userID, patID string, platformEntityType PlatformEntityType, optionalDomainID string, optionalDomainEntityType DomainEntityType, operation OperationType, entityIDs ...string) error + RemoveAllScopeEntry(ctx context.Context, userID, patID string) error } diff --git a/auth/service.go b/auth/service.go index 3a587e9c38..b15d7570ba 100644 --- a/auth/service.go +++ b/auth/service.go @@ -72,11 +72,14 @@ var ( MembershipPermission, } - errCreatePAT = errors.New("failed to create PAT") - errUpdatePAT = errors.New("failed to update PAT") - errRetrievePAT = errors.New("failed to retrieve PAT") - errDeletePAT = errors.New("failed to delete PAT") - errRevokePAT = errors.New("failed to revoke PAT") + errCreatePAT = errors.New("failed to create PAT") + errUpdatePAT = errors.New("failed to update PAT") + errRetrievePAT = errors.New("failed to retrieve PAT") + errDeletePAT = errors.New("failed to delete PAT") + errRevokePAT = errors.New("failed to revoke PAT") + errAddScope = errors.New("failed to add entry in scope") + errRemoveScope = errors.New("failed to remove entry in scope") + errClearAllScope = errors.New("failed to clear all entry in scope") ) // Authn specifies an API that must be fulfilled by the domain service @@ -1177,37 +1180,52 @@ func (svc service) RevokeToken(ctx context.Context, token, patID string) error { } return nil } + func (svc service) AddScope(ctx context.Context, token, patID string, platformEntityType PlatformEntityType, optionalDomainID string, optionalDomainEntityType DomainEntityType, operation OperationType, entityIDs ...string) (Scope, error) { key, err := svc.Identify(ctx, token) if err != nil { return Scope{}, err } - if err := svc.pats.AddScope(ctx, key.User, patID); err != nil { - return errors.Wrap(errRevokePAT, err) + scope, err := svc.pats.AddScopeEntry(ctx, key.User, patID, platformEntityType, optionalDomainID, optionalDomainEntityType, operation, entityIDs...) + if err != nil { + + return Scope{}, errors.Wrap(errRevokePAT, err) } - return nil + return scope, nil } func (svc service) RemoveScope(ctx context.Context, token, patID string, platformEntityType PlatformEntityType, optionalDomainID string, optionalDomainEntityType DomainEntityType, operation OperationType, entityIDs ...string) (Scope, error) { key, err := svc.Identify(ctx, token) if err != nil { return Scope{}, err } - return nil + scope, err := svc.pats.RemoveScopeEntry(ctx, key.User, patID, platformEntityType, optionalDomainID, optionalDomainEntityType, operation, entityIDs...) + if err != nil { + return Scope{}, err + } + return scope, nil } func (svc service) ClearAllScope(ctx context.Context, token, patID string) error { key, err := svc.Identify(ctx, token) if err != nil { return err } + if err := svc.pats.RemoveAllScopeEntry(ctx, key.User, patID); err != nil { + return errors.Wrap(errClearAllScope, err) + } return nil } + func (svc service) TestCheckScope(ctx context.Context, token, patID string, platformEntityType PlatformEntityType, optionalDomainID string, optionalDomainEntityType DomainEntityType, operation OperationType, entityIDs ...string) error { key, err := svc.Identify(ctx, token) if err != nil { - return PAT{}, err + return err + } + if err := svc.pats.CheckScopeEntry(ctx, key.User, patID, platformEntityType, optionalDomainID, optionalDomainEntityType, operation, entityIDs...); err != nil { + return err } return nil } + func (svc service) IdentifyPAT(ctx context.Context, paToken string) (PAT, error) { return PAT{}, nil } diff --git a/auth/tracing/tracing.go b/auth/tracing/tracing.go index fe58626b04..a2b02c0d52 100644 --- a/auth/tracing/tracing.go +++ b/auth/tracing/tracing.go @@ -6,6 +6,7 @@ package tracing import ( "context" "fmt" + "time" "github.com/absmach/magistrala/auth" "go.opentelemetry.io/otel/attribute" @@ -312,3 +313,82 @@ func (tm *tracingMiddleware) DeleteEntityPolicies(ctx context.Context, entityTyp defer span.End() return tm.svc.DeleteEntityPolicies(ctx, entityType, id) } + +func (tm *tracingMiddleware) Create(ctx context.Context, token, name, description string, duration time.Duration, scope auth.Scope) (auth.PAT, error) { + ctx, span := tm.tracer.Start(ctx, "create_pat", trace.WithAttributes( + attribute.String("name", name), + attribute.String("description", description), + attribute.String("duration", duration.String()), + attribute.String("scope", scope.String()), + )) + defer span.End() + return tm.svc.Create(ctx, token, name, description, duration, scope) +} +func (tm *tracingMiddleware) UpdateName(ctx context.Context, token, patID, name string) (auth.PAT, error) { + ctx, span := tm.tracer.Start(ctx, "update_pat_name", trace.WithAttributes( + attribute.String("pat_id", patID), + attribute.String("name", name), + )) + defer span.End() + return tm.svc.UpdateName(ctx, token, patID, name) +} +func (tm *tracingMiddleware) UpdateDescription(ctx context.Context, token, patID, description string) (auth.PAT, error) { + ctx, span := tm.tracer.Start(ctx, "update_pat_description", trace.WithAttributes( + attribute.String("pat_id", patID), + attribute.String("description", description), + )) + defer span.End() + return tm.svc.UpdateDescription(ctx, token, patID, description) +} +func (tm *tracingMiddleware) Retrieve(ctx context.Context, token string, patID string) (auth.PAT, error) { + ctx, span := tm.tracer.Start(ctx, "retrieve_pat", trace.WithAttributes( + attribute.String("pat_id", patID), + )) + defer span.End() + return tm.svc.Retrieve(ctx, token, patID) +} +func (tm *tracingMiddleware) List(ctx context.Context, token string) (auth.PATSPage, error) { + ctx, span := tm.tracer.Start(ctx, "list_pat") + defer span.End() + return tm.svc.List(ctx, token) +} +func (tm *tracingMiddleware) Delete(ctx context.Context, token string, patID string) error { + ctx, span := tm.tracer.Start(ctx, "delete_pat", trace.WithAttributes( + attribute.String("pat_id", patID), + )) + defer span.End() + return tm.svc.Delete(ctx, token, patID) +} +func (tm *tracingMiddleware) ResetToken(ctx context.Context, token, patID string, duration time.Duration) (auth.PAT, error) { + ctx, span := tm.tracer.Start(ctx, "reset_pat", trace.WithAttributes( + attribute.String("pat_id", patID), + attribute.String("duration", duration.String()), + )) + defer span.End() + return tm.svc.ResetToken(ctx, token, patID, duration) +} +func (tm *tracingMiddleware) RevokeToken(ctx context.Context, token string, patID string) error { + ctx, span := tm.tracer.Start(ctx, "revoke_pat", trace.WithAttributes( + attribute.String("pat_id", patID), + )) + defer span.End() + return tm.svc.RevokeToken(ctx, token, patID) +} +func (tm *tracingMiddleware) AddScope(ctx context.Context, token, patID string, platformEntityType auth.PlatformEntityType, optionalDomainID string, optionalDomainEntityType auth.DomainEntityType, operation auth.OperationType, entityIDs ...string) (auth.Scope, error) { + return tm.svc.AddScope(ctx, token, patID, platformEntityType, optionalDomainID, optionalDomainEntityType, operation, entityIDs...) +} +func (tm *tracingMiddleware) RemoveScope(ctx context.Context, token, patID string, platformEntityType auth.PlatformEntityType, optionalDomainID string, optionalDomainEntityType auth.DomainEntityType, operation auth.OperationType, entityIDs ...string) (auth.Scope, error) { + return tm.svc.RemoveScope(ctx, token, patID, platformEntityType, optionalDomainID, optionalDomainEntityType, operation, entityIDs...) +} +func (tm *tracingMiddleware) ClearAllScope(ctx context.Context, token, patID string) error { + return tm.svc.ClearAllScope(ctx, token, patID) +} +func (tm *tracingMiddleware) TestCheckScope(ctx context.Context, token, patID string, platformEntityType auth.PlatformEntityType, optionalDomainID string, optionalDomainEntityType auth.DomainEntityType, operation auth.OperationType, entityIDs ...string) error { + return tm.svc.TestCheckScope(ctx, token, patID, platformEntityType, optionalDomainID, optionalDomainEntityType, operation, entityIDs...) +} +func (tm *tracingMiddleware) IdentifyPAT(ctx context.Context, paToken string) (auth.PAT, error) { + return tm.svc.IdentifyPAT(ctx, paToken) +} +func (tm *tracingMiddleware) AuthorizationPAT(ctx context.Context, paToken string) (auth.PAT, error) { + return tm.svc.AuthorizationPAT(ctx, paToken) +} From c11cdde48c650dc5a8ebb33765cf62f73d2f18b0 Mon Sep 17 00:00:00 2001 From: Arvindh Date: Mon, 24 Jun 2024 11:02:13 +0530 Subject: [PATCH 08/24] poc pat Signed-off-by: Arvindh --- auth/bolt/pat.go | 8 ++++++ auth/pat.go | 9 ++++--- auth/tracing/tracing.go | 54 ++++++++++++++++++++++++++++++++++++++--- 3 files changed, 65 insertions(+), 6 deletions(-) diff --git a/auth/bolt/pat.go b/auth/bolt/pat.go index ed32c5c796..fd76b933ae 100644 --- a/auth/bolt/pat.go +++ b/auth/bolt/pat.go @@ -43,6 +43,10 @@ func (pr *patRepo) UpdateTokenHash(ctx context.Context, userID, patID, tokenHash return auth.PAT{}, nil } +func (pr *patRepo) UpdateLastUsed(ctx context.Context, token, patID, description string) (auth.PAT, error) { + return auth.PAT{}, nil +} + func (pr *patRepo) RetrieveAll(ctx context.Context, userID string) (pats auth.PATSPage, err error) { return auth.PATSPage{}, nil } @@ -63,6 +67,10 @@ func (pr *patRepo) RemoveScopeEntry(ctx context.Context, userID, patID string, p return auth.Scope{}, nil } +func (pr *patRepo) CheckScopeEntry(ctx context.Context, userID, patID string, platformEntityType auth.PlatformEntityType, optionalDomainID string, optionalDomainEntityType auth.DomainEntityType, operation auth.OperationType, entityIDs ...string) error { + return nil +} + func (pr *patRepo) RemoveAllScopeEntry(ctx context.Context, userID, patID string) error { return nil } diff --git a/auth/pat.go b/auth/pat.go index 32d47454c3..c1a76dbd91 100644 --- a/auth/pat.go +++ b/auth/pat.go @@ -58,6 +58,7 @@ func ParseOperationType(ot string) (OperationType, error) { func (ot OperationType) MarshalJSON() ([]byte, error) { return []byte(ot.String()), nil } + func (ot OperationType) MarshalText() (text []byte, err error) { return []byte(ot.String()), nil } @@ -111,6 +112,7 @@ func ParseDomainEntityType(det string) (DomainEntityType, error) { func (det DomainEntityType) MarshalJSON() ([]byte, error) { return []byte(det.String()), nil } + func (det DomainEntityType) MarshalText() ([]byte, error) { return []byte(det.String()), nil } @@ -153,6 +155,7 @@ func ParsePlatformEntityType(pet string) (PlatformEntityType, error) { func (pet PlatformEntityType) MarshalJSON() ([]byte, error) { return []byte(pet.String()), nil } + func (pet PlatformEntityType) MarshalText() (text []byte, err error) { return []byte(pet.String()), nil } @@ -570,12 +573,12 @@ type PATS interface { UpdateName(ctx context.Context, token, patID, name string) (PAT, error) UpdateDescription(ctx context.Context, token, patID, description string) (PAT, error) - Retrieve(ctx context.Context, token string, patID string) (PAT, error) + Retrieve(ctx context.Context, token, patID string) (PAT, error) List(ctx context.Context, token string) (PATSPage, error) - Delete(ctx context.Context, token string, patID string) error + Delete(ctx context.Context, token, patID string) error ResetToken(ctx context.Context, token, patID string, duration time.Duration) (PAT, error) - RevokeToken(ctx context.Context, token string, patID string) error + RevokeToken(ctx context.Context, token, patID string) error AddScope(ctx context.Context, token, patID string, platformEntityType PlatformEntityType, optionalDomainID string, optionalDomainEntityType DomainEntityType, operation OperationType, entityIDs ...string) (Scope, error) RemoveScope(ctx context.Context, token, patID string, platformEntityType PlatformEntityType, optionalDomainID string, optionalDomainEntityType DomainEntityType, operation OperationType, entityIDs ...string) (Scope, error) diff --git a/auth/tracing/tracing.go b/auth/tracing/tracing.go index a2b02c0d52..c3cc7d0fd6 100644 --- a/auth/tracing/tracing.go +++ b/auth/tracing/tracing.go @@ -324,6 +324,7 @@ func (tm *tracingMiddleware) Create(ctx context.Context, token, name, descriptio defer span.End() return tm.svc.Create(ctx, token, name, description, duration, scope) } + func (tm *tracingMiddleware) UpdateName(ctx context.Context, token, patID, name string) (auth.PAT, error) { ctx, span := tm.tracer.Start(ctx, "update_pat_name", trace.WithAttributes( attribute.String("pat_id", patID), @@ -332,6 +333,7 @@ func (tm *tracingMiddleware) UpdateName(ctx context.Context, token, patID, name defer span.End() return tm.svc.UpdateName(ctx, token, patID, name) } + func (tm *tracingMiddleware) UpdateDescription(ctx context.Context, token, patID, description string) (auth.PAT, error) { ctx, span := tm.tracer.Start(ctx, "update_pat_description", trace.WithAttributes( attribute.String("pat_id", patID), @@ -340,25 +342,29 @@ func (tm *tracingMiddleware) UpdateDescription(ctx context.Context, token, patID defer span.End() return tm.svc.UpdateDescription(ctx, token, patID, description) } -func (tm *tracingMiddleware) Retrieve(ctx context.Context, token string, patID string) (auth.PAT, error) { + +func (tm *tracingMiddleware) Retrieve(ctx context.Context, token, patID string) (auth.PAT, error) { ctx, span := tm.tracer.Start(ctx, "retrieve_pat", trace.WithAttributes( attribute.String("pat_id", patID), )) defer span.End() return tm.svc.Retrieve(ctx, token, patID) } + func (tm *tracingMiddleware) List(ctx context.Context, token string) (auth.PATSPage, error) { ctx, span := tm.tracer.Start(ctx, "list_pat") defer span.End() return tm.svc.List(ctx, token) } -func (tm *tracingMiddleware) Delete(ctx context.Context, token string, patID string) error { + +func (tm *tracingMiddleware) Delete(ctx context.Context, token, patID string) error { ctx, span := tm.tracer.Start(ctx, "delete_pat", trace.WithAttributes( attribute.String("pat_id", patID), )) defer span.End() return tm.svc.Delete(ctx, token, patID) } + func (tm *tracingMiddleware) ResetToken(ctx context.Context, token, patID string, duration time.Duration) (auth.PAT, error) { ctx, span := tm.tracer.Start(ctx, "reset_pat", trace.WithAttributes( attribute.String("pat_id", patID), @@ -367,28 +373,70 @@ func (tm *tracingMiddleware) ResetToken(ctx context.Context, token, patID string defer span.End() return tm.svc.ResetToken(ctx, token, patID, duration) } -func (tm *tracingMiddleware) RevokeToken(ctx context.Context, token string, patID string) error { + +func (tm *tracingMiddleware) RevokeToken(ctx context.Context, token, patID string) error { ctx, span := tm.tracer.Start(ctx, "revoke_pat", trace.WithAttributes( attribute.String("pat_id", patID), )) defer span.End() return tm.svc.RevokeToken(ctx, token, patID) } + func (tm *tracingMiddleware) AddScope(ctx context.Context, token, patID string, platformEntityType auth.PlatformEntityType, optionalDomainID string, optionalDomainEntityType auth.DomainEntityType, operation auth.OperationType, entityIDs ...string) (auth.Scope, error) { + ctx, span := tm.tracer.Start(ctx, "add_pat_scope", trace.WithAttributes( + attribute.String("pat_id", patID), + attribute.String("platform_entity", platformEntityType.String()), + attribute.String("optional_domain_id", optionalDomainID), + attribute.String("optional_domain_entity", optionalDomainEntityType.String()), + attribute.String("operation", operation.String()), + attribute.StringSlice("entities", entityIDs), + )) + defer span.End() return tm.svc.AddScope(ctx, token, patID, platformEntityType, optionalDomainID, optionalDomainEntityType, operation, entityIDs...) } + func (tm *tracingMiddleware) RemoveScope(ctx context.Context, token, patID string, platformEntityType auth.PlatformEntityType, optionalDomainID string, optionalDomainEntityType auth.DomainEntityType, operation auth.OperationType, entityIDs ...string) (auth.Scope, error) { + ctx, span := tm.tracer.Start(ctx, "remove_pat_scope", trace.WithAttributes( + attribute.String("pat_id", patID), + attribute.String("platform_entity", platformEntityType.String()), + attribute.String("optional_domain_id", optionalDomainID), + attribute.String("optional_domain_entity", optionalDomainEntityType.String()), + attribute.String("operation", operation.String()), + attribute.StringSlice("entities", entityIDs), + )) + defer span.End() return tm.svc.RemoveScope(ctx, token, patID, platformEntityType, optionalDomainID, optionalDomainEntityType, operation, entityIDs...) } + func (tm *tracingMiddleware) ClearAllScope(ctx context.Context, token, patID string) error { + ctx, span := tm.tracer.Start(ctx, "clear_pat_all_scope", trace.WithAttributes( + attribute.String("pat_id", patID), + )) + defer span.End() return tm.svc.ClearAllScope(ctx, token, patID) } + func (tm *tracingMiddleware) TestCheckScope(ctx context.Context, token, patID string, platformEntityType auth.PlatformEntityType, optionalDomainID string, optionalDomainEntityType auth.DomainEntityType, operation auth.OperationType, entityIDs ...string) error { + ctx, span := tm.tracer.Start(ctx, "remove_pat_scope", trace.WithAttributes( + attribute.String("pat_id", patID), + attribute.String("platform_entity", platformEntityType.String()), + attribute.String("optional_domain_id", optionalDomainID), + attribute.String("optional_domain_entity", optionalDomainEntityType.String()), + attribute.String("operation", operation.String()), + attribute.StringSlice("entities", entityIDs), + )) + defer span.End() return tm.svc.TestCheckScope(ctx, token, patID, platformEntityType, optionalDomainID, optionalDomainEntityType, operation, entityIDs...) } + func (tm *tracingMiddleware) IdentifyPAT(ctx context.Context, paToken string) (auth.PAT, error) { + ctx, span := tm.tracer.Start(ctx, "identity_pat") + defer span.End() return tm.svc.IdentifyPAT(ctx, paToken) } + func (tm *tracingMiddleware) AuthorizationPAT(ctx context.Context, paToken string) (auth.PAT, error) { + ctx, span := tm.tracer.Start(ctx, "authorize_pat") + defer span.End() return tm.svc.AuthorizationPAT(ctx, paToken) } From bd7117b88a15d8efb93694a33eb100eaab48aa31 Mon Sep 17 00:00:00 2001 From: Arvindh Date: Mon, 24 Jun 2024 11:39:32 +0530 Subject: [PATCH 09/24] poc pat Signed-off-by: Arvindh --- auth/pat.go | 37 ++++++++++++++++++++++++++++++------- auth/service.go | 13 +++++++------ auth/tracing/tracing.go | 22 +++++++++++----------- 3 files changed, 48 insertions(+), 24 deletions(-) diff --git a/auth/pat.go b/auth/pat.go index c1a76dbd91..88ea966dff 100644 --- a/auth/pat.go +++ b/auth/pat.go @@ -567,33 +567,56 @@ func (pat PAT) Expired() bool { return pat.ExpiresAt.UTC().Before(time.Now().UTC()) } +// PATS specifies function which are required for Personal access Token implementation. +//go:generate mockery --name PATS --output=./mocks --filename pats.go --quiet --note "Copyright (c) Abstract Machines" + type PATS interface { + // Create function creates new PAT for given valid inputs. Create(ctx context.Context, token, name, description string, duration time.Duration, scope Scope) (PAT, error) + // UpdateName function updates the name for the given PAT ID. UpdateName(ctx context.Context, token, patID, name string) (PAT, error) + + // UpdateDescription function updates the description for the given PAT ID. UpdateDescription(ctx context.Context, token, patID, description string) (PAT, error) + // Retrieve function retrieves the PAT for given ID. Retrieve(ctx context.Context, token, patID string) (PAT, error) + + // List function lists all the PATs for the user. List(ctx context.Context, token string) (PATSPage, error) + + // Delete function deletes the PAT for given ID. Delete(ctx context.Context, token, patID string) error - ResetToken(ctx context.Context, token, patID string, duration time.Duration) (PAT, error) - RevokeToken(ctx context.Context, token, patID string) error + // ResetSecret function reset the secret and creates new secret for the given ID. + ResetSecret(ctx context.Context, token, patID string, duration time.Duration) (PAT, error) + // RevokeSecret function revokes the secret for the given ID. + RevokeSecret(ctx context.Context, token, patID string) error + + // AddScope function adds a new scope entry. AddScope(ctx context.Context, token, patID string, platformEntityType PlatformEntityType, optionalDomainID string, optionalDomainEntityType DomainEntityType, operation OperationType, entityIDs ...string) (Scope, error) + + // RemoveScope function removes a scope entry. RemoveScope(ctx context.Context, token, patID string, platformEntityType PlatformEntityType, optionalDomainID string, optionalDomainEntityType DomainEntityType, operation OperationType, entityIDs ...string) (Scope, error) + + // ClearAllScope function removes all scope entry. ClearAllScope(ctx context.Context, token, patID string) error - // This will be removed during PR merge. - TestCheckScope(ctx context.Context, token, patID string, platformEntityType PlatformEntityType, optionalDomainID string, optionalDomainEntityType DomainEntityType, operation OperationType, entityIDs ...string) error + // This will be removed during PR merge. TestCheckScope will check the given scope exists. + TestCheckScope(ctx context.Context, paToken string, platformEntityType PlatformEntityType, optionalDomainID string, optionalDomainEntityType DomainEntityType, operation OperationType, entityIDs ...string) error + // IdentifyPAT function will valid the secret. IdentifyPAT(ctx context.Context, paToken string) (PAT, error) - AuthorizationPAT(ctx context.Context, paToken string) (PAT, error) + + // AuthorizePAT function will valid the secret and check the given scope exists. + AuthorizePAT(ctx context.Context, paToken string) (PAT, error) } -// PATSRepository specifies Key persistence API. +// PATSRepository specifies PATS persistence API. // -//go:generate mockery --name KeyRepository --output=./mocks --filename keys.go --quiet --note "Copyright (c) Abstract Machines" +//go:generate mockery --name PATSRepository --output=./mocks --filename patsrepo.go --quiet --note "Copyright (c) Abstract Machines" type PATSRepository interface { // Save persists the PAT Save(ctx context.Context, pat PAT) (id string, err error) diff --git a/auth/service.go b/auth/service.go index b15d7570ba..19e84b153f 100644 --- a/auth/service.go +++ b/auth/service.go @@ -1153,7 +1153,7 @@ func (svc service) Delete(ctx context.Context, token, patID string) error { } return nil } -func (svc service) ResetToken(ctx context.Context, token, patID string, duration time.Duration) (PAT, error) { +func (svc service) ResetSecret(ctx context.Context, token, patID string, duration time.Duration) (PAT, error) { key, err := svc.Identify(ctx, token) if err != nil { return PAT{}, err @@ -1169,7 +1169,7 @@ func (svc service) ResetToken(ctx context.Context, token, patID string, duration } return pat, nil } -func (svc service) RevokeToken(ctx context.Context, token, patID string) error { +func (svc service) RevokeSecret(ctx context.Context, token, patID string) error { key, err := svc.Identify(ctx, token) if err != nil { return err @@ -1215,12 +1215,12 @@ func (svc service) ClearAllScope(ctx context.Context, token, patID string) error return nil } -func (svc service) TestCheckScope(ctx context.Context, token, patID string, platformEntityType PlatformEntityType, optionalDomainID string, optionalDomainEntityType DomainEntityType, operation OperationType, entityIDs ...string) error { - key, err := svc.Identify(ctx, token) +func (svc service) TestCheckScope(ctx context.Context, paToken string, platformEntityType PlatformEntityType, optionalDomainID string, optionalDomainEntityType DomainEntityType, operation OperationType, entityIDs ...string) error { + res, err := svc.IdentifyPAT(ctx, paToken) if err != nil { return err } - if err := svc.pats.CheckScopeEntry(ctx, key.User, patID, platformEntityType, optionalDomainID, optionalDomainEntityType, operation, entityIDs...); err != nil { + if err := svc.pats.CheckScopeEntry(ctx, res.User, paToken, platformEntityType, optionalDomainID, optionalDomainEntityType, operation, entityIDs...); err != nil { return err } return nil @@ -1229,6 +1229,7 @@ func (svc service) TestCheckScope(ctx context.Context, token, patID string, plat func (svc service) IdentifyPAT(ctx context.Context, paToken string) (PAT, error) { return PAT{}, nil } -func (svc service) AuthorizationPAT(ctx context.Context, paToken string) (PAT, error) { + +func (svc service) AuthorizePAT(ctx context.Context, paToken string) (PAT, error) { return PAT{}, nil } diff --git a/auth/tracing/tracing.go b/auth/tracing/tracing.go index c3cc7d0fd6..1db13e84d1 100644 --- a/auth/tracing/tracing.go +++ b/auth/tracing/tracing.go @@ -365,21 +365,21 @@ func (tm *tracingMiddleware) Delete(ctx context.Context, token, patID string) er return tm.svc.Delete(ctx, token, patID) } -func (tm *tracingMiddleware) ResetToken(ctx context.Context, token, patID string, duration time.Duration) (auth.PAT, error) { - ctx, span := tm.tracer.Start(ctx, "reset_pat", trace.WithAttributes( +func (tm *tracingMiddleware) ResetSecret(ctx context.Context, token, patID string, duration time.Duration) (auth.PAT, error) { + ctx, span := tm.tracer.Start(ctx, "reset_pat_secret", trace.WithAttributes( attribute.String("pat_id", patID), attribute.String("duration", duration.String()), )) defer span.End() - return tm.svc.ResetToken(ctx, token, patID, duration) + return tm.svc.ResetSecret(ctx, token, patID, duration) } -func (tm *tracingMiddleware) RevokeToken(ctx context.Context, token, patID string) error { - ctx, span := tm.tracer.Start(ctx, "revoke_pat", trace.WithAttributes( +func (tm *tracingMiddleware) RevokeSecret(ctx context.Context, token, patID string) error { + ctx, span := tm.tracer.Start(ctx, "revoke_pat_secret", trace.WithAttributes( attribute.String("pat_id", patID), )) defer span.End() - return tm.svc.RevokeToken(ctx, token, patID) + return tm.svc.RevokeSecret(ctx, token, patID) } func (tm *tracingMiddleware) AddScope(ctx context.Context, token, patID string, platformEntityType auth.PlatformEntityType, optionalDomainID string, optionalDomainEntityType auth.DomainEntityType, operation auth.OperationType, entityIDs ...string) (auth.Scope, error) { @@ -416,9 +416,9 @@ func (tm *tracingMiddleware) ClearAllScope(ctx context.Context, token, patID str return tm.svc.ClearAllScope(ctx, token, patID) } -func (tm *tracingMiddleware) TestCheckScope(ctx context.Context, token, patID string, platformEntityType auth.PlatformEntityType, optionalDomainID string, optionalDomainEntityType auth.DomainEntityType, operation auth.OperationType, entityIDs ...string) error { +func (tm *tracingMiddleware) TestCheckScope(ctx context.Context, paToken string, platformEntityType auth.PlatformEntityType, optionalDomainID string, optionalDomainEntityType auth.DomainEntityType, operation auth.OperationType, entityIDs ...string) error { ctx, span := tm.tracer.Start(ctx, "remove_pat_scope", trace.WithAttributes( - attribute.String("pat_id", patID), + attribute.String("personal_access_token", paToken), attribute.String("platform_entity", platformEntityType.String()), attribute.String("optional_domain_id", optionalDomainID), attribute.String("optional_domain_entity", optionalDomainEntityType.String()), @@ -426,7 +426,7 @@ func (tm *tracingMiddleware) TestCheckScope(ctx context.Context, token, patID st attribute.StringSlice("entities", entityIDs), )) defer span.End() - return tm.svc.TestCheckScope(ctx, token, patID, platformEntityType, optionalDomainID, optionalDomainEntityType, operation, entityIDs...) + return tm.svc.TestCheckScope(ctx, paToken, platformEntityType, optionalDomainID, optionalDomainEntityType, operation, entityIDs...) } func (tm *tracingMiddleware) IdentifyPAT(ctx context.Context, paToken string) (auth.PAT, error) { @@ -435,8 +435,8 @@ func (tm *tracingMiddleware) IdentifyPAT(ctx context.Context, paToken string) (a return tm.svc.IdentifyPAT(ctx, paToken) } -func (tm *tracingMiddleware) AuthorizationPAT(ctx context.Context, paToken string) (auth.PAT, error) { +func (tm *tracingMiddleware) AuthorizePAT(ctx context.Context, paToken string) (auth.PAT, error) { ctx, span := tm.tracer.Start(ctx, "authorize_pat") defer span.End() - return tm.svc.AuthorizationPAT(ctx, paToken) + return tm.svc.AuthorizePAT(ctx, paToken) } From a23494ac26bd0c0fc227ad1358e265a0d6521246 Mon Sep 17 00:00:00 2001 From: Arvindh Date: Wed, 3 Jul 2024 17:19:45 +0530 Subject: [PATCH 10/24] update PAT bbolt repo Signed-off-by: Arvindh --- auth/bolt/init.go | 15 + auth/bolt/pat.go | 582 ++++++++++++++++++++++++++- auth/mocks/pats.go | 407 +++++++++++++++++++ auth/mocks/patsrepo.go | 379 +++++++++++++++++ auth/mocks/service.go | 418 +++++++++++++++++++ auth/pat.go | 61 ++- auth/service.go | 5 +- cmd/test/patrepo/main.go | 71 ++++ cmd/test/stringsbench/concat_test.go | 34 ++ internal/clients/bolt/bolt.go | 82 ++++ internal/clients/bolt/doc.go | 9 + 11 files changed, 2037 insertions(+), 26 deletions(-) create mode 100644 auth/mocks/pats.go create mode 100644 auth/mocks/patsrepo.go create mode 100644 cmd/test/patrepo/main.go create mode 100644 cmd/test/stringsbench/concat_test.go create mode 100644 internal/clients/bolt/bolt.go create mode 100644 internal/clients/bolt/doc.go diff --git a/auth/bolt/init.go b/auth/bolt/init.go index dcd06ac566..9d496e65ca 100644 --- a/auth/bolt/init.go +++ b/auth/bolt/init.go @@ -4,3 +4,18 @@ // Package bolt contains PAT repository implementations using // bolt as the underlying database. package bolt + +import ( + "github.com/absmach/magistrala/pkg/errors" + bolt "go.etcd.io/bbolt" +) + +var errInit = errors.New("failed to initialize BoltDB") + +func Init(tx *bolt.Tx, bucket string) error { + _, err := tx.CreateBucketIfNotExists([]byte(bucket)) + if err != nil { + return errors.Wrap(errInit, err) + } + return nil +} diff --git a/auth/bolt/pat.go b/auth/bolt/pat.go index fd76b933ae..3c8a487b98 100644 --- a/auth/bolt/pat.go +++ b/auth/bolt/pat.go @@ -4,47 +4,137 @@ package bolt import ( + "bytes" "context" + "encoding/binary" + "fmt" + "strings" "time" "github.com/absmach/magistrala/auth" - "go.etcd.io/bbolt" + "github.com/absmach/magistrala/pkg/errors" + repoerr "github.com/absmach/magistrala/pkg/errors/repository" + + bolt "go.etcd.io/bbolt" +) + +const ( + idKey = "id" + userKey = "user" + nameKey = "name" + descriptionKey = "description" + tokenHashKey = "token_hash" + scopeKey = "scope" + issuedAtKey = "issued_at" + expiresAtKey = "expires_at" + updatedAtKey = "updated_at" + lastUsedAtKey = "last_used_at" + revokedKey = "revoked" + revokedAtKey = "revoked_at" + platformEntitiesKey = "platform_entities" + + keySeparator = ":" + anyID = "*" +) + +var ( + revokedValue = []byte{0x01} + entityValue = []byte{0x02} + anyIDValue = []byte{0x03} + selectedIDsValue = []byte{0x04} + + errBucketNotFound = errors.New("bucket not found") ) type patRepo struct { - db *bbolt.DB + db *bolt.DB + bucketName string } -// NewPATRepository instantiates a bbolt +// NewPATSRepository instantiates a bolt // implementation of PAT repository. -func NewDomainRepository(db *bbolt.DB) auth.PATSRepository { +func NewPATSRepository(db *bolt.DB, bucketName string) auth.PATSRepository { return &patRepo{ - db: db, + db: db, + bucketName: bucketName, } } -func (pr *patRepo) Save(ctx context.Context, pat auth.PAT) (id string, err error) { - return "", nil +func (pr *patRepo) Save(ctx context.Context, pat auth.PAT) error { + kv, err := patToKeyValue(pat) + if err != nil { + return err + } + return pr.db.Update(func(tx *bolt.Tx) error { + b, err := pr.createRetrieveUserBucket(tx, pat.User) + if err != nil { + return errors.Wrap(repoerr.ErrCreateEntity, err) + } + for key, value := range kv { + fullKey := []byte(pat.ID + keySeparator + key) + if err := b.Put(fullKey, value); err != nil { + return errors.Wrap(repoerr.ErrCreateEntity, err) + } + } + return nil + }) } -func (pr *patRepo) Retrieve(ctx context.Context, userID, patID string) (pat auth.PAT, err error) { - return auth.PAT{}, nil +func (pr *patRepo) Retrieve(ctx context.Context, userID, patID string) (auth.PAT, error) { + var pat auth.PAT + prefix := []byte(patID + keySeparator) + kv := map[string][]byte{} + if err := pr.db.Update(func(tx *bolt.Tx) error { + b, err := pr.retrieveUserBucket(tx, userID) + if err != nil { + return errors.Wrap(repoerr.ErrRemoveEntity, err) + } + c := b.Cursor() + for k, v := c.Seek(prefix); k != nil && bytes.HasPrefix(k, prefix); k, v = c.Next() { + kv[string(k)] = v + } + return nil + }); err != nil { + return auth.PAT{}, err + } + + for k, v := range kv { + fmt.Println(k, string(v)) + } + fmt.Println(kv) + return pat, nil } func (pr *patRepo) UpdateName(ctx context.Context, userID, patID, name string) (auth.PAT, error) { - return auth.PAT{}, nil + return pr.updatePATField(ctx, userID, patID, nameKey, []byte(name)) } func (pr *patRepo) UpdateDescription(ctx context.Context, userID, patID, description string) (auth.PAT, error) { - return auth.PAT{}, nil + return pr.updatePATField(ctx, userID, patID, descriptionKey, []byte(description)) } func (pr *patRepo) UpdateTokenHash(ctx context.Context, userID, patID, tokenHash string, expiryAt time.Time) (auth.PAT, error) { - return auth.PAT{}, nil -} - -func (pr *patRepo) UpdateLastUsed(ctx context.Context, token, patID, description string) (auth.PAT, error) { - return auth.PAT{}, nil + var pat auth.PAT + if err := pr.db.Update(func(tx *bolt.Tx) error { + b, err := pr.retrieveUserBucket(tx, userID) + if err != nil { + return errors.Wrap(repoerr.ErrUpdateEntity, err) + } + if err := b.Put([]byte(patID+keySeparator+tokenHashKey), []byte(tokenHash)); err != nil { + return errors.Wrap(repoerr.ErrUpdateEntity, err) + } + if err := b.Put([]byte(patID+keySeparator+expiresAtKey), timeToBytes(expiryAt)); err != nil { + return errors.Wrap(repoerr.ErrUpdateEntity, err) + } + pat, err = pr.retrievePAT(b, patID) + if err != nil { + return err + } + return nil + }); err != nil { + return auth.PAT{}, err + } + return pat, nil } func (pr *patRepo) RetrieveAll(ctx context.Context, userID string) (pats auth.PATSPage, err error) { @@ -52,25 +142,483 @@ func (pr *patRepo) RetrieveAll(ctx context.Context, userID string) (pats auth.PA } func (pr *patRepo) Revoke(ctx context.Context, userID, patID string) error { + if err := pr.db.Update(func(tx *bolt.Tx) error { + b, err := pr.retrieveUserBucket(tx, userID) + if err != nil { + return errors.Wrap(repoerr.ErrUpdateEntity, err) + } + if err := b.Put([]byte(patID+keySeparator+revokedKey), revokedValue); err != nil { + return errors.Wrap(repoerr.ErrUpdateEntity, err) + } + if err := b.Put([]byte(patID+keySeparator+revokedAtKey), timeToBytes(time.Now())); err != nil { + return errors.Wrap(repoerr.ErrUpdateEntity, err) + } + return nil + }); err != nil { + return err + } return nil } func (pr *patRepo) Remove(ctx context.Context, userID, patID string) error { + prefix := []byte(patID + keySeparator) + + if err := pr.db.Update(func(tx *bolt.Tx) error { + b, err := pr.retrieveUserBucket(tx, userID) + if err != nil { + return errors.Wrap(repoerr.ErrRemoveEntity, err) + } + c := b.Cursor() + for k, _ := c.Seek(prefix); k != nil && bytes.HasPrefix(k, prefix); k, _ = c.Next() { + if err := b.Delete(k); err != nil { + return errors.Wrap(repoerr.ErrRemoveEntity, err) + } + } + return nil + }); err != nil { + return err + } + return nil } func (pr *patRepo) AddScopeEntry(ctx context.Context, userID, patID string, platformEntityType auth.PlatformEntityType, optionalDomainID string, optionalDomainEntityType auth.DomainEntityType, operation auth.OperationType, entityIDs ...string) (auth.Scope, error) { + if err := pr.db.Update(func(tx *bolt.Tx) error { + b, err := pr.retrieveUserBucket(tx, userID) + if err != nil { + return errors.Wrap(repoerr.ErrCreateEntity, err) + } + kv, err := scopeEntryToKeyValue(platformEntityType, optionalDomainID, optionalDomainEntityType, operation, entityIDs...) + if err != nil { + return err + } + for key, value := range kv { + fullKey := []byte(patID + keySeparator + key) + if err := b.Put(fullKey, value); err != nil { + return errors.Wrap(repoerr.ErrCreateEntity, err) + } + } + return nil + }); err != nil { + return auth.Scope{}, err + } + return auth.Scope{}, nil } func (pr *patRepo) RemoveScopeEntry(ctx context.Context, userID, patID string, platformEntityType auth.PlatformEntityType, optionalDomainID string, optionalDomainEntityType auth.DomainEntityType, operation auth.OperationType, entityIDs ...string) (auth.Scope, error) { + if len(entityIDs) == 0 { + return auth.Scope{}, repoerr.ErrMalformedEntity + } + if err := pr.db.Update(func(tx *bolt.Tx) error { + b, err := pr.retrieveUserBucket(tx, userID) + if err != nil { + return errors.Wrap(repoerr.ErrRemoveEntity, err) + } + kv, err := scopeEntryToKeyValue(platformEntityType, optionalDomainID, optionalDomainEntityType, operation, entityIDs...) + if err != nil { + return err + } + for key, _ := range kv { + fullKey := []byte(patID + keySeparator + key) + if err := b.Delete(fullKey); err != nil { + return errors.Wrap(repoerr.ErrRemoveEntity, err) + } + } + return nil + }); err != nil { + return auth.Scope{}, err + } return auth.Scope{}, nil } func (pr *patRepo) CheckScopeEntry(ctx context.Context, userID, patID string, platformEntityType auth.PlatformEntityType, optionalDomainID string, optionalDomainEntityType auth.DomainEntityType, operation auth.OperationType, entityIDs ...string) error { - return nil + return pr.db.Update(func(tx *bolt.Tx) error { + b, err := pr.retrieveUserBucket(tx, userID) + if err != nil { + return errors.Wrap(repoerr.ErrViewEntity, err) + } + srootKey, err := scopeRootKey(platformEntityType, optionalDomainID, optionalDomainEntityType, operation) + if err != nil { + return errors.Wrap(repoerr.ErrViewEntity, err) + } + + rootKey := patID + keySeparator + srootKey + if value := b.Get([]byte(rootKey)); bytes.Equal(value, anyIDValue) { + return nil + } + for _, entity := range entityIDs { + value := b.Get([]byte(rootKey + keySeparator + entity)) + if !bytes.Equal(value, entityValue) { + return repoerr.ErrNotFound + } + } + return nil + }) } func (pr *patRepo) RemoveAllScopeEntry(ctx context.Context, userID, patID string) error { return nil } + +func (pr *patRepo) createRetrieveUserBucket(tx *bolt.Tx, userID string) (*bolt.Bucket, error) { + rootBucket := tx.Bucket([]byte(pr.bucketName)) + if rootBucket == nil { + return nil, errors.Wrap(repoerr.ErrCreateEntity, fmt.Errorf("bucket %s not found", pr.bucketName)) + } + + userBucket, err := rootBucket.CreateBucketIfNotExists([]byte(userID)) + if err != nil { + return nil, errors.Wrap(repoerr.ErrCreateEntity, fmt.Errorf("failed to retrieve or create bucket for user %s : %w", userID, err)) + } + + return userBucket, nil +} + +func (pr *patRepo) retrieveUserBucket(tx *bolt.Tx, userID string) (*bolt.Bucket, error) { + rootBucket := tx.Bucket([]byte(pr.bucketName)) + if rootBucket == nil { + return nil, fmt.Errorf("bucket %s not found", pr.bucketName) + } + + return rootBucket.Bucket([]byte(userID)), nil +} + +func (pr *patRepo) updatePATField(_ context.Context, userID, patID, key string, value []byte) (auth.PAT, error) { + var pat auth.PAT + prefix := []byte(patID + keySeparator) + kv := map[string][]byte{} + if err := pr.db.Update(func(tx *bolt.Tx) error { + b, err := pr.retrieveUserBucket(tx, userID) + if err != nil { + return errors.Wrap(repoerr.ErrUpdateEntity, err) + } + if err := b.Put([]byte(patID+keySeparator+key), value); err != nil { + return errors.Wrap(repoerr.ErrUpdateEntity, err) + } + c := b.Cursor() + for k, v := c.Seek(prefix); k != nil && bytes.HasPrefix(k, prefix); k, v = c.Next() { + kv[string(k)] = v + } + return nil + }); err != nil { + return auth.PAT{}, err + } + return pat, nil +} + +func (pr *patRepo) scopeKeyBuilder(patID string, platformEntityType auth.PlatformEntityType, optionalDomainID string, optionalDomainEntityType auth.DomainEntityType, operation auth.OperationType) (string, error) { + op, err := operation.ValidString() + if err != nil { + return "", errors.Wrap(repoerr.ErrMalformedEntity, err) + } + switch platformEntityType { + case auth.PlatformUsersScope: + return patID + keySeparator + scopeKey + keySeparator + platformEntityType.String() + keySeparator + op, nil + case auth.PlatformDomainsScope: + if optionalDomainID == "" { + return "", fmt.Errorf("failed to add platform %s scope: invalid domain id", platformEntityType.String()) + } + odet, err := optionalDomainEntityType.ValidString() + if err != nil { + return "", errors.Wrap(repoerr.ErrMalformedEntity, err) + } + + return patID + keySeparator + scopeKey + keySeparator + platformEntityType.String() + keySeparator + optionalDomainID + keySeparator + odet + keySeparator + op + keySeparator, nil + default: + return "", errors.Wrap(repoerr.ErrMalformedEntity, fmt.Errorf("invalid platform entity type %s", platformEntityType.String())) + } +} + +func (pr *patRepo) updateLastUsed(ctx context.Context, userID, patID string) error { + return nil +} + +func (pr *patRepo) retrievePAT(b *bolt.Bucket, patID string) (auth.PAT, error) { + var pat auth.PAT + + id := b.Get([]byte(patID + ":id")) + if id == nil { + return pat, repoerr.ErrNotFound + } + pat.ID = string(id) + pat.User = getString(b, patID, userKey) + pat.Name = getString(b, patID, nameKey) + pat.Description = getString(b, patID, descriptionKey) + pat.IssuedAt = getTime(b, patID, issuedAtKey) + pat.ExpiresAt = getTime(b, patID, expiresAtKey) + pat.UpdatedAt = getTime(b, patID, updatedAtKey) + pat.LastUsedAt = getTime(b, patID, lastUsedAtKey) + pat.Revoked = getBool(b, patID, revokedKey) + pat.RevokedAt = getTime(b, patID, revokedAtKey) + return pat, nil +} + +func (pr *patRepo) retrieveScope(b *bolt.Bucket, patID string) (auth.Scope, error) { + var scope auth.Scope + + c := b.Cursor() + prefix := []byte(patID + keySeparator + scopeKey + keySeparator) + for k, v := c.Seek(prefix); k != nil && bytes.HasPrefix(k, prefix); k, v = c.Next() { + fmt.Printf("key=%s, value=%v\n", k, v) + keyWithoutPrefix := k[len(prefix):] + fmt.Printf("key=%s, value=%v\n", keyWithoutPrefix, v) + } + return scope, nil +} + +func patToKeyValue(pat auth.PAT) (map[string][]byte, error) { + kv := map[string][]byte{ + idKey: []byte(pat.ID), + userKey: []byte(pat.User), + nameKey: []byte(pat.Name), + descriptionKey: []byte(pat.Description), + tokenHashKey: []byte(pat.Token), + issuedAtKey: timeToBytes(pat.IssuedAt), + expiresAtKey: timeToBytes(pat.ExpiresAt), + updatedAtKey: timeToBytes(pat.UpdatedAt), + lastUsedAtKey: timeToBytes(pat.LastUsedAt), + revokedKey: booleanToBytes(pat.Revoked), + revokedAtKey: timeToBytes(pat.RevokedAt), + } + scopeKV, err := scopeToKeyValue(pat.Scope) + if err != nil { + return nil, err + } + for k, v := range scopeKV { + kv[k] = v + } + return kv, nil +} + +func scopeToKeyValue(scope auth.Scope) (map[string][]byte, error) { + kv := map[string][]byte{} + for opType, scopeValue := range scope.Users.Operations { + tempKV, err := scopeEntryToKeyValue(auth.PlatformUsersScope, "", auth.DomainNullScope, opType, scopeValue.Values()...) + if err != nil { + return nil, err + } + for k, v := range tempKV { + kv[k] = v + } + } + for domainID, domainScope := range scope.Domains { + for opType, scopeValue := range domainScope.DomainManagement.Operations { + tempKV, err := scopeEntryToKeyValue(auth.PlatformDomainsScope, domainID, auth.DomainManagementScope, opType, scopeValue.Values()...) + if err != nil { + return nil, errors.Wrap(repoerr.ErrCreateEntity, err) + } + for k, v := range tempKV { + kv[k] = v + } + } + for entityType, scope := range domainScope.Entities { + for opType, scopeValue := range scope.Operations { + tempKV, err := scopeEntryToKeyValue(auth.PlatformDomainsScope, domainID, entityType, opType, scopeValue.Values()...) + if err != nil { + return nil, errors.Wrap(repoerr.ErrCreateEntity, err) + } + for k, v := range tempKV { + kv[k] = v + } + } + } + } + return kv, nil +} +func scopeEntryToKeyValue(platformEntityType auth.PlatformEntityType, optionalDomainID string, optionalDomainEntityType auth.DomainEntityType, operation auth.OperationType, entityIDs ...string) (map[string][]byte, error) { + if len(entityIDs) == 0 { + return nil, repoerr.ErrMalformedEntity + } + + rootKey, err := scopeRootKey(platformEntityType, optionalDomainID, optionalDomainEntityType, operation) + if err != nil { + return nil, err + } + if len(entityIDs) == 1 && entityIDs[0] == anyID { + return map[string][]byte{rootKey: anyIDValue}, nil + } + + kv := map[string][]byte{rootKey: selectedIDsValue} + + for _, entryID := range entityIDs { + if entryID == anyID { + return nil, repoerr.ErrMalformedEntity + } + kv[rootKey+keySeparator+entryID] = entityValue + } + + return kv, nil +} + +func scopeRootKey(platformEntityType auth.PlatformEntityType, optionalDomainID string, optionalDomainEntityType auth.DomainEntityType, operation auth.OperationType) (string, error) { + + op, err := operation.ValidString() + if err != nil { + return "", errors.Wrap(repoerr.ErrMalformedEntity, err) + } + + var rootKey strings.Builder + + rootKey.WriteString(scopeKey) + rootKey.WriteString(keySeparator) + rootKey.WriteString(platformEntityType.String()) + rootKey.WriteString(keySeparator) + + switch platformEntityType { + case auth.PlatformUsersScope: + rootKey.WriteString(op) + case auth.PlatformDomainsScope: + if optionalDomainID == "" { + return "", fmt.Errorf("failed to add platform %s scope: invalid domain id", platformEntityType.String()) + } + odet, err := optionalDomainEntityType.ValidString() + if err != nil { + return "", errors.Wrap(repoerr.ErrMalformedEntity, err) + } + rootKey.WriteString(optionalDomainID) + rootKey.WriteString(keySeparator) + rootKey.WriteString(odet) + rootKey.WriteString(keySeparator) + rootKey.WriteString(op) + default: + return "", errors.Wrap(repoerr.ErrMalformedEntity, fmt.Errorf("invalid platform entity type %s", platformEntityType.String())) + } + + return rootKey.String(), nil +} +func keyValueToPAT(kv map[string][]byte) (auth.PAT, error) { + var pat auth.PAT + for k, v := range kv { + switch { + case strings.HasSuffix(k, keySeparator+idKey): + pat.ID = string(v) + case strings.HasSuffix(k, keySeparator+userKey): + pat.User = string(v) + case strings.HasSuffix(k, keySeparator+nameKey): + pat.Name = string(v) + case strings.HasSuffix(k, keySeparator+descriptionKey): + pat.Description = string(v) + case strings.HasSuffix(k, keySeparator+issuedAtKey): + pat.IssuedAt = bytesToTime(v) + case strings.HasSuffix(k, keySeparator+expiresAtKey): + pat.ExpiresAt = bytesToTime(v) + case strings.HasSuffix(k, keySeparator+updatedAtKey): + pat.UpdatedAt = bytesToTime(v) + case strings.HasSuffix(k, keySeparator+lastUsedAtKey): + pat.LastUsedAt = bytesToTime(v) + case strings.HasSuffix(k, keySeparator+revokedKey): + pat.Revoked = bytesToBoolean(v) + case strings.HasSuffix(k, keySeparator+revokedAtKey): + pat.RevokedAt = bytesToTime(v) + } + } + return pat, nil +} + +func parseKeyValueToScope(kv map[string][]byte) (auth.Scope, error) { + scope := auth.Scope{ + Domains: make(map[string]auth.DomainScope), + } + + for key, value := range kv { + if strings.Index(key, keySeparator+scopeKey) > 0 { + parts := strings.Split(key, keySeparator) + if len(parts) < 4 { + return auth.Scope{}, errors.Wrap(repoerr.ErrViewEntity, fmt.Errorf("invalid key format: %s", key)) + } + + platformEntityType, err := auth.ParsePlatformEntityType(parts[2]) + if err != nil { + return auth.Scope{}, errors.Wrap(repoerr.ErrViewEntity, err) + } + opType, err := auth.ParseOperationType(parts[len(parts)-1]) + if err != nil { + return auth.Scope{}, errors.Wrap(repoerr.ErrViewEntity, err) + } + + switch platformEntityType { + case auth.PlatformUsersScope: + if len(parts) != 4 { + return auth.Scope{}, fmt.Errorf("invalid user scope key format: %s", key) + } + if scope.Users.Operations == nil { + scope.Users.Operations = make(map[auth.OperationType]auth.ScopeValue) + } + scope.Users.Operations[opType] = auth.ScopeValue{values: parseValues(value)} + case auth.PlatformDomainsScope: + if len(parts) < 5 { + return auth.Scope{}, fmt.Errorf("invalid domain scope key format: %s", key) + } + domainID := parts[2] + entityType := auth.DomainEntityType(parts[3]) + if scope.Domains[domainID].Entities == nil { + scope.Domains[domainID] = auth.DomainScope{ + Entities: make(map[auth.DomainEntityType]auth.EntityScope), + } + } + if scope.Domains[domainID].Entities[entityType].Operations == nil { + scope.Domains[domainID].Entities[entityType] = auth.EntityScope{ + Operations: make(map[auth.OperationType]auth.ScopeValue), + } + } + scope.Domains[domainID].Entities[entityType].Operations[opType] = auth.ScopeValue{values: parseValues(value)} + default: + return auth.Scope{}, fmt.Errorf("invalid platform entity type: %s", parts[2]) + } + } + + } + + return scope, nil +} + +func getString(b *bolt.Bucket, patID, key string) string { + value := b.Get([]byte(patID + keySeparator + key)) + if value != nil { + return string(value) + } + return "" +} + +func getTime(b *bolt.Bucket, patID, key string) time.Time { + value := b.Get([]byte(patID + keySeparator + key)) + if value != nil { + return bytesToTime(value) + } + return time.Time{} +} + +func getBool(b *bolt.Bucket, patID, key string) bool { + value := b.Get([]byte(patID + keySeparator + key)) + if value != nil { + return bytesToBoolean(value) + } + return false +} + +func timeToBytes(t time.Time) []byte { + timeBytes := make([]byte, 8) + binary.BigEndian.PutUint64(timeBytes, uint64(t.Nanosecond())) + return timeBytes +} + +func bytesToTime(b []byte) time.Time { + var timeAtNs uint64 + binary.BigEndian.AppendUint64(b, timeAtNs) + return time.Unix(0, int64(timeAtNs)) +} + +func booleanToBytes(b bool) []byte { + if b { + return []byte{1} + } + return []byte{0} +} + +func bytesToBoolean(b []byte) bool { + if len(b) > 0 && b[0] == 1 { + return true + } + return false +} diff --git a/auth/mocks/pats.go b/auth/mocks/pats.go new file mode 100644 index 0000000000..cfa65fd845 --- /dev/null +++ b/auth/mocks/pats.go @@ -0,0 +1,407 @@ +// Code generated by mockery v2.42.3. DO NOT EDIT. + +// Copyright (c) Abstract Machines + +package mocks + +import ( + context "context" + + auth "github.com/absmach/magistrala/auth" + + mock "github.com/stretchr/testify/mock" + + time "time" +) + +// PATS is an autogenerated mock type for the PATS type +type PATS struct { + mock.Mock +} + +// AddScope provides a mock function with given fields: ctx, token, patID, platformEntityType, optionalDomainID, optionalDomainEntityType, operation, entityIDs +func (_m *PATS) AddScope(ctx context.Context, token string, patID string, platformEntityType auth.PlatformEntityType, optionalDomainID string, optionalDomainEntityType auth.DomainEntityType, operation auth.OperationType, entityIDs ...string) (auth.Scope, error) { + _va := make([]interface{}, len(entityIDs)) + for _i := range entityIDs { + _va[_i] = entityIDs[_i] + } + var _ca []interface{} + _ca = append(_ca, ctx, token, patID, platformEntityType, optionalDomainID, optionalDomainEntityType, operation) + _ca = append(_ca, _va...) + ret := _m.Called(_ca...) + + if len(ret) == 0 { + panic("no return value specified for AddScope") + } + + var r0 auth.Scope + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, string, string, auth.PlatformEntityType, string, auth.DomainEntityType, auth.OperationType, ...string) (auth.Scope, error)); ok { + return rf(ctx, token, patID, platformEntityType, optionalDomainID, optionalDomainEntityType, operation, entityIDs...) + } + if rf, ok := ret.Get(0).(func(context.Context, string, string, auth.PlatformEntityType, string, auth.DomainEntityType, auth.OperationType, ...string) auth.Scope); ok { + r0 = rf(ctx, token, patID, platformEntityType, optionalDomainID, optionalDomainEntityType, operation, entityIDs...) + } else { + r0 = ret.Get(0).(auth.Scope) + } + + if rf, ok := ret.Get(1).(func(context.Context, string, string, auth.PlatformEntityType, string, auth.DomainEntityType, auth.OperationType, ...string) error); ok { + r1 = rf(ctx, token, patID, platformEntityType, optionalDomainID, optionalDomainEntityType, operation, entityIDs...) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// AuthorizePAT provides a mock function with given fields: ctx, paToken +func (_m *PATS) AuthorizePAT(ctx context.Context, paToken string) (auth.PAT, error) { + ret := _m.Called(ctx, paToken) + + if len(ret) == 0 { + panic("no return value specified for AuthorizePAT") + } + + var r0 auth.PAT + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, string) (auth.PAT, error)); ok { + return rf(ctx, paToken) + } + if rf, ok := ret.Get(0).(func(context.Context, string) auth.PAT); ok { + r0 = rf(ctx, paToken) + } else { + r0 = ret.Get(0).(auth.PAT) + } + + if rf, ok := ret.Get(1).(func(context.Context, string) error); ok { + r1 = rf(ctx, paToken) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// ClearAllScope provides a mock function with given fields: ctx, token, patID +func (_m *PATS) ClearAllScope(ctx context.Context, token string, patID string) error { + ret := _m.Called(ctx, token, patID) + + if len(ret) == 0 { + panic("no return value specified for ClearAllScope") + } + + var r0 error + if rf, ok := ret.Get(0).(func(context.Context, string, string) error); ok { + r0 = rf(ctx, token, patID) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// Create provides a mock function with given fields: ctx, token, name, description, duration, scope +func (_m *PATS) Create(ctx context.Context, token string, name string, description string, duration time.Duration, scope auth.Scope) (auth.PAT, error) { + ret := _m.Called(ctx, token, name, description, duration, scope) + + if len(ret) == 0 { + panic("no return value specified for Create") + } + + var r0 auth.PAT + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, string, string, string, time.Duration, auth.Scope) (auth.PAT, error)); ok { + return rf(ctx, token, name, description, duration, scope) + } + if rf, ok := ret.Get(0).(func(context.Context, string, string, string, time.Duration, auth.Scope) auth.PAT); ok { + r0 = rf(ctx, token, name, description, duration, scope) + } else { + r0 = ret.Get(0).(auth.PAT) + } + + if rf, ok := ret.Get(1).(func(context.Context, string, string, string, time.Duration, auth.Scope) error); ok { + r1 = rf(ctx, token, name, description, duration, scope) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// Delete provides a mock function with given fields: ctx, token, patID +func (_m *PATS) Delete(ctx context.Context, token string, patID string) error { + ret := _m.Called(ctx, token, patID) + + if len(ret) == 0 { + panic("no return value specified for Delete") + } + + var r0 error + if rf, ok := ret.Get(0).(func(context.Context, string, string) error); ok { + r0 = rf(ctx, token, patID) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// IdentifyPAT provides a mock function with given fields: ctx, paToken +func (_m *PATS) IdentifyPAT(ctx context.Context, paToken string) (auth.PAT, error) { + ret := _m.Called(ctx, paToken) + + if len(ret) == 0 { + panic("no return value specified for IdentifyPAT") + } + + var r0 auth.PAT + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, string) (auth.PAT, error)); ok { + return rf(ctx, paToken) + } + if rf, ok := ret.Get(0).(func(context.Context, string) auth.PAT); ok { + r0 = rf(ctx, paToken) + } else { + r0 = ret.Get(0).(auth.PAT) + } + + if rf, ok := ret.Get(1).(func(context.Context, string) error); ok { + r1 = rf(ctx, paToken) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// List provides a mock function with given fields: ctx, token +func (_m *PATS) List(ctx context.Context, token string) (auth.PATSPage, error) { + ret := _m.Called(ctx, token) + + if len(ret) == 0 { + panic("no return value specified for List") + } + + var r0 auth.PATSPage + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, string) (auth.PATSPage, error)); ok { + return rf(ctx, token) + } + if rf, ok := ret.Get(0).(func(context.Context, string) auth.PATSPage); ok { + r0 = rf(ctx, token) + } else { + r0 = ret.Get(0).(auth.PATSPage) + } + + if rf, ok := ret.Get(1).(func(context.Context, string) error); ok { + r1 = rf(ctx, token) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// RemoveScope provides a mock function with given fields: ctx, token, patID, platformEntityType, optionalDomainID, optionalDomainEntityType, operation, entityIDs +func (_m *PATS) RemoveScope(ctx context.Context, token string, patID string, platformEntityType auth.PlatformEntityType, optionalDomainID string, optionalDomainEntityType auth.DomainEntityType, operation auth.OperationType, entityIDs ...string) (auth.Scope, error) { + _va := make([]interface{}, len(entityIDs)) + for _i := range entityIDs { + _va[_i] = entityIDs[_i] + } + var _ca []interface{} + _ca = append(_ca, ctx, token, patID, platformEntityType, optionalDomainID, optionalDomainEntityType, operation) + _ca = append(_ca, _va...) + ret := _m.Called(_ca...) + + if len(ret) == 0 { + panic("no return value specified for RemoveScope") + } + + var r0 auth.Scope + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, string, string, auth.PlatformEntityType, string, auth.DomainEntityType, auth.OperationType, ...string) (auth.Scope, error)); ok { + return rf(ctx, token, patID, platformEntityType, optionalDomainID, optionalDomainEntityType, operation, entityIDs...) + } + if rf, ok := ret.Get(0).(func(context.Context, string, string, auth.PlatformEntityType, string, auth.DomainEntityType, auth.OperationType, ...string) auth.Scope); ok { + r0 = rf(ctx, token, patID, platformEntityType, optionalDomainID, optionalDomainEntityType, operation, entityIDs...) + } else { + r0 = ret.Get(0).(auth.Scope) + } + + if rf, ok := ret.Get(1).(func(context.Context, string, string, auth.PlatformEntityType, string, auth.DomainEntityType, auth.OperationType, ...string) error); ok { + r1 = rf(ctx, token, patID, platformEntityType, optionalDomainID, optionalDomainEntityType, operation, entityIDs...) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// ResetSecret provides a mock function with given fields: ctx, token, patID, duration +func (_m *PATS) ResetSecret(ctx context.Context, token string, patID string, duration time.Duration) (auth.PAT, error) { + ret := _m.Called(ctx, token, patID, duration) + + if len(ret) == 0 { + panic("no return value specified for ResetSecret") + } + + var r0 auth.PAT + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, string, string, time.Duration) (auth.PAT, error)); ok { + return rf(ctx, token, patID, duration) + } + if rf, ok := ret.Get(0).(func(context.Context, string, string, time.Duration) auth.PAT); ok { + r0 = rf(ctx, token, patID, duration) + } else { + r0 = ret.Get(0).(auth.PAT) + } + + if rf, ok := ret.Get(1).(func(context.Context, string, string, time.Duration) error); ok { + r1 = rf(ctx, token, patID, duration) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// Retrieve provides a mock function with given fields: ctx, token, patID +func (_m *PATS) Retrieve(ctx context.Context, token string, patID string) (auth.PAT, error) { + ret := _m.Called(ctx, token, patID) + + if len(ret) == 0 { + panic("no return value specified for Retrieve") + } + + var r0 auth.PAT + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, string, string) (auth.PAT, error)); ok { + return rf(ctx, token, patID) + } + if rf, ok := ret.Get(0).(func(context.Context, string, string) auth.PAT); ok { + r0 = rf(ctx, token, patID) + } else { + r0 = ret.Get(0).(auth.PAT) + } + + if rf, ok := ret.Get(1).(func(context.Context, string, string) error); ok { + r1 = rf(ctx, token, patID) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// RevokeSecret provides a mock function with given fields: ctx, token, patID +func (_m *PATS) RevokeSecret(ctx context.Context, token string, patID string) error { + ret := _m.Called(ctx, token, patID) + + if len(ret) == 0 { + panic("no return value specified for RevokeSecret") + } + + var r0 error + if rf, ok := ret.Get(0).(func(context.Context, string, string) error); ok { + r0 = rf(ctx, token, patID) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// TestCheckScope provides a mock function with given fields: ctx, paToken, platformEntityType, optionalDomainID, optionalDomainEntityType, operation, entityIDs +func (_m *PATS) TestCheckScope(ctx context.Context, paToken string, platformEntityType auth.PlatformEntityType, optionalDomainID string, optionalDomainEntityType auth.DomainEntityType, operation auth.OperationType, entityIDs ...string) error { + _va := make([]interface{}, len(entityIDs)) + for _i := range entityIDs { + _va[_i] = entityIDs[_i] + } + var _ca []interface{} + _ca = append(_ca, ctx, paToken, platformEntityType, optionalDomainID, optionalDomainEntityType, operation) + _ca = append(_ca, _va...) + ret := _m.Called(_ca...) + + if len(ret) == 0 { + panic("no return value specified for TestCheckScope") + } + + var r0 error + if rf, ok := ret.Get(0).(func(context.Context, string, auth.PlatformEntityType, string, auth.DomainEntityType, auth.OperationType, ...string) error); ok { + r0 = rf(ctx, paToken, platformEntityType, optionalDomainID, optionalDomainEntityType, operation, entityIDs...) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// UpdateDescription provides a mock function with given fields: ctx, token, patID, description +func (_m *PATS) UpdateDescription(ctx context.Context, token string, patID string, description string) (auth.PAT, error) { + ret := _m.Called(ctx, token, patID, description) + + if len(ret) == 0 { + panic("no return value specified for UpdateDescription") + } + + var r0 auth.PAT + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, string, string, string) (auth.PAT, error)); ok { + return rf(ctx, token, patID, description) + } + if rf, ok := ret.Get(0).(func(context.Context, string, string, string) auth.PAT); ok { + r0 = rf(ctx, token, patID, description) + } else { + r0 = ret.Get(0).(auth.PAT) + } + + if rf, ok := ret.Get(1).(func(context.Context, string, string, string) error); ok { + r1 = rf(ctx, token, patID, description) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// UpdateName provides a mock function with given fields: ctx, token, patID, name +func (_m *PATS) UpdateName(ctx context.Context, token string, patID string, name string) (auth.PAT, error) { + ret := _m.Called(ctx, token, patID, name) + + if len(ret) == 0 { + panic("no return value specified for UpdateName") + } + + var r0 auth.PAT + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, string, string, string) (auth.PAT, error)); ok { + return rf(ctx, token, patID, name) + } + if rf, ok := ret.Get(0).(func(context.Context, string, string, string) auth.PAT); ok { + r0 = rf(ctx, token, patID, name) + } else { + r0 = ret.Get(0).(auth.PAT) + } + + if rf, ok := ret.Get(1).(func(context.Context, string, string, string) error); ok { + r1 = rf(ctx, token, patID, name) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// NewPATS creates a new instance of PATS. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +// The first argument is typically a *testing.T value. +func NewPATS(t interface { + mock.TestingT + Cleanup(func()) +}) *PATS { + mock := &PATS{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/auth/mocks/patsrepo.go b/auth/mocks/patsrepo.go new file mode 100644 index 0000000000..ff4a6cd382 --- /dev/null +++ b/auth/mocks/patsrepo.go @@ -0,0 +1,379 @@ +// Code generated by mockery v2.42.3. DO NOT EDIT. + +// Copyright (c) Abstract Machines + +package mocks + +import ( + context "context" + + auth "github.com/absmach/magistrala/auth" + + mock "github.com/stretchr/testify/mock" + + time "time" +) + +// PATSRepository is an autogenerated mock type for the PATSRepository type +type PATSRepository struct { + mock.Mock +} + +// AddScopeEntry provides a mock function with given fields: ctx, userID, patID, platformEntityType, optionalDomainID, optionalDomainEntityType, operation, entityIDs +func (_m *PATSRepository) AddScopeEntry(ctx context.Context, userID string, patID string, platformEntityType auth.PlatformEntityType, optionalDomainID string, optionalDomainEntityType auth.DomainEntityType, operation auth.OperationType, entityIDs ...string) (auth.Scope, error) { + _va := make([]interface{}, len(entityIDs)) + for _i := range entityIDs { + _va[_i] = entityIDs[_i] + } + var _ca []interface{} + _ca = append(_ca, ctx, userID, patID, platformEntityType, optionalDomainID, optionalDomainEntityType, operation) + _ca = append(_ca, _va...) + ret := _m.Called(_ca...) + + if len(ret) == 0 { + panic("no return value specified for AddScopeEntry") + } + + var r0 auth.Scope + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, string, string, auth.PlatformEntityType, string, auth.DomainEntityType, auth.OperationType, ...string) (auth.Scope, error)); ok { + return rf(ctx, userID, patID, platformEntityType, optionalDomainID, optionalDomainEntityType, operation, entityIDs...) + } + if rf, ok := ret.Get(0).(func(context.Context, string, string, auth.PlatformEntityType, string, auth.DomainEntityType, auth.OperationType, ...string) auth.Scope); ok { + r0 = rf(ctx, userID, patID, platformEntityType, optionalDomainID, optionalDomainEntityType, operation, entityIDs...) + } else { + r0 = ret.Get(0).(auth.Scope) + } + + if rf, ok := ret.Get(1).(func(context.Context, string, string, auth.PlatformEntityType, string, auth.DomainEntityType, auth.OperationType, ...string) error); ok { + r1 = rf(ctx, userID, patID, platformEntityType, optionalDomainID, optionalDomainEntityType, operation, entityIDs...) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// CheckScopeEntry provides a mock function with given fields: ctx, userID, patID, platformEntityType, optionalDomainID, optionalDomainEntityType, operation, entityIDs +func (_m *PATSRepository) CheckScopeEntry(ctx context.Context, userID string, patID string, platformEntityType auth.PlatformEntityType, optionalDomainID string, optionalDomainEntityType auth.DomainEntityType, operation auth.OperationType, entityIDs ...string) error { + _va := make([]interface{}, len(entityIDs)) + for _i := range entityIDs { + _va[_i] = entityIDs[_i] + } + var _ca []interface{} + _ca = append(_ca, ctx, userID, patID, platformEntityType, optionalDomainID, optionalDomainEntityType, operation) + _ca = append(_ca, _va...) + ret := _m.Called(_ca...) + + if len(ret) == 0 { + panic("no return value specified for CheckScopeEntry") + } + + var r0 error + if rf, ok := ret.Get(0).(func(context.Context, string, string, auth.PlatformEntityType, string, auth.DomainEntityType, auth.OperationType, ...string) error); ok { + r0 = rf(ctx, userID, patID, platformEntityType, optionalDomainID, optionalDomainEntityType, operation, entityIDs...) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// Remove provides a mock function with given fields: ctx, userID, patID +func (_m *PATSRepository) Remove(ctx context.Context, userID string, patID string) error { + ret := _m.Called(ctx, userID, patID) + + if len(ret) == 0 { + panic("no return value specified for Remove") + } + + var r0 error + if rf, ok := ret.Get(0).(func(context.Context, string, string) error); ok { + r0 = rf(ctx, userID, patID) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// RemoveAllScopeEntry provides a mock function with given fields: ctx, userID, patID +func (_m *PATSRepository) RemoveAllScopeEntry(ctx context.Context, userID string, patID string) error { + ret := _m.Called(ctx, userID, patID) + + if len(ret) == 0 { + panic("no return value specified for RemoveAllScopeEntry") + } + + var r0 error + if rf, ok := ret.Get(0).(func(context.Context, string, string) error); ok { + r0 = rf(ctx, userID, patID) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// RemoveScopeEntry provides a mock function with given fields: ctx, userID, patID, platformEntityType, optionalDomainID, optionalDomainEntityType, operation, entityIDs +func (_m *PATSRepository) RemoveScopeEntry(ctx context.Context, userID string, patID string, platformEntityType auth.PlatformEntityType, optionalDomainID string, optionalDomainEntityType auth.DomainEntityType, operation auth.OperationType, entityIDs ...string) (auth.Scope, error) { + _va := make([]interface{}, len(entityIDs)) + for _i := range entityIDs { + _va[_i] = entityIDs[_i] + } + var _ca []interface{} + _ca = append(_ca, ctx, userID, patID, platformEntityType, optionalDomainID, optionalDomainEntityType, operation) + _ca = append(_ca, _va...) + ret := _m.Called(_ca...) + + if len(ret) == 0 { + panic("no return value specified for RemoveScopeEntry") + } + + var r0 auth.Scope + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, string, string, auth.PlatformEntityType, string, auth.DomainEntityType, auth.OperationType, ...string) (auth.Scope, error)); ok { + return rf(ctx, userID, patID, platformEntityType, optionalDomainID, optionalDomainEntityType, operation, entityIDs...) + } + if rf, ok := ret.Get(0).(func(context.Context, string, string, auth.PlatformEntityType, string, auth.DomainEntityType, auth.OperationType, ...string) auth.Scope); ok { + r0 = rf(ctx, userID, patID, platformEntityType, optionalDomainID, optionalDomainEntityType, operation, entityIDs...) + } else { + r0 = ret.Get(0).(auth.Scope) + } + + if rf, ok := ret.Get(1).(func(context.Context, string, string, auth.PlatformEntityType, string, auth.DomainEntityType, auth.OperationType, ...string) error); ok { + r1 = rf(ctx, userID, patID, platformEntityType, optionalDomainID, optionalDomainEntityType, operation, entityIDs...) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// Retrieve provides a mock function with given fields: ctx, userID, patID +func (_m *PATSRepository) Retrieve(ctx context.Context, userID string, patID string) (auth.PAT, error) { + ret := _m.Called(ctx, userID, patID) + + if len(ret) == 0 { + panic("no return value specified for Retrieve") + } + + var r0 auth.PAT + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, string, string) (auth.PAT, error)); ok { + return rf(ctx, userID, patID) + } + if rf, ok := ret.Get(0).(func(context.Context, string, string) auth.PAT); ok { + r0 = rf(ctx, userID, patID) + } else { + r0 = ret.Get(0).(auth.PAT) + } + + if rf, ok := ret.Get(1).(func(context.Context, string, string) error); ok { + r1 = rf(ctx, userID, patID) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// RetrieveAll provides a mock function with given fields: ctx, userID +func (_m *PATSRepository) RetrieveAll(ctx context.Context, userID string) (auth.PATSPage, error) { + ret := _m.Called(ctx, userID) + + if len(ret) == 0 { + panic("no return value specified for RetrieveAll") + } + + var r0 auth.PATSPage + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, string) (auth.PATSPage, error)); ok { + return rf(ctx, userID) + } + if rf, ok := ret.Get(0).(func(context.Context, string) auth.PATSPage); ok { + r0 = rf(ctx, userID) + } else { + r0 = ret.Get(0).(auth.PATSPage) + } + + if rf, ok := ret.Get(1).(func(context.Context, string) error); ok { + r1 = rf(ctx, userID) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// Revoke provides a mock function with given fields: ctx, userID, patID +func (_m *PATSRepository) Revoke(ctx context.Context, userID string, patID string) error { + ret := _m.Called(ctx, userID, patID) + + if len(ret) == 0 { + panic("no return value specified for Revoke") + } + + var r0 error + if rf, ok := ret.Get(0).(func(context.Context, string, string) error); ok { + r0 = rf(ctx, userID, patID) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// Save provides a mock function with given fields: ctx, pat +func (_m *PATSRepository) Save(ctx context.Context, pat auth.PAT) (string, error) { + ret := _m.Called(ctx, pat) + + if len(ret) == 0 { + panic("no return value specified for Save") + } + + var r0 string + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, auth.PAT) (string, error)); ok { + return rf(ctx, pat) + } + if rf, ok := ret.Get(0).(func(context.Context, auth.PAT) string); ok { + r0 = rf(ctx, pat) + } else { + r0 = ret.Get(0).(string) + } + + if rf, ok := ret.Get(1).(func(context.Context, auth.PAT) error); ok { + r1 = rf(ctx, pat) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// UpdateDescription provides a mock function with given fields: ctx, userID, patID, description +func (_m *PATSRepository) UpdateDescription(ctx context.Context, userID string, patID string, description string) (auth.PAT, error) { + ret := _m.Called(ctx, userID, patID, description) + + if len(ret) == 0 { + panic("no return value specified for UpdateDescription") + } + + var r0 auth.PAT + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, string, string, string) (auth.PAT, error)); ok { + return rf(ctx, userID, patID, description) + } + if rf, ok := ret.Get(0).(func(context.Context, string, string, string) auth.PAT); ok { + r0 = rf(ctx, userID, patID, description) + } else { + r0 = ret.Get(0).(auth.PAT) + } + + if rf, ok := ret.Get(1).(func(context.Context, string, string, string) error); ok { + r1 = rf(ctx, userID, patID, description) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// UpdateLastUsed provides a mock function with given fields: ctx, token, patID, description +func (_m *PATSRepository) UpdateLastUsed(ctx context.Context, token string, patID string, description string) (auth.PAT, error) { + ret := _m.Called(ctx, token, patID, description) + + if len(ret) == 0 { + panic("no return value specified for UpdateLastUsed") + } + + var r0 auth.PAT + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, string, string, string) (auth.PAT, error)); ok { + return rf(ctx, token, patID, description) + } + if rf, ok := ret.Get(0).(func(context.Context, string, string, string) auth.PAT); ok { + r0 = rf(ctx, token, patID, description) + } else { + r0 = ret.Get(0).(auth.PAT) + } + + if rf, ok := ret.Get(1).(func(context.Context, string, string, string) error); ok { + r1 = rf(ctx, token, patID, description) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// UpdateName provides a mock function with given fields: ctx, userID, patID, name +func (_m *PATSRepository) UpdateName(ctx context.Context, userID string, patID string, name string) (auth.PAT, error) { + ret := _m.Called(ctx, userID, patID, name) + + if len(ret) == 0 { + panic("no return value specified for UpdateName") + } + + var r0 auth.PAT + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, string, string, string) (auth.PAT, error)); ok { + return rf(ctx, userID, patID, name) + } + if rf, ok := ret.Get(0).(func(context.Context, string, string, string) auth.PAT); ok { + r0 = rf(ctx, userID, patID, name) + } else { + r0 = ret.Get(0).(auth.PAT) + } + + if rf, ok := ret.Get(1).(func(context.Context, string, string, string) error); ok { + r1 = rf(ctx, userID, patID, name) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// UpdateTokenHash provides a mock function with given fields: ctx, userID, patID, tokenHash, expiryAt +func (_m *PATSRepository) UpdateTokenHash(ctx context.Context, userID string, patID string, tokenHash string, expiryAt time.Time) (auth.PAT, error) { + ret := _m.Called(ctx, userID, patID, tokenHash, expiryAt) + + if len(ret) == 0 { + panic("no return value specified for UpdateTokenHash") + } + + var r0 auth.PAT + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, string, string, string, time.Time) (auth.PAT, error)); ok { + return rf(ctx, userID, patID, tokenHash, expiryAt) + } + if rf, ok := ret.Get(0).(func(context.Context, string, string, string, time.Time) auth.PAT); ok { + r0 = rf(ctx, userID, patID, tokenHash, expiryAt) + } else { + r0 = ret.Get(0).(auth.PAT) + } + + if rf, ok := ret.Get(1).(func(context.Context, string, string, string, time.Time) error); ok { + r1 = rf(ctx, userID, patID, tokenHash, expiryAt) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// NewPATSRepository creates a new instance of PATSRepository. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +// The first argument is typically a *testing.T value. +func NewPATSRepository(t interface { + mock.TestingT + Cleanup(func()) +}) *PATSRepository { + mock := &PATSRepository{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/auth/mocks/service.go b/auth/mocks/service.go index 45d3b3d7f9..80d1d19602 100644 --- a/auth/mocks/service.go +++ b/auth/mocks/service.go @@ -10,6 +10,8 @@ import ( auth "github.com/absmach/magistrala/auth" mock "github.com/stretchr/testify/mock" + + time "time" ) // Service is an autogenerated mock type for the Service type @@ -53,6 +55,41 @@ func (_m *Service) AddPolicy(ctx context.Context, pr auth.PolicyReq) error { return r0 } +// AddScope provides a mock function with given fields: ctx, token, patID, platformEntityType, optionalDomainID, optionalDomainEntityType, operation, entityIDs +func (_m *Service) AddScope(ctx context.Context, token string, patID string, platformEntityType auth.PlatformEntityType, optionalDomainID string, optionalDomainEntityType auth.DomainEntityType, operation auth.OperationType, entityIDs ...string) (auth.Scope, error) { + _va := make([]interface{}, len(entityIDs)) + for _i := range entityIDs { + _va[_i] = entityIDs[_i] + } + var _ca []interface{} + _ca = append(_ca, ctx, token, patID, platformEntityType, optionalDomainID, optionalDomainEntityType, operation) + _ca = append(_ca, _va...) + ret := _m.Called(_ca...) + + if len(ret) == 0 { + panic("no return value specified for AddScope") + } + + var r0 auth.Scope + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, string, string, auth.PlatformEntityType, string, auth.DomainEntityType, auth.OperationType, ...string) (auth.Scope, error)); ok { + return rf(ctx, token, patID, platformEntityType, optionalDomainID, optionalDomainEntityType, operation, entityIDs...) + } + if rf, ok := ret.Get(0).(func(context.Context, string, string, auth.PlatformEntityType, string, auth.DomainEntityType, auth.OperationType, ...string) auth.Scope); ok { + r0 = rf(ctx, token, patID, platformEntityType, optionalDomainID, optionalDomainEntityType, operation, entityIDs...) + } else { + r0 = ret.Get(0).(auth.Scope) + } + + if rf, ok := ret.Get(1).(func(context.Context, string, string, auth.PlatformEntityType, string, auth.DomainEntityType, auth.OperationType, ...string) error); ok { + r1 = rf(ctx, token, patID, platformEntityType, optionalDomainID, optionalDomainEntityType, operation, entityIDs...) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + // AssignUsers provides a mock function with given fields: ctx, token, id, userIds, relation func (_m *Service) AssignUsers(ctx context.Context, token string, id string, userIds []string, relation string) error { ret := _m.Called(ctx, token, id, userIds, relation) @@ -89,6 +126,34 @@ func (_m *Service) Authorize(ctx context.Context, pr auth.PolicyReq) error { return r0 } +// AuthorizePAT provides a mock function with given fields: ctx, paToken +func (_m *Service) AuthorizePAT(ctx context.Context, paToken string) (auth.PAT, error) { + ret := _m.Called(ctx, paToken) + + if len(ret) == 0 { + panic("no return value specified for AuthorizePAT") + } + + var r0 auth.PAT + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, string) (auth.PAT, error)); ok { + return rf(ctx, paToken) + } + if rf, ok := ret.Get(0).(func(context.Context, string) auth.PAT); ok { + r0 = rf(ctx, paToken) + } else { + r0 = ret.Get(0).(auth.PAT) + } + + if rf, ok := ret.Get(1).(func(context.Context, string) error); ok { + r1 = rf(ctx, paToken) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + // ChangeDomainStatus provides a mock function with given fields: ctx, token, id, d func (_m *Service) ChangeDomainStatus(ctx context.Context, token string, id string, d auth.DomainReq) (auth.Domain, error) { ret := _m.Called(ctx, token, id, d) @@ -117,6 +182,24 @@ func (_m *Service) ChangeDomainStatus(ctx context.Context, token string, id stri return r0, r1 } +// ClearAllScope provides a mock function with given fields: ctx, token, patID +func (_m *Service) ClearAllScope(ctx context.Context, token string, patID string) error { + ret := _m.Called(ctx, token, patID) + + if len(ret) == 0 { + panic("no return value specified for ClearAllScope") + } + + var r0 error + if rf, ok := ret.Get(0).(func(context.Context, string, string) error); ok { + r0 = rf(ctx, token, patID) + } else { + r0 = ret.Error(0) + } + + return r0 +} + // CountObjects provides a mock function with given fields: ctx, pr func (_m *Service) CountObjects(ctx context.Context, pr auth.PolicyReq) (uint64, error) { ret := _m.Called(ctx, pr) @@ -173,6 +256,34 @@ func (_m *Service) CountSubjects(ctx context.Context, pr auth.PolicyReq) (uint64 return r0, r1 } +// Create provides a mock function with given fields: ctx, token, name, description, duration, scope +func (_m *Service) Create(ctx context.Context, token string, name string, description string, duration time.Duration, scope auth.Scope) (auth.PAT, error) { + ret := _m.Called(ctx, token, name, description, duration, scope) + + if len(ret) == 0 { + panic("no return value specified for Create") + } + + var r0 auth.PAT + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, string, string, string, time.Duration, auth.Scope) (auth.PAT, error)); ok { + return rf(ctx, token, name, description, duration, scope) + } + if rf, ok := ret.Get(0).(func(context.Context, string, string, string, time.Duration, auth.Scope) auth.PAT); ok { + r0 = rf(ctx, token, name, description, duration, scope) + } else { + r0 = ret.Get(0).(auth.PAT) + } + + if rf, ok := ret.Get(1).(func(context.Context, string, string, string, time.Duration, auth.Scope) error); ok { + r1 = rf(ctx, token, name, description, duration, scope) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + // CreateDomain provides a mock function with given fields: ctx, token, d func (_m *Service) CreateDomain(ctx context.Context, token string, d auth.Domain) (auth.Domain, error) { ret := _m.Called(ctx, token, d) @@ -219,6 +330,24 @@ func (_m *Service) DeleteEntityPolicies(ctx context.Context, entityType string, return r0 } +// Delete provides a mock function with given fields: ctx, token, patID +func (_m *Service) Delete(ctx context.Context, token string, patID string) error { + ret := _m.Called(ctx, token, patID) + + if len(ret) == 0 { + panic("no return value specified for Delete") + } + + var r0 error + if rf, ok := ret.Get(0).(func(context.Context, string, string) error); ok { + r0 = rf(ctx, token, patID) + } else { + r0 = ret.Error(0) + } + + return r0 +} + // DeletePolicies provides a mock function with given fields: ctx, prs func (_m *Service) DeletePolicies(ctx context.Context, prs []auth.PolicyReq) error { ret := _m.Called(ctx, prs) @@ -283,6 +412,34 @@ func (_m *Service) Identify(ctx context.Context, token string) (auth.Key, error) return r0, r1 } +// IdentifyPAT provides a mock function with given fields: ctx, paToken +func (_m *Service) IdentifyPAT(ctx context.Context, paToken string) (auth.PAT, error) { + ret := _m.Called(ctx, paToken) + + if len(ret) == 0 { + panic("no return value specified for IdentifyPAT") + } + + var r0 auth.PAT + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, string) (auth.PAT, error)); ok { + return rf(ctx, paToken) + } + if rf, ok := ret.Get(0).(func(context.Context, string) auth.PAT); ok { + r0 = rf(ctx, paToken) + } else { + r0 = ret.Get(0).(auth.PAT) + } + + if rf, ok := ret.Get(1).(func(context.Context, string) error); ok { + r1 = rf(ctx, paToken) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + // Issue provides a mock function with given fields: ctx, token, key func (_m *Service) Issue(ctx context.Context, token string, key auth.Key) (auth.Token, error) { ret := _m.Called(ctx, token, key) @@ -311,6 +468,34 @@ func (_m *Service) Issue(ctx context.Context, token string, key auth.Key) (auth. return r0, r1 } +// List provides a mock function with given fields: ctx, token +func (_m *Service) List(ctx context.Context, token string) (auth.PATSPage, error) { + ret := _m.Called(ctx, token) + + if len(ret) == 0 { + panic("no return value specified for List") + } + + var r0 auth.PATSPage + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, string) (auth.PATSPage, error)); ok { + return rf(ctx, token) + } + if rf, ok := ret.Get(0).(func(context.Context, string) auth.PATSPage); ok { + r0 = rf(ctx, token) + } else { + r0 = ret.Get(0).(auth.PATSPage) + } + + if rf, ok := ret.Get(1).(func(context.Context, string) error); ok { + r1 = rf(ctx, token) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + // ListAllObjects provides a mock function with given fields: ctx, pr func (_m *Service) ListAllObjects(ctx context.Context, pr auth.PolicyReq) (auth.PolicyPage, error) { ret := _m.Called(ctx, pr) @@ -509,6 +694,97 @@ func (_m *Service) ListUserDomains(ctx context.Context, token string, userID str return r0, r1 } +// RemoveScope provides a mock function with given fields: ctx, token, patID, platformEntityType, optionalDomainID, optionalDomainEntityType, operation, entityIDs +func (_m *Service) RemoveScope(ctx context.Context, token string, patID string, platformEntityType auth.PlatformEntityType, optionalDomainID string, optionalDomainEntityType auth.DomainEntityType, operation auth.OperationType, entityIDs ...string) (auth.Scope, error) { + _va := make([]interface{}, len(entityIDs)) + for _i := range entityIDs { + _va[_i] = entityIDs[_i] + } + var _ca []interface{} + _ca = append(_ca, ctx, token, patID, platformEntityType, optionalDomainID, optionalDomainEntityType, operation) + _ca = append(_ca, _va...) + ret := _m.Called(_ca...) + + if len(ret) == 0 { + panic("no return value specified for RemoveScope") + } + + var r0 auth.Scope + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, string, string, auth.PlatformEntityType, string, auth.DomainEntityType, auth.OperationType, ...string) (auth.Scope, error)); ok { + return rf(ctx, token, patID, platformEntityType, optionalDomainID, optionalDomainEntityType, operation, entityIDs...) + } + if rf, ok := ret.Get(0).(func(context.Context, string, string, auth.PlatformEntityType, string, auth.DomainEntityType, auth.OperationType, ...string) auth.Scope); ok { + r0 = rf(ctx, token, patID, platformEntityType, optionalDomainID, optionalDomainEntityType, operation, entityIDs...) + } else { + r0 = ret.Get(0).(auth.Scope) + } + + if rf, ok := ret.Get(1).(func(context.Context, string, string, auth.PlatformEntityType, string, auth.DomainEntityType, auth.OperationType, ...string) error); ok { + r1 = rf(ctx, token, patID, platformEntityType, optionalDomainID, optionalDomainEntityType, operation, entityIDs...) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// ResetSecret provides a mock function with given fields: ctx, token, patID, duration +func (_m *Service) ResetSecret(ctx context.Context, token string, patID string, duration time.Duration) (auth.PAT, error) { + ret := _m.Called(ctx, token, patID, duration) + + if len(ret) == 0 { + panic("no return value specified for ResetSecret") + } + + var r0 auth.PAT + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, string, string, time.Duration) (auth.PAT, error)); ok { + return rf(ctx, token, patID, duration) + } + if rf, ok := ret.Get(0).(func(context.Context, string, string, time.Duration) auth.PAT); ok { + r0 = rf(ctx, token, patID, duration) + } else { + r0 = ret.Get(0).(auth.PAT) + } + + if rf, ok := ret.Get(1).(func(context.Context, string, string, time.Duration) error); ok { + r1 = rf(ctx, token, patID, duration) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// Retrieve provides a mock function with given fields: ctx, token, patID +func (_m *Service) Retrieve(ctx context.Context, token string, patID string) (auth.PAT, error) { + ret := _m.Called(ctx, token, patID) + + if len(ret) == 0 { + panic("no return value specified for Retrieve") + } + + var r0 auth.PAT + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, string, string) (auth.PAT, error)); ok { + return rf(ctx, token, patID) + } + if rf, ok := ret.Get(0).(func(context.Context, string, string) auth.PAT); ok { + r0 = rf(ctx, token, patID) + } else { + r0 = ret.Get(0).(auth.PAT) + } + + if rf, ok := ret.Get(1).(func(context.Context, string, string) error); ok { + r1 = rf(ctx, token, patID) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + // RetrieveDomain provides a mock function with given fields: ctx, token, id func (_m *Service) RetrieveDomain(ctx context.Context, token string, id string) (auth.Domain, error) { ret := _m.Called(ctx, token, id) @@ -613,6 +889,92 @@ func (_m *Service) Revoke(ctx context.Context, token string, id string) error { return r0 } +// RevokeSecret provides a mock function with given fields: ctx, token, patID +func (_m *Service) RevokeSecret(ctx context.Context, token string, patID string) error { + ret := _m.Called(ctx, token, patID) + + if len(ret) == 0 { + panic("no return value specified for RevokeSecret") + } + + var r0 error + if rf, ok := ret.Get(0).(func(context.Context, string, string) error); ok { + r0 = rf(ctx, token, patID) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// TestCheckScope provides a mock function with given fields: ctx, paToken, platformEntityType, optionalDomainID, optionalDomainEntityType, operation, entityIDs +func (_m *Service) TestCheckScope(ctx context.Context, paToken string, platformEntityType auth.PlatformEntityType, optionalDomainID string, optionalDomainEntityType auth.DomainEntityType, operation auth.OperationType, entityIDs ...string) error { + _va := make([]interface{}, len(entityIDs)) + for _i := range entityIDs { + _va[_i] = entityIDs[_i] + } + var _ca []interface{} + _ca = append(_ca, ctx, paToken, platformEntityType, optionalDomainID, optionalDomainEntityType, operation) + _ca = append(_ca, _va...) + ret := _m.Called(_ca...) + + if len(ret) == 0 { + panic("no return value specified for TestCheckScope") + } + + var r0 error + if rf, ok := ret.Get(0).(func(context.Context, string, auth.PlatformEntityType, string, auth.DomainEntityType, auth.OperationType, ...string) error); ok { + r0 = rf(ctx, paToken, platformEntityType, optionalDomainID, optionalDomainEntityType, operation, entityIDs...) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// RevokeSecret provides a mock function with given fields: ctx, token, patID +func (_m *Service) RevokeSecret(ctx context.Context, token string, patID string) error { + ret := _m.Called(ctx, token, patID) + + if len(ret) == 0 { + panic("no return value specified for RevokeSecret") + } + + var r0 error + if rf, ok := ret.Get(0).(func(context.Context, string, string) error); ok { + r0 = rf(ctx, token, patID) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// TestCheckScope provides a mock function with given fields: ctx, paToken, platformEntityType, optionalDomainID, optionalDomainEntityType, operation, entityIDs +func (_m *Service) TestCheckScope(ctx context.Context, paToken string, platformEntityType auth.PlatformEntityType, optionalDomainID string, optionalDomainEntityType auth.DomainEntityType, operation auth.OperationType, entityIDs ...string) error { + _va := make([]interface{}, len(entityIDs)) + for _i := range entityIDs { + _va[_i] = entityIDs[_i] + } + var _ca []interface{} + _ca = append(_ca, ctx, paToken, platformEntityType, optionalDomainID, optionalDomainEntityType, operation) + _ca = append(_ca, _va...) + ret := _m.Called(_ca...) + + if len(ret) == 0 { + panic("no return value specified for TestCheckScope") + } + + var r0 error + if rf, ok := ret.Get(0).(func(context.Context, string, auth.PlatformEntityType, string, auth.DomainEntityType, auth.OperationType, ...string) error); ok { + r0 = rf(ctx, paToken, platformEntityType, optionalDomainID, optionalDomainEntityType, operation, entityIDs...) + } else { + r0 = ret.Error(0) + } + + return r0 +} + // UnassignUser provides a mock function with given fields: ctx, token, id, userID func (_m *Service) UnassignUser(ctx context.Context, token string, id string, userID string) error { ret := _m.Called(ctx, token, id, userID) @@ -631,6 +993,34 @@ func (_m *Service) UnassignUser(ctx context.Context, token string, id string, us return r0 } +// UpdateDescription provides a mock function with given fields: ctx, token, patID, description +func (_m *Service) UpdateDescription(ctx context.Context, token string, patID string, description string) (auth.PAT, error) { + ret := _m.Called(ctx, token, patID, description) + + if len(ret) == 0 { + panic("no return value specified for UpdateDescription") + } + + var r0 auth.PAT + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, string, string, string) (auth.PAT, error)); ok { + return rf(ctx, token, patID, description) + } + if rf, ok := ret.Get(0).(func(context.Context, string, string, string) auth.PAT); ok { + r0 = rf(ctx, token, patID, description) + } else { + r0 = ret.Get(0).(auth.PAT) + } + + if rf, ok := ret.Get(1).(func(context.Context, string, string, string) error); ok { + r1 = rf(ctx, token, patID, description) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + // UpdateDomain provides a mock function with given fields: ctx, token, id, d func (_m *Service) UpdateDomain(ctx context.Context, token string, id string, d auth.DomainReq) (auth.Domain, error) { ret := _m.Called(ctx, token, id, d) @@ -659,6 +1049,34 @@ func (_m *Service) UpdateDomain(ctx context.Context, token string, id string, d return r0, r1 } +// UpdateName provides a mock function with given fields: ctx, token, patID, name +func (_m *Service) UpdateName(ctx context.Context, token string, patID string, name string) (auth.PAT, error) { + ret := _m.Called(ctx, token, patID, name) + + if len(ret) == 0 { + panic("no return value specified for UpdateName") + } + + var r0 auth.PAT + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, string, string, string) (auth.PAT, error)); ok { + return rf(ctx, token, patID, name) + } + if rf, ok := ret.Get(0).(func(context.Context, string, string, string) auth.PAT); ok { + r0 = rf(ctx, token, patID, name) + } else { + r0 = ret.Get(0).(auth.PAT) + } + + if rf, ok := ret.Get(1).(func(context.Context, string, string, string) error); ok { + r1 = rf(ctx, token, patID, name) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + // NewService creates a new instance of Service. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. // The first argument is typically a *testing.T value. func NewService(t interface { diff --git a/auth/pat.go b/auth/pat.go index 88ea966dff..dff1da67e3 100644 --- a/auth/pat.go +++ b/auth/pat.go @@ -21,6 +21,23 @@ const ( DeleteOp ) +func (ot OperationType) ValidString() (string, error) { + switch ot { + case CreateOp: + return "create", nil + case ReadOp: + return "read", nil + case ListOp: + return "list", nil + case UpdateOp: + return "update", nil + case DeleteOp: + return "delete", nil + default: + return "", fmt.Errorf("unknown operation type %d", ot) + } +} + func (ot OperationType) String() string { switch ot { case CreateOp: @@ -79,6 +96,21 @@ const ( DomainNullScope ) +func (det DomainEntityType) ValidString() (string, error) { + switch det { + case DomainManagementScope: + return "domain_management", nil + case DomainGroupsScope: + return "groups", nil + case DomainChannelsScope: + return "channels", nil + case DomainThingsScope: + return "things", nil + default: + return "", fmt.Errorf("unknown domain entity type %d", det) + } +} + func (det DomainEntityType) String() string { switch det { case DomainManagementScope: @@ -130,6 +162,17 @@ const ( PlatformDomainsScope ) +func (pet PlatformEntityType) ValidString() (string, error) { + switch pet { + case PlatformUsersScope: + return "users", nil + case PlatformDomainsScope: + return "domains", nil + default: + return "", fmt.Errorf("unknown platform entity type %d", pet) + } +} + func (pet PlatformEntityType) String() string { switch pet { case PlatformUsersScope: @@ -168,17 +211,26 @@ func (pet *PlatformEntityType) UnmarshalText(data []byte) (err error) { // ScopeValue interface for Any entity ids or for sets of entity ids. type ScopeValue interface { Contains(id string) bool + Values() []string } // AnyIDs implements ScopeValue for any entity id value. type AnyIDs struct{} func (s AnyIDs) Contains(id string) bool { return true } +func (s AnyIDs) Values() []string { return []string{"*"} } // SelectedIDs implements ScopeValue for sets of entity ids. type SelectedIDs map[string]struct{} func (s SelectedIDs) Contains(id string) bool { _, ok := s[id]; return ok } +func (s SelectedIDs) Values() []string { + values := []string{} + for value := range s { + values = append(values, value) + } + return values +} // OperationScope contains map of OperationType with value of AnyIDs or SelectedIDs. type OperationScope struct { @@ -536,8 +588,8 @@ type PAT struct { ID string `json:"id,omitempty"` User string `json:"user,omitempty"` Name string `json:"name,omitempty"` - Description string `json:"Description,omitempty"` - Token string `json:"Token,omitempty"` + Description string `json:"description,omitempty"` + Token string `json:"token,omitempty"` Scope Scope `json:"scope,omitempty"` IssuedAt time.Time `json:"issued_at,omitempty"` ExpiresAt time.Time `json:"expires_at,omitempty"` @@ -619,7 +671,7 @@ type PATS interface { //go:generate mockery --name PATSRepository --output=./mocks --filename patsrepo.go --quiet --note "Copyright (c) Abstract Machines" type PATSRepository interface { // Save persists the PAT - Save(ctx context.Context, pat PAT) (id string, err error) + Save(ctx context.Context, pat PAT) (err error) // Retrieve retrieves users PAT by its unique identifier. Retrieve(ctx context.Context, userID, patID string) (pat PAT, err error) @@ -633,9 +685,6 @@ type PATSRepository interface { // UpdateTokenHash updates the token hash of a PAT. UpdateTokenHash(ctx context.Context, userID, patID, tokenHash string, expiryAt time.Time) (PAT, error) - // UpdateLastUsed updates the last used details of a PAT. - UpdateLastUsed(ctx context.Context, token, patID, description string) (PAT, error) - // RetrieveAll retrieves all PATs belongs to userID. RetrieveAll(ctx context.Context, userID string) (pats PATSPage, err error) diff --git a/auth/service.go b/auth/service.go index 19e84b153f..691087ef87 100644 --- a/auth/service.go +++ b/auth/service.go @@ -1091,13 +1091,12 @@ func (svc service) Create(ctx context.Context, token, name, description string, pat := PAT{ User: key.User, } - id, err := svc.pats.Save(ctx, pat) - if err != nil { + if err := svc.pats.Save(ctx, pat); err != nil { return PAT{}, errors.Wrap(errCreatePAT, err) } - pat.ID = id return pat, nil } + func (svc service) UpdateName(ctx context.Context, token, patID, name string) (PAT, error) { key, err := svc.Identify(ctx, token) if err != nil { diff --git a/cmd/test/patrepo/main.go b/cmd/test/patrepo/main.go new file mode 100644 index 0000000000..ca6b41f802 --- /dev/null +++ b/cmd/test/patrepo/main.go @@ -0,0 +1,71 @@ +package main + +import ( + "context" + "fmt" + "time" + + "github.com/absmach/magistrala/auth" + "github.com/absmach/magistrala/auth/bolt" + boltclient "github.com/absmach/magistrala/internal/clients/bolt" + "github.com/caarlos0/env/v10" + "github.com/google/uuid" +) + +func main() { + + boltDBConfig := boltclient.Config{} + if err := env.ParseWithOptions(&boltDBConfig, env.Options{}); err != nil { + panic(err) + } + + client, err := boltclient.Connect(boltDBConfig, bolt.Init) + if err != nil { + panic(err) + } + defer client.Close() + + patrepo := bolt.NewPATSRepository(client, boltDBConfig.Bucket) + + pat := auth.PAT{ + ID: uuid.New().String(), + User: "user123", + Name: "user 123", + IssuedAt: time.Now(), + ExpiresAt: time.Now().Add(24 * time.Hour), + Scope: auth.Scope{ + Users: auth.OperationScope{ + Operations: map[auth.OperationType]auth.ScopeValue{ + auth.ReadOp: auth.AnyIDs{}, + }, + }, + Domains: map[string]auth.DomainScope{ + "domain_1": { + DomainManagement: auth.OperationScope{ + Operations: map[auth.OperationType]auth.ScopeValue{ + auth.ReadOp: auth.AnyIDs{}, + }, + }, + Entities: map[auth.DomainEntityType]auth.OperationScope{ + auth.DomainGroupsScope: { + Operations: map[auth.OperationType]auth.ScopeValue{ + auth.ReadOp: auth.SelectedIDs{"group_1": {}, "group_2": {}}, + }, + }, + }, + }, + }, + }, + } + + if err := patrepo.Save(context.Background(), pat); err != nil { + panic(err) + } + + rPAT, err := patrepo.Retrieve(context.Background(), pat.User, pat.ID) + if err != nil { + panic(err) + } + fmt.Println(rPAT.String()) + +} diff --git a/cmd/test/stringsbench/concat_test.go b/cmd/test/stringsbench/concat_test.go new file mode 100644 index 0000000000..fdda6684fb --- /dev/null +++ b/cmd/test/stringsbench/concat_test.go @@ -0,0 +1,34 @@ +package main + +import ( + "strings" + "testing" +) + +func concatUsingPlus(n int) string { + s := "" + for i := 0; i < n; i++ { + s += "a" + } + return s +} + +func concatUsingBuilder(n int) string { + var builder strings.Builder + for i := 0; i < n; i++ { + builder.WriteString("a") + } + return builder.String() +} + +func BenchmarkConcatUsingPlus(b *testing.B) { + for i := 0; i < b.N; i++ { + concatUsingPlus(1000) + } +} + +func BenchmarkConcatUsingBuilder(b *testing.B) { + for i := 0; i < b.N; i++ { + concatUsingBuilder(1000) + } +} diff --git a/internal/clients/bolt/bolt.go b/internal/clients/bolt/bolt.go new file mode 100644 index 0000000000..845e6f395e --- /dev/null +++ b/internal/clients/bolt/bolt.go @@ -0,0 +1,82 @@ +// Copyright (c) Abstract Machines +// SPDX-License-Identifier: Apache-2.0 + +package bolt + +import ( + "io/fs" + "strconv" + "time" + + "github.com/absmach/magistrala/pkg/errors" + "github.com/caarlos0/env/v10" + + bolt "go.etcd.io/bbolt" +) + +var ( + errConfig = errors.New("failed to load BoltDB configuration") + errConnect = errors.New("failed to connect to BoltDB database") + errInit = errors.New("failed to initialize to BoltDB database") +) + +type FileMode fs.FileMode + +func (fm *FileMode) UnmarshalText(text []byte) error { + temp, err := strconv.ParseUint(string(text), 8, 32) + if err != nil { + return err + } + *fm = FileMode(temp) + return nil +} + +// Config contains BoltDB specific parameters. +type Config struct { + FilePath string `env:"FILE_PATH" envDefault:"./bolt.db"` + FileMode FileMode `env:"FILE_MODE" envDefault:"0600"` + Bucket string `env:"BUCKET" envDefault:"magistrala"` + Timeout time.Duration `env:"TIMEOUT" envDefault:"0"` +} + +// Setup load configuration from environment and creates new BoltDB. +func Setup(envPrefix string, initFn func(*bolt.Tx, string) error) (*bolt.DB, error) { + return SetupDB(envPrefix, initFn) +} + +// SetupDB load configuration from environment, +func SetupDB(envPrefix string, initFn func(*bolt.Tx, string) error) (*bolt.DB, error) { + cfg := Config{} + if err := env.ParseWithOptions(&cfg, env.Options{Prefix: envPrefix}); err != nil { + return nil, errors.Wrap(errConfig, err) + } + bdb, err := Connect(cfg, initFn) + if err != nil { + return nil, err + } + + return bdb, nil +} + +// Connect establishes connection to the BoltDB. +func Connect(cfg Config, initFn func(*bolt.Tx, string) error) (*bolt.DB, error) { + db, err := bolt.Open(cfg.FilePath, fs.FileMode(cfg.FileMode), nil) + if err != nil { + return nil, errors.Wrap(errConnect, err) + } + if initFn != nil { + if err := Init(db, cfg, initFn); err != nil { + return nil, err + } + } + return db, nil +} + +func Init(db *bolt.DB, cfg Config, initFn func(*bolt.Tx, string) error) error { + if err := db.Update(func(tx *bolt.Tx) error { + return initFn(tx, cfg.Bucket) + }); err != nil { + return errors.Wrap(errInit, err) + } + return nil +} diff --git a/internal/clients/bolt/doc.go b/internal/clients/bolt/doc.go new file mode 100644 index 0000000000..3941091882 --- /dev/null +++ b/internal/clients/bolt/doc.go @@ -0,0 +1,9 @@ +// Copyright (c) Abstract Machines +// SPDX-License-Identifier: Apache-2.0 + +// Package BoltDB contains the domain concept definitions needed to support +// Magistrala BoltDB database functionality. +// +// It provides the abstraction of the BoltDB database service, which is used +// to configure, setup and connect to the BoltDB database. +package bolt From 71743de676d47de41e459f69945cfa1b7920a8cc Mon Sep 17 00:00:00 2001 From: Arvindh Date: Fri, 5 Jul 2024 16:47:32 +0530 Subject: [PATCH 11/24] update PAT bbolt repo Signed-off-by: Arvindh --- auth/bolt/pat.go | 522 ++++++++++++++++++++++++++++-------- auth/pat.go | 53 +++- badgergob/badgergob_test.go | 55 ---- badgergob/bgob.go | 187 ------------- cmd/test/patrepo/main.go | 14 +- 5 files changed, 469 insertions(+), 362 deletions(-) delete mode 100644 badgergob/badgergob_test.go delete mode 100644 badgergob/bgob.go diff --git a/auth/bolt/pat.go b/auth/bolt/pat.go index 3c8a487b98..82e414848d 100644 --- a/auth/bolt/pat.go +++ b/auth/bolt/pat.go @@ -81,7 +81,6 @@ func (pr *patRepo) Save(ctx context.Context, pat auth.PAT) error { } func (pr *patRepo) Retrieve(ctx context.Context, userID, patID string) (auth.PAT, error) { - var pat auth.PAT prefix := []byte(patID + keySeparator) kv := map[string][]byte{} if err := pr.db.Update(func(tx *bolt.Tx) error { @@ -98,11 +97,7 @@ func (pr *patRepo) Retrieve(ctx context.Context, userID, patID string) (auth.PAT return auth.PAT{}, err } - for k, v := range kv { - fmt.Println(k, string(v)) - } - fmt.Println(kv) - return pat, nil + return keyValueToPAT(kv) } func (pr *patRepo) UpdateName(ctx context.Context, userID, patID, name string) (auth.PAT, error) { @@ -114,7 +109,8 @@ func (pr *patRepo) UpdateDescription(ctx context.Context, userID, patID, descrip } func (pr *patRepo) UpdateTokenHash(ctx context.Context, userID, patID, tokenHash string, expiryAt time.Time) (auth.PAT, error) { - var pat auth.PAT + prefix := []byte(patID + keySeparator) + kv := map[string][]byte{} if err := pr.db.Update(func(tx *bolt.Tx) error { b, err := pr.retrieveUserBucket(tx, userID) if err != nil { @@ -126,15 +122,15 @@ func (pr *patRepo) UpdateTokenHash(ctx context.Context, userID, patID, tokenHash if err := b.Put([]byte(patID+keySeparator+expiresAtKey), timeToBytes(expiryAt)); err != nil { return errors.Wrap(repoerr.ErrUpdateEntity, err) } - pat, err = pr.retrievePAT(b, patID) - if err != nil { - return err + c := b.Cursor() + for k, v := c.Seek(prefix); k != nil && bytes.HasPrefix(k, prefix); k, v = c.Next() { + kv[string(k)] = v } return nil }); err != nil { return auth.PAT{}, err } - return pat, nil + return keyValueToPAT(kv) } func (pr *patRepo) RetrieveAll(ctx context.Context, userID string) (pats auth.PATSPage, err error) { @@ -183,6 +179,8 @@ func (pr *patRepo) Remove(ctx context.Context, userID, patID string) error { } func (pr *patRepo) AddScopeEntry(ctx context.Context, userID, patID string, platformEntityType auth.PlatformEntityType, optionalDomainID string, optionalDomainEntityType auth.DomainEntityType, operation auth.OperationType, entityIDs ...string) (auth.Scope, error) { + prefix := []byte(patID + keySeparator + scopeKey) + var rKV map[string][]byte if err := pr.db.Update(func(tx *bolt.Tx) error { b, err := pr.retrieveUserBucket(tx, userID) if err != nil { @@ -198,18 +196,24 @@ func (pr *patRepo) AddScopeEntry(ctx context.Context, userID, patID string, plat return errors.Wrap(repoerr.ErrCreateEntity, err) } } + c := b.Cursor() + for k, v := c.Seek(prefix); k != nil && bytes.HasPrefix(k, prefix); k, v = c.Next() { + rKV[string(k)] = v + } return nil }); err != nil { return auth.Scope{}, err } - return auth.Scope{}, nil + return parseKeyValueToScope(rKV) } func (pr *patRepo) RemoveScopeEntry(ctx context.Context, userID, patID string, platformEntityType auth.PlatformEntityType, optionalDomainID string, optionalDomainEntityType auth.DomainEntityType, operation auth.OperationType, entityIDs ...string) (auth.Scope, error) { if len(entityIDs) == 0 { return auth.Scope{}, repoerr.ErrMalformedEntity } + prefix := []byte(patID + keySeparator + scopeKey) + var rKV map[string][]byte if err := pr.db.Update(func(tx *bolt.Tx) error { b, err := pr.retrieveUserBucket(tx, userID) if err != nil { @@ -225,11 +229,15 @@ func (pr *patRepo) RemoveScopeEntry(ctx context.Context, userID, patID string, p return errors.Wrap(repoerr.ErrRemoveEntity, err) } } + c := b.Cursor() + for k, v := c.Seek(prefix); k != nil && bytes.HasPrefix(k, prefix); k, v = c.Next() { + rKV[string(k)] = v + } return nil }); err != nil { return auth.Scope{}, err } - return auth.Scope{}, nil + return parseKeyValueToScope(rKV) } func (pr *patRepo) CheckScopeEntry(ctx context.Context, userID, patID string, platformEntityType auth.PlatformEntityType, optionalDomainID string, optionalDomainEntityType auth.DomainEntityType, operation auth.OperationType, entityIDs ...string) error { @@ -285,7 +293,6 @@ func (pr *patRepo) retrieveUserBucket(tx *bolt.Tx, userID string) (*bolt.Bucket, } func (pr *patRepo) updatePATField(_ context.Context, userID, patID, key string, value []byte) (auth.PAT, error) { - var pat auth.PAT prefix := []byte(patID + keySeparator) kv := map[string][]byte{} if err := pr.db.Update(func(tx *bolt.Tx) error { @@ -304,67 +311,7 @@ func (pr *patRepo) updatePATField(_ context.Context, userID, patID, key string, }); err != nil { return auth.PAT{}, err } - return pat, nil -} - -func (pr *patRepo) scopeKeyBuilder(patID string, platformEntityType auth.PlatformEntityType, optionalDomainID string, optionalDomainEntityType auth.DomainEntityType, operation auth.OperationType) (string, error) { - op, err := operation.ValidString() - if err != nil { - return "", errors.Wrap(repoerr.ErrMalformedEntity, err) - } - switch platformEntityType { - case auth.PlatformUsersScope: - return patID + keySeparator + scopeKey + keySeparator + platformEntityType.String() + keySeparator + op, nil - case auth.PlatformDomainsScope: - if optionalDomainID == "" { - return "", fmt.Errorf("failed to add platform %s scope: invalid domain id", platformEntityType.String()) - } - odet, err := optionalDomainEntityType.ValidString() - if err != nil { - return "", errors.Wrap(repoerr.ErrMalformedEntity, err) - } - - return patID + keySeparator + scopeKey + keySeparator + platformEntityType.String() + keySeparator + optionalDomainID + keySeparator + odet + keySeparator + op + keySeparator, nil - default: - return "", errors.Wrap(repoerr.ErrMalformedEntity, fmt.Errorf("invalid platform entity type %s", platformEntityType.String())) - } -} - -func (pr *patRepo) updateLastUsed(ctx context.Context, userID, patID string) error { - return nil -} - -func (pr *patRepo) retrievePAT(b *bolt.Bucket, patID string) (auth.PAT, error) { - var pat auth.PAT - - id := b.Get([]byte(patID + ":id")) - if id == nil { - return pat, repoerr.ErrNotFound - } - pat.ID = string(id) - pat.User = getString(b, patID, userKey) - pat.Name = getString(b, patID, nameKey) - pat.Description = getString(b, patID, descriptionKey) - pat.IssuedAt = getTime(b, patID, issuedAtKey) - pat.ExpiresAt = getTime(b, patID, expiresAtKey) - pat.UpdatedAt = getTime(b, patID, updatedAtKey) - pat.LastUsedAt = getTime(b, patID, lastUsedAtKey) - pat.Revoked = getBool(b, patID, revokedKey) - pat.RevokedAt = getTime(b, patID, revokedAtKey) - return pat, nil -} - -func (pr *patRepo) retrieveScope(b *bolt.Bucket, patID string) (auth.Scope, error) { - var scope auth.Scope - - c := b.Cursor() - prefix := []byte(patID + keySeparator + scopeKey + keySeparator) - for k, v := c.Seek(prefix); k != nil && bytes.HasPrefix(k, prefix); k, v = c.Next() { - fmt.Printf("key=%s, value=%v\n", k, v) - keyWithoutPrefix := k[len(prefix):] - fmt.Printf("key=%s, value=%v\n", keyWithoutPrefix, v) - } - return scope, nil + return keyValueToPAT(kv) } func patToKeyValue(pat auth.PAT) (map[string][]byte, error) { @@ -452,7 +399,6 @@ func scopeEntryToKeyValue(platformEntityType auth.PlatformEntityType, optionalDo } func scopeRootKey(platformEntityType auth.PlatformEntityType, optionalDomainID string, optionalDomainEntityType auth.DomainEntityType, operation auth.OperationType) (string, error) { - op, err := operation.ValidString() if err != nil { return "", errors.Wrap(repoerr.ErrMalformedEntity, err) @@ -487,6 +433,7 @@ func scopeRootKey(platformEntityType auth.PlatformEntityType, optionalDomainID s return rootKey.String(), nil } + func keyValueToPAT(kv map[string][]byte) (auth.PAT, error) { var pat auth.PAT for k, v := range kv { @@ -513,64 +460,429 @@ func keyValueToPAT(kv map[string][]byte) (auth.PAT, error) { pat.RevokedAt = bytesToTime(v) } } + scope, err := parseKeyValueToScope(kv) + if err != nil { + return auth.PAT{}, err + } + pat.Scope = scope return pat, nil } -func parseKeyValueToScope(kv map[string][]byte) (auth.Scope, error) { +// 129eab11-681b-4c62-89e4-7a9ce0eda29d:scope:users:read +// 129eab11-681b-4c62-89e4-7a9ce0eda29d:scope:domains:domain_1:groups:read +// 129eab11-681b-4c62-89e4-7a9ce0eda29d:scope:domains:domain_1:groups:read:group_2 +// 129eab11-681b-4c62-89e4-7a9ce0eda29d:scope:domains:domain_1:domain_management:read +// 129eab11-681b-4c62-89e4-7a9ce0eda29d:scope:domains:domain_1:groups:read:group_1 + +func parseKeyValueToScopeLegacy(kv map[string][]byte) (auth.Scope, error) { scope := auth.Scope{ Domains: make(map[string]auth.DomainScope), } - for key, value := range kv { - if strings.Index(key, keySeparator+scopeKey) > 0 { - parts := strings.Split(key, keySeparator) - if len(parts) < 4 { - return auth.Scope{}, errors.Wrap(repoerr.ErrViewEntity, fmt.Errorf("invalid key format: %s", key)) - } + if strings.Index(key, keySeparator+scopeKey+keySeparator) > 0 { + keyParts := strings.Split(key, keySeparator) - platformEntityType, err := auth.ParsePlatformEntityType(parts[2]) + platformEntityType, err := auth.ParsePlatformEntityType(keyParts[2]) if err != nil { return auth.Scope{}, errors.Wrap(repoerr.ErrViewEntity, err) } - opType, err := auth.ParseOperationType(parts[len(parts)-1]) + + switch platformEntityType { + case auth.PlatformUsersScope: + switch string(value) { + case string(entityValue): + if len(keyParts) != 5 { + return auth.Scope{}, fmt.Errorf("invalid scope key format: %s", key) + } + opType, err := auth.ParseOperationType(keyParts[len(keyParts)-2]) + if err != nil { + return auth.Scope{}, errors.Wrap(repoerr.ErrViewEntity, err) + } + entityID := keyParts[len(keyParts)-1] + if scope.Users.Operations == nil { + scope.Users.Operations = make(map[auth.OperationType]auth.ScopeValue) + } + + if _, oValueExists := scope.Users.Operations[opType]; !oValueExists { + scope.Users.Operations[opType] = &auth.SelectedIDs{} + } + oValue := scope.Users.Operations[opType] + if err := oValue.AddValues(entityID); err != nil { + return auth.Scope{}, fmt.Errorf("failed to add scope key %s with entity value %v : %w", key, entityID, err) + } + scope.Users.Operations[opType] = oValue + case string(anyIDValue): + if len(keyParts) != 4 { + return auth.Scope{}, fmt.Errorf("invalid scope key format: %s", key) + } + opType, err := auth.ParseOperationType(keyParts[len(keyParts)-1]) + if err != nil { + return auth.Scope{}, errors.Wrap(repoerr.ErrViewEntity, err) + } + + if scope.Users.Operations == nil { + scope.Users.Operations = make(map[auth.OperationType]auth.ScopeValue) + } + + if oValue, oValueExists := scope.Users.Operations[opType]; oValueExists && oValue != nil { + if _, ok := oValue.(*auth.AnyIDs); !ok { + return auth.Scope{}, fmt.Errorf("failed to add scope key %s with entity anyIDs scope value : key already initialized with different type", key) + } + } + scope.Users.Operations[opType] = &auth.AnyIDs{} + case string(selectedIDsValue): + if len(keyParts) != 4 { + return auth.Scope{}, fmt.Errorf("invalid scope key format: %s", key) + } + opType, err := auth.ParseOperationType(keyParts[len(keyParts)-1]) + if err != nil { + return auth.Scope{}, errors.Wrap(repoerr.ErrViewEntity, err) + } + + if scope.Users.Operations == nil { + scope.Users.Operations = make(map[auth.OperationType]auth.ScopeValue) + } + + oValue, oValueExists := scope.Users.Operations[opType] + if oValueExists && oValue != nil { + if _, ok := oValue.(*auth.SelectedIDs); !ok { + return auth.Scope{}, fmt.Errorf("failed to add scope key %s with entity selectedIDs scope value : key already initialized with different type", key) + } + } + if !oValueExists { + scope.Users.Operations[opType] = &auth.SelectedIDs{} + } + default: + return auth.Scope{}, fmt.Errorf("key %s have invalid value %v", key, value) + } + + case auth.PlatformDomainsScope: + if len(keyParts) < 6 { + return auth.Scope{}, fmt.Errorf("invalid scope key format: %s", key) + } + domainID := keyParts[3] + if scope.Domains == nil { + scope.Domains = make(map[string]auth.DomainScope) + } + if _, ok := scope.Domains[domainID]; !ok { + scope.Domains[domainID] = auth.DomainScope{} + } + domainScope := scope.Domains[domainID] + + entityType := keyParts[4] + + switch entityType { + case auth.DomainManagementScope.String(): + switch string(value) { + case string(entityValue): + if len(keyParts) != 7 { + return auth.Scope{}, fmt.Errorf("invalid scope key format: %s", key) + } + opType, err := auth.ParseOperationType(keyParts[len(keyParts)-2]) + if err != nil { + return auth.Scope{}, errors.Wrap(repoerr.ErrViewEntity, err) + } + entityID := keyParts[len(keyParts)-1] + + if domainScope.DomainManagement.Operations == nil { + domainScope.DomainManagement.Operations = make(map[auth.OperationType]auth.ScopeValue) + } + + if _, oValueExists := domainScope.DomainManagement.Operations[opType]; !oValueExists { + domainScope.DomainManagement.Operations[opType] = &auth.SelectedIDs{} + } + oValue := domainScope.DomainManagement.Operations[opType] + if err := oValue.AddValues(entityID); err != nil { + return auth.Scope{}, fmt.Errorf("failed to add scope key %s with entity value %v : %w", key, entityID, err) + } + domainScope.DomainManagement.Operations[opType] = oValue + case string(anyIDValue): + if len(keyParts) != 6 { + return auth.Scope{}, fmt.Errorf("invalid scope key format: %s", key) + } + opType, err := auth.ParseOperationType(keyParts[len(keyParts)-1]) + if err != nil { + return auth.Scope{}, errors.Wrap(repoerr.ErrViewEntity, err) + } + + if domainScope.DomainManagement.Operations == nil { + domainScope.DomainManagement.Operations = make(map[auth.OperationType]auth.ScopeValue) + } + + if oValue, oValueExists := domainScope.DomainManagement.Operations[opType]; oValueExists && oValue != nil { + if _, ok := oValue.(*auth.AnyIDs); !ok { + return auth.Scope{}, fmt.Errorf("failed to add scope key %s with entity anyIDs scope value : key already initialized with different type", key) + } + } + domainScope.DomainManagement.Operations[opType] = &auth.AnyIDs{} + case string(selectedIDsValue): + if len(keyParts) != 6 { + return auth.Scope{}, fmt.Errorf("invalid scope key format: %s", key) + } + opType, err := auth.ParseOperationType(keyParts[len(keyParts)-1]) + if err != nil { + return auth.Scope{}, errors.Wrap(repoerr.ErrViewEntity, err) + } + + if domainScope.DomainManagement.Operations == nil { + domainScope.DomainManagement.Operations = make(map[auth.OperationType]auth.ScopeValue) + } + + oValue, oValueExists := domainScope.DomainManagement.Operations[opType] + if oValueExists && oValue != nil { + if _, ok := oValue.(*auth.SelectedIDs); !ok { + return auth.Scope{}, fmt.Errorf("failed to add scope key %s with entity selectedIDs scope value : key already initialized with different type", key) + } + } + if !oValueExists { + domainScope.DomainManagement.Operations[opType] = &auth.SelectedIDs{} + } + default: + return auth.Scope{}, fmt.Errorf("key %s have invalid value %v", key, value) + } + default: + etype, err := auth.ParseDomainEntityType(entityType) + if err != nil { + return auth.Scope{}, fmt.Errorf("key %s invalid entity type %s : %w", key, entityType, err) + } + if domainScope.Entities == nil { + domainScope.Entities = make(map[auth.DomainEntityType]auth.OperationScope) + } + if _, ok := domainScope.Entities[etype]; !ok { + domainScope.Entities[etype] = auth.OperationScope{} + } + entityOperationScope := domainScope.Entities[etype] + switch string(value) { + case string(entityValue): + if len(keyParts) != 7 { + return auth.Scope{}, fmt.Errorf("invalid scope key format: %s", key) + } + opType, err := auth.ParseOperationType(keyParts[len(keyParts)-2]) + if err != nil { + return auth.Scope{}, errors.Wrap(repoerr.ErrViewEntity, err) + } + entityID := keyParts[len(keyParts)-1] + if entityOperationScope.Operations == nil { + entityOperationScope.Operations = make(map[auth.OperationType]auth.ScopeValue) + } + + if _, oValueExists := entityOperationScope.Operations[opType]; !oValueExists { + entityOperationScope.Operations[opType] = &auth.SelectedIDs{} + } + oValue := entityOperationScope.Operations[opType] + if err := oValue.AddValues(entityID); err != nil { + return auth.Scope{}, fmt.Errorf("failed to add scope key %s with entity value %v : %w", key, entityID, err) + } + entityOperationScope.Operations[opType] = oValue + case string(anyIDValue): + if len(keyParts) != 6 { + return auth.Scope{}, fmt.Errorf("invalid scope key format: %s", key) + } + opType, err := auth.ParseOperationType(keyParts[len(keyParts)-1]) + if err != nil { + return auth.Scope{}, errors.Wrap(repoerr.ErrViewEntity, err) + } + + if entityOperationScope.Operations == nil { + entityOperationScope.Operations = make(map[auth.OperationType]auth.ScopeValue) + } + + if oValue, oValueExists := entityOperationScope.Operations[opType]; oValueExists && oValue != nil { + if _, ok := oValue.(*auth.AnyIDs); !ok { + return auth.Scope{}, fmt.Errorf("failed to add scope key %s with entity anyIDs scope value : key already initialized with different type", key) + } + } + entityOperationScope.Operations[opType] = &auth.AnyIDs{} + case string(selectedIDsValue): + if len(keyParts) != 6 { + return auth.Scope{}, fmt.Errorf("invalid scope key format: %s", key) + } + opType, err := auth.ParseOperationType(keyParts[len(keyParts)-1]) + if err != nil { + return auth.Scope{}, errors.Wrap(repoerr.ErrViewEntity, err) + } + + if entityOperationScope.Operations == nil { + entityOperationScope.Operations = make(map[auth.OperationType]auth.ScopeValue) + } + + oValue, oValueExists := entityOperationScope.Operations[opType] + if oValueExists && oValue != nil { + if _, ok := oValue.(*auth.SelectedIDs); !ok { + return auth.Scope{}, fmt.Errorf("failed to add scope key %s with entity selectedIDs scope value : key already initialized with different type", key) + } + } + if !oValueExists { + entityOperationScope.Operations[opType] = &auth.SelectedIDs{} + } + default: + return auth.Scope{}, fmt.Errorf("key %s have invalid value %v", key, value) + } + + domainScope.Entities[etype] = entityOperationScope + } + + scope.Domains[domainID] = domainScope + default: + return auth.Scope{}, errors.Wrap(repoerr.ErrViewEntity, fmt.Errorf("invalid platform entity type : %s", platformEntityType.String())) + } + } + } + return scope, nil +} + +func parseKeyValueToScope(kv map[string][]byte) (auth.Scope, error) { + scope := auth.Scope{ + Domains: make(map[string]auth.DomainScope), + } + for key, value := range kv { + if strings.Index(key, keySeparator+scopeKey+keySeparator) > 0 { + keyParts := strings.Split(key, keySeparator) + + platformEntityType, err := auth.ParsePlatformEntityType(keyParts[2]) if err != nil { return auth.Scope{}, errors.Wrap(repoerr.ErrViewEntity, err) } switch platformEntityType { case auth.PlatformUsersScope: - if len(parts) != 4 { - return auth.Scope{}, fmt.Errorf("invalid user scope key format: %s", key) - } - if scope.Users.Operations == nil { - scope.Users.Operations = make(map[auth.OperationType]auth.ScopeValue) + scope.Users, err = parseOperation(platformEntityType, scope.Users, key, keyParts, value) + if err != nil { + return auth.Scope{}, errors.Wrap(repoerr.ErrViewEntity, err) } - scope.Users.Operations[opType] = auth.ScopeValue{values: parseValues(value)} + case auth.PlatformDomainsScope: - if len(parts) < 5 { - return auth.Scope{}, fmt.Errorf("invalid domain scope key format: %s", key) + if len(keyParts) < 6 { + return auth.Scope{}, fmt.Errorf("invalid scope key format: %s", key) } - domainID := parts[2] - entityType := auth.DomainEntityType(parts[3]) - if scope.Domains[domainID].Entities == nil { - scope.Domains[domainID] = auth.DomainScope{ - Entities: make(map[auth.DomainEntityType]auth.EntityScope), - } + domainID := keyParts[3] + if scope.Domains == nil { + scope.Domains = make(map[string]auth.DomainScope) + } + if _, ok := scope.Domains[domainID]; !ok { + scope.Domains[domainID] = auth.DomainScope{} } - if scope.Domains[domainID].Entities[entityType].Operations == nil { - scope.Domains[domainID].Entities[entityType] = auth.EntityScope{ - Operations: make(map[auth.OperationType]auth.ScopeValue), + domainScope := scope.Domains[domainID] + + entityType := keyParts[4] + + switch entityType { + case auth.DomainManagementScope.String(): + domainScope.DomainManagement, err = parseOperation(platformEntityType, domainScope.DomainManagement, key, keyParts, value) + if err != nil { + return auth.Scope{}, errors.Wrap(repoerr.ErrViewEntity, err) + } + default: + etype, err := auth.ParseDomainEntityType(entityType) + if err != nil { + return auth.Scope{}, fmt.Errorf("key %s invalid entity type %s : %w", key, entityType, err) + } + if domainScope.Entities == nil { + domainScope.Entities = make(map[auth.DomainEntityType]auth.OperationScope) + } + if _, ok := domainScope.Entities[etype]; !ok { + domainScope.Entities[etype] = auth.OperationScope{} } + entityOperationScope := domainScope.Entities[etype] + entityOperationScope, err = parseOperation(platformEntityType, entityOperationScope, key, keyParts, value) + if err != nil { + return auth.Scope{}, errors.Wrap(repoerr.ErrViewEntity, err) + } + domainScope.Entities[etype] = entityOperationScope } - scope.Domains[domainID].Entities[entityType].Operations[opType] = auth.ScopeValue{values: parseValues(value)} + scope.Domains[domainID] = domainScope default: - return auth.Scope{}, fmt.Errorf("invalid platform entity type: %s", parts[2]) + return auth.Scope{}, errors.Wrap(repoerr.ErrViewEntity, fmt.Errorf("invalid platform entity type : %s", platformEntityType.String())) } } + } + return scope, nil +} +func parseOperation(platformEntityType auth.PlatformEntityType, opScope auth.OperationScope, key string, keyParts []string, value []byte) (auth.OperationScope, error) { + if opScope.Operations == nil { + opScope.Operations = make(map[auth.OperationType]auth.ScopeValue) } - return scope, nil + if err := validateOperation(platformEntityType, opScope, key, keyParts, value); err != nil { + return auth.OperationScope{}, err + } + + switch string(value) { + case string(entityValue): + opType, err := auth.ParseOperationType(keyParts[len(keyParts)-2]) + if err != nil { + return auth.OperationScope{}, errors.Wrap(repoerr.ErrViewEntity, err) + } + entityID := keyParts[len(keyParts)-1] + + if _, oValueExists := opScope.Operations[opType]; !oValueExists { + opScope.Operations[opType] = &auth.SelectedIDs{} + } + oValue := opScope.Operations[opType] + if err := oValue.AddValues(entityID); err != nil { + return auth.OperationScope{}, fmt.Errorf("failed to add scope key %s with entity value %v : %w", key, entityID, err) + } + opScope.Operations[opType] = oValue + case string(anyIDValue): + opType, err := auth.ParseOperationType(keyParts[len(keyParts)-1]) + if err != nil { + return auth.OperationScope{}, errors.Wrap(repoerr.ErrViewEntity, err) + } + if oValue, oValueExists := opScope.Operations[opType]; oValueExists && oValue != nil { + if _, ok := oValue.(*auth.AnyIDs); !ok { + return auth.OperationScope{}, fmt.Errorf("failed to add scope key %s with entity anyIDs scope value : key already initialized with different type", key) + } + } + opScope.Operations[opType] = &auth.AnyIDs{} + case string(selectedIDsValue): + opType, err := auth.ParseOperationType(keyParts[len(keyParts)-1]) + if err != nil { + return auth.OperationScope{}, errors.Wrap(repoerr.ErrViewEntity, err) + } + oValue, oValueExists := opScope.Operations[opType] + if oValueExists && oValue != nil { + if _, ok := oValue.(*auth.SelectedIDs); !ok { + return auth.OperationScope{}, fmt.Errorf("failed to add scope key %s with entity selectedIDs scope value : key already initialized with different type", key) + } + } + if !oValueExists { + opScope.Operations[opType] = &auth.SelectedIDs{} + } + default: + return auth.OperationScope{}, fmt.Errorf("key %s have invalid value %v", key, value) + } + return opScope, nil +} + +func validateOperation(platformEntityType auth.PlatformEntityType, opScope auth.OperationScope, key string, keyParts []string, value []byte) error { + expectedKeyPartsLength := 0 + switch string(value) { + case string(entityValue): + switch platformEntityType { + case auth.PlatformDomainsScope: + expectedKeyPartsLength = 7 + case auth.PlatformUsersScope: + expectedKeyPartsLength = 5 + default: + return fmt.Errorf("invalid platform entity type : %s", platformEntityType.String()) + } + case string(selectedIDsValue), string(anyIDValue): + switch platformEntityType { + case auth.PlatformDomainsScope: + expectedKeyPartsLength = 6 + case auth.PlatformUsersScope: + expectedKeyPartsLength = 4 + default: + return fmt.Errorf("invalid platform entity type : %s", platformEntityType.String()) + } + default: + return fmt.Errorf("key %s have invalid value %v", key, value) + } + if len(keyParts) != expectedKeyPartsLength { + return fmt.Errorf("invalid scope key format: %s", key) + } + return nil } func getString(b *bolt.Bucket, patID, key string) string { diff --git a/auth/pat.go b/auth/pat.go index dff1da67e3..4d4297e11f 100644 --- a/auth/pat.go +++ b/auth/pat.go @@ -8,6 +8,12 @@ import ( "encoding/json" "fmt" "time" + + "github.com/absmach/magistrala/pkg/errors" +) + +var ( + errAddEntityToAnyIDs = errors.New("could not add entity id to any ID scope value") ) // Define OperationType. @@ -212,13 +218,17 @@ func (pet *PlatformEntityType) UnmarshalText(data []byte) (err error) { type ScopeValue interface { Contains(id string) bool Values() []string + AddValues(ids ...string) error + RemoveValues(ids ...string) error } // AnyIDs implements ScopeValue for any entity id value. type AnyIDs struct{} -func (s AnyIDs) Contains(id string) bool { return true } -func (s AnyIDs) Values() []string { return []string{"*"} } +func (s AnyIDs) Contains(id string) bool { return true } +func (s AnyIDs) Values() []string { return []string{"*"} } +func (s *AnyIDs) AddValues(ids ...string) error { return errAddEntityToAnyIDs } +func (s *AnyIDs) RemoveValues(ids ...string) error { return errAddEntityToAnyIDs } // SelectedIDs implements ScopeValue for sets of entity ids. type SelectedIDs map[string]struct{} @@ -231,6 +241,25 @@ func (s SelectedIDs) Values() []string { } return values } +func (s *SelectedIDs) AddValues(ids ...string) error { + if *s == nil { + *s = make(SelectedIDs) + } + for _, id := range ids { + (*s)[id] = struct{}{} + } + return nil +} + +func (s *SelectedIDs) RemoveValues(ids ...string) error { + if *s == nil { + return nil + } + for _, id := range ids { + delete(*s, id) + } + return nil +} // OperationScope contains map of OperationType with value of AnyIDs or SelectedIDs. type OperationScope struct { @@ -257,9 +286,9 @@ func (os *OperationScope) UnmarshalJSON(data []byte) error { if err := json.Unmarshal(rawMessage, &stringValue); err == nil { switch { case stringValue == "*": - os.Operations[opType] = AnyIDs{} + os.Operations[opType] = &AnyIDs{} default: - os.Operations[opType] = SelectedIDs{stringValue: {}} + os.Operations[opType] = &SelectedIDs{stringValue: {}} } continue } @@ -270,7 +299,7 @@ func (os *OperationScope) UnmarshalJSON(data []byte) error { for _, stringVal := range stringArrayValue { sids[stringVal] = struct{}{} } - os.Operations[opType] = sids + os.Operations[opType] = &sids continue } @@ -293,7 +322,7 @@ func (os *OperationScope) Add(operation OperationType, entityIDs ...string) erro } switch { case len(entityIDs) == 1 && entityIDs[0] == "*": - value = AnyIDs{} + value = &AnyIDs{} default: var sids SelectedIDs for _, entityID := range entityIDs { @@ -305,7 +334,7 @@ func (os *OperationScope) Add(operation OperationType, entityIDs ...string) erro } sids[entityID] = struct{}{} } - value = sids + value = &sids } os.Operations[operation] = value return nil @@ -326,21 +355,21 @@ func (os *OperationScope) Delete(operation OperationType, entityIDs ...string) e } switch eIDs := opEntityIDs.(type) { - case AnyIDs: + case *AnyIDs: if !(len(entityIDs) == 1 && entityIDs[0] == "*") { return fmt.Errorf("failed to delete operation %s: invalid list", operation.String()) } delete(os.Operations, operation) return nil - case SelectedIDs: + case *SelectedIDs: for _, entityID := range entityIDs { if !eIDs.Contains(entityID) { return fmt.Errorf("failed to delete operation %s: invalid entity ID in list", operation.String()) } } for _, entityID := range entityIDs { - delete(eIDs, entityID) - if len(eIDs) == 0 { + delete(*eIDs, entityID) + if len(*eIDs) == 0 { delete(os.Operations, operation) } } @@ -357,7 +386,7 @@ func (os *OperationScope) Check(operation OperationType, entityIDs ...string) bo if scopeValue, ok := os.Operations[operation]; ok { if len(entityIDs) == 0 { - _, ok := scopeValue.(AnyIDs) + _, ok := scopeValue.(*AnyIDs) return ok } for _, entityID := range entityIDs { diff --git a/badgergob/badgergob_test.go b/badgergob/badgergob_test.go deleted file mode 100644 index 4439ca0391..0000000000 --- a/badgergob/badgergob_test.go +++ /dev/null @@ -1,55 +0,0 @@ -package badgergob_test - -import ( - "log" - "testing" - "time" - - "github.com/absmach/magistrala/auth" - "github.com/absmach/magistrala/badgergob" - badger "github.com/dgraph-io/badger/v4" - "github.com/google/uuid" -) - -func generateTestPAT() auth.PAT { - return auth.PAT{ - ID: uuid.New().String(), - User: "user123", - Name: "Test Token", - IssuedAt: time.Now(), - ExpiresAt: time.Now().Add(24 * time.Hour), - Scope: auth.Scope{ - Users: auth.OperationScope{ - Operations: map[auth.OperationType]auth.ScopeValue{ - auth.CreateOp: auth.SelectedIDs{"entity1": {}, "entity2": {}}, - }, - }, - }, - } -} - -func BenchmarkGetPAT(b *testing.B) { - // Open Badger database - opts := badger.DefaultOptions("./badger") - opts.Logger = nil - db, err := badger.Open(opts) - if err != nil { - log.Fatal(err) - } - defer db.Close() - - // Store a PAT to retrieve - pat := generateTestPAT() - err = badgergob.StorePAT(db, pat) - if err != nil { - log.Fatal(err) - } - - b.ResetTimer() - for i := 0; i < b.N; i++ { - _, err := badgergob.GetPAT(db, pat.ID) - if err != nil { - log.Fatal(err) - } - } -} diff --git a/badgergob/bgob.go b/badgergob/bgob.go deleted file mode 100644 index 4727d84a94..0000000000 --- a/badgergob/bgob.go +++ /dev/null @@ -1,187 +0,0 @@ -package badgergob - -import ( - "bytes" - "encoding/gob" - "fmt" - "log" - "strings" - "time" - - "github.com/absmach/magistrala/auth" - badger "github.com/dgraph-io/badger/v4" - "github.com/google/uuid" -) - -const timeLayout = "2006-01-02 15:04:05.999999999 -0700 MST" - -func init() { - gob.Register(auth.SelectedIDs{}) - gob.Register(auth.AnyIDs{}) -} -func main() { - // Open Badger database - db, err := badger.Open(badger.DefaultOptions("./badger")) - if err != nil { - log.Fatal(err) - } - defer db.Close() - - // Create a new PAT - pat := auth.PAT{ - ID: uuid.New().String(), - User: "user123", - Name: "Test Token", - IssuedAt: time.Now(), - ExpiresAt: time.Now().Add(24 * time.Hour), - } - - // Set scope - pat.Scope.Users = auth.OperationScope{ - Operations: map[auth.OperationType]auth.ScopeValue{ - auth.CreateOp: auth.SelectedIDs{"entity1": {}, "entity2": {}}, - }, - } - - // Store PAT - if err := StorePAT(db, pat); err != nil { - log.Fatal(err) - } - - // Retrieve PAT - retrievedPAT, err := GetPAT(db, pat.ID) - if err != nil { - log.Fatal(err) - } - fmt.Printf("Retrieved PAT: %+v\n", retrievedPAT) -} - -func StorePAT(db *badger.DB, pat auth.PAT) error { - return db.Update(func(txn *badger.Txn) error { - if err := txn.Set([]byte(fmt.Sprintf("pat:%s:id", pat.ID)), []byte(pat.ID)); err != nil { - return err - } - if err := txn.Set([]byte(fmt.Sprintf("pat:%s:user", pat.ID)), []byte(pat.User)); err != nil { - return err - } - if err := txn.Set([]byte(fmt.Sprintf("pat:%s:name", pat.ID)), []byte(pat.Name)); err != nil { - return err - } - if err := txn.Set([]byte(fmt.Sprintf("pat:%s:issued_at", pat.ID)), []byte(pat.IssuedAt.String())); err != nil { - return err - } - if err := txn.Set([]byte(fmt.Sprintf("pat:%s:expires_at", pat.ID)), []byte(pat.ExpiresAt.String())); err != nil { - return err - } - // Store scope - scopeBytes, err := EncodeScopeToGob(pat.Scope) - if err != nil { - return err - } - if err := txn.Set([]byte(fmt.Sprintf("pat:%s:scope", pat.ID)), scopeBytes); err != nil { - return err - } - return nil - }) -} - -func GetPAT(db *badger.DB, id string) (auth.PAT, error) { - var pat auth.PAT - err := db.View(func(txn *badger.Txn) error { - patID, err := txn.Get([]byte(fmt.Sprintf("pat:%s:id", id))) - if err != nil { - return err - } - err = patID.Value(func(val []byte) error { - pat.ID = string(val) - return nil - }) - if err != nil { - return err - } - - user, err := txn.Get([]byte(fmt.Sprintf("pat:%s:user", id))) - if err != nil { - return err - } - err = user.Value(func(val []byte) error { - pat.User = string(val) - return nil - }) - if err != nil { - return err - } - - name, err := txn.Get([]byte(fmt.Sprintf("pat:%s:name", id))) - if err != nil { - return err - } - err = name.Value(func(val []byte) error { - pat.Name = string(val) - return nil - }) - if err != nil { - return err - } - - issuedAt, err := txn.Get([]byte(fmt.Sprintf("pat:%s:issued_at", id))) - if err != nil { - return err - } - err = issuedAt.Value(func(val []byte) error { - pat.IssuedAt, err = time.Parse(timeLayout, strings.Split(string(val), " m=")[0]) - return err - }) - if err != nil { - return err - } - - expiresAt, err := txn.Get([]byte(fmt.Sprintf("pat:%s:expires_at", id))) - if err != nil { - return err - } - err = expiresAt.Value(func(val []byte) error { - pat.ExpiresAt, err = time.Parse(timeLayout, strings.Split(string(val), " m=")[0]) - return err - }) - if err != nil { - return err - } - - // Retrieve scope - scope, err := txn.Get([]byte(fmt.Sprintf("pat:%s:scope", id))) - if err != nil { - return err - } - - err = scope.Value(func(val []byte) error { - pat.Scope, err = DecodeGobToScope(val) - return err - }) - if err != nil { - return err - } - - return nil - }) - return pat, err -} - -func EncodeScopeToGob(scope auth.Scope) ([]byte, error) { - buf := bytes.NewBuffer([]byte{}) - enc := gob.NewEncoder(buf) - if err := enc.Encode(scope); err != nil { - return nil, err - } - return buf.Bytes(), nil -} - -func DecodeGobToScope(scopeBytes []byte) (auth.Scope, error) { - buf := bytes.NewBuffer(scopeBytes) - var scope auth.Scope - dec := gob.NewDecoder(buf) - if err := dec.Decode(&scope); err != nil { - return auth.Scope{}, err - } - return scope, nil -} diff --git a/cmd/test/patrepo/main.go b/cmd/test/patrepo/main.go index ca6b41f802..4e27041ab3 100644 --- a/cmd/test/patrepo/main.go +++ b/cmd/test/patrepo/main.go @@ -36,20 +36,20 @@ func main() { Scope: auth.Scope{ Users: auth.OperationScope{ Operations: map[auth.OperationType]auth.ScopeValue{ - auth.ReadOp: auth.AnyIDs{}, + auth.ReadOp: &auth.AnyIDs{}, }, }, Domains: map[string]auth.DomainScope{ "domain_1": { DomainManagement: auth.OperationScope{ Operations: map[auth.OperationType]auth.ScopeValue{ - auth.ReadOp: auth.AnyIDs{}, + auth.ReadOp: &auth.AnyIDs{}, }, }, Entities: map[auth.DomainEntityType]auth.OperationScope{ auth.DomainGroupsScope: { Operations: map[auth.OperationType]auth.ScopeValue{ - auth.ReadOp: auth.SelectedIDs{"group_1": {}, "group_2": {}}, + auth.ReadOp: &auth.SelectedIDs{"group_1": {}, "group_2": {}}, }, }, }, @@ -68,4 +68,12 @@ func main() { } fmt.Println(rPAT.String()) + fmt.Println(patrepo.CheckScopeEntry(context.Background(), pat.User, pat.ID, auth.PlatformDomainsScope, "domain_1", auth.DomainGroupsScope, auth.ReadOp, "group_1")) + fmt.Println(patrepo.CheckScopeEntry(context.Background(), pat.User, pat.ID, auth.PlatformDomainsScope, "domain_1", auth.DomainGroupsScope, auth.ReadOp, "group_2")) + fmt.Println(patrepo.CheckScopeEntry(context.Background(), pat.User, pat.ID, auth.PlatformDomainsScope, "domain_1", auth.DomainGroupsScope, auth.UpdateOp, "group_1")) + fmt.Println(patrepo.CheckScopeEntry(context.Background(), pat.User, pat.ID, auth.PlatformDomainsScope, "domain_1", auth.DomainGroupsScope, auth.UpdateOp, "group_2")) + fmt.Println(patrepo.CheckScopeEntry(context.Background(), pat.User, pat.ID, auth.PlatformDomainsScope, "domain_1", auth.DomainThingsScope, auth.UpdateOp, "group_2")) + fmt.Println(patrepo.CheckScopeEntry(context.Background(), pat.User, pat.ID, auth.PlatformUsersScope, "", auth.DomainNullScope, auth.ReadOp, "user_123")) + fmt.Println(patrepo.CheckScopeEntry(context.Background(), pat.User, pat.ID, auth.PlatformUsersScope, "", auth.DomainNullScope, auth.UpdateOp, "user_123")) + } From 7f318ae4d18a745deb104c6ef410a771b10a75f8 Mon Sep 17 00:00:00 2001 From: Arvindh Date: Wed, 10 Jul 2024 14:23:16 +0530 Subject: [PATCH 12/24] add pat endpoints Signed-off-by: Arvindh --- auth/api/http/pats/endpoint.go | 203 ++++++++++++++++++ auth/api/http/pats/requests.go | 358 ++++++++++++++++++++++++++++++++ auth/api/http/pats/responses.go | 208 +++++++++++++++++++ auth/api/http/pats/transport.go | 277 ++++++++++++++++++++++++ auth/api/logging.go | 230 ++++++++++++++++++++ auth/api/metrics.go | 110 ++++++++++ auth/bolt/pat.go | 63 +++++- auth/mocks/pats.go | 42 ++-- auth/mocks/patsrepo.go | 66 ++---- auth/mocks/service.go | 42 ++-- auth/pat.go | 18 +- auth/service.go | 19 +- auth/service_test.go | 10 +- auth/tracing/tracing.go | 33 +-- cmd/auth/main.go | 26 ++- docker/nginx/nginx-key.conf | 2 +- go.mod | 6 +- go.sum | 3 + pkg/apiutil/errors.go | 6 + 19 files changed, 1581 insertions(+), 141 deletions(-) create mode 100644 auth/api/http/pats/endpoint.go create mode 100644 auth/api/http/pats/requests.go create mode 100644 auth/api/http/pats/responses.go create mode 100644 auth/api/http/pats/transport.go diff --git a/auth/api/http/pats/endpoint.go b/auth/api/http/pats/endpoint.go new file mode 100644 index 0000000000..3d751c92b2 --- /dev/null +++ b/auth/api/http/pats/endpoint.go @@ -0,0 +1,203 @@ +// Copyright (c) Abstract Machines +// SPDX-License-Identifier: Apache-2.0 + +package pats + +import ( + "context" + + "github.com/absmach/magistrala/auth" + "github.com/go-kit/kit/endpoint" +) + +func createPatEndpoint(svc auth.Service) endpoint.Endpoint { + return func(ctx context.Context, request interface{}) (interface{}, error) { + req := request.(createPatReq) + if err := req.validate(); err != nil { + return nil, err + } + + pat, err := svc.Create(ctx, req.token, req.Name, req.Description, req.Duration, req.Scope) + if err != nil { + return nil, err + } + + return createPatRes{pat}, nil + } +} + +func retrieveEndpoint(svc auth.Service) endpoint.Endpoint { + return func(ctx context.Context, request interface{}) (interface{}, error) { + req := request.(retrievePatReq) + if err := req.validate(); err != nil { + return nil, err + } + + pat, err := svc.Retrieve(ctx, req.token, req.id) + if err != nil { + return nil, err + } + + return retrievePatRes{pat}, nil + } +} + +func updateNameEndpoint(svc auth.Service) endpoint.Endpoint { + return func(ctx context.Context, request interface{}) (interface{}, error) { + req := request.(updatePatNameReq) + if err := req.validate(); err != nil { + return nil, err + } + + pat, err := svc.UpdateName(ctx, req.token, req.id, req.Name) + if err != nil { + return nil, err + } + + return updatePatNameRes{pat}, nil + } +} + +func updateDescriptionEndpoint(svc auth.Service) endpoint.Endpoint { + return func(ctx context.Context, request interface{}) (interface{}, error) { + req := request.(updatePatDescriptionReq) + if err := req.validate(); err != nil { + return nil, err + } + + pat, err := svc.UpdateDescription(ctx, req.token, req.id, req.Description) + if err != nil { + return nil, err + } + + return updatePatDescriptionRes{pat}, nil + } +} + +func listEndpoint(svc auth.Service) endpoint.Endpoint { + return func(ctx context.Context, request interface{}) (interface{}, error) { + req := request.(listPatsReq) + if err := req.validate(); err != nil { + return nil, err + } + + pm := auth.PATSPageMeta{ + Limit: req.limit, + Offset: req.offset, + } + patsPage, err := svc.List(ctx, req.token, pm) + if err != nil { + return nil, err + } + + return listPatsRes{patsPage}, nil + } +} + +func deleteEndpoint(svc auth.Service) endpoint.Endpoint { + return func(ctx context.Context, request interface{}) (interface{}, error) { + req := request.(deletePatReq) + if err := req.validate(); err != nil { + return nil, err + } + + if err := svc.Delete(ctx, req.token, req.id); err != nil { + return nil, err + } + + return deletePatRes{}, nil + } +} + +func resetSecretEndpoint(svc auth.Service) endpoint.Endpoint { + return func(ctx context.Context, request interface{}) (interface{}, error) { + req := request.(resetPatSecretReq) + if err := req.validate(); err != nil { + return nil, err + } + + pat, err := svc.ResetSecret(ctx, req.token, req.id, req.Duration) + if err != nil { + return nil, err + } + + return resetPatSecretRes{pat}, nil + } +} + +func revokeSecretEndpoint(svc auth.Service) endpoint.Endpoint { + return func(ctx context.Context, request interface{}) (interface{}, error) { + req := request.(revokePatSecretReq) + if err := req.validate(); err != nil { + return nil, err + } + + if err := svc.RevokeSecret(ctx, req.token, req.id); err != nil { + return nil, err + } + + return revokePatSecretRes{}, nil + } +} + +func addScopeEntryEndpoint(svc auth.Service) endpoint.Endpoint { + return func(ctx context.Context, request interface{}) (interface{}, error) { + req := request.(addPatScopeEntryReq) + if err := req.validate(); err != nil { + return nil, err + } + + scope, err := svc.AddScopeEntry(ctx, req.token, req.id, req.PlatformEntityType, req.OptionalDomainID, req.OptionalDomainEntityType, req.Operation, req.EntityIDs...) + if err != nil { + return nil, err + } + + return addPatScopeEntryRes{scope}, nil + } +} + +func removeScopeEntryEndpoint(svc auth.Service) endpoint.Endpoint { + return func(ctx context.Context, request interface{}) (interface{}, error) { + req := request.(removePatScopeEntryReq) + if err := req.validate(); err != nil { + return nil, err + } + + scope, err := svc.RemoveScopeEntry(ctx, req.token, req.id, req.PlatformEntityType, req.OptionalDomainID, req.OptionalDomainEntityType, req.Operation, req.EntityIDs...) + if err != nil { + return nil, err + } + return removePatScopeEntryRes{scope}, nil + } + +} + +func clearAllScopeEntryEndpoint(svc auth.Service) endpoint.Endpoint { + return func(ctx context.Context, request interface{}) (interface{}, error) { + req := request.(clearAllScopeEntryReq) + if err := req.validate(); err != nil { + return nil, err + } + + if err := svc.ClearAllScopeEntry(ctx, req.token, req.id); err != nil { + return nil, err + } + + return clearAllScopeEntryRes{}, nil + } +} + +func testCheckScopeEntryEndpoint(svc auth.Service) endpoint.Endpoint { + return func(ctx context.Context, request interface{}) (interface{}, error) { + req := request.(testCheckPatScopeReq) + if err := req.validate(); err != nil { + return nil, err + } + + if err := svc.TestCheckScopeEntry(ctx, req.token, req.PlatformEntityType, req.OptionalDomainID, req.OptionalDomainEntityType, req.Operation, req.EntityIDs...); err != nil { + return nil, err + } + + return revokePatSecretRes{}, nil + } +} diff --git a/auth/api/http/pats/requests.go b/auth/api/http/pats/requests.go new file mode 100644 index 0000000000..0724067412 --- /dev/null +++ b/auth/api/http/pats/requests.go @@ -0,0 +1,358 @@ +// Copyright (c) Abstract Machines +// SPDX-License-Identifier: Apache-2.0 + +package pats + +import ( + "encoding/json" + "strings" + "time" + + "github.com/absmach/magistrala/auth" + "github.com/absmach/magistrala/internal/apiutil" + "github.com/absmach/magistrala/pkg/errors" +) + +type createPatReq struct { + token string + Name string `json:"name,omitempty"` + Description string `json:"description,omitempty"` + Duration time.Duration `json:"duration,omitempty"` + Scope auth.Scope `json:"scope,omitempty"` +} + +func (cpr *createPatReq) UnmarshalJSON(data []byte) error { + var temp struct { + Name string `json:"name,omitempty"` + Description string `json:"description,omitempty"` + Duration string `json:"duration,omitempty"` + Scope auth.Scope `json:"scope,omitempty"` + } + if err := json.Unmarshal(data, &temp); err != nil { + return err + } + duration, err := time.ParseDuration(temp.Duration) + if err != nil { + return err + } + cpr.Name = temp.Name + cpr.Description = temp.Description + cpr.Duration = duration + cpr.Scope = temp.Scope + return nil +} + +func (req createPatReq) validate() (err error) { + if req.token == "" { + return apiutil.ErrBearerToken + } + + if strings.TrimSpace(req.Name) == "" { + return apiutil.ErrMissingName + } + + if _, err := time.ParseDuration(req.Description); err != nil { + return errors.Wrap(apiutil.ErrInvalidInterval, err) + } + return nil +} + +type retrievePatReq struct { + token string + id string +} + +func (req retrievePatReq) validate() (err error) { + if req.token == "" { + return apiutil.ErrBearerToken + } + if req.id == "" { + return apiutil.ErrMissingID + } + return nil +} + +type updatePatNameReq struct { + token string + id string + Name string `json:"name,omitempty"` +} + +func (req updatePatNameReq) validate() (err error) { + if req.token == "" { + return apiutil.ErrBearerToken + } + if req.id == "" { + return apiutil.ErrMissingID + } + if strings.TrimSpace(req.Name) == "" { + return apiutil.ErrMissingName + } + return nil +} + +type updatePatDescriptionReq struct { + token string + id string + Description string `json:"description,omitempty"` +} + +func (req updatePatDescriptionReq) validate() (err error) { + if req.token == "" { + return apiutil.ErrBearerToken + } + if req.id == "" { + return apiutil.ErrMissingID + } + if strings.TrimSpace(req.Description) == "" { + return apiutil.ErrMissingDescription + } + return nil +} + +type listPatsReq struct { + token string + offset uint64 + limit uint64 +} + +func (req listPatsReq) validate() (err error) { + if req.token == "" { + return apiutil.ErrBearerToken + } + return nil +} + +type deletePatReq struct { + token string + id string +} + +func (req deletePatReq) validate() (err error) { + if req.token == "" { + return apiutil.ErrBearerToken + } + if req.id == "" { + return apiutil.ErrMissingID + } + return nil +} + +type resetPatSecretReq struct { + token string + id string + Duration time.Duration `json:"duration,omitempty"` +} + +func (rspr *resetPatSecretReq) UnmarshalJSON(data []byte) error { + var temp struct { + Duration string `json:"duration,omitempty"` + } + + err := json.Unmarshal(data, &temp) + if err != nil { + return err + } + rspr.Duration, err = time.ParseDuration(temp.Duration) + if err != nil { + return err + } + return nil +} + +func (req resetPatSecretReq) validate() (err error) { + if req.token == "" { + return apiutil.ErrBearerToken + } + if req.id == "" { + return apiutil.ErrMissingID + } + return nil +} + +type revokePatSecretReq struct { + token string + id string +} + +func (req revokePatSecretReq) validate() (err error) { + if req.token == "" { + return apiutil.ErrBearerToken + } + if req.id == "" { + return apiutil.ErrMissingID + } + return nil +} + +type addPatScopeEntryReq struct { + token string + id string + PlatformEntityType auth.PlatformEntityType `json:"platform_entity_type,omitempty"` + OptionalDomainID string `json:"optional_domain_id,omitempty"` + OptionalDomainEntityType auth.DomainEntityType `json:"optional_domain_entity_type,omitempty"` + Operation auth.OperationType `json:"operation,omitempty"` + EntityIDs []string `json:"entity_ids,omitempty"` +} + +func (apser *addPatScopeEntryReq) UnmarshalJSON(data []byte) error { + var temp struct { + PlatformEntityType string `json:"platform_entity_type,omitempty"` + OptionalDomainID string `json:"optional_domain_id,omitempty"` + OptionalDomainEntityType string `json:"optional_domain_entity_type,omitempty"` + Operation string `json:"operation,omitempty"` + EntityIDs []string `json:"entity_ids,omitempty"` + } + + if err := json.Unmarshal(data, &temp); err != nil { + return err + } + + pet, err := auth.ParsePlatformEntityType(temp.PlatformEntityType) + if err != nil { + return err + } + odt, err := auth.ParseDomainEntityType(temp.OptionalDomainEntityType) + if err != nil { + return err + } + op, err := auth.ParseOperationType(temp.Operation) + if err != nil { + return err + } + apser.PlatformEntityType = pet + apser.OptionalDomainID = temp.OptionalDomainID + apser.OptionalDomainEntityType = odt + apser.Operation = op + apser.EntityIDs = temp.EntityIDs + return nil + +} +func (req addPatScopeEntryReq) validate() (err error) { + if req.token == "" { + return apiutil.ErrBearerToken + } + if req.id == "" { + return apiutil.ErrMissingID + } + return nil +} + +type removePatScopeEntryReq struct { + token string + id string + PlatformEntityType auth.PlatformEntityType `json:"platform_entity_type,omitempty"` + OptionalDomainID string `json:"optional_domain_id,omitempty"` + OptionalDomainEntityType auth.DomainEntityType `json:"optional_domain_entity_type,omitempty"` + Operation auth.OperationType `json:"operation,omitempty"` + EntityIDs []string `json:"entity_ids,omitempty"` +} + +func (rpser *removePatScopeEntryReq) UnmarshalJSON(data []byte) error { + var temp struct { + PlatformEntityType string `json:"platform_entity_type,omitempty"` + OptionalDomainID string `json:"optional_domain_id,omitempty"` + OptionalDomainEntityType string `json:"optional_domain_entity_type,omitempty"` + Operation string `json:"operation,omitempty"` + EntityIDs []string `json:"entity_ids,omitempty"` + } + + if err := json.Unmarshal(data, &temp); err != nil { + return err + } + + pet, err := auth.ParsePlatformEntityType(temp.PlatformEntityType) + if err != nil { + return err + } + odt, err := auth.ParseDomainEntityType(temp.OptionalDomainEntityType) + if err != nil { + return err + } + op, err := auth.ParseOperationType(temp.Operation) + if err != nil { + return err + } + rpser.PlatformEntityType = pet + rpser.OptionalDomainID = temp.OptionalDomainID + rpser.OptionalDomainEntityType = odt + rpser.Operation = op + rpser.EntityIDs = temp.EntityIDs + return nil + +} + +func (req removePatScopeEntryReq) validate() (err error) { + if req.token == "" { + return apiutil.ErrBearerToken + } + if req.id == "" { + return apiutil.ErrMissingID + } + return nil +} + +type clearAllScopeEntryReq struct { + token string + id string +} + +func (req clearAllScopeEntryReq) validate() (err error) { + if req.token == "" { + return apiutil.ErrBearerToken + } + if req.id == "" { + return apiutil.ErrMissingID + } + return nil +} + +type testCheckPatScopeReq struct { + token string + PlatformEntityType auth.PlatformEntityType `json:"platform_entity_type,omitempty"` + OptionalDomainID string `json:"optional_domain_id,omitempty"` + OptionalDomainEntityType auth.DomainEntityType `json:"optional_domain_entity_type,omitempty"` + Operation auth.OperationType `json:"operation,omitempty"` + EntityIDs []string `json:"entity_ids,omitempty"` +} + +func (tcpsr *testCheckPatScopeReq) UnmarshalJSON(data []byte) error { + var temp struct { + PlatformEntityType string `json:"platform_entity_type,omitempty"` + OptionalDomainID string `json:"optional_domain_id,omitempty"` + OptionalDomainEntityType string `json:"optional_domain_entity_type,omitempty"` + Operation string `json:"operation,omitempty"` + EntityIDs []string `json:"entity_ids,omitempty"` + } + + if err := json.Unmarshal(data, &temp); err != nil { + return err + } + + pet, err := auth.ParsePlatformEntityType(temp.PlatformEntityType) + if err != nil { + return err + } + odt, err := auth.ParseDomainEntityType(temp.OptionalDomainEntityType) + if err != nil { + return err + } + op, err := auth.ParseOperationType(temp.Operation) + if err != nil { + return err + } + tcpsr.PlatformEntityType = pet + tcpsr.OptionalDomainID = temp.OptionalDomainID + tcpsr.OptionalDomainEntityType = odt + tcpsr.Operation = op + tcpsr.EntityIDs = temp.EntityIDs + return nil +} + +func (req testCheckPatScopeReq) validate() (err error) { + if req.token == "" { + return apiutil.ErrBearerToken + } + + return nil +} diff --git a/auth/api/http/pats/responses.go b/auth/api/http/pats/responses.go new file mode 100644 index 0000000000..2f9020fc0f --- /dev/null +++ b/auth/api/http/pats/responses.go @@ -0,0 +1,208 @@ +// Copyright (c) Abstract Machines +// SPDX-License-Identifier: Apache-2.0 + +package pats + +import ( + "net/http" + + "github.com/absmach/magistrala" + "github.com/absmach/magistrala/auth" +) + +var ( + _ magistrala.Response = (*createPatRes)(nil) + _ magistrala.Response = (*retrievePatRes)(nil) + _ magistrala.Response = (*updatePatNameRes)(nil) + _ magistrala.Response = (*updatePatDescriptionRes)(nil) + _ magistrala.Response = (*deletePatRes)(nil) + _ magistrala.Response = (*resetPatSecretRes)(nil) + _ magistrala.Response = (*revokePatSecretRes)(nil) + _ magistrala.Response = (*addPatScopeEntryRes)(nil) + _ magistrala.Response = (*removePatScopeEntryRes)(nil) + _ magistrala.Response = (*clearAllScopeEntryRes)(nil) +) + +type createPatRes struct { + auth.PAT +} + +func (res createPatRes) Code() int { + return http.StatusCreated +} + +func (res createPatRes) Headers() map[string]string { + return map[string]string{} +} + +func (res createPatRes) Empty() bool { + return false +} + +type retrievePatRes struct { + auth.PAT +} + +func (res retrievePatRes) Code() int { + return http.StatusOK +} + +func (res retrievePatRes) Headers() map[string]string { + return map[string]string{} +} + +func (res retrievePatRes) Empty() bool { + return false +} + +type updatePatNameRes struct { + auth.PAT +} + +func (res updatePatNameRes) Code() int { + return http.StatusAccepted +} + +func (res updatePatNameRes) Headers() map[string]string { + return map[string]string{} +} + +func (res updatePatNameRes) Empty() bool { + return false +} + +type updatePatDescriptionRes struct { + auth.PAT +} + +func (res updatePatDescriptionRes) Code() int { + return http.StatusAccepted +} + +func (res updatePatDescriptionRes) Headers() map[string]string { + return map[string]string{} +} + +func (res updatePatDescriptionRes) Empty() bool { + return false +} + +type listPatsRes struct { + auth.PATSPage +} + +func (res listPatsRes) Code() int { + return http.StatusOK +} + +func (res listPatsRes) Headers() map[string]string { + return map[string]string{} +} + +func (res listPatsRes) Empty() bool { + return false +} + +type deletePatRes struct{} + +func (res deletePatRes) Code() int { + return http.StatusNoContent +} + +func (res deletePatRes) Headers() map[string]string { + return map[string]string{} +} + +func (res deletePatRes) Empty() bool { + return true +} + +type resetPatSecretRes struct { + auth.PAT +} + +func (res resetPatSecretRes) Code() int { + return http.StatusOK +} + +func (res resetPatSecretRes) Headers() map[string]string { + return map[string]string{} +} + +func (res resetPatSecretRes) Empty() bool { + return false +} + +type revokePatSecretRes struct{} + +func (res revokePatSecretRes) Code() int { + return http.StatusNoContent +} + +func (res revokePatSecretRes) Headers() map[string]string { + return map[string]string{} +} + +func (res revokePatSecretRes) Empty() bool { + return true +} + +type addPatScopeEntryRes struct { + auth.Scope +} + +func (res addPatScopeEntryRes) Code() int { + return http.StatusAccepted +} + +func (res addPatScopeEntryRes) Headers() map[string]string { + return map[string]string{} +} + +func (res addPatScopeEntryRes) Empty() bool { + return false +} + +type removePatScopeEntryRes struct { + auth.Scope +} + +func (res removePatScopeEntryRes) Code() int { + return http.StatusAccepted +} + +func (res removePatScopeEntryRes) Headers() map[string]string { + return map[string]string{} +} + +func (res removePatScopeEntryRes) Empty() bool { + return false +} + +type clearAllScopeEntryRes struct{} + +func (res clearAllScopeEntryRes) Code() int { + return http.StatusOK +} + +func (res clearAllScopeEntryRes) Headers() map[string]string { + return map[string]string{} +} + +func (res clearAllScopeEntryRes) Empty() bool { + return true +} + +type testCheckPatScopeRes struct{} + +func (res testCheckPatScopeRes) Code() int { + return http.StatusOK +} + +func (res testCheckPatScopeRes) Headers() map[string]string { + return map[string]string{} +} + +func (res testCheckPatScopeRes) Empty() bool { + return true +} diff --git a/auth/api/http/pats/transport.go b/auth/api/http/pats/transport.go new file mode 100644 index 0000000000..d1ce3190cd --- /dev/null +++ b/auth/api/http/pats/transport.go @@ -0,0 +1,277 @@ +// Copyright (c) Abstract Machines +// SPDX-License-Identifier: Apache-2.0 + +package pats + +import ( + "context" + "encoding/json" + "log/slog" + "net/http" + "strings" + + "github.com/absmach/magistrala/auth" + "github.com/absmach/magistrala/internal/api" + "github.com/absmach/magistrala/internal/apiutil" + "github.com/absmach/magistrala/pkg/errors" + "github.com/go-chi/chi/v5" + kithttp "github.com/go-kit/kit/transport/http" +) + +const ( + contentType = "application/json" + defInterval = "30d" +) + +// MakeHandler returns a HTTP handler for API endpoints. +func MakeHandler(svc auth.Service, mux *chi.Mux, logger *slog.Logger) *chi.Mux { + opts := []kithttp.ServerOption{ + kithttp.ServerErrorEncoder(apiutil.LoggingErrorEncoder(logger, api.EncodeError)), + } + mux.Route("/pats", func(r chi.Router) { + r.Post("/", kithttp.NewServer( + createPatEndpoint(svc), + decodeCreatePatRequest, + api.EncodeResponse, + opts..., + ).ServeHTTP) + + r.Get("/{id}", kithttp.NewServer( + (retrieveEndpoint(svc)), + decodeRetrievePatRequest, + api.EncodeResponse, + opts..., + ).ServeHTTP) + + r.Put("/{id}/name", kithttp.NewServer( + (updateNameEndpoint(svc)), + decodeUpdateNameRequest, + api.EncodeResponse, + opts..., + ).ServeHTTP) + + r.Put("/{id}/description", kithttp.NewServer( + (updateDescriptionEndpoint(svc)), + decodeUpdateDescriptionRequest, + api.EncodeResponse, + opts..., + ).ServeHTTP) + + r.Get("/", kithttp.NewServer( + (listEndpoint(svc)), + decodeListRequest, + api.EncodeResponse, + opts..., + ).ServeHTTP) + + r.Delete("/{id}", kithttp.NewServer( + (deleteEndpoint(svc)), + decodeDeleteRequest, + api.EncodeResponse, + opts..., + ).ServeHTTP) + + r.Put("/{id}/secret/reset", kithttp.NewServer( + (resetSecretEndpoint(svc)), + decodeResetSecretRequest, + api.EncodeResponse, + opts..., + ).ServeHTTP) + + r.Put("/{id}/secret/revoke", kithttp.NewServer( + (revokeSecretEndpoint(svc)), + decodeRevokeSecretRequest, + api.EncodeResponse, + opts..., + ).ServeHTTP) + + r.Put("/{id}/scope/add", kithttp.NewServer( + (addScopeEntryEndpoint(svc)), + decodeAddScopeEntryRequest, + api.EncodeResponse, + opts..., + ).ServeHTTP) + + r.Put("/{id}/scope/remove", kithttp.NewServer( + (removeScopeEntryEndpoint(svc)), + decodeRemoveScopeEntryRequest, + api.EncodeResponse, + opts..., + ).ServeHTTP) + + r.Delete("/{id}/scope", kithttp.NewServer( + (clearAllScopeEntryEndpoint(svc)), + decodeClearAllScopeEntryRequest, + api.EncodeResponse, + opts..., + ).ServeHTTP) + + r.Get("/check", kithttp.NewServer( + (testCheckScopeEntryEndpoint(svc)), + decodeTestCheckScopeEntryRequest, + api.EncodeResponse, + opts..., + ).ServeHTTP) + }) + return mux +} + +func decodeCreatePatRequest(_ context.Context, r *http.Request) (interface{}, error) { + if !strings.Contains(r.Header.Get("Content-Type"), contentType) { + return nil, apiutil.ErrUnsupportedContentType + } + + req := createPatReq{token: apiutil.ExtractBearerToken(r)} + if err := json.NewDecoder(r.Body).Decode(&req); err != nil { + return nil, errors.Wrap(apiutil.ErrValidation, errors.Wrap(err, errors.ErrMalformedEntity)) + } + return req, nil +} + +func decodeRetrievePatRequest(_ context.Context, r *http.Request) (interface{}, error) { + req := retrievePatReq{ + token: apiutil.ExtractBearerToken(r), + id: chi.URLParam(r, "id"), + } + return req, nil +} + +func decodeUpdateNameRequest(_ context.Context, r *http.Request) (interface{}, error) { + if !strings.Contains(r.Header.Get("Content-Type"), contentType) { + return nil, apiutil.ErrUnsupportedContentType + } + + req := updatePatNameReq{ + token: apiutil.ExtractBearerToken(r), + id: chi.URLParam(r, "id"), + } + if err := json.NewDecoder(r.Body).Decode(&req); err != nil { + return nil, errors.Wrap(errors.ErrMalformedEntity, err) + } + return req, nil +} + +func decodeUpdateDescriptionRequest(_ context.Context, r *http.Request) (interface{}, error) { + if !strings.Contains(r.Header.Get("Content-Type"), contentType) { + return nil, apiutil.ErrUnsupportedContentType + } + + req := updatePatDescriptionReq{ + token: apiutil.ExtractBearerToken(r), + id: chi.URLParam(r, "id"), + } + if err := json.NewDecoder(r.Body).Decode(&req); err != nil { + return nil, errors.Wrap(errors.ErrMalformedEntity, err) + } + return req, nil +} + +func decodeListRequest(_ context.Context, r *http.Request) (interface{}, error) { + l, err := apiutil.ReadNumQuery[uint64](r, api.LimitKey, api.DefLimit) + if err != nil { + return nil, errors.Wrap(apiutil.ErrValidation, err) + } + o, err := apiutil.ReadNumQuery[uint64](r, api.OffsetKey, api.DefOffset) + if err != nil { + return nil, errors.Wrap(apiutil.ErrValidation, err) + } + req := listPatsReq{ + token: apiutil.ExtractBearerToken(r), + limit: l, + offset: o, + } + if err := json.NewDecoder(r.Body).Decode(&req); err != nil { + return nil, errors.Wrap(errors.ErrMalformedEntity, err) + } + return req, nil +} + +func decodeDeleteRequest(_ context.Context, r *http.Request) (interface{}, error) { + if !strings.Contains(r.Header.Get("Content-Type"), contentType) { + return nil, apiutil.ErrUnsupportedContentType + } + + return deletePatReq{ + token: apiutil.ExtractBearerToken(r), + id: chi.URLParam(r, "id"), + }, nil +} + +func decodeResetSecretRequest(_ context.Context, r *http.Request) (interface{}, error) { + if !strings.Contains(r.Header.Get("Content-Type"), contentType) { + return nil, apiutil.ErrUnsupportedContentType + } + + req := resetPatSecretReq{ + token: apiutil.ExtractBearerToken(r), + id: chi.URLParam(r, "id"), + } + if err := json.NewDecoder(r.Body).Decode(&req); err != nil { + return nil, errors.Wrap(errors.ErrMalformedEntity, err) + } + return req, nil +} + +func decodeRevokeSecretRequest(_ context.Context, r *http.Request) (interface{}, error) { + if !strings.Contains(r.Header.Get("Content-Type"), contentType) { + return nil, apiutil.ErrUnsupportedContentType + } + + return revokePatSecretReq{ + token: apiutil.ExtractBearerToken(r), + id: chi.URLParam(r, "id"), + }, nil +} + +func decodeAddScopeEntryRequest(_ context.Context, r *http.Request) (interface{}, error) { + if !strings.Contains(r.Header.Get("Content-Type"), contentType) { + return nil, apiutil.ErrUnsupportedContentType + } + + req := addPatScopeEntryReq{ + token: apiutil.ExtractBearerToken(r), + id: chi.URLParam(r, "id"), + } + if err := json.NewDecoder(r.Body).Decode(&req); err != nil { + return nil, errors.Wrap(errors.ErrMalformedEntity, err) + } + return req, nil +} + +func decodeRemoveScopeEntryRequest(_ context.Context, r *http.Request) (interface{}, error) { + if !strings.Contains(r.Header.Get("Content-Type"), contentType) { + return nil, apiutil.ErrUnsupportedContentType + } + + req := removePatScopeEntryReq{ + token: apiutil.ExtractBearerToken(r), + id: chi.URLParam(r, "id"), + } + if err := json.NewDecoder(r.Body).Decode(&req); err != nil { + return nil, errors.Wrap(errors.ErrMalformedEntity, err) + } + return req, nil +} + +func decodeClearAllScopeEntryRequest(_ context.Context, r *http.Request) (interface{}, error) { + if !strings.Contains(r.Header.Get("Content-Type"), contentType) { + return nil, apiutil.ErrUnsupportedContentType + } + + return clearAllScopeEntryReq{ + token: apiutil.ExtractBearerToken(r), + id: chi.URLParam(r, "id"), + }, nil +} + +func decodeTestCheckScopeEntryRequest(_ context.Context, r *http.Request) (interface{}, error) { + if !strings.Contains(r.Header.Get("Content-Type"), contentType) { + return nil, apiutil.ErrUnsupportedContentType + } + + req := testCheckPatScopeReq{token: apiutil.ExtractBearerToken(r)} + if err := json.NewDecoder(r.Body).Decode(&req); err != nil { + return nil, errors.Wrap(errors.ErrMalformedEntity, err) + } + return req, nil +} diff --git a/auth/api/logging.go b/auth/api/logging.go index 7240af2d27..fe984f969d 100644 --- a/auth/api/logging.go +++ b/auth/api/logging.go @@ -507,3 +507,233 @@ func (lm *loggingMiddleware) DeleteEntityPolicies(ctx context.Context, entityTyp }(time.Now()) return lm.svc.DeleteEntityPolicies(ctx, entityType, id) } + +func (lm *loggingMiddleware) Create(ctx context.Context, token, name, description string, duration time.Duration, scope auth.Scope) (pa auth.PAT, err error) { + defer func(begin time.Time) { + args := []any{ + slog.String("duration", time.Since(begin).String()), + slog.String("name", name), + slog.String("description", description), + slog.String("pat_duration", duration.String()), + slog.String("scope", scope.String()), + } + if err != nil { + args = append(args, slog.Any("error", err)) + lm.logger.Warn("Create PAT failed to complete successfully", args...) + return + } + lm.logger.Info("Create PAT completed successfully", args...) + }(time.Now()) + return lm.svc.Create(ctx, token, name, description, duration, scope) +} +func (lm *loggingMiddleware) UpdateName(ctx context.Context, token, patID, name string) (pa auth.PAT, err error) { + defer func(begin time.Time) { + args := []any{ + slog.String("duration", time.Since(begin).String()), + slog.String("pat_id", patID), + slog.String("name", name), + } + if err != nil { + args = append(args, slog.Any("error", err)) + lm.logger.Warn("Update PAT name failed to complete successfully", args...) + return + } + lm.logger.Info("Update PAT name completed successfully", args...) + }(time.Now()) + return lm.svc.UpdateName(ctx, token, patID, name) +} +func (lm *loggingMiddleware) UpdateDescription(ctx context.Context, token, patID, description string) (pa auth.PAT, err error) { + defer func(begin time.Time) { + args := []any{ + slog.String("duration", time.Since(begin).String()), + slog.String("pat_id", patID), + slog.String("description", description), + } + if err != nil { + args = append(args, slog.Any("error", err)) + lm.logger.Warn("Update PAT description failed to complete successfully", args...) + return + } + lm.logger.Info("Update PAT description completed successfully", args...) + }(time.Now()) + return lm.svc.UpdateDescription(ctx, token, patID, description) +} +func (lm *loggingMiddleware) Retrieve(ctx context.Context, token, patID string) (pa auth.PAT, err error) { + defer func(begin time.Time) { + args := []any{ + slog.String("duration", time.Since(begin).String()), + slog.String("pat_id", patID), + } + if err != nil { + args = append(args, slog.Any("error", err)) + lm.logger.Warn("Retrieve PAT failed to complete successfully", args...) + return + } + lm.logger.Info("Retrieve PAT completed successfully", args...) + }(time.Now()) + return lm.svc.Retrieve(ctx, token, patID) +} +func (lm *loggingMiddleware) List(ctx context.Context, token string, pm auth.PATSPageMeta) (pp auth.PATSPage, err error) { + defer func(begin time.Time) { + args := []any{ + slog.String("duration", time.Since(begin).String()), + slog.Uint64("limit", pm.Limit), + slog.Uint64("offset", pm.Offset), + } + if err != nil { + args = append(args, slog.Any("error", err)) + lm.logger.Warn("List PATS failed to complete successfully", args...) + return + } + lm.logger.Info("List PATS completed successfully", args...) + }(time.Now()) + return lm.svc.List(ctx, token, pm) +} +func (lm *loggingMiddleware) Delete(ctx context.Context, token, patID string) (err error) { + defer func(begin time.Time) { + args := []any{ + slog.String("duration", time.Since(begin).String()), + slog.String("pat_id", patID), + } + if err != nil { + args = append(args, slog.Any("error", err)) + lm.logger.Warn("Delete PAT failed to complete successfully", args...) + return + } + lm.logger.Info("Delete PAT completed successfully", args...) + }(time.Now()) + return lm.svc.Delete(ctx, token, patID) +} +func (lm *loggingMiddleware) ResetSecret(ctx context.Context, token, patID string, duration time.Duration) (pa auth.PAT, err error) { + defer func(begin time.Time) { + args := []any{ + slog.String("duration", time.Since(begin).String()), + slog.String("pat_id", patID), + slog.String("pat_duration", duration.String()), + } + if err != nil { + args = append(args, slog.Any("error", err)) + lm.logger.Warn("Reset PAT secret failed to complete successfully", args...) + return + } + lm.logger.Info("Reset PAT secret completed successfully", args...) + }(time.Now()) + return lm.svc.ResetSecret(ctx, token, patID, duration) +} +func (lm *loggingMiddleware) RevokeSecret(ctx context.Context, token, patID string) (err error) { + defer func(begin time.Time) { + args := []any{ + slog.String("duration", time.Since(begin).String()), + slog.String("pat_id", patID), + } + if err != nil { + args = append(args, slog.Any("error", err)) + lm.logger.Warn("Revoke PAT secret failed to complete successfully", args...) + return + } + lm.logger.Info("Revoke PAT secret completed successfully", args...) + }(time.Now()) + return lm.svc.RevokeSecret(ctx, token, patID) +} +func (lm *loggingMiddleware) AddScopeEntry(ctx context.Context, token, patID string, platformEntityType auth.PlatformEntityType, optionalDomainID string, optionalDomainEntityType auth.DomainEntityType, operation auth.OperationType, entityIDs ...string) (sc auth.Scope, err error) { + defer func(begin time.Time) { + args := []any{ + slog.String("duration", time.Since(begin).String()), + slog.String("pat_id", patID), + slog.String("platform_entity_type", platformEntityType.String()), + slog.String("optional_domain_id", optionalDomainID), + slog.String("optional_domain_entity_type", optionalDomainEntityType.String()), + slog.String("operation", operation.String()), + slog.Any("entities", entityIDs), + } + if err != nil { + args = append(args, slog.Any("error", err)) + lm.logger.Warn("Add entry to PAT scope failed to complete successfully", args...) + return + } + lm.logger.Info("Add entry to PAT scope completed successfully", args...) + }(time.Now()) + return lm.svc.AddScopeEntry(ctx, token, patID, platformEntityType, optionalDomainID, optionalDomainEntityType, operation, entityIDs...) +} +func (lm *loggingMiddleware) RemoveScopeEntry(ctx context.Context, token, patID string, platformEntityType auth.PlatformEntityType, optionalDomainID string, optionalDomainEntityType auth.DomainEntityType, operation auth.OperationType, entityIDs ...string) (sc auth.Scope, err error) { + defer func(begin time.Time) { + args := []any{ + slog.String("duration", time.Since(begin).String()), + slog.String("pat_id", patID), + slog.String("platform_entity_type", platformEntityType.String()), + slog.String("optional_domain_id", optionalDomainID), + slog.String("optional_domain_entity_type", optionalDomainEntityType.String()), + slog.String("operation", operation.String()), + slog.Any("entities", entityIDs), + } + if err != nil { + args = append(args, slog.Any("error", err)) + lm.logger.Warn("Remove entry from PAT scope failed to complete successfully", args...) + return + } + lm.logger.Info("Remove entry from PAT scope completed successfully", args...) + }(time.Now()) + return lm.svc.RemoveScopeEntry(ctx, token, patID, platformEntityType, optionalDomainID, optionalDomainEntityType, operation, entityIDs...) +} +func (lm *loggingMiddleware) ClearAllScopeEntry(ctx context.Context, token, patID string) (err error) { + defer func(begin time.Time) { + args := []any{ + slog.String("duration", time.Since(begin).String()), + slog.String("pat_id", patID), + } + if err != nil { + args = append(args, slog.Any("error", err)) + lm.logger.Warn("Clear all entry from PAT scope failed to complete successfully", args...) + return + } + lm.logger.Info("Clear all entry from PAT scope completed successfully", args...) + }(time.Now()) + return lm.svc.ClearAllScopeEntry(ctx, token, patID) +} +func (lm *loggingMiddleware) TestCheckScopeEntry(ctx context.Context, paToken string, platformEntityType auth.PlatformEntityType, optionalDomainID string, optionalDomainEntityType auth.DomainEntityType, operation auth.OperationType, entityIDs ...string) (err error) { + defer func(begin time.Time) { + args := []any{ + slog.String("duration", time.Since(begin).String()), + slog.String("platform_entity_type", platformEntityType.String()), + slog.String("optional_domain_id", optionalDomainID), + slog.String("optional_domain_entity_type", optionalDomainEntityType.String()), + slog.String("operation", operation.String()), + slog.Any("entities", entityIDs), + } + if err != nil { + args = append(args, slog.Any("error", err)) + lm.logger.Warn("Test Check entry in PAT scope failed complete successfully", args...) + return + } + lm.logger.Info("Test Check entry in PAT scope completed successfully", args...) + }(time.Now()) + return lm.svc.TestCheckScopeEntry(ctx, paToken, platformEntityType, optionalDomainID, optionalDomainEntityType, operation, entityIDs...) +} +func (lm *loggingMiddleware) IdentifyPAT(ctx context.Context, paToken string) (pa auth.PAT, err error) { + defer func(begin time.Time) { + args := []any{ + slog.String("duration", time.Since(begin).String()), + } + if err != nil { + args = append(args, slog.Any("error", err)) + lm.logger.Warn("Identify PAT failed to complete successfully", args...) + return + } + lm.logger.Info("Identify PAT completed successfully", args...) + }(time.Now()) + return lm.svc.IdentifyPAT(ctx, paToken) +} +func (lm *loggingMiddleware) AuthorizePAT(ctx context.Context, paToken string) (pa auth.PAT, err error) { + defer func(begin time.Time) { + args := []any{ + slog.String("duration", time.Since(begin).String()), + } + if err != nil { + args = append(args, slog.Any("error", err)) + lm.logger.Warn("Authorize PAT failed to complete successfully", args...) + return + } + lm.logger.Info("Authorize PAT completed successfully", args...) + }(time.Now()) + return lm.svc.AuthorizePAT(ctx, paToken) +} diff --git a/auth/api/metrics.go b/auth/api/metrics.go index 8ed201a82d..722846ffa0 100644 --- a/auth/api/metrics.go +++ b/auth/api/metrics.go @@ -247,3 +247,113 @@ func (ms *metricsMiddleware) DeleteEntityPolicies(ctx context.Context, entityTyp }(time.Now()) return ms.svc.DeleteEntityPolicies(ctx, entityType, id) } + +func (ms *metricsMiddleware) Create(ctx context.Context, token, name, description string, duration time.Duration, scope auth.Scope) (auth.PAT, error) { + defer func(begin time.Time) { + ms.counter.With("method", "create_pat").Add(1) + ms.latency.With("method", "create_pat").Observe(time.Since(begin).Seconds()) + }(time.Now()) + return ms.svc.Create(ctx, token, name, description, duration, scope) +} + +func (ms *metricsMiddleware) UpdateName(ctx context.Context, token, patID, name string) (auth.PAT, error) { + defer func(begin time.Time) { + ms.counter.With("method", "update_pat_name").Add(1) + ms.latency.With("method", "update_pat_name").Observe(time.Since(begin).Seconds()) + }(time.Now()) + return ms.svc.UpdateName(ctx, token, patID, name) +} + +func (ms *metricsMiddleware) UpdateDescription(ctx context.Context, token, patID, description string) (auth.PAT, error) { + defer func(begin time.Time) { + ms.counter.With("method", "update_pat_description").Add(1) + ms.latency.With("method", "update_pat_description").Observe(time.Since(begin).Seconds()) + }(time.Now()) + return ms.svc.UpdateDescription(ctx, token, patID, description) +} + +func (ms *metricsMiddleware) Retrieve(ctx context.Context, token, patID string) (auth.PAT, error) { + defer func(begin time.Time) { + ms.counter.With("method", "retrieve_pat").Add(1) + ms.latency.With("method", "retrieve_pat").Observe(time.Since(begin).Seconds()) + }(time.Now()) + return ms.svc.Retrieve(ctx, token, patID) +} + +func (ms *metricsMiddleware) List(ctx context.Context, token string, pm auth.PATSPageMeta) (auth.PATSPage, error) { + defer func(begin time.Time) { + ms.counter.With("method", "list_pats").Add(1) + ms.latency.With("method", "list_pats").Observe(time.Since(begin).Seconds()) + }(time.Now()) + return ms.svc.List(ctx, token, pm) +} +func (ms *metricsMiddleware) Delete(ctx context.Context, token, patID string) error { + defer func(begin time.Time) { + ms.counter.With("method", "delete_pat").Add(1) + ms.latency.With("method", "delete_pat").Observe(time.Since(begin).Seconds()) + }(time.Now()) + return ms.svc.Delete(ctx, token, patID) +} + +func (ms *metricsMiddleware) ResetSecret(ctx context.Context, token, patID string, duration time.Duration) (auth.PAT, error) { + defer func(begin time.Time) { + ms.counter.With("method", "reset_pat_secret").Add(1) + ms.latency.With("method", "reset_pat_secret").Observe(time.Since(begin).Seconds()) + }(time.Now()) + return ms.svc.ResetSecret(ctx, token, patID, duration) +} + +func (ms *metricsMiddleware) RevokeSecret(ctx context.Context, token, patID string) error { + defer func(begin time.Time) { + ms.counter.With("method", "revoke_pat_secret").Add(1) + ms.latency.With("method", "revoke_pat_secret").Observe(time.Since(begin).Seconds()) + }(time.Now()) + return ms.svc.RevokeSecret(ctx, token, patID) +} +func (ms *metricsMiddleware) AddScopeEntry(ctx context.Context, token, patID string, platformEntityType auth.PlatformEntityType, optionalDomainID string, optionalDomainEntityType auth.DomainEntityType, operation auth.OperationType, entityIDs ...string) (auth.Scope, error) { + defer func(begin time.Time) { + ms.counter.With("method", "add_pat_scope_entry").Add(1) + ms.latency.With("method", "add_pat_scope_entry").Observe(time.Since(begin).Seconds()) + }(time.Now()) + return ms.svc.AddScopeEntry(ctx, token, patID, platformEntityType, optionalDomainID, optionalDomainEntityType, operation, entityIDs...) +} + +func (ms *metricsMiddleware) RemoveScopeEntry(ctx context.Context, token, patID string, platformEntityType auth.PlatformEntityType, optionalDomainID string, optionalDomainEntityType auth.DomainEntityType, operation auth.OperationType, entityIDs ...string) (auth.Scope, error) { + defer func(begin time.Time) { + ms.counter.With("method", "remove_pat_scope_entry").Add(1) + ms.latency.With("method", "remove_pat_scope_entry").Observe(time.Since(begin).Seconds()) + }(time.Now()) + return ms.svc.RemoveScopeEntry(ctx, token, patID, platformEntityType, optionalDomainID, optionalDomainEntityType, operation, entityIDs...) +} + +func (ms *metricsMiddleware) ClearAllScopeEntry(ctx context.Context, token, patID string) error { + defer func(begin time.Time) { + ms.counter.With("method", "clear_pat_all_scope_entry").Add(1) + ms.latency.With("method", "clear_pat_all_scope_entry").Observe(time.Since(begin).Seconds()) + }(time.Now()) + return ms.svc.ClearAllScopeEntry(ctx, token, patID) +} + +func (ms *metricsMiddleware) TestCheckScopeEntry(ctx context.Context, paToken string, platformEntityType auth.PlatformEntityType, optionalDomainID string, optionalDomainEntityType auth.DomainEntityType, operation auth.OperationType, entityIDs ...string) error { + defer func(begin time.Time) { + ms.counter.With("method", "test_check_pat_scope_entry").Add(1) + ms.latency.With("method", "test_check_pat_scope_entry").Observe(time.Since(begin).Seconds()) + }(time.Now()) + return ms.svc.TestCheckScopeEntry(ctx, paToken, platformEntityType, optionalDomainID, optionalDomainEntityType, operation, entityIDs...) +} + +func (ms *metricsMiddleware) IdentifyPAT(ctx context.Context, paToken string) (auth.PAT, error) { + defer func(begin time.Time) { + ms.counter.With("method", "identify_pat").Add(1) + ms.latency.With("method", "identify_pat").Observe(time.Since(begin).Seconds()) + }(time.Now()) + return ms.svc.IdentifyPAT(ctx, paToken) +} + +func (ms *metricsMiddleware) AuthorizePAT(ctx context.Context, paToken string) (auth.PAT, error) { + defer func(begin time.Time) { + ms.counter.With("method", "authorize_pat").Add(1) + ms.latency.With("method", "authorize_pat").Observe(time.Since(begin).Seconds()) + }(time.Now()) + return ms.svc.AuthorizePAT(ctx, paToken) +} diff --git a/auth/bolt/pat.go b/auth/bolt/pat.go index 82e414848d..940b6cc8a1 100644 --- a/auth/bolt/pat.go +++ b/auth/bolt/pat.go @@ -23,7 +23,7 @@ const ( userKey = "user" nameKey = "name" descriptionKey = "description" - tokenHashKey = "token_hash" + secretKey = "secret_key" scopeKey = "scope" issuedAtKey = "issued_at" expiresAtKey = "expires_at" @@ -32,6 +32,7 @@ const ( revokedKey = "revoked" revokedAtKey = "revoked_at" platformEntitiesKey = "platform_entities" + patKey = "pat" keySeparator = ":" anyID = "*" @@ -76,6 +77,9 @@ func (pr *patRepo) Save(ctx context.Context, pat auth.PAT) error { return errors.Wrap(repoerr.ErrCreateEntity, err) } } + if err := b.Put([]byte(pat.User+keySeparator+patKey+pat.ID), []byte(pat.ID)); err != nil { + return errors.Wrap(repoerr.ErrCreateEntity, err) + } return nil }) } @@ -116,7 +120,7 @@ func (pr *patRepo) UpdateTokenHash(ctx context.Context, userID, patID, tokenHash if err != nil { return errors.Wrap(repoerr.ErrUpdateEntity, err) } - if err := b.Put([]byte(patID+keySeparator+tokenHashKey), []byte(tokenHash)); err != nil { + if err := b.Put([]byte(patID+keySeparator+secretKey), []byte(tokenHash)); err != nil { return errors.Wrap(repoerr.ErrUpdateEntity, err) } if err := b.Put([]byte(patID+keySeparator+expiresAtKey), timeToBytes(expiryAt)); err != nil { @@ -133,8 +137,57 @@ func (pr *patRepo) UpdateTokenHash(ctx context.Context, userID, patID, tokenHash return keyValueToPAT(kv) } -func (pr *patRepo) RetrieveAll(ctx context.Context, userID string) (pats auth.PATSPage, err error) { - return auth.PATSPage{}, nil +func (pr *patRepo) RetrieveAll(ctx context.Context, userID string, pm auth.PATSPageMeta) (auth.PATSPage, error) { + prefix := []byte(userID + keySeparator + patKey + keySeparator) + + patIDs := []string{} + if err := pr.db.Update(func(tx *bolt.Tx) error { + b, err := pr.retrieveUserBucket(tx, userID) + if err != nil { + return errors.Wrap(repoerr.ErrRemoveEntity, err) + } + c := b.Cursor() + for k, v := c.Seek(prefix); k != nil && bytes.HasPrefix(k, prefix); k, v = c.Next() { + if v != nil { + patIDs = append(patIDs, string(v)) + } + } + return nil + }); err != nil { + return auth.PATSPage{}, err + } + + total := len(patIDs) + + var pats []auth.PAT + + var patsPage = auth.PATSPage{ + Total: uint64(total), + Limit: pm.Limit, + Offset: pm.Offset, + PATS: pats, + } + + if int(pm.Offset) >= total { + return patsPage, nil + } + + aLimit := pm.Limit + if rLimit := total - int(pm.Offset); int(pm.Limit) > rLimit { + aLimit = uint64(rLimit) + } + + for i := pm.Offset; i < aLimit; i++ { + if total < int(i) { + pat, err := pr.Retrieve(ctx, userID, patIDs[i]) + if err != nil { + return patsPage, err + } + patsPage.PATS = append(patsPage.PATS, pat) + } + } + + return patsPage, nil } func (pr *patRepo) Revoke(ctx context.Context, userID, patID string) error { @@ -320,7 +373,7 @@ func patToKeyValue(pat auth.PAT) (map[string][]byte, error) { userKey: []byte(pat.User), nameKey: []byte(pat.Name), descriptionKey: []byte(pat.Description), - tokenHashKey: []byte(pat.Token), + secretKey: []byte(pat.Secret), issuedAtKey: timeToBytes(pat.IssuedAt), expiresAtKey: timeToBytes(pat.ExpiresAt), updatedAtKey: timeToBytes(pat.UpdatedAt), diff --git a/auth/mocks/pats.go b/auth/mocks/pats.go index cfa65fd845..9f15fdf508 100644 --- a/auth/mocks/pats.go +++ b/auth/mocks/pats.go @@ -19,8 +19,8 @@ type PATS struct { mock.Mock } -// AddScope provides a mock function with given fields: ctx, token, patID, platformEntityType, optionalDomainID, optionalDomainEntityType, operation, entityIDs -func (_m *PATS) AddScope(ctx context.Context, token string, patID string, platformEntityType auth.PlatformEntityType, optionalDomainID string, optionalDomainEntityType auth.DomainEntityType, operation auth.OperationType, entityIDs ...string) (auth.Scope, error) { +// AddScopeEntry provides a mock function with given fields: ctx, token, patID, platformEntityType, optionalDomainID, optionalDomainEntityType, operation, entityIDs +func (_m *PATS) AddScopeEntry(ctx context.Context, token string, patID string, platformEntityType auth.PlatformEntityType, optionalDomainID string, optionalDomainEntityType auth.DomainEntityType, operation auth.OperationType, entityIDs ...string) (auth.Scope, error) { _va := make([]interface{}, len(entityIDs)) for _i := range entityIDs { _va[_i] = entityIDs[_i] @@ -31,7 +31,7 @@ func (_m *PATS) AddScope(ctx context.Context, token string, patID string, platfo ret := _m.Called(_ca...) if len(ret) == 0 { - panic("no return value specified for AddScope") + panic("no return value specified for AddScopeEntry") } var r0 auth.Scope @@ -82,12 +82,12 @@ func (_m *PATS) AuthorizePAT(ctx context.Context, paToken string) (auth.PAT, err return r0, r1 } -// ClearAllScope provides a mock function with given fields: ctx, token, patID -func (_m *PATS) ClearAllScope(ctx context.Context, token string, patID string) error { +// ClearAllScopeEntry provides a mock function with given fields: ctx, token, patID +func (_m *PATS) ClearAllScopeEntry(ctx context.Context, token string, patID string) error { ret := _m.Called(ctx, token, patID) if len(ret) == 0 { - panic("no return value specified for ClearAllScope") + panic("no return value specified for ClearAllScopeEntry") } var r0 error @@ -174,9 +174,9 @@ func (_m *PATS) IdentifyPAT(ctx context.Context, paToken string) (auth.PAT, erro return r0, r1 } -// List provides a mock function with given fields: ctx, token -func (_m *PATS) List(ctx context.Context, token string) (auth.PATSPage, error) { - ret := _m.Called(ctx, token) +// List provides a mock function with given fields: ctx, token, pm +func (_m *PATS) List(ctx context.Context, token string, pm auth.PATSPageMeta) (auth.PATSPage, error) { + ret := _m.Called(ctx, token, pm) if len(ret) == 0 { panic("no return value specified for List") @@ -184,17 +184,17 @@ func (_m *PATS) List(ctx context.Context, token string) (auth.PATSPage, error) { var r0 auth.PATSPage var r1 error - if rf, ok := ret.Get(0).(func(context.Context, string) (auth.PATSPage, error)); ok { - return rf(ctx, token) + if rf, ok := ret.Get(0).(func(context.Context, string, auth.PATSPageMeta) (auth.PATSPage, error)); ok { + return rf(ctx, token, pm) } - if rf, ok := ret.Get(0).(func(context.Context, string) auth.PATSPage); ok { - r0 = rf(ctx, token) + if rf, ok := ret.Get(0).(func(context.Context, string, auth.PATSPageMeta) auth.PATSPage); ok { + r0 = rf(ctx, token, pm) } else { r0 = ret.Get(0).(auth.PATSPage) } - if rf, ok := ret.Get(1).(func(context.Context, string) error); ok { - r1 = rf(ctx, token) + if rf, ok := ret.Get(1).(func(context.Context, string, auth.PATSPageMeta) error); ok { + r1 = rf(ctx, token, pm) } else { r1 = ret.Error(1) } @@ -202,8 +202,8 @@ func (_m *PATS) List(ctx context.Context, token string) (auth.PATSPage, error) { return r0, r1 } -// RemoveScope provides a mock function with given fields: ctx, token, patID, platformEntityType, optionalDomainID, optionalDomainEntityType, operation, entityIDs -func (_m *PATS) RemoveScope(ctx context.Context, token string, patID string, platformEntityType auth.PlatformEntityType, optionalDomainID string, optionalDomainEntityType auth.DomainEntityType, operation auth.OperationType, entityIDs ...string) (auth.Scope, error) { +// RemoveScopeEntry provides a mock function with given fields: ctx, token, patID, platformEntityType, optionalDomainID, optionalDomainEntityType, operation, entityIDs +func (_m *PATS) RemoveScopeEntry(ctx context.Context, token string, patID string, platformEntityType auth.PlatformEntityType, optionalDomainID string, optionalDomainEntityType auth.DomainEntityType, operation auth.OperationType, entityIDs ...string) (auth.Scope, error) { _va := make([]interface{}, len(entityIDs)) for _i := range entityIDs { _va[_i] = entityIDs[_i] @@ -214,7 +214,7 @@ func (_m *PATS) RemoveScope(ctx context.Context, token string, patID string, pla ret := _m.Called(_ca...) if len(ret) == 0 { - panic("no return value specified for RemoveScope") + panic("no return value specified for RemoveScopeEntry") } var r0 auth.Scope @@ -311,8 +311,8 @@ func (_m *PATS) RevokeSecret(ctx context.Context, token string, patID string) er return r0 } -// TestCheckScope provides a mock function with given fields: ctx, paToken, platformEntityType, optionalDomainID, optionalDomainEntityType, operation, entityIDs -func (_m *PATS) TestCheckScope(ctx context.Context, paToken string, platformEntityType auth.PlatformEntityType, optionalDomainID string, optionalDomainEntityType auth.DomainEntityType, operation auth.OperationType, entityIDs ...string) error { +// TestCheckScopeEntry provides a mock function with given fields: ctx, paToken, platformEntityType, optionalDomainID, optionalDomainEntityType, operation, entityIDs +func (_m *PATS) TestCheckScopeEntry(ctx context.Context, paToken string, platformEntityType auth.PlatformEntityType, optionalDomainID string, optionalDomainEntityType auth.DomainEntityType, operation auth.OperationType, entityIDs ...string) error { _va := make([]interface{}, len(entityIDs)) for _i := range entityIDs { _va[_i] = entityIDs[_i] @@ -323,7 +323,7 @@ func (_m *PATS) TestCheckScope(ctx context.Context, paToken string, platformEnti ret := _m.Called(_ca...) if len(ret) == 0 { - panic("no return value specified for TestCheckScope") + panic("no return value specified for TestCheckScopeEntry") } var r0 error diff --git a/auth/mocks/patsrepo.go b/auth/mocks/patsrepo.go index ff4a6cd382..6eca8cfc5e 100644 --- a/auth/mocks/patsrepo.go +++ b/auth/mocks/patsrepo.go @@ -178,9 +178,9 @@ func (_m *PATSRepository) Retrieve(ctx context.Context, userID string, patID str return r0, r1 } -// RetrieveAll provides a mock function with given fields: ctx, userID -func (_m *PATSRepository) RetrieveAll(ctx context.Context, userID string) (auth.PATSPage, error) { - ret := _m.Called(ctx, userID) +// RetrieveAll provides a mock function with given fields: ctx, userID, pm +func (_m *PATSRepository) RetrieveAll(ctx context.Context, userID string, pm auth.PATSPageMeta) (auth.PATSPage, error) { + ret := _m.Called(ctx, userID, pm) if len(ret) == 0 { panic("no return value specified for RetrieveAll") @@ -188,17 +188,17 @@ func (_m *PATSRepository) RetrieveAll(ctx context.Context, userID string) (auth. var r0 auth.PATSPage var r1 error - if rf, ok := ret.Get(0).(func(context.Context, string) (auth.PATSPage, error)); ok { - return rf(ctx, userID) + if rf, ok := ret.Get(0).(func(context.Context, string, auth.PATSPageMeta) (auth.PATSPage, error)); ok { + return rf(ctx, userID, pm) } - if rf, ok := ret.Get(0).(func(context.Context, string) auth.PATSPage); ok { - r0 = rf(ctx, userID) + if rf, ok := ret.Get(0).(func(context.Context, string, auth.PATSPageMeta) auth.PATSPage); ok { + r0 = rf(ctx, userID, pm) } else { r0 = ret.Get(0).(auth.PATSPage) } - if rf, ok := ret.Get(1).(func(context.Context, string) error); ok { - r1 = rf(ctx, userID) + if rf, ok := ret.Get(1).(func(context.Context, string, auth.PATSPageMeta) error); ok { + r1 = rf(ctx, userID, pm) } else { r1 = ret.Error(1) } @@ -225,31 +225,21 @@ func (_m *PATSRepository) Revoke(ctx context.Context, userID string, patID strin } // Save provides a mock function with given fields: ctx, pat -func (_m *PATSRepository) Save(ctx context.Context, pat auth.PAT) (string, error) { +func (_m *PATSRepository) Save(ctx context.Context, pat auth.PAT) error { ret := _m.Called(ctx, pat) if len(ret) == 0 { panic("no return value specified for Save") } - var r0 string - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, auth.PAT) (string, error)); ok { - return rf(ctx, pat) - } - if rf, ok := ret.Get(0).(func(context.Context, auth.PAT) string); ok { + var r0 error + if rf, ok := ret.Get(0).(func(context.Context, auth.PAT) error); ok { r0 = rf(ctx, pat) } else { - r0 = ret.Get(0).(string) - } - - if rf, ok := ret.Get(1).(func(context.Context, auth.PAT) error); ok { - r1 = rf(ctx, pat) - } else { - r1 = ret.Error(1) + r0 = ret.Error(0) } - return r0, r1 + return r0 } // UpdateDescription provides a mock function with given fields: ctx, userID, patID, description @@ -280,34 +270,6 @@ func (_m *PATSRepository) UpdateDescription(ctx context.Context, userID string, return r0, r1 } -// UpdateLastUsed provides a mock function with given fields: ctx, token, patID, description -func (_m *PATSRepository) UpdateLastUsed(ctx context.Context, token string, patID string, description string) (auth.PAT, error) { - ret := _m.Called(ctx, token, patID, description) - - if len(ret) == 0 { - panic("no return value specified for UpdateLastUsed") - } - - var r0 auth.PAT - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, string, string, string) (auth.PAT, error)); ok { - return rf(ctx, token, patID, description) - } - if rf, ok := ret.Get(0).(func(context.Context, string, string, string) auth.PAT); ok { - r0 = rf(ctx, token, patID, description) - } else { - r0 = ret.Get(0).(auth.PAT) - } - - if rf, ok := ret.Get(1).(func(context.Context, string, string, string) error); ok { - r1 = rf(ctx, token, patID, description) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - // UpdateName provides a mock function with given fields: ctx, userID, patID, name func (_m *PATSRepository) UpdateName(ctx context.Context, userID string, patID string, name string) (auth.PAT, error) { ret := _m.Called(ctx, userID, patID, name) diff --git a/auth/mocks/service.go b/auth/mocks/service.go index 80d1d19602..a769e78b8e 100644 --- a/auth/mocks/service.go +++ b/auth/mocks/service.go @@ -55,8 +55,8 @@ func (_m *Service) AddPolicy(ctx context.Context, pr auth.PolicyReq) error { return r0 } -// AddScope provides a mock function with given fields: ctx, token, patID, platformEntityType, optionalDomainID, optionalDomainEntityType, operation, entityIDs -func (_m *Service) AddScope(ctx context.Context, token string, patID string, platformEntityType auth.PlatformEntityType, optionalDomainID string, optionalDomainEntityType auth.DomainEntityType, operation auth.OperationType, entityIDs ...string) (auth.Scope, error) { +// AddScopeEntry provides a mock function with given fields: ctx, token, patID, platformEntityType, optionalDomainID, optionalDomainEntityType, operation, entityIDs +func (_m *Service) AddScopeEntry(ctx context.Context, token string, patID string, platformEntityType auth.PlatformEntityType, optionalDomainID string, optionalDomainEntityType auth.DomainEntityType, operation auth.OperationType, entityIDs ...string) (auth.Scope, error) { _va := make([]interface{}, len(entityIDs)) for _i := range entityIDs { _va[_i] = entityIDs[_i] @@ -67,7 +67,7 @@ func (_m *Service) AddScope(ctx context.Context, token string, patID string, pla ret := _m.Called(_ca...) if len(ret) == 0 { - panic("no return value specified for AddScope") + panic("no return value specified for AddScopeEntry") } var r0 auth.Scope @@ -182,12 +182,12 @@ func (_m *Service) ChangeDomainStatus(ctx context.Context, token string, id stri return r0, r1 } -// ClearAllScope provides a mock function with given fields: ctx, token, patID -func (_m *Service) ClearAllScope(ctx context.Context, token string, patID string) error { +// ClearAllScopeEntry provides a mock function with given fields: ctx, token, patID +func (_m *Service) ClearAllScopeEntry(ctx context.Context, token string, patID string) error { ret := _m.Called(ctx, token, patID) if len(ret) == 0 { - panic("no return value specified for ClearAllScope") + panic("no return value specified for ClearAllScopeEntry") } var r0 error @@ -468,9 +468,9 @@ func (_m *Service) Issue(ctx context.Context, token string, key auth.Key) (auth. return r0, r1 } -// List provides a mock function with given fields: ctx, token -func (_m *Service) List(ctx context.Context, token string) (auth.PATSPage, error) { - ret := _m.Called(ctx, token) +// List provides a mock function with given fields: ctx, token, pm +func (_m *Service) List(ctx context.Context, token string, pm auth.PATSPageMeta) (auth.PATSPage, error) { + ret := _m.Called(ctx, token, pm) if len(ret) == 0 { panic("no return value specified for List") @@ -478,17 +478,17 @@ func (_m *Service) List(ctx context.Context, token string) (auth.PATSPage, error var r0 auth.PATSPage var r1 error - if rf, ok := ret.Get(0).(func(context.Context, string) (auth.PATSPage, error)); ok { - return rf(ctx, token) + if rf, ok := ret.Get(0).(func(context.Context, string, auth.PATSPageMeta) (auth.PATSPage, error)); ok { + return rf(ctx, token, pm) } - if rf, ok := ret.Get(0).(func(context.Context, string) auth.PATSPage); ok { - r0 = rf(ctx, token) + if rf, ok := ret.Get(0).(func(context.Context, string, auth.PATSPageMeta) auth.PATSPage); ok { + r0 = rf(ctx, token, pm) } else { r0 = ret.Get(0).(auth.PATSPage) } - if rf, ok := ret.Get(1).(func(context.Context, string) error); ok { - r1 = rf(ctx, token) + if rf, ok := ret.Get(1).(func(context.Context, string, auth.PATSPageMeta) error); ok { + r1 = rf(ctx, token, pm) } else { r1 = ret.Error(1) } @@ -694,8 +694,8 @@ func (_m *Service) ListUserDomains(ctx context.Context, token string, userID str return r0, r1 } -// RemoveScope provides a mock function with given fields: ctx, token, patID, platformEntityType, optionalDomainID, optionalDomainEntityType, operation, entityIDs -func (_m *Service) RemoveScope(ctx context.Context, token string, patID string, platformEntityType auth.PlatformEntityType, optionalDomainID string, optionalDomainEntityType auth.DomainEntityType, operation auth.OperationType, entityIDs ...string) (auth.Scope, error) { +// RemoveScopeEntry provides a mock function with given fields: ctx, token, patID, platformEntityType, optionalDomainID, optionalDomainEntityType, operation, entityIDs +func (_m *Service) RemoveScopeEntry(ctx context.Context, token string, patID string, platformEntityType auth.PlatformEntityType, optionalDomainID string, optionalDomainEntityType auth.DomainEntityType, operation auth.OperationType, entityIDs ...string) (auth.Scope, error) { _va := make([]interface{}, len(entityIDs)) for _i := range entityIDs { _va[_i] = entityIDs[_i] @@ -706,7 +706,7 @@ func (_m *Service) RemoveScope(ctx context.Context, token string, patID string, ret := _m.Called(_ca...) if len(ret) == 0 { - panic("no return value specified for RemoveScope") + panic("no return value specified for RemoveScopeEntry") } var r0 auth.Scope @@ -907,8 +907,8 @@ func (_m *Service) RevokeSecret(ctx context.Context, token string, patID string) return r0 } -// TestCheckScope provides a mock function with given fields: ctx, paToken, platformEntityType, optionalDomainID, optionalDomainEntityType, operation, entityIDs -func (_m *Service) TestCheckScope(ctx context.Context, paToken string, platformEntityType auth.PlatformEntityType, optionalDomainID string, optionalDomainEntityType auth.DomainEntityType, operation auth.OperationType, entityIDs ...string) error { +// TestCheckScopeEntry provides a mock function with given fields: ctx, paToken, platformEntityType, optionalDomainID, optionalDomainEntityType, operation, entityIDs +func (_m *Service) TestCheckScopeEntry(ctx context.Context, paToken string, platformEntityType auth.PlatformEntityType, optionalDomainID string, optionalDomainEntityType auth.DomainEntityType, operation auth.OperationType, entityIDs ...string) error { _va := make([]interface{}, len(entityIDs)) for _i := range entityIDs { _va[_i] = entityIDs[_i] @@ -919,7 +919,7 @@ func (_m *Service) TestCheckScope(ctx context.Context, paToken string, platformE ret := _m.Called(_ca...) if len(ret) == 0 { - panic("no return value specified for TestCheckScope") + panic("no return value specified for TestCheckScopeEntry") } var r0 error diff --git a/auth/pat.go b/auth/pat.go index 4d4297e11f..a669a27641 100644 --- a/auth/pat.go +++ b/auth/pat.go @@ -618,7 +618,7 @@ type PAT struct { User string `json:"user,omitempty"` Name string `json:"name,omitempty"` Description string `json:"description,omitempty"` - Token string `json:"token,omitempty"` + Secret string `json:"secret,omitempty"` Scope Scope `json:"scope,omitempty"` IssuedAt time.Time `json:"issued_at,omitempty"` ExpiresAt time.Time `json:"expires_at,omitempty"` @@ -628,6 +628,10 @@ type PAT struct { RevokedAt time.Time `json:"revoked_at,omitempty"` } +type PATSPageMeta struct { + Offset uint64 `json:"offset"` + Limit uint64 `json:"limit"` +} type PATSPage struct { Total uint64 `json:"total"` Offset uint64 `json:"offset"` @@ -665,7 +669,7 @@ type PATS interface { Retrieve(ctx context.Context, token, patID string) (PAT, error) // List function lists all the PATs for the user. - List(ctx context.Context, token string) (PATSPage, error) + List(ctx context.Context, token string, pm PATSPageMeta) (PATSPage, error) // Delete function deletes the PAT for given ID. Delete(ctx context.Context, token, patID string) error @@ -677,16 +681,16 @@ type PATS interface { RevokeSecret(ctx context.Context, token, patID string) error // AddScope function adds a new scope entry. - AddScope(ctx context.Context, token, patID string, platformEntityType PlatformEntityType, optionalDomainID string, optionalDomainEntityType DomainEntityType, operation OperationType, entityIDs ...string) (Scope, error) + AddScopeEntry(ctx context.Context, token, patID string, platformEntityType PlatformEntityType, optionalDomainID string, optionalDomainEntityType DomainEntityType, operation OperationType, entityIDs ...string) (Scope, error) // RemoveScope function removes a scope entry. - RemoveScope(ctx context.Context, token, patID string, platformEntityType PlatformEntityType, optionalDomainID string, optionalDomainEntityType DomainEntityType, operation OperationType, entityIDs ...string) (Scope, error) + RemoveScopeEntry(ctx context.Context, token, patID string, platformEntityType PlatformEntityType, optionalDomainID string, optionalDomainEntityType DomainEntityType, operation OperationType, entityIDs ...string) (Scope, error) // ClearAllScope function removes all scope entry. - ClearAllScope(ctx context.Context, token, patID string) error + ClearAllScopeEntry(ctx context.Context, token, patID string) error // This will be removed during PR merge. TestCheckScope will check the given scope exists. - TestCheckScope(ctx context.Context, paToken string, platformEntityType PlatformEntityType, optionalDomainID string, optionalDomainEntityType DomainEntityType, operation OperationType, entityIDs ...string) error + TestCheckScopeEntry(ctx context.Context, paToken string, platformEntityType PlatformEntityType, optionalDomainID string, optionalDomainEntityType DomainEntityType, operation OperationType, entityIDs ...string) error // IdentifyPAT function will valid the secret. IdentifyPAT(ctx context.Context, paToken string) (PAT, error) @@ -715,7 +719,7 @@ type PATSRepository interface { UpdateTokenHash(ctx context.Context, userID, patID, tokenHash string, expiryAt time.Time) (PAT, error) // RetrieveAll retrieves all PATs belongs to userID. - RetrieveAll(ctx context.Context, userID string) (pats PATSPage, err error) + RetrieveAll(ctx context.Context, userID string, pm PATSPageMeta) (pats PATSPage, err error) // Revoke PAT with provided ID. Revoke(ctx context.Context, userID, patID string) error diff --git a/auth/service.go b/auth/service.go index 691087ef87..e42fee9697 100644 --- a/auth/service.go +++ b/auth/service.go @@ -132,11 +132,12 @@ type service struct { } // New instantiates the auth service implementation. -func New(keys KeyRepository, domains DomainsRepository, idp magistrala.IDProvider, tokenizer Tokenizer, policyAgent PolicyAgent, loginDuration, refreshDuration, invitationDuration time.Duration) Service { +func New(keys KeyRepository, domains DomainsRepository, pats PATSRepository, idp magistrala.IDProvider, tokenizer Tokenizer, policyAgent PolicyAgent, loginDuration, refreshDuration, invitationDuration time.Duration) Service { return &service{ tokenizer: tokenizer, domains: domains, keys: keys, + pats: pats, idProvider: idp, agent: policyAgent, loginDuration: loginDuration, @@ -1131,12 +1132,12 @@ func (svc service) Retrieve(ctx context.Context, token, patID string) (PAT, erro } return pat, nil } -func (svc service) List(ctx context.Context, token string) (PATSPage, error) { +func (svc service) List(ctx context.Context, token string, pm PATSPageMeta) (PATSPage, error) { key, err := svc.Identify(ctx, token) if err != nil { return PATSPage{}, err } - patsPage, err := svc.pats.RetrieveAll(ctx, key.User) + patsPage, err := svc.pats.RetrieveAll(ctx, key.User, pm) if err != nil { return PATSPage{}, errors.Wrap(errRetrievePAT, err) } @@ -1158,11 +1159,11 @@ func (svc service) ResetSecret(ctx context.Context, token, patID string, duratio return PAT{}, err } - var newTokenHash string + var paTokenHash string var newExpiry time.Time // Generate new HashToken take place here - pat, err := svc.pats.UpdateTokenHash(ctx, key.User, patID, newTokenHash, newExpiry) + pat, err := svc.pats.UpdateTokenHash(ctx, key.User, patID, paTokenHash, newExpiry) if err != nil { return PAT{}, errors.Wrap(errUpdatePAT, err) } @@ -1180,7 +1181,7 @@ func (svc service) RevokeSecret(ctx context.Context, token, patID string) error return nil } -func (svc service) AddScope(ctx context.Context, token, patID string, platformEntityType PlatformEntityType, optionalDomainID string, optionalDomainEntityType DomainEntityType, operation OperationType, entityIDs ...string) (Scope, error) { +func (svc service) AddScopeEntry(ctx context.Context, token, patID string, platformEntityType PlatformEntityType, optionalDomainID string, optionalDomainEntityType DomainEntityType, operation OperationType, entityIDs ...string) (Scope, error) { key, err := svc.Identify(ctx, token) if err != nil { return Scope{}, err @@ -1192,7 +1193,7 @@ func (svc service) AddScope(ctx context.Context, token, patID string, platformEn } return scope, nil } -func (svc service) RemoveScope(ctx context.Context, token, patID string, platformEntityType PlatformEntityType, optionalDomainID string, optionalDomainEntityType DomainEntityType, operation OperationType, entityIDs ...string) (Scope, error) { +func (svc service) RemoveScopeEntry(ctx context.Context, token, patID string, platformEntityType PlatformEntityType, optionalDomainID string, optionalDomainEntityType DomainEntityType, operation OperationType, entityIDs ...string) (Scope, error) { key, err := svc.Identify(ctx, token) if err != nil { return Scope{}, err @@ -1203,7 +1204,7 @@ func (svc service) RemoveScope(ctx context.Context, token, patID string, platfor } return scope, nil } -func (svc service) ClearAllScope(ctx context.Context, token, patID string) error { +func (svc service) ClearAllScopeEntry(ctx context.Context, token, patID string) error { key, err := svc.Identify(ctx, token) if err != nil { return err @@ -1214,7 +1215,7 @@ func (svc service) ClearAllScope(ctx context.Context, token, patID string) error return nil } -func (svc service) TestCheckScope(ctx context.Context, paToken string, platformEntityType PlatformEntityType, optionalDomainID string, optionalDomainEntityType DomainEntityType, operation OperationType, entityIDs ...string) error { +func (svc service) TestCheckScopeEntry(ctx context.Context, paToken string, platformEntityType PlatformEntityType, optionalDomainID string, optionalDomainEntityType DomainEntityType, operation OperationType, entityIDs ...string) error { res, err := svc.IdentifyPAT(ctx, paToken) if err != nil { return err diff --git a/auth/service_test.go b/auth/service_test.go index 7547c87db5..09cb07dbee 100644 --- a/auth/service_test.go +++ b/auth/service_test.go @@ -58,15 +58,17 @@ var ( ) var ( - krepo *mocks.KeyRepository - prepo *mocks.PolicyAgent - drepo *mocks.DomainsRepository + krepo *mocks.KeyRepository + prepo *mocks.PolicyAgent + drepo *mocks.DomainsRepository + patsrepo *mocks.PATSRepository ) func newService() (auth.Service, string) { krepo = new(mocks.KeyRepository) prepo = new(mocks.PolicyAgent) drepo = new(mocks.DomainsRepository) + patsrepo = new(mocks.PATSRepository) idProvider := uuid.NewMock() t := jwt.New([]byte(secret)) @@ -80,7 +82,7 @@ func newService() (auth.Service, string) { } token, _ := t.Issue(key) - return auth.New(krepo, drepo, idProvider, t, prepo, loginDuration, refreshDuration, invalidDuration), token + return auth.New(krepo, drepo, patsrepo, idProvider, t, prepo, loginDuration, refreshDuration, invalidDuration), token } func TestIssue(t *testing.T) { diff --git a/auth/tracing/tracing.go b/auth/tracing/tracing.go index 1db13e84d1..455258342f 100644 --- a/auth/tracing/tracing.go +++ b/auth/tracing/tracing.go @@ -351,10 +351,13 @@ func (tm *tracingMiddleware) Retrieve(ctx context.Context, token, patID string) return tm.svc.Retrieve(ctx, token, patID) } -func (tm *tracingMiddleware) List(ctx context.Context, token string) (auth.PATSPage, error) { - ctx, span := tm.tracer.Start(ctx, "list_pat") +func (tm *tracingMiddleware) List(ctx context.Context, token string, pm auth.PATSPageMeta) (auth.PATSPage, error) { + ctx, span := tm.tracer.Start(ctx, "list_pat", trace.WithAttributes( + attribute.Int64("limit", int64(pm.Limit)), + attribute.Int64("offset", int64(pm.Offset)), + )) defer span.End() - return tm.svc.List(ctx, token) + return tm.svc.List(ctx, token, pm) } func (tm *tracingMiddleware) Delete(ctx context.Context, token, patID string) error { @@ -382,8 +385,8 @@ func (tm *tracingMiddleware) RevokeSecret(ctx context.Context, token, patID stri return tm.svc.RevokeSecret(ctx, token, patID) } -func (tm *tracingMiddleware) AddScope(ctx context.Context, token, patID string, platformEntityType auth.PlatformEntityType, optionalDomainID string, optionalDomainEntityType auth.DomainEntityType, operation auth.OperationType, entityIDs ...string) (auth.Scope, error) { - ctx, span := tm.tracer.Start(ctx, "add_pat_scope", trace.WithAttributes( +func (tm *tracingMiddleware) AddScopeEntry(ctx context.Context, token, patID string, platformEntityType auth.PlatformEntityType, optionalDomainID string, optionalDomainEntityType auth.DomainEntityType, operation auth.OperationType, entityIDs ...string) (auth.Scope, error) { + ctx, span := tm.tracer.Start(ctx, "add_pat_scope_entry", trace.WithAttributes( attribute.String("pat_id", patID), attribute.String("platform_entity", platformEntityType.String()), attribute.String("optional_domain_id", optionalDomainID), @@ -392,11 +395,11 @@ func (tm *tracingMiddleware) AddScope(ctx context.Context, token, patID string, attribute.StringSlice("entities", entityIDs), )) defer span.End() - return tm.svc.AddScope(ctx, token, patID, platformEntityType, optionalDomainID, optionalDomainEntityType, operation, entityIDs...) + return tm.svc.AddScopeEntry(ctx, token, patID, platformEntityType, optionalDomainID, optionalDomainEntityType, operation, entityIDs...) } -func (tm *tracingMiddleware) RemoveScope(ctx context.Context, token, patID string, platformEntityType auth.PlatformEntityType, optionalDomainID string, optionalDomainEntityType auth.DomainEntityType, operation auth.OperationType, entityIDs ...string) (auth.Scope, error) { - ctx, span := tm.tracer.Start(ctx, "remove_pat_scope", trace.WithAttributes( +func (tm *tracingMiddleware) RemoveScopeEntry(ctx context.Context, token, patID string, platformEntityType auth.PlatformEntityType, optionalDomainID string, optionalDomainEntityType auth.DomainEntityType, operation auth.OperationType, entityIDs ...string) (auth.Scope, error) { + ctx, span := tm.tracer.Start(ctx, "remove_pat_scope_entry", trace.WithAttributes( attribute.String("pat_id", patID), attribute.String("platform_entity", platformEntityType.String()), attribute.String("optional_domain_id", optionalDomainID), @@ -405,19 +408,19 @@ func (tm *tracingMiddleware) RemoveScope(ctx context.Context, token, patID strin attribute.StringSlice("entities", entityIDs), )) defer span.End() - return tm.svc.RemoveScope(ctx, token, patID, platformEntityType, optionalDomainID, optionalDomainEntityType, operation, entityIDs...) + return tm.svc.RemoveScopeEntry(ctx, token, patID, platformEntityType, optionalDomainID, optionalDomainEntityType, operation, entityIDs...) } -func (tm *tracingMiddleware) ClearAllScope(ctx context.Context, token, patID string) error { - ctx, span := tm.tracer.Start(ctx, "clear_pat_all_scope", trace.WithAttributes( +func (tm *tracingMiddleware) ClearAllScopeEntry(ctx context.Context, token, patID string) error { + ctx, span := tm.tracer.Start(ctx, "clear_pat_all_scope_entry", trace.WithAttributes( attribute.String("pat_id", patID), )) defer span.End() - return tm.svc.ClearAllScope(ctx, token, patID) + return tm.svc.ClearAllScopeEntry(ctx, token, patID) } -func (tm *tracingMiddleware) TestCheckScope(ctx context.Context, paToken string, platformEntityType auth.PlatformEntityType, optionalDomainID string, optionalDomainEntityType auth.DomainEntityType, operation auth.OperationType, entityIDs ...string) error { - ctx, span := tm.tracer.Start(ctx, "remove_pat_scope", trace.WithAttributes( +func (tm *tracingMiddleware) TestCheckScopeEntry(ctx context.Context, paToken string, platformEntityType auth.PlatformEntityType, optionalDomainID string, optionalDomainEntityType auth.DomainEntityType, operation auth.OperationType, entityIDs ...string) error { + ctx, span := tm.tracer.Start(ctx, "test_check_pat_scope_entry", trace.WithAttributes( attribute.String("personal_access_token", paToken), attribute.String("platform_entity", platformEntityType.String()), attribute.String("optional_domain_id", optionalDomainID), @@ -426,7 +429,7 @@ func (tm *tracingMiddleware) TestCheckScope(ctx context.Context, paToken string, attribute.StringSlice("entities", entityIDs), )) defer span.End() - return tm.svc.TestCheckScope(ctx, paToken, platformEntityType, optionalDomainID, optionalDomainEntityType, operation, entityIDs...) + return tm.svc.TestCheckScopeEntry(ctx, paToken, platformEntityType, optionalDomainID, optionalDomainEntityType, operation, entityIDs...) } func (tm *tracingMiddleware) IdentifyPAT(ctx context.Context, paToken string) (auth.PAT, error) { diff --git a/cmd/auth/main.go b/cmd/auth/main.go index 1a3ae89aa3..a1f103d360 100644 --- a/cmd/auth/main.go +++ b/cmd/auth/main.go @@ -18,11 +18,18 @@ import ( api "github.com/absmach/magistrala/auth/api" grpcapi "github.com/absmach/magistrala/auth/api/grpc" httpapi "github.com/absmach/magistrala/auth/api/http" + "github.com/absmach/magistrala/auth/bolt" "github.com/absmach/magistrala/auth/events" "github.com/absmach/magistrala/auth/jwt" apostgres "github.com/absmach/magistrala/auth/postgres" "github.com/absmach/magistrala/auth/spicedb" "github.com/absmach/magistrala/auth/tracing" + boltclient "github.com/absmach/magistrala/internal/clients/bolt" + pgclient "github.com/absmach/magistrala/internal/clients/postgres" + "github.com/absmach/magistrala/internal/postgres" + "github.com/absmach/magistrala/internal/server" + grpcserver "github.com/absmach/magistrala/internal/server/grpc" + httpserver "github.com/absmach/magistrala/internal/server/http" mglog "github.com/absmach/magistrala/logger" "github.com/absmach/magistrala/pkg/jaeger" "github.com/absmach/magistrala/pkg/postgres" @@ -129,7 +136,20 @@ func main() { return } - svc := newService(ctx, db, tracer, cfg, dbConfig, logger, spicedbclient) + boltDBConfig := boltclient.Config{} + if err := env.ParseWithOptions(&boltDBConfig, env.Options{}); err != nil { + panic(err) + } + + client, err := boltclient.Connect(boltDBConfig, bolt.Init) + if err != nil { + panic(err) + } + defer client.Close() + + patsRepo := bolt.NewPATSRepository(client, boltDBConfig.Bucket) + + svc := newService(db, tracer, cfg, dbConfig, logger, spicedbclient, patsRepo) httpServerConfig := server.Config{Port: defSvcHTTPPort} if err := env.ParseWithOptions(&httpServerConfig, env.Options{Prefix: envPrefixHTTP}); err != nil { @@ -203,7 +223,7 @@ func initSchema(ctx context.Context, client *authzed.ClientWithExperimental, sch return nil } -func newService(ctx context.Context, db *sqlx.DB, tracer trace.Tracer, cfg config, dbConfig pgclient.Config, logger *slog.Logger, spicedbClient *authzed.ClientWithExperimental) auth.Service { +func newService(db *sqlx.DB, tracer trace.Tracer, cfg config, dbConfig pgclient.Config, logger *slog.Logger, spicedbClient *authzed.ClientWithExperimental, pats auth.PATSRepository) auth.Service { database := postgres.NewDatabase(db, dbConfig, tracer) keysRepo := apostgres.New(database) domainsRepo := apostgres.NewDomainRepository(database) @@ -212,7 +232,7 @@ func newService(ctx context.Context, db *sqlx.DB, tracer trace.Tracer, cfg confi t := jwt.New([]byte(cfg.SecretKey)) - svc := auth.New(keysRepo, domainsRepo, idProvider, t, pa, cfg.AccessDuration, cfg.RefreshDuration, cfg.InvitationDuration) + svc := auth.New(keysRepo, domainsRepo, pats, idProvider, t, pa, cfg.AccessDuration, cfg.RefreshDuration, cfg.InvitationDuration) svc, err := events.NewEventStoreMiddleware(ctx, svc, cfg.ESURL) if err != nil { logger.Error(fmt.Sprintf("failed to init event store middleware : %s", err)) diff --git a/docker/nginx/nginx-key.conf b/docker/nginx/nginx-key.conf index b9a4eee838..c9a59e7c34 100644 --- a/docker/nginx/nginx-key.conf +++ b/docker/nginx/nginx-key.conf @@ -114,7 +114,7 @@ http { # Proxy pass to auth service - location ~ ^/(domains|keys) { + location ~ ^/(domains|keys|pats) { include snippets/proxy-headers.conf; add_header Access-Control-Expose-Headers Location; proxy_pass http://auth:${MG_AUTH_HTTP_PORT}; diff --git a/go.mod b/go.mod index 07be4cf3da..3c3a42932f 100644 --- a/go.mod +++ b/go.mod @@ -11,6 +11,7 @@ require ( github.com/authzed/grpcutil v0.0.0-20240123194739-2ea1e3d2d98b github.com/caarlos0/env/v10 v10.0.0 github.com/cenkalti/backoff/v4 v4.3.0 + github.com/dgraph-io/badger/v4 v4.2.0 github.com/eclipse/paho.mqtt.golang v1.4.3 github.com/fatih/color v1.17.0 github.com/fiorix/go-smpp v0.0.0-20210403173735-2894b96e70ba @@ -19,6 +20,7 @@ require ( github.com/go-redis/redis/v8 v8.11.5 github.com/gocql/gocql v1.6.0 github.com/gofrs/uuid v4.4.0+incompatible + github.com/google/uuid v1.6.0 github.com/gookit/color v1.5.4 github.com/gopcua/opcua v0.1.6 github.com/gorilla/websocket v1.5.3 @@ -44,6 +46,7 @@ require ( github.com/spf13/cobra v1.8.1 github.com/spf13/viper v1.19.0 github.com/stretchr/testify v1.9.0 + go.etcd.io/bbolt v1.3.10 go.mongodb.org/mongo-driver v1.15.0 go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.53.0 go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.53.0 @@ -80,7 +83,6 @@ require ( github.com/containerd/continuity v0.4.3 // indirect github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/decred/dcrd/dcrec/secp256k1/v4 v4.3.0 // indirect - github.com/dgraph-io/badger/v4 v4.2.0 // indirect github.com/dgraph-io/ristretto v0.1.1 // indirect github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect github.com/docker/cli v26.0.0+incompatible // indirect @@ -107,7 +109,6 @@ require ( github.com/golang/snappy v0.0.4 // indirect github.com/google/flatbuffers v1.12.1 // indirect github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect - github.com/google/uuid v1.6.0 // indirect github.com/grpc-ecosystem/go-grpc-middleware v1.4.0 // indirect github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0 // indirect github.com/hailocab/go-hostpool v0.0.0-20160125115350-e80d13ce29ed // indirect @@ -180,7 +181,6 @@ require ( github.com/xeipuuv/gojsonschema v1.2.0 // indirect github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect github.com/youmark/pkcs8 v0.0.0-20201027041543-1326539a0a0a // indirect - go.etcd.io/bbolt v1.3.10 // indirect go.opencensus.io v0.24.0 // indirect go.opentelemetry.io/otel/metric v1.28.0 // indirect go.opentelemetry.io/proto/otlp v1.2.0 // indirect diff --git a/go.sum b/go.sum index 199cb2e239..7cbeb9862d 100644 --- a/go.sum +++ b/go.sum @@ -77,6 +77,7 @@ github.com/dgraph-io/badger/v4 v4.2.0/go.mod h1:qfCqhPoWDFJRx1gp5QwwyGo8xk1lbHUx github.com/dgraph-io/ristretto v0.1.1 h1:6CWw5tJNgpegArSHpNHJKldNeq03FQCwYvfMVWajOK8= github.com/dgraph-io/ristretto v0.1.1/go.mod h1:S1GPSBCYCIhmVNfcth17y2zZtQT6wzkzgwUve0VDWWA= github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= +github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2 h1:tdlZCpZ/P9DhczCTSixgIKmwPv6+wP5DGjqLYw5SUiA= github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw= 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= @@ -668,6 +669,8 @@ 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.12.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= +golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4= golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI= diff --git a/pkg/apiutil/errors.go b/pkg/apiutil/errors.go index 833f8ecbd8..58b56f0276 100644 --- a/pkg/apiutil/errors.go +++ b/pkg/apiutil/errors.go @@ -90,6 +90,9 @@ var ( // ErrMissingHost indicates missing host. ErrMissingHost = errors.New("missing host") + // ErrMissingDescription indicates missing description. + ErrMissingDescription = errors.New("missing description") + // ErrMissingPass indicates missing password. ErrMissingPass = errors.New("missing password") @@ -179,4 +182,7 @@ var ( // ErrInvalidTimeFormat indicates invalid time format i.e not unix time. ErrInvalidTimeFormat = errors.New("invalid time format use unix time") + + // ErrInvalidDuration indicates invalid duration value. + ErrInvalidDuration = errors.New("invalid duration value") ) From ec988eb5630f2783a45607d5a328e1ec3da44f1e Mon Sep 17 00:00:00 2001 From: Arvindh Date: Wed, 10 Jul 2024 15:26:46 +0530 Subject: [PATCH 13/24] rename functions and corrected import paths Signed-off-by: Arvindh --- auth/api/http/keys/endpoint_test.go | 3 +- auth/api/http/pats/endpoint.go | 48 ++-- auth/api/http/pats/requests.go | 2 +- auth/api/http/pats/transport.go | 74 +++--- auth/api/logging.go | 48 ++-- auth/api/metrics.go | 48 ++-- auth/events/streams.go | 44 ++++ auth/mocks/pats.go | 74 +++--- auth/mocks/patsrepo.go | 2 +- auth/mocks/service.go | 351 ++++++++++++---------------- auth/pat.go | 24 +- auth/service.go | 24 +- auth/tracing/tracing.go | 48 ++-- bootstrap/postgres/configs.go | 6 +- cmd/auth/main.go | 9 +- go.sum | 17 +- 16 files changed, 412 insertions(+), 410 deletions(-) diff --git a/auth/api/http/keys/endpoint_test.go b/auth/api/http/keys/endpoint_test.go index f200f9f5ee..fd31e58588 100644 --- a/auth/api/http/keys/endpoint_test.go +++ b/auth/api/http/keys/endpoint_test.go @@ -70,11 +70,12 @@ func newService() (auth.Service, *mocks.KeyRepository) { krepo := new(mocks.KeyRepository) prepo := new(mocks.PolicyAgent) drepo := new(mocks.DomainsRepository) + patsRepo := new(mocks.PATSRepository) idProvider := uuid.NewMock() t := jwt.New([]byte(secret)) - return auth.New(krepo, drepo, idProvider, t, prepo, loginDuration, refreshDuration, invalidDuration), krepo + return auth.New(krepo, drepo, patsRepo, idProvider, t, prepo, loginDuration, refreshDuration, invalidDuration), krepo } func newServer(svc auth.Service) *httptest.Server { diff --git a/auth/api/http/pats/endpoint.go b/auth/api/http/pats/endpoint.go index 3d751c92b2..3c35241078 100644 --- a/auth/api/http/pats/endpoint.go +++ b/auth/api/http/pats/endpoint.go @@ -10,14 +10,14 @@ import ( "github.com/go-kit/kit/endpoint" ) -func createPatEndpoint(svc auth.Service) endpoint.Endpoint { +func createPATEndpoint(svc auth.Service) endpoint.Endpoint { return func(ctx context.Context, request interface{}) (interface{}, error) { req := request.(createPatReq) if err := req.validate(); err != nil { return nil, err } - pat, err := svc.Create(ctx, req.token, req.Name, req.Description, req.Duration, req.Scope) + pat, err := svc.CreatePAT(ctx, req.token, req.Name, req.Description, req.Duration, req.Scope) if err != nil { return nil, err } @@ -26,14 +26,14 @@ func createPatEndpoint(svc auth.Service) endpoint.Endpoint { } } -func retrieveEndpoint(svc auth.Service) endpoint.Endpoint { +func retrievePATEndpoint(svc auth.Service) endpoint.Endpoint { return func(ctx context.Context, request interface{}) (interface{}, error) { req := request.(retrievePatReq) if err := req.validate(); err != nil { return nil, err } - pat, err := svc.Retrieve(ctx, req.token, req.id) + pat, err := svc.RetrievePAT(ctx, req.token, req.id) if err != nil { return nil, err } @@ -42,14 +42,14 @@ func retrieveEndpoint(svc auth.Service) endpoint.Endpoint { } } -func updateNameEndpoint(svc auth.Service) endpoint.Endpoint { +func updatePATNameEndpoint(svc auth.Service) endpoint.Endpoint { return func(ctx context.Context, request interface{}) (interface{}, error) { req := request.(updatePatNameReq) if err := req.validate(); err != nil { return nil, err } - pat, err := svc.UpdateName(ctx, req.token, req.id, req.Name) + pat, err := svc.UpdatePATName(ctx, req.token, req.id, req.Name) if err != nil { return nil, err } @@ -58,14 +58,14 @@ func updateNameEndpoint(svc auth.Service) endpoint.Endpoint { } } -func updateDescriptionEndpoint(svc auth.Service) endpoint.Endpoint { +func updatePATDescriptionEndpoint(svc auth.Service) endpoint.Endpoint { return func(ctx context.Context, request interface{}) (interface{}, error) { req := request.(updatePatDescriptionReq) if err := req.validate(); err != nil { return nil, err } - pat, err := svc.UpdateDescription(ctx, req.token, req.id, req.Description) + pat, err := svc.UpdatePATDescription(ctx, req.token, req.id, req.Description) if err != nil { return nil, err } @@ -74,7 +74,7 @@ func updateDescriptionEndpoint(svc auth.Service) endpoint.Endpoint { } } -func listEndpoint(svc auth.Service) endpoint.Endpoint { +func listPATSEndpoint(svc auth.Service) endpoint.Endpoint { return func(ctx context.Context, request interface{}) (interface{}, error) { req := request.(listPatsReq) if err := req.validate(); err != nil { @@ -85,7 +85,7 @@ func listEndpoint(svc auth.Service) endpoint.Endpoint { Limit: req.limit, Offset: req.offset, } - patsPage, err := svc.List(ctx, req.token, pm) + patsPage, err := svc.ListPATS(ctx, req.token, pm) if err != nil { return nil, err } @@ -94,14 +94,14 @@ func listEndpoint(svc auth.Service) endpoint.Endpoint { } } -func deleteEndpoint(svc auth.Service) endpoint.Endpoint { +func deletePATEndpoint(svc auth.Service) endpoint.Endpoint { return func(ctx context.Context, request interface{}) (interface{}, error) { req := request.(deletePatReq) if err := req.validate(); err != nil { return nil, err } - if err := svc.Delete(ctx, req.token, req.id); err != nil { + if err := svc.DeletePAT(ctx, req.token, req.id); err != nil { return nil, err } @@ -109,14 +109,14 @@ func deleteEndpoint(svc auth.Service) endpoint.Endpoint { } } -func resetSecretEndpoint(svc auth.Service) endpoint.Endpoint { +func resetPATSecretEndpoint(svc auth.Service) endpoint.Endpoint { return func(ctx context.Context, request interface{}) (interface{}, error) { req := request.(resetPatSecretReq) if err := req.validate(); err != nil { return nil, err } - pat, err := svc.ResetSecret(ctx, req.token, req.id, req.Duration) + pat, err := svc.ResetPATSecret(ctx, req.token, req.id, req.Duration) if err != nil { return nil, err } @@ -125,14 +125,14 @@ func resetSecretEndpoint(svc auth.Service) endpoint.Endpoint { } } -func revokeSecretEndpoint(svc auth.Service) endpoint.Endpoint { +func revokePATSecretEndpoint(svc auth.Service) endpoint.Endpoint { return func(ctx context.Context, request interface{}) (interface{}, error) { req := request.(revokePatSecretReq) if err := req.validate(); err != nil { return nil, err } - if err := svc.RevokeSecret(ctx, req.token, req.id); err != nil { + if err := svc.RevokePATSecret(ctx, req.token, req.id); err != nil { return nil, err } @@ -140,14 +140,14 @@ func revokeSecretEndpoint(svc auth.Service) endpoint.Endpoint { } } -func addScopeEntryEndpoint(svc auth.Service) endpoint.Endpoint { +func addPATScopeEntryEndpoint(svc auth.Service) endpoint.Endpoint { return func(ctx context.Context, request interface{}) (interface{}, error) { req := request.(addPatScopeEntryReq) if err := req.validate(); err != nil { return nil, err } - scope, err := svc.AddScopeEntry(ctx, req.token, req.id, req.PlatformEntityType, req.OptionalDomainID, req.OptionalDomainEntityType, req.Operation, req.EntityIDs...) + scope, err := svc.AddPATScopeEntry(ctx, req.token, req.id, req.PlatformEntityType, req.OptionalDomainID, req.OptionalDomainEntityType, req.Operation, req.EntityIDs...) if err != nil { return nil, err } @@ -156,14 +156,14 @@ func addScopeEntryEndpoint(svc auth.Service) endpoint.Endpoint { } } -func removeScopeEntryEndpoint(svc auth.Service) endpoint.Endpoint { +func removePATScopeEntryEndpoint(svc auth.Service) endpoint.Endpoint { return func(ctx context.Context, request interface{}) (interface{}, error) { req := request.(removePatScopeEntryReq) if err := req.validate(); err != nil { return nil, err } - scope, err := svc.RemoveScopeEntry(ctx, req.token, req.id, req.PlatformEntityType, req.OptionalDomainID, req.OptionalDomainEntityType, req.Operation, req.EntityIDs...) + scope, err := svc.RemovePATScopeEntry(ctx, req.token, req.id, req.PlatformEntityType, req.OptionalDomainID, req.OptionalDomainEntityType, req.Operation, req.EntityIDs...) if err != nil { return nil, err } @@ -172,14 +172,14 @@ func removeScopeEntryEndpoint(svc auth.Service) endpoint.Endpoint { } -func clearAllScopeEntryEndpoint(svc auth.Service) endpoint.Endpoint { +func clearPATAllScopeEntryEndpoint(svc auth.Service) endpoint.Endpoint { return func(ctx context.Context, request interface{}) (interface{}, error) { req := request.(clearAllScopeEntryReq) if err := req.validate(); err != nil { return nil, err } - if err := svc.ClearAllScopeEntry(ctx, req.token, req.id); err != nil { + if err := svc.ClearPATAllScopeEntry(ctx, req.token, req.id); err != nil { return nil, err } @@ -187,14 +187,14 @@ func clearAllScopeEntryEndpoint(svc auth.Service) endpoint.Endpoint { } } -func testCheckScopeEntryEndpoint(svc auth.Service) endpoint.Endpoint { +func testCheckPATScopeEntryEndpoint(svc auth.Service) endpoint.Endpoint { return func(ctx context.Context, request interface{}) (interface{}, error) { req := request.(testCheckPatScopeReq) if err := req.validate(); err != nil { return nil, err } - if err := svc.TestCheckScopeEntry(ctx, req.token, req.PlatformEntityType, req.OptionalDomainID, req.OptionalDomainEntityType, req.Operation, req.EntityIDs...); err != nil { + if err := svc.TestCheckPATScopeEntry(ctx, req.token, req.PlatformEntityType, req.OptionalDomainID, req.OptionalDomainEntityType, req.Operation, req.EntityIDs...); err != nil { return nil, err } diff --git a/auth/api/http/pats/requests.go b/auth/api/http/pats/requests.go index 0724067412..44ce84c988 100644 --- a/auth/api/http/pats/requests.go +++ b/auth/api/http/pats/requests.go @@ -9,7 +9,7 @@ import ( "time" "github.com/absmach/magistrala/auth" - "github.com/absmach/magistrala/internal/apiutil" + "github.com/absmach/magistrala/pkg/apiutil" "github.com/absmach/magistrala/pkg/errors" ) diff --git a/auth/api/http/pats/transport.go b/auth/api/http/pats/transport.go index d1ce3190cd..e32180ebd1 100644 --- a/auth/api/http/pats/transport.go +++ b/auth/api/http/pats/transport.go @@ -12,7 +12,7 @@ import ( "github.com/absmach/magistrala/auth" "github.com/absmach/magistrala/internal/api" - "github.com/absmach/magistrala/internal/apiutil" + "github.com/absmach/magistrala/pkg/apiutil" "github.com/absmach/magistrala/pkg/errors" "github.com/go-chi/chi/v5" kithttp "github.com/go-kit/kit/transport/http" @@ -30,85 +30,85 @@ func MakeHandler(svc auth.Service, mux *chi.Mux, logger *slog.Logger) *chi.Mux { } mux.Route("/pats", func(r chi.Router) { r.Post("/", kithttp.NewServer( - createPatEndpoint(svc), - decodeCreatePatRequest, + createPATEndpoint(svc), + decodeCreatePATRequest, api.EncodeResponse, opts..., ).ServeHTTP) r.Get("/{id}", kithttp.NewServer( - (retrieveEndpoint(svc)), - decodeRetrievePatRequest, + (retrievePATEndpoint(svc)), + decodeRetrievePATRequest, api.EncodeResponse, opts..., ).ServeHTTP) r.Put("/{id}/name", kithttp.NewServer( - (updateNameEndpoint(svc)), - decodeUpdateNameRequest, + (updatePATNameEndpoint(svc)), + decodeUpdatePATNameRequest, api.EncodeResponse, opts..., ).ServeHTTP) r.Put("/{id}/description", kithttp.NewServer( - (updateDescriptionEndpoint(svc)), - decodeUpdateDescriptionRequest, + (updatePATDescriptionEndpoint(svc)), + decodeUpdatePATDescriptionRequest, api.EncodeResponse, opts..., ).ServeHTTP) r.Get("/", kithttp.NewServer( - (listEndpoint(svc)), - decodeListRequest, + (listPATSEndpoint(svc)), + decodeListPATSRequest, api.EncodeResponse, opts..., ).ServeHTTP) r.Delete("/{id}", kithttp.NewServer( - (deleteEndpoint(svc)), - decodeDeleteRequest, + (deletePATEndpoint(svc)), + decodeDeletePATRequest, api.EncodeResponse, opts..., ).ServeHTTP) r.Put("/{id}/secret/reset", kithttp.NewServer( - (resetSecretEndpoint(svc)), - decodeResetSecretRequest, + (resetPATSecretEndpoint(svc)), + decodeResetPATSecretRequest, api.EncodeResponse, opts..., ).ServeHTTP) r.Put("/{id}/secret/revoke", kithttp.NewServer( - (revokeSecretEndpoint(svc)), - decodeRevokeSecretRequest, + (revokePATSecretEndpoint(svc)), + decodeRevokePATSecretRequest, api.EncodeResponse, opts..., ).ServeHTTP) r.Put("/{id}/scope/add", kithttp.NewServer( - (addScopeEntryEndpoint(svc)), - decodeAddScopeEntryRequest, + (addPATScopeEntryEndpoint(svc)), + decodeAddPATScopeEntryRequest, api.EncodeResponse, opts..., ).ServeHTTP) r.Put("/{id}/scope/remove", kithttp.NewServer( - (removeScopeEntryEndpoint(svc)), - decodeRemoveScopeEntryRequest, + (removePATScopeEntryEndpoint(svc)), + decodeRemovePATScopeEntryRequest, api.EncodeResponse, opts..., ).ServeHTTP) r.Delete("/{id}/scope", kithttp.NewServer( - (clearAllScopeEntryEndpoint(svc)), - decodeClearAllScopeEntryRequest, + (clearPATAllScopeEntryEndpoint(svc)), + decodeClearPATAllScopeEntryRequest, api.EncodeResponse, opts..., ).ServeHTTP) r.Get("/check", kithttp.NewServer( - (testCheckScopeEntryEndpoint(svc)), - decodeTestCheckScopeEntryRequest, + (testCheckPATScopeEntryEndpoint(svc)), + decodeTestCheckPATScopeEntryRequest, api.EncodeResponse, opts..., ).ServeHTTP) @@ -116,7 +116,7 @@ func MakeHandler(svc auth.Service, mux *chi.Mux, logger *slog.Logger) *chi.Mux { return mux } -func decodeCreatePatRequest(_ context.Context, r *http.Request) (interface{}, error) { +func decodeCreatePATRequest(_ context.Context, r *http.Request) (interface{}, error) { if !strings.Contains(r.Header.Get("Content-Type"), contentType) { return nil, apiutil.ErrUnsupportedContentType } @@ -128,7 +128,7 @@ func decodeCreatePatRequest(_ context.Context, r *http.Request) (interface{}, er return req, nil } -func decodeRetrievePatRequest(_ context.Context, r *http.Request) (interface{}, error) { +func decodeRetrievePATRequest(_ context.Context, r *http.Request) (interface{}, error) { req := retrievePatReq{ token: apiutil.ExtractBearerToken(r), id: chi.URLParam(r, "id"), @@ -136,7 +136,7 @@ func decodeRetrievePatRequest(_ context.Context, r *http.Request) (interface{}, return req, nil } -func decodeUpdateNameRequest(_ context.Context, r *http.Request) (interface{}, error) { +func decodeUpdatePATNameRequest(_ context.Context, r *http.Request) (interface{}, error) { if !strings.Contains(r.Header.Get("Content-Type"), contentType) { return nil, apiutil.ErrUnsupportedContentType } @@ -151,7 +151,7 @@ func decodeUpdateNameRequest(_ context.Context, r *http.Request) (interface{}, e return req, nil } -func decodeUpdateDescriptionRequest(_ context.Context, r *http.Request) (interface{}, error) { +func decodeUpdatePATDescriptionRequest(_ context.Context, r *http.Request) (interface{}, error) { if !strings.Contains(r.Header.Get("Content-Type"), contentType) { return nil, apiutil.ErrUnsupportedContentType } @@ -166,7 +166,7 @@ func decodeUpdateDescriptionRequest(_ context.Context, r *http.Request) (interfa return req, nil } -func decodeListRequest(_ context.Context, r *http.Request) (interface{}, error) { +func decodeListPATSRequest(_ context.Context, r *http.Request) (interface{}, error) { l, err := apiutil.ReadNumQuery[uint64](r, api.LimitKey, api.DefLimit) if err != nil { return nil, errors.Wrap(apiutil.ErrValidation, err) @@ -186,7 +186,7 @@ func decodeListRequest(_ context.Context, r *http.Request) (interface{}, error) return req, nil } -func decodeDeleteRequest(_ context.Context, r *http.Request) (interface{}, error) { +func decodeDeletePATRequest(_ context.Context, r *http.Request) (interface{}, error) { if !strings.Contains(r.Header.Get("Content-Type"), contentType) { return nil, apiutil.ErrUnsupportedContentType } @@ -197,7 +197,7 @@ func decodeDeleteRequest(_ context.Context, r *http.Request) (interface{}, error }, nil } -func decodeResetSecretRequest(_ context.Context, r *http.Request) (interface{}, error) { +func decodeResetPATSecretRequest(_ context.Context, r *http.Request) (interface{}, error) { if !strings.Contains(r.Header.Get("Content-Type"), contentType) { return nil, apiutil.ErrUnsupportedContentType } @@ -212,7 +212,7 @@ func decodeResetSecretRequest(_ context.Context, r *http.Request) (interface{}, return req, nil } -func decodeRevokeSecretRequest(_ context.Context, r *http.Request) (interface{}, error) { +func decodeRevokePATSecretRequest(_ context.Context, r *http.Request) (interface{}, error) { if !strings.Contains(r.Header.Get("Content-Type"), contentType) { return nil, apiutil.ErrUnsupportedContentType } @@ -223,7 +223,7 @@ func decodeRevokeSecretRequest(_ context.Context, r *http.Request) (interface{}, }, nil } -func decodeAddScopeEntryRequest(_ context.Context, r *http.Request) (interface{}, error) { +func decodeAddPATScopeEntryRequest(_ context.Context, r *http.Request) (interface{}, error) { if !strings.Contains(r.Header.Get("Content-Type"), contentType) { return nil, apiutil.ErrUnsupportedContentType } @@ -238,7 +238,7 @@ func decodeAddScopeEntryRequest(_ context.Context, r *http.Request) (interface{} return req, nil } -func decodeRemoveScopeEntryRequest(_ context.Context, r *http.Request) (interface{}, error) { +func decodeRemovePATScopeEntryRequest(_ context.Context, r *http.Request) (interface{}, error) { if !strings.Contains(r.Header.Get("Content-Type"), contentType) { return nil, apiutil.ErrUnsupportedContentType } @@ -253,7 +253,7 @@ func decodeRemoveScopeEntryRequest(_ context.Context, r *http.Request) (interfac return req, nil } -func decodeClearAllScopeEntryRequest(_ context.Context, r *http.Request) (interface{}, error) { +func decodeClearPATAllScopeEntryRequest(_ context.Context, r *http.Request) (interface{}, error) { if !strings.Contains(r.Header.Get("Content-Type"), contentType) { return nil, apiutil.ErrUnsupportedContentType } @@ -264,7 +264,7 @@ func decodeClearAllScopeEntryRequest(_ context.Context, r *http.Request) (interf }, nil } -func decodeTestCheckScopeEntryRequest(_ context.Context, r *http.Request) (interface{}, error) { +func decodeTestCheckPATScopeEntryRequest(_ context.Context, r *http.Request) (interface{}, error) { if !strings.Contains(r.Header.Get("Content-Type"), contentType) { return nil, apiutil.ErrUnsupportedContentType } diff --git a/auth/api/logging.go b/auth/api/logging.go index fe984f969d..210af37907 100644 --- a/auth/api/logging.go +++ b/auth/api/logging.go @@ -508,7 +508,7 @@ func (lm *loggingMiddleware) DeleteEntityPolicies(ctx context.Context, entityTyp return lm.svc.DeleteEntityPolicies(ctx, entityType, id) } -func (lm *loggingMiddleware) Create(ctx context.Context, token, name, description string, duration time.Duration, scope auth.Scope) (pa auth.PAT, err error) { +func (lm *loggingMiddleware) CreatePAT(ctx context.Context, token, name, description string, duration time.Duration, scope auth.Scope) (pa auth.PAT, err error) { defer func(begin time.Time) { args := []any{ slog.String("duration", time.Since(begin).String()), @@ -524,9 +524,9 @@ func (lm *loggingMiddleware) Create(ctx context.Context, token, name, descriptio } lm.logger.Info("Create PAT completed successfully", args...) }(time.Now()) - return lm.svc.Create(ctx, token, name, description, duration, scope) + return lm.svc.CreatePAT(ctx, token, name, description, duration, scope) } -func (lm *loggingMiddleware) UpdateName(ctx context.Context, token, patID, name string) (pa auth.PAT, err error) { +func (lm *loggingMiddleware) UpdatePATName(ctx context.Context, token, patID, name string) (pa auth.PAT, err error) { defer func(begin time.Time) { args := []any{ slog.String("duration", time.Since(begin).String()), @@ -540,9 +540,9 @@ func (lm *loggingMiddleware) UpdateName(ctx context.Context, token, patID, name } lm.logger.Info("Update PAT name completed successfully", args...) }(time.Now()) - return lm.svc.UpdateName(ctx, token, patID, name) + return lm.svc.UpdatePATName(ctx, token, patID, name) } -func (lm *loggingMiddleware) UpdateDescription(ctx context.Context, token, patID, description string) (pa auth.PAT, err error) { +func (lm *loggingMiddleware) UpdatePATDescription(ctx context.Context, token, patID, description string) (pa auth.PAT, err error) { defer func(begin time.Time) { args := []any{ slog.String("duration", time.Since(begin).String()), @@ -556,9 +556,9 @@ func (lm *loggingMiddleware) UpdateDescription(ctx context.Context, token, patID } lm.logger.Info("Update PAT description completed successfully", args...) }(time.Now()) - return lm.svc.UpdateDescription(ctx, token, patID, description) + return lm.svc.UpdatePATDescription(ctx, token, patID, description) } -func (lm *loggingMiddleware) Retrieve(ctx context.Context, token, patID string) (pa auth.PAT, err error) { +func (lm *loggingMiddleware) RetrievePAT(ctx context.Context, token, patID string) (pa auth.PAT, err error) { defer func(begin time.Time) { args := []any{ slog.String("duration", time.Since(begin).String()), @@ -571,9 +571,9 @@ func (lm *loggingMiddleware) Retrieve(ctx context.Context, token, patID string) } lm.logger.Info("Retrieve PAT completed successfully", args...) }(time.Now()) - return lm.svc.Retrieve(ctx, token, patID) + return lm.svc.RetrievePAT(ctx, token, patID) } -func (lm *loggingMiddleware) List(ctx context.Context, token string, pm auth.PATSPageMeta) (pp auth.PATSPage, err error) { +func (lm *loggingMiddleware) ListPATS(ctx context.Context, token string, pm auth.PATSPageMeta) (pp auth.PATSPage, err error) { defer func(begin time.Time) { args := []any{ slog.String("duration", time.Since(begin).String()), @@ -587,9 +587,9 @@ func (lm *loggingMiddleware) List(ctx context.Context, token string, pm auth.PAT } lm.logger.Info("List PATS completed successfully", args...) }(time.Now()) - return lm.svc.List(ctx, token, pm) + return lm.svc.ListPATS(ctx, token, pm) } -func (lm *loggingMiddleware) Delete(ctx context.Context, token, patID string) (err error) { +func (lm *loggingMiddleware) DeletePAT(ctx context.Context, token, patID string) (err error) { defer func(begin time.Time) { args := []any{ slog.String("duration", time.Since(begin).String()), @@ -602,9 +602,9 @@ func (lm *loggingMiddleware) Delete(ctx context.Context, token, patID string) (e } lm.logger.Info("Delete PAT completed successfully", args...) }(time.Now()) - return lm.svc.Delete(ctx, token, patID) + return lm.svc.DeletePAT(ctx, token, patID) } -func (lm *loggingMiddleware) ResetSecret(ctx context.Context, token, patID string, duration time.Duration) (pa auth.PAT, err error) { +func (lm *loggingMiddleware) ResetPATSecret(ctx context.Context, token, patID string, duration time.Duration) (pa auth.PAT, err error) { defer func(begin time.Time) { args := []any{ slog.String("duration", time.Since(begin).String()), @@ -618,9 +618,9 @@ func (lm *loggingMiddleware) ResetSecret(ctx context.Context, token, patID strin } lm.logger.Info("Reset PAT secret completed successfully", args...) }(time.Now()) - return lm.svc.ResetSecret(ctx, token, patID, duration) + return lm.svc.ResetPATSecret(ctx, token, patID, duration) } -func (lm *loggingMiddleware) RevokeSecret(ctx context.Context, token, patID string) (err error) { +func (lm *loggingMiddleware) RevokePATSecret(ctx context.Context, token, patID string) (err error) { defer func(begin time.Time) { args := []any{ slog.String("duration", time.Since(begin).String()), @@ -633,9 +633,9 @@ func (lm *loggingMiddleware) RevokeSecret(ctx context.Context, token, patID stri } lm.logger.Info("Revoke PAT secret completed successfully", args...) }(time.Now()) - return lm.svc.RevokeSecret(ctx, token, patID) + return lm.svc.RevokePATSecret(ctx, token, patID) } -func (lm *loggingMiddleware) AddScopeEntry(ctx context.Context, token, patID string, platformEntityType auth.PlatformEntityType, optionalDomainID string, optionalDomainEntityType auth.DomainEntityType, operation auth.OperationType, entityIDs ...string) (sc auth.Scope, err error) { +func (lm *loggingMiddleware) AddPATScopeEntry(ctx context.Context, token, patID string, platformEntityType auth.PlatformEntityType, optionalDomainID string, optionalDomainEntityType auth.DomainEntityType, operation auth.OperationType, entityIDs ...string) (sc auth.Scope, err error) { defer func(begin time.Time) { args := []any{ slog.String("duration", time.Since(begin).String()), @@ -653,9 +653,9 @@ func (lm *loggingMiddleware) AddScopeEntry(ctx context.Context, token, patID str } lm.logger.Info("Add entry to PAT scope completed successfully", args...) }(time.Now()) - return lm.svc.AddScopeEntry(ctx, token, patID, platformEntityType, optionalDomainID, optionalDomainEntityType, operation, entityIDs...) + return lm.svc.AddPATScopeEntry(ctx, token, patID, platformEntityType, optionalDomainID, optionalDomainEntityType, operation, entityIDs...) } -func (lm *loggingMiddleware) RemoveScopeEntry(ctx context.Context, token, patID string, platformEntityType auth.PlatformEntityType, optionalDomainID string, optionalDomainEntityType auth.DomainEntityType, operation auth.OperationType, entityIDs ...string) (sc auth.Scope, err error) { +func (lm *loggingMiddleware) RemovePATScopeEntry(ctx context.Context, token, patID string, platformEntityType auth.PlatformEntityType, optionalDomainID string, optionalDomainEntityType auth.DomainEntityType, operation auth.OperationType, entityIDs ...string) (sc auth.Scope, err error) { defer func(begin time.Time) { args := []any{ slog.String("duration", time.Since(begin).String()), @@ -673,9 +673,9 @@ func (lm *loggingMiddleware) RemoveScopeEntry(ctx context.Context, token, patID } lm.logger.Info("Remove entry from PAT scope completed successfully", args...) }(time.Now()) - return lm.svc.RemoveScopeEntry(ctx, token, patID, platformEntityType, optionalDomainID, optionalDomainEntityType, operation, entityIDs...) + return lm.svc.RemovePATScopeEntry(ctx, token, patID, platformEntityType, optionalDomainID, optionalDomainEntityType, operation, entityIDs...) } -func (lm *loggingMiddleware) ClearAllScopeEntry(ctx context.Context, token, patID string) (err error) { +func (lm *loggingMiddleware) ClearPATAllScopeEntry(ctx context.Context, token, patID string) (err error) { defer func(begin time.Time) { args := []any{ slog.String("duration", time.Since(begin).String()), @@ -688,9 +688,9 @@ func (lm *loggingMiddleware) ClearAllScopeEntry(ctx context.Context, token, patI } lm.logger.Info("Clear all entry from PAT scope completed successfully", args...) }(time.Now()) - return lm.svc.ClearAllScopeEntry(ctx, token, patID) + return lm.svc.ClearPATAllScopeEntry(ctx, token, patID) } -func (lm *loggingMiddleware) TestCheckScopeEntry(ctx context.Context, paToken string, platformEntityType auth.PlatformEntityType, optionalDomainID string, optionalDomainEntityType auth.DomainEntityType, operation auth.OperationType, entityIDs ...string) (err error) { +func (lm *loggingMiddleware) TestCheckPATScopeEntry(ctx context.Context, paToken string, platformEntityType auth.PlatformEntityType, optionalDomainID string, optionalDomainEntityType auth.DomainEntityType, operation auth.OperationType, entityIDs ...string) (err error) { defer func(begin time.Time) { args := []any{ slog.String("duration", time.Since(begin).String()), @@ -707,7 +707,7 @@ func (lm *loggingMiddleware) TestCheckScopeEntry(ctx context.Context, paToken st } lm.logger.Info("Test Check entry in PAT scope completed successfully", args...) }(time.Now()) - return lm.svc.TestCheckScopeEntry(ctx, paToken, platformEntityType, optionalDomainID, optionalDomainEntityType, operation, entityIDs...) + return lm.svc.TestCheckPATScopeEntry(ctx, paToken, platformEntityType, optionalDomainID, optionalDomainEntityType, operation, entityIDs...) } func (lm *loggingMiddleware) IdentifyPAT(ctx context.Context, paToken string) (pa auth.PAT, err error) { defer func(begin time.Time) { diff --git a/auth/api/metrics.go b/auth/api/metrics.go index 722846ffa0..29922329c0 100644 --- a/auth/api/metrics.go +++ b/auth/api/metrics.go @@ -248,98 +248,98 @@ func (ms *metricsMiddleware) DeleteEntityPolicies(ctx context.Context, entityTyp return ms.svc.DeleteEntityPolicies(ctx, entityType, id) } -func (ms *metricsMiddleware) Create(ctx context.Context, token, name, description string, duration time.Duration, scope auth.Scope) (auth.PAT, error) { +func (ms *metricsMiddleware) CreatePAT(ctx context.Context, token, name, description string, duration time.Duration, scope auth.Scope) (auth.PAT, error) { defer func(begin time.Time) { ms.counter.With("method", "create_pat").Add(1) ms.latency.With("method", "create_pat").Observe(time.Since(begin).Seconds()) }(time.Now()) - return ms.svc.Create(ctx, token, name, description, duration, scope) + return ms.svc.CreatePAT(ctx, token, name, description, duration, scope) } -func (ms *metricsMiddleware) UpdateName(ctx context.Context, token, patID, name string) (auth.PAT, error) { +func (ms *metricsMiddleware) UpdatePATName(ctx context.Context, token, patID, name string) (auth.PAT, error) { defer func(begin time.Time) { ms.counter.With("method", "update_pat_name").Add(1) ms.latency.With("method", "update_pat_name").Observe(time.Since(begin).Seconds()) }(time.Now()) - return ms.svc.UpdateName(ctx, token, patID, name) + return ms.svc.UpdatePATName(ctx, token, patID, name) } -func (ms *metricsMiddleware) UpdateDescription(ctx context.Context, token, patID, description string) (auth.PAT, error) { +func (ms *metricsMiddleware) UpdatePATDescription(ctx context.Context, token, patID, description string) (auth.PAT, error) { defer func(begin time.Time) { ms.counter.With("method", "update_pat_description").Add(1) ms.latency.With("method", "update_pat_description").Observe(time.Since(begin).Seconds()) }(time.Now()) - return ms.svc.UpdateDescription(ctx, token, patID, description) + return ms.svc.UpdatePATDescription(ctx, token, patID, description) } -func (ms *metricsMiddleware) Retrieve(ctx context.Context, token, patID string) (auth.PAT, error) { +func (ms *metricsMiddleware) RetrievePAT(ctx context.Context, token, patID string) (auth.PAT, error) { defer func(begin time.Time) { ms.counter.With("method", "retrieve_pat").Add(1) ms.latency.With("method", "retrieve_pat").Observe(time.Since(begin).Seconds()) }(time.Now()) - return ms.svc.Retrieve(ctx, token, patID) + return ms.svc.RetrievePAT(ctx, token, patID) } -func (ms *metricsMiddleware) List(ctx context.Context, token string, pm auth.PATSPageMeta) (auth.PATSPage, error) { +func (ms *metricsMiddleware) ListPATS(ctx context.Context, token string, pm auth.PATSPageMeta) (auth.PATSPage, error) { defer func(begin time.Time) { ms.counter.With("method", "list_pats").Add(1) ms.latency.With("method", "list_pats").Observe(time.Since(begin).Seconds()) }(time.Now()) - return ms.svc.List(ctx, token, pm) + return ms.svc.ListPATS(ctx, token, pm) } -func (ms *metricsMiddleware) Delete(ctx context.Context, token, patID string) error { +func (ms *metricsMiddleware) DeletePAT(ctx context.Context, token, patID string) error { defer func(begin time.Time) { ms.counter.With("method", "delete_pat").Add(1) ms.latency.With("method", "delete_pat").Observe(time.Since(begin).Seconds()) }(time.Now()) - return ms.svc.Delete(ctx, token, patID) + return ms.svc.DeletePAT(ctx, token, patID) } -func (ms *metricsMiddleware) ResetSecret(ctx context.Context, token, patID string, duration time.Duration) (auth.PAT, error) { +func (ms *metricsMiddleware) ResetPATSecret(ctx context.Context, token, patID string, duration time.Duration) (auth.PAT, error) { defer func(begin time.Time) { ms.counter.With("method", "reset_pat_secret").Add(1) ms.latency.With("method", "reset_pat_secret").Observe(time.Since(begin).Seconds()) }(time.Now()) - return ms.svc.ResetSecret(ctx, token, patID, duration) + return ms.svc.ResetPATSecret(ctx, token, patID, duration) } -func (ms *metricsMiddleware) RevokeSecret(ctx context.Context, token, patID string) error { +func (ms *metricsMiddleware) RevokePATSecret(ctx context.Context, token, patID string) error { defer func(begin time.Time) { ms.counter.With("method", "revoke_pat_secret").Add(1) ms.latency.With("method", "revoke_pat_secret").Observe(time.Since(begin).Seconds()) }(time.Now()) - return ms.svc.RevokeSecret(ctx, token, patID) + return ms.svc.RevokePATSecret(ctx, token, patID) } -func (ms *metricsMiddleware) AddScopeEntry(ctx context.Context, token, patID string, platformEntityType auth.PlatformEntityType, optionalDomainID string, optionalDomainEntityType auth.DomainEntityType, operation auth.OperationType, entityIDs ...string) (auth.Scope, error) { +func (ms *metricsMiddleware) AddPATScopeEntry(ctx context.Context, token, patID string, platformEntityType auth.PlatformEntityType, optionalDomainID string, optionalDomainEntityType auth.DomainEntityType, operation auth.OperationType, entityIDs ...string) (auth.Scope, error) { defer func(begin time.Time) { ms.counter.With("method", "add_pat_scope_entry").Add(1) ms.latency.With("method", "add_pat_scope_entry").Observe(time.Since(begin).Seconds()) }(time.Now()) - return ms.svc.AddScopeEntry(ctx, token, patID, platformEntityType, optionalDomainID, optionalDomainEntityType, operation, entityIDs...) + return ms.svc.AddPATScopeEntry(ctx, token, patID, platformEntityType, optionalDomainID, optionalDomainEntityType, operation, entityIDs...) } -func (ms *metricsMiddleware) RemoveScopeEntry(ctx context.Context, token, patID string, platformEntityType auth.PlatformEntityType, optionalDomainID string, optionalDomainEntityType auth.DomainEntityType, operation auth.OperationType, entityIDs ...string) (auth.Scope, error) { +func (ms *metricsMiddleware) RemovePATScopeEntry(ctx context.Context, token, patID string, platformEntityType auth.PlatformEntityType, optionalDomainID string, optionalDomainEntityType auth.DomainEntityType, operation auth.OperationType, entityIDs ...string) (auth.Scope, error) { defer func(begin time.Time) { ms.counter.With("method", "remove_pat_scope_entry").Add(1) ms.latency.With("method", "remove_pat_scope_entry").Observe(time.Since(begin).Seconds()) }(time.Now()) - return ms.svc.RemoveScopeEntry(ctx, token, patID, platformEntityType, optionalDomainID, optionalDomainEntityType, operation, entityIDs...) + return ms.svc.RemovePATScopeEntry(ctx, token, patID, platformEntityType, optionalDomainID, optionalDomainEntityType, operation, entityIDs...) } -func (ms *metricsMiddleware) ClearAllScopeEntry(ctx context.Context, token, patID string) error { +func (ms *metricsMiddleware) ClearPATAllScopeEntry(ctx context.Context, token, patID string) error { defer func(begin time.Time) { ms.counter.With("method", "clear_pat_all_scope_entry").Add(1) ms.latency.With("method", "clear_pat_all_scope_entry").Observe(time.Since(begin).Seconds()) }(time.Now()) - return ms.svc.ClearAllScopeEntry(ctx, token, patID) + return ms.svc.ClearPATAllScopeEntry(ctx, token, patID) } -func (ms *metricsMiddleware) TestCheckScopeEntry(ctx context.Context, paToken string, platformEntityType auth.PlatformEntityType, optionalDomainID string, optionalDomainEntityType auth.DomainEntityType, operation auth.OperationType, entityIDs ...string) error { +func (ms *metricsMiddleware) TestCheckPATScopeEntry(ctx context.Context, paToken string, platformEntityType auth.PlatformEntityType, optionalDomainID string, optionalDomainEntityType auth.DomainEntityType, operation auth.OperationType, entityIDs ...string) error { defer func(begin time.Time) { ms.counter.With("method", "test_check_pat_scope_entry").Add(1) ms.latency.With("method", "test_check_pat_scope_entry").Observe(time.Since(begin).Seconds()) }(time.Now()) - return ms.svc.TestCheckScopeEntry(ctx, paToken, platformEntityType, optionalDomainID, optionalDomainEntityType, operation, entityIDs...) + return ms.svc.TestCheckPATScopeEntry(ctx, paToken, platformEntityType, optionalDomainID, optionalDomainEntityType, operation, entityIDs...) } func (ms *metricsMiddleware) IdentifyPAT(ctx context.Context, paToken string) (auth.PAT, error) { diff --git a/auth/events/streams.go b/auth/events/streams.go index 0081a962fa..e2d971cdad 100644 --- a/auth/events/streams.go +++ b/auth/events/streams.go @@ -5,6 +5,7 @@ package events import ( "context" + "time" "github.com/absmach/magistrala/auth" "github.com/absmach/magistrala/pkg/events" @@ -262,3 +263,46 @@ func (es *eventStore) CountSubjects(ctx context.Context, pr auth.PolicyReq) (uin func (es *eventStore) ListPermissions(ctx context.Context, pr auth.PolicyReq, filterPermission []string) (auth.Permissions, error) { return es.svc.ListPermissions(ctx, pr, filterPermission) } + +func (es *eventStore) CreatePAT(ctx context.Context, token, name, description string, duration time.Duration, scope auth.Scope) (auth.PAT, error) { + return es.svc.CreatePAT(ctx, token, name, description, duration, scope) +} +func (es *eventStore) UpdatePATName(ctx context.Context, token, patID, name string) (auth.PAT, error) { + return es.svc.UpdatePATName(ctx, token, patID, name) +} +func (es *eventStore) UpdatePATDescription(ctx context.Context, token, patID, description string) (auth.PAT, error) { + return es.svc.UpdatePATDescription(ctx, token, patID, description) +} +func (es *eventStore) RetrievePAT(ctx context.Context, token, patID string) (auth.PAT, error) { + return es.svc.RetrievePAT(ctx, token, patID) +} +func (es *eventStore) ListPATS(ctx context.Context, token string, pm auth.PATSPageMeta) (auth.PATSPage, error) { + return es.svc.ListPATS(ctx, token, pm) +} +func (es *eventStore) DeletePAT(ctx context.Context, token, patID string) error { + return es.svc.DeletePAT(ctx, token, patID) +} +func (es *eventStore) ResetPATSecret(ctx context.Context, token, patID string, duration time.Duration) (auth.PAT, error) { + return es.svc.ResetPATSecret(ctx, token, patID, duration) +} +func (es *eventStore) RevokePATSecret(ctx context.Context, token, patID string) error { + return es.svc.RevokePATSecret(ctx, token, patID) +} +func (es *eventStore) AddPATScopeEntry(ctx context.Context, token, patID string, platformEntityType auth.PlatformEntityType, optionalDomainID string, optionalDomainEntityType auth.DomainEntityType, operation auth.OperationType, entityIDs ...string) (auth.Scope, error) { + return es.svc.AddPATScopeEntry(ctx, token, patID, platformEntityType, optionalDomainID, optionalDomainEntityType, operation, entityIDs...) +} +func (es *eventStore) RemovePATScopeEntry(ctx context.Context, token, patID string, platformEntityType auth.PlatformEntityType, optionalDomainID string, optionalDomainEntityType auth.DomainEntityType, operation auth.OperationType, entityIDs ...string) (auth.Scope, error) { + return es.svc.RemovePATScopeEntry(ctx, token, patID, platformEntityType, optionalDomainID, optionalDomainEntityType, operation, entityIDs...) +} +func (es *eventStore) ClearPATAllScopeEntry(ctx context.Context, token, patID string) error { + return es.svc.ClearPATAllScopeEntry(ctx, token, patID) +} +func (es *eventStore) TestCheckPATScopeEntry(ctx context.Context, paToken string, platformEntityType auth.PlatformEntityType, optionalDomainID string, optionalDomainEntityType auth.DomainEntityType, operation auth.OperationType, entityIDs ...string) error { + return es.svc.TestCheckPATScopeEntry(ctx, paToken, platformEntityType, optionalDomainID, optionalDomainEntityType, operation, entityIDs...) +} +func (es *eventStore) IdentifyPAT(ctx context.Context, paToken string) (auth.PAT, error) { + return es.svc.IdentifyPAT(ctx, paToken) +} +func (es *eventStore) AuthorizePAT(ctx context.Context, paToken string) (auth.PAT, error) { + return es.svc.AuthorizePAT(ctx, paToken) +} diff --git a/auth/mocks/pats.go b/auth/mocks/pats.go index 9f15fdf508..3b53eaf647 100644 --- a/auth/mocks/pats.go +++ b/auth/mocks/pats.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.42.3. DO NOT EDIT. +// Code generated by mockery v2.43.2. DO NOT EDIT. // Copyright (c) Abstract Machines @@ -19,8 +19,8 @@ type PATS struct { mock.Mock } -// AddScopeEntry provides a mock function with given fields: ctx, token, patID, platformEntityType, optionalDomainID, optionalDomainEntityType, operation, entityIDs -func (_m *PATS) AddScopeEntry(ctx context.Context, token string, patID string, platformEntityType auth.PlatformEntityType, optionalDomainID string, optionalDomainEntityType auth.DomainEntityType, operation auth.OperationType, entityIDs ...string) (auth.Scope, error) { +// AddPATScopeEntry provides a mock function with given fields: ctx, token, patID, platformEntityType, optionalDomainID, optionalDomainEntityType, operation, entityIDs +func (_m *PATS) AddPATScopeEntry(ctx context.Context, token string, patID string, platformEntityType auth.PlatformEntityType, optionalDomainID string, optionalDomainEntityType auth.DomainEntityType, operation auth.OperationType, entityIDs ...string) (auth.Scope, error) { _va := make([]interface{}, len(entityIDs)) for _i := range entityIDs { _va[_i] = entityIDs[_i] @@ -31,7 +31,7 @@ func (_m *PATS) AddScopeEntry(ctx context.Context, token string, patID string, p ret := _m.Called(_ca...) if len(ret) == 0 { - panic("no return value specified for AddScopeEntry") + panic("no return value specified for AddPATScopeEntry") } var r0 auth.Scope @@ -82,12 +82,12 @@ func (_m *PATS) AuthorizePAT(ctx context.Context, paToken string) (auth.PAT, err return r0, r1 } -// ClearAllScopeEntry provides a mock function with given fields: ctx, token, patID -func (_m *PATS) ClearAllScopeEntry(ctx context.Context, token string, patID string) error { +// ClearPATAllScopeEntry provides a mock function with given fields: ctx, token, patID +func (_m *PATS) ClearPATAllScopeEntry(ctx context.Context, token string, patID string) error { ret := _m.Called(ctx, token, patID) if len(ret) == 0 { - panic("no return value specified for ClearAllScopeEntry") + panic("no return value specified for ClearPATAllScopeEntry") } var r0 error @@ -100,12 +100,12 @@ func (_m *PATS) ClearAllScopeEntry(ctx context.Context, token string, patID stri return r0 } -// Create provides a mock function with given fields: ctx, token, name, description, duration, scope -func (_m *PATS) Create(ctx context.Context, token string, name string, description string, duration time.Duration, scope auth.Scope) (auth.PAT, error) { +// CreatePAT provides a mock function with given fields: ctx, token, name, description, duration, scope +func (_m *PATS) CreatePAT(ctx context.Context, token string, name string, description string, duration time.Duration, scope auth.Scope) (auth.PAT, error) { ret := _m.Called(ctx, token, name, description, duration, scope) if len(ret) == 0 { - panic("no return value specified for Create") + panic("no return value specified for CreatePAT") } var r0 auth.PAT @@ -128,12 +128,12 @@ func (_m *PATS) Create(ctx context.Context, token string, name string, descripti return r0, r1 } -// Delete provides a mock function with given fields: ctx, token, patID -func (_m *PATS) Delete(ctx context.Context, token string, patID string) error { +// DeletePAT provides a mock function with given fields: ctx, token, patID +func (_m *PATS) DeletePAT(ctx context.Context, token string, patID string) error { ret := _m.Called(ctx, token, patID) if len(ret) == 0 { - panic("no return value specified for Delete") + panic("no return value specified for DeletePAT") } var r0 error @@ -174,12 +174,12 @@ func (_m *PATS) IdentifyPAT(ctx context.Context, paToken string) (auth.PAT, erro return r0, r1 } -// List provides a mock function with given fields: ctx, token, pm -func (_m *PATS) List(ctx context.Context, token string, pm auth.PATSPageMeta) (auth.PATSPage, error) { +// ListPATS provides a mock function with given fields: ctx, token, pm +func (_m *PATS) ListPATS(ctx context.Context, token string, pm auth.PATSPageMeta) (auth.PATSPage, error) { ret := _m.Called(ctx, token, pm) if len(ret) == 0 { - panic("no return value specified for List") + panic("no return value specified for ListPATS") } var r0 auth.PATSPage @@ -202,8 +202,8 @@ func (_m *PATS) List(ctx context.Context, token string, pm auth.PATSPageMeta) (a return r0, r1 } -// RemoveScopeEntry provides a mock function with given fields: ctx, token, patID, platformEntityType, optionalDomainID, optionalDomainEntityType, operation, entityIDs -func (_m *PATS) RemoveScopeEntry(ctx context.Context, token string, patID string, platformEntityType auth.PlatformEntityType, optionalDomainID string, optionalDomainEntityType auth.DomainEntityType, operation auth.OperationType, entityIDs ...string) (auth.Scope, error) { +// RemovePATScopeEntry provides a mock function with given fields: ctx, token, patID, platformEntityType, optionalDomainID, optionalDomainEntityType, operation, entityIDs +func (_m *PATS) RemovePATScopeEntry(ctx context.Context, token string, patID string, platformEntityType auth.PlatformEntityType, optionalDomainID string, optionalDomainEntityType auth.DomainEntityType, operation auth.OperationType, entityIDs ...string) (auth.Scope, error) { _va := make([]interface{}, len(entityIDs)) for _i := range entityIDs { _va[_i] = entityIDs[_i] @@ -214,7 +214,7 @@ func (_m *PATS) RemoveScopeEntry(ctx context.Context, token string, patID string ret := _m.Called(_ca...) if len(ret) == 0 { - panic("no return value specified for RemoveScopeEntry") + panic("no return value specified for RemovePATScopeEntry") } var r0 auth.Scope @@ -237,12 +237,12 @@ func (_m *PATS) RemoveScopeEntry(ctx context.Context, token string, patID string return r0, r1 } -// ResetSecret provides a mock function with given fields: ctx, token, patID, duration -func (_m *PATS) ResetSecret(ctx context.Context, token string, patID string, duration time.Duration) (auth.PAT, error) { +// ResetPATSecret provides a mock function with given fields: ctx, token, patID, duration +func (_m *PATS) ResetPATSecret(ctx context.Context, token string, patID string, duration time.Duration) (auth.PAT, error) { ret := _m.Called(ctx, token, patID, duration) if len(ret) == 0 { - panic("no return value specified for ResetSecret") + panic("no return value specified for ResetPATSecret") } var r0 auth.PAT @@ -265,12 +265,12 @@ func (_m *PATS) ResetSecret(ctx context.Context, token string, patID string, dur return r0, r1 } -// Retrieve provides a mock function with given fields: ctx, token, patID -func (_m *PATS) Retrieve(ctx context.Context, token string, patID string) (auth.PAT, error) { +// RetrievePAT provides a mock function with given fields: ctx, token, patID +func (_m *PATS) RetrievePAT(ctx context.Context, token string, patID string) (auth.PAT, error) { ret := _m.Called(ctx, token, patID) if len(ret) == 0 { - panic("no return value specified for Retrieve") + panic("no return value specified for RetrievePAT") } var r0 auth.PAT @@ -293,12 +293,12 @@ func (_m *PATS) Retrieve(ctx context.Context, token string, patID string) (auth. return r0, r1 } -// RevokeSecret provides a mock function with given fields: ctx, token, patID -func (_m *PATS) RevokeSecret(ctx context.Context, token string, patID string) error { +// RevokePATSecret provides a mock function with given fields: ctx, token, patID +func (_m *PATS) RevokePATSecret(ctx context.Context, token string, patID string) error { ret := _m.Called(ctx, token, patID) if len(ret) == 0 { - panic("no return value specified for RevokeSecret") + panic("no return value specified for RevokePATSecret") } var r0 error @@ -311,8 +311,8 @@ func (_m *PATS) RevokeSecret(ctx context.Context, token string, patID string) er return r0 } -// TestCheckScopeEntry provides a mock function with given fields: ctx, paToken, platformEntityType, optionalDomainID, optionalDomainEntityType, operation, entityIDs -func (_m *PATS) TestCheckScopeEntry(ctx context.Context, paToken string, platformEntityType auth.PlatformEntityType, optionalDomainID string, optionalDomainEntityType auth.DomainEntityType, operation auth.OperationType, entityIDs ...string) error { +// TestCheckPATScopeEntry provides a mock function with given fields: ctx, paToken, platformEntityType, optionalDomainID, optionalDomainEntityType, operation, entityIDs +func (_m *PATS) TestCheckPATScopeEntry(ctx context.Context, paToken string, platformEntityType auth.PlatformEntityType, optionalDomainID string, optionalDomainEntityType auth.DomainEntityType, operation auth.OperationType, entityIDs ...string) error { _va := make([]interface{}, len(entityIDs)) for _i := range entityIDs { _va[_i] = entityIDs[_i] @@ -323,7 +323,7 @@ func (_m *PATS) TestCheckScopeEntry(ctx context.Context, paToken string, platfor ret := _m.Called(_ca...) if len(ret) == 0 { - panic("no return value specified for TestCheckScopeEntry") + panic("no return value specified for TestCheckPATScopeEntry") } var r0 error @@ -336,12 +336,12 @@ func (_m *PATS) TestCheckScopeEntry(ctx context.Context, paToken string, platfor return r0 } -// UpdateDescription provides a mock function with given fields: ctx, token, patID, description -func (_m *PATS) UpdateDescription(ctx context.Context, token string, patID string, description string) (auth.PAT, error) { +// UpdatePATDescription provides a mock function with given fields: ctx, token, patID, description +func (_m *PATS) UpdatePATDescription(ctx context.Context, token string, patID string, description string) (auth.PAT, error) { ret := _m.Called(ctx, token, patID, description) if len(ret) == 0 { - panic("no return value specified for UpdateDescription") + panic("no return value specified for UpdatePATDescription") } var r0 auth.PAT @@ -364,12 +364,12 @@ func (_m *PATS) UpdateDescription(ctx context.Context, token string, patID strin return r0, r1 } -// UpdateName provides a mock function with given fields: ctx, token, patID, name -func (_m *PATS) UpdateName(ctx context.Context, token string, patID string, name string) (auth.PAT, error) { +// UpdatePATName provides a mock function with given fields: ctx, token, patID, name +func (_m *PATS) UpdatePATName(ctx context.Context, token string, patID string, name string) (auth.PAT, error) { ret := _m.Called(ctx, token, patID, name) if len(ret) == 0 { - panic("no return value specified for UpdateName") + panic("no return value specified for UpdatePATName") } var r0 auth.PAT diff --git a/auth/mocks/patsrepo.go b/auth/mocks/patsrepo.go index 6eca8cfc5e..10cc48612a 100644 --- a/auth/mocks/patsrepo.go +++ b/auth/mocks/patsrepo.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.42.3. DO NOT EDIT. +// Code generated by mockery v2.43.2. DO NOT EDIT. // Copyright (c) Abstract Machines diff --git a/auth/mocks/service.go b/auth/mocks/service.go index a769e78b8e..ccf50a34f2 100644 --- a/auth/mocks/service.go +++ b/auth/mocks/service.go @@ -19,6 +19,41 @@ type Service struct { mock.Mock } +// AddPATScopeEntry provides a mock function with given fields: ctx, token, patID, platformEntityType, optionalDomainID, optionalDomainEntityType, operation, entityIDs +func (_m *Service) AddPATScopeEntry(ctx context.Context, token string, patID string, platformEntityType auth.PlatformEntityType, optionalDomainID string, optionalDomainEntityType auth.DomainEntityType, operation auth.OperationType, entityIDs ...string) (auth.Scope, error) { + _va := make([]interface{}, len(entityIDs)) + for _i := range entityIDs { + _va[_i] = entityIDs[_i] + } + var _ca []interface{} + _ca = append(_ca, ctx, token, patID, platformEntityType, optionalDomainID, optionalDomainEntityType, operation) + _ca = append(_ca, _va...) + ret := _m.Called(_ca...) + + if len(ret) == 0 { + panic("no return value specified for AddPATScopeEntry") + } + + var r0 auth.Scope + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, string, string, auth.PlatformEntityType, string, auth.DomainEntityType, auth.OperationType, ...string) (auth.Scope, error)); ok { + return rf(ctx, token, patID, platformEntityType, optionalDomainID, optionalDomainEntityType, operation, entityIDs...) + } + if rf, ok := ret.Get(0).(func(context.Context, string, string, auth.PlatformEntityType, string, auth.DomainEntityType, auth.OperationType, ...string) auth.Scope); ok { + r0 = rf(ctx, token, patID, platformEntityType, optionalDomainID, optionalDomainEntityType, operation, entityIDs...) + } else { + r0 = ret.Get(0).(auth.Scope) + } + + if rf, ok := ret.Get(1).(func(context.Context, string, string, auth.PlatformEntityType, string, auth.DomainEntityType, auth.OperationType, ...string) error); ok { + r1 = rf(ctx, token, patID, platformEntityType, optionalDomainID, optionalDomainEntityType, operation, entityIDs...) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + // AddPolicies provides a mock function with given fields: ctx, prs func (_m *Service) AddPolicies(ctx context.Context, prs []auth.PolicyReq) error { ret := _m.Called(ctx, prs) @@ -55,41 +90,6 @@ func (_m *Service) AddPolicy(ctx context.Context, pr auth.PolicyReq) error { return r0 } -// AddScopeEntry provides a mock function with given fields: ctx, token, patID, platformEntityType, optionalDomainID, optionalDomainEntityType, operation, entityIDs -func (_m *Service) AddScopeEntry(ctx context.Context, token string, patID string, platformEntityType auth.PlatformEntityType, optionalDomainID string, optionalDomainEntityType auth.DomainEntityType, operation auth.OperationType, entityIDs ...string) (auth.Scope, error) { - _va := make([]interface{}, len(entityIDs)) - for _i := range entityIDs { - _va[_i] = entityIDs[_i] - } - var _ca []interface{} - _ca = append(_ca, ctx, token, patID, platformEntityType, optionalDomainID, optionalDomainEntityType, operation) - _ca = append(_ca, _va...) - ret := _m.Called(_ca...) - - if len(ret) == 0 { - panic("no return value specified for AddScopeEntry") - } - - var r0 auth.Scope - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, string, string, auth.PlatformEntityType, string, auth.DomainEntityType, auth.OperationType, ...string) (auth.Scope, error)); ok { - return rf(ctx, token, patID, platformEntityType, optionalDomainID, optionalDomainEntityType, operation, entityIDs...) - } - if rf, ok := ret.Get(0).(func(context.Context, string, string, auth.PlatformEntityType, string, auth.DomainEntityType, auth.OperationType, ...string) auth.Scope); ok { - r0 = rf(ctx, token, patID, platformEntityType, optionalDomainID, optionalDomainEntityType, operation, entityIDs...) - } else { - r0 = ret.Get(0).(auth.Scope) - } - - if rf, ok := ret.Get(1).(func(context.Context, string, string, auth.PlatformEntityType, string, auth.DomainEntityType, auth.OperationType, ...string) error); ok { - r1 = rf(ctx, token, patID, platformEntityType, optionalDomainID, optionalDomainEntityType, operation, entityIDs...) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - // AssignUsers provides a mock function with given fields: ctx, token, id, userIds, relation func (_m *Service) AssignUsers(ctx context.Context, token string, id string, userIds []string, relation string) error { ret := _m.Called(ctx, token, id, userIds, relation) @@ -182,12 +182,12 @@ func (_m *Service) ChangeDomainStatus(ctx context.Context, token string, id stri return r0, r1 } -// ClearAllScopeEntry provides a mock function with given fields: ctx, token, patID -func (_m *Service) ClearAllScopeEntry(ctx context.Context, token string, patID string) error { +// ClearPATAllScopeEntry provides a mock function with given fields: ctx, token, patID +func (_m *Service) ClearPATAllScopeEntry(ctx context.Context, token string, patID string) error { ret := _m.Called(ctx, token, patID) if len(ret) == 0 { - panic("no return value specified for ClearAllScopeEntry") + panic("no return value specified for ClearPATAllScopeEntry") } var r0 error @@ -256,27 +256,27 @@ func (_m *Service) CountSubjects(ctx context.Context, pr auth.PolicyReq) (uint64 return r0, r1 } -// Create provides a mock function with given fields: ctx, token, name, description, duration, scope -func (_m *Service) Create(ctx context.Context, token string, name string, description string, duration time.Duration, scope auth.Scope) (auth.PAT, error) { - ret := _m.Called(ctx, token, name, description, duration, scope) +// CreateDomain provides a mock function with given fields: ctx, token, d +func (_m *Service) CreateDomain(ctx context.Context, token string, d auth.Domain) (auth.Domain, error) { + ret := _m.Called(ctx, token, d) if len(ret) == 0 { - panic("no return value specified for Create") + panic("no return value specified for CreateDomain") } - var r0 auth.PAT + var r0 auth.Domain var r1 error - if rf, ok := ret.Get(0).(func(context.Context, string, string, string, time.Duration, auth.Scope) (auth.PAT, error)); ok { - return rf(ctx, token, name, description, duration, scope) + if rf, ok := ret.Get(0).(func(context.Context, string, auth.Domain) (auth.Domain, error)); ok { + return rf(ctx, token, d) } - if rf, ok := ret.Get(0).(func(context.Context, string, string, string, time.Duration, auth.Scope) auth.PAT); ok { - r0 = rf(ctx, token, name, description, duration, scope) + if rf, ok := ret.Get(0).(func(context.Context, string, auth.Domain) auth.Domain); ok { + r0 = rf(ctx, token, d) } else { - r0 = ret.Get(0).(auth.PAT) + r0 = ret.Get(0).(auth.Domain) } - if rf, ok := ret.Get(1).(func(context.Context, string, string, string, time.Duration, auth.Scope) error); ok { - r1 = rf(ctx, token, name, description, duration, scope) + if rf, ok := ret.Get(1).(func(context.Context, string, auth.Domain) error); ok { + r1 = rf(ctx, token, d) } else { r1 = ret.Error(1) } @@ -284,27 +284,27 @@ func (_m *Service) Create(ctx context.Context, token string, name string, descri return r0, r1 } -// CreateDomain provides a mock function with given fields: ctx, token, d -func (_m *Service) CreateDomain(ctx context.Context, token string, d auth.Domain) (auth.Domain, error) { - ret := _m.Called(ctx, token, d) +// CreatePAT provides a mock function with given fields: ctx, token, name, description, duration, scope +func (_m *Service) CreatePAT(ctx context.Context, token string, name string, description string, duration time.Duration, scope auth.Scope) (auth.PAT, error) { + ret := _m.Called(ctx, token, name, description, duration, scope) if len(ret) == 0 { - panic("no return value specified for CreateDomain") + panic("no return value specified for CreatePAT") } - var r0 auth.Domain + var r0 auth.PAT var r1 error - if rf, ok := ret.Get(0).(func(context.Context, string, auth.Domain) (auth.Domain, error)); ok { - return rf(ctx, token, d) + if rf, ok := ret.Get(0).(func(context.Context, string, string, string, time.Duration, auth.Scope) (auth.PAT, error)); ok { + return rf(ctx, token, name, description, duration, scope) } - if rf, ok := ret.Get(0).(func(context.Context, string, auth.Domain) auth.Domain); ok { - r0 = rf(ctx, token, d) + if rf, ok := ret.Get(0).(func(context.Context, string, string, string, time.Duration, auth.Scope) auth.PAT); ok { + r0 = rf(ctx, token, name, description, duration, scope) } else { - r0 = ret.Get(0).(auth.Domain) + r0 = ret.Get(0).(auth.PAT) } - if rf, ok := ret.Get(1).(func(context.Context, string, auth.Domain) error); ok { - r1 = rf(ctx, token, d) + if rf, ok := ret.Get(1).(func(context.Context, string, string, string, time.Duration, auth.Scope) error); ok { + r1 = rf(ctx, token, name, description, duration, scope) } else { r1 = ret.Error(1) } @@ -330,12 +330,12 @@ func (_m *Service) DeleteEntityPolicies(ctx context.Context, entityType string, return r0 } -// Delete provides a mock function with given fields: ctx, token, patID -func (_m *Service) Delete(ctx context.Context, token string, patID string) error { +// DeletePAT provides a mock function with given fields: ctx, token, patID +func (_m *Service) DeletePAT(ctx context.Context, token string, patID string) error { ret := _m.Called(ctx, token, patID) if len(ret) == 0 { - panic("no return value specified for Delete") + panic("no return value specified for DeletePAT") } var r0 error @@ -468,34 +468,6 @@ func (_m *Service) Issue(ctx context.Context, token string, key auth.Key) (auth. return r0, r1 } -// List provides a mock function with given fields: ctx, token, pm -func (_m *Service) List(ctx context.Context, token string, pm auth.PATSPageMeta) (auth.PATSPage, error) { - ret := _m.Called(ctx, token, pm) - - if len(ret) == 0 { - panic("no return value specified for List") - } - - var r0 auth.PATSPage - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, string, auth.PATSPageMeta) (auth.PATSPage, error)); ok { - return rf(ctx, token, pm) - } - if rf, ok := ret.Get(0).(func(context.Context, string, auth.PATSPageMeta) auth.PATSPage); ok { - r0 = rf(ctx, token, pm) - } else { - r0 = ret.Get(0).(auth.PATSPage) - } - - if rf, ok := ret.Get(1).(func(context.Context, string, auth.PATSPageMeta) error); ok { - r1 = rf(ctx, token, pm) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - // ListAllObjects provides a mock function with given fields: ctx, pr func (_m *Service) ListAllObjects(ctx context.Context, pr auth.PolicyReq) (auth.PolicyPage, error) { ret := _m.Called(ctx, pr) @@ -608,6 +580,34 @@ func (_m *Service) ListObjects(ctx context.Context, pr auth.PolicyReq, nextPageT return r0, r1 } +// ListPATS provides a mock function with given fields: ctx, token, pm +func (_m *Service) ListPATS(ctx context.Context, token string, pm auth.PATSPageMeta) (auth.PATSPage, error) { + ret := _m.Called(ctx, token, pm) + + if len(ret) == 0 { + panic("no return value specified for ListPATS") + } + + var r0 auth.PATSPage + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, string, auth.PATSPageMeta) (auth.PATSPage, error)); ok { + return rf(ctx, token, pm) + } + if rf, ok := ret.Get(0).(func(context.Context, string, auth.PATSPageMeta) auth.PATSPage); ok { + r0 = rf(ctx, token, pm) + } else { + r0 = ret.Get(0).(auth.PATSPage) + } + + if rf, ok := ret.Get(1).(func(context.Context, string, auth.PATSPageMeta) error); ok { + r1 = rf(ctx, token, pm) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + // ListPermissions provides a mock function with given fields: ctx, pr, filterPermission func (_m *Service) ListPermissions(ctx context.Context, pr auth.PolicyReq, filterPermission []string) (auth.Permissions, error) { ret := _m.Called(ctx, pr, filterPermission) @@ -694,8 +694,8 @@ func (_m *Service) ListUserDomains(ctx context.Context, token string, userID str return r0, r1 } -// RemoveScopeEntry provides a mock function with given fields: ctx, token, patID, platformEntityType, optionalDomainID, optionalDomainEntityType, operation, entityIDs -func (_m *Service) RemoveScopeEntry(ctx context.Context, token string, patID string, platformEntityType auth.PlatformEntityType, optionalDomainID string, optionalDomainEntityType auth.DomainEntityType, operation auth.OperationType, entityIDs ...string) (auth.Scope, error) { +// RemovePATScopeEntry provides a mock function with given fields: ctx, token, patID, platformEntityType, optionalDomainID, optionalDomainEntityType, operation, entityIDs +func (_m *Service) RemovePATScopeEntry(ctx context.Context, token string, patID string, platformEntityType auth.PlatformEntityType, optionalDomainID string, optionalDomainEntityType auth.DomainEntityType, operation auth.OperationType, entityIDs ...string) (auth.Scope, error) { _va := make([]interface{}, len(entityIDs)) for _i := range entityIDs { _va[_i] = entityIDs[_i] @@ -706,7 +706,7 @@ func (_m *Service) RemoveScopeEntry(ctx context.Context, token string, patID str ret := _m.Called(_ca...) if len(ret) == 0 { - panic("no return value specified for RemoveScopeEntry") + panic("no return value specified for RemovePATScopeEntry") } var r0 auth.Scope @@ -729,12 +729,12 @@ func (_m *Service) RemoveScopeEntry(ctx context.Context, token string, patID str return r0, r1 } -// ResetSecret provides a mock function with given fields: ctx, token, patID, duration -func (_m *Service) ResetSecret(ctx context.Context, token string, patID string, duration time.Duration) (auth.PAT, error) { +// ResetPATSecret provides a mock function with given fields: ctx, token, patID, duration +func (_m *Service) ResetPATSecret(ctx context.Context, token string, patID string, duration time.Duration) (auth.PAT, error) { ret := _m.Called(ctx, token, patID, duration) if len(ret) == 0 { - panic("no return value specified for ResetSecret") + panic("no return value specified for ResetPATSecret") } var r0 auth.PAT @@ -757,34 +757,6 @@ func (_m *Service) ResetSecret(ctx context.Context, token string, patID string, return r0, r1 } -// Retrieve provides a mock function with given fields: ctx, token, patID -func (_m *Service) Retrieve(ctx context.Context, token string, patID string) (auth.PAT, error) { - ret := _m.Called(ctx, token, patID) - - if len(ret) == 0 { - panic("no return value specified for Retrieve") - } - - var r0 auth.PAT - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, string, string) (auth.PAT, error)); ok { - return rf(ctx, token, patID) - } - if rf, ok := ret.Get(0).(func(context.Context, string, string) auth.PAT); ok { - r0 = rf(ctx, token, patID) - } else { - r0 = ret.Get(0).(auth.PAT) - } - - if rf, ok := ret.Get(1).(func(context.Context, string, string) error); ok { - r1 = rf(ctx, token, patID) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - // RetrieveDomain provides a mock function with given fields: ctx, token, id func (_m *Service) RetrieveDomain(ctx context.Context, token string, id string) (auth.Domain, error) { ret := _m.Called(ctx, token, id) @@ -871,60 +843,45 @@ func (_m *Service) RetrieveKey(ctx context.Context, token string, id string) (au return r0, r1 } -// Revoke provides a mock function with given fields: ctx, token, id -func (_m *Service) Revoke(ctx context.Context, token string, id string) error { - ret := _m.Called(ctx, token, id) +// RetrievePAT provides a mock function with given fields: ctx, token, patID +func (_m *Service) RetrievePAT(ctx context.Context, token string, patID string) (auth.PAT, error) { + ret := _m.Called(ctx, token, patID) if len(ret) == 0 { - panic("no return value specified for Revoke") + panic("no return value specified for RetrievePAT") } - var r0 error - if rf, ok := ret.Get(0).(func(context.Context, string, string) error); ok { - r0 = rf(ctx, token, id) - } else { - r0 = ret.Error(0) + var r0 auth.PAT + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, string, string) (auth.PAT, error)); ok { + return rf(ctx, token, patID) } - - return r0 -} - -// RevokeSecret provides a mock function with given fields: ctx, token, patID -func (_m *Service) RevokeSecret(ctx context.Context, token string, patID string) error { - ret := _m.Called(ctx, token, patID) - - if len(ret) == 0 { - panic("no return value specified for RevokeSecret") + if rf, ok := ret.Get(0).(func(context.Context, string, string) auth.PAT); ok { + r0 = rf(ctx, token, patID) + } else { + r0 = ret.Get(0).(auth.PAT) } - var r0 error - if rf, ok := ret.Get(0).(func(context.Context, string, string) error); ok { - r0 = rf(ctx, token, patID) + if rf, ok := ret.Get(1).(func(context.Context, string, string) error); ok { + r1 = rf(ctx, token, patID) } else { - r0 = ret.Error(0) + r1 = ret.Error(1) } - return r0 + return r0, r1 } -// TestCheckScopeEntry provides a mock function with given fields: ctx, paToken, platformEntityType, optionalDomainID, optionalDomainEntityType, operation, entityIDs -func (_m *Service) TestCheckScopeEntry(ctx context.Context, paToken string, platformEntityType auth.PlatformEntityType, optionalDomainID string, optionalDomainEntityType auth.DomainEntityType, operation auth.OperationType, entityIDs ...string) error { - _va := make([]interface{}, len(entityIDs)) - for _i := range entityIDs { - _va[_i] = entityIDs[_i] - } - var _ca []interface{} - _ca = append(_ca, ctx, paToken, platformEntityType, optionalDomainID, optionalDomainEntityType, operation) - _ca = append(_ca, _va...) - ret := _m.Called(_ca...) +// Revoke provides a mock function with given fields: ctx, token, id +func (_m *Service) Revoke(ctx context.Context, token string, id string) error { + ret := _m.Called(ctx, token, id) if len(ret) == 0 { - panic("no return value specified for TestCheckScopeEntry") + panic("no return value specified for Revoke") } var r0 error - if rf, ok := ret.Get(0).(func(context.Context, string, auth.PlatformEntityType, string, auth.DomainEntityType, auth.OperationType, ...string) error); ok { - r0 = rf(ctx, paToken, platformEntityType, optionalDomainID, optionalDomainEntityType, operation, entityIDs...) + if rf, ok := ret.Get(0).(func(context.Context, string, string) error); ok { + r0 = rf(ctx, token, id) } else { r0 = ret.Error(0) } @@ -932,12 +889,12 @@ func (_m *Service) TestCheckScopeEntry(ctx context.Context, paToken string, plat return r0 } -// RevokeSecret provides a mock function with given fields: ctx, token, patID -func (_m *Service) RevokeSecret(ctx context.Context, token string, patID string) error { +// RevokePATSecret provides a mock function with given fields: ctx, token, patID +func (_m *Service) RevokePATSecret(ctx context.Context, token string, patID string) error { ret := _m.Called(ctx, token, patID) if len(ret) == 0 { - panic("no return value specified for RevokeSecret") + panic("no return value specified for RevokePATSecret") } var r0 error @@ -950,8 +907,8 @@ func (_m *Service) RevokeSecret(ctx context.Context, token string, patID string) return r0 } -// TestCheckScope provides a mock function with given fields: ctx, paToken, platformEntityType, optionalDomainID, optionalDomainEntityType, operation, entityIDs -func (_m *Service) TestCheckScope(ctx context.Context, paToken string, platformEntityType auth.PlatformEntityType, optionalDomainID string, optionalDomainEntityType auth.DomainEntityType, operation auth.OperationType, entityIDs ...string) error { +// TestCheckPATScopeEntry provides a mock function with given fields: ctx, paToken, platformEntityType, optionalDomainID, optionalDomainEntityType, operation, entityIDs +func (_m *Service) TestCheckPATScopeEntry(ctx context.Context, paToken string, platformEntityType auth.PlatformEntityType, optionalDomainID string, optionalDomainEntityType auth.DomainEntityType, operation auth.OperationType, entityIDs ...string) error { _va := make([]interface{}, len(entityIDs)) for _i := range entityIDs { _va[_i] = entityIDs[_i] @@ -962,7 +919,7 @@ func (_m *Service) TestCheckScope(ctx context.Context, paToken string, platformE ret := _m.Called(_ca...) if len(ret) == 0 { - panic("no return value specified for TestCheckScope") + panic("no return value specified for TestCheckPATScopeEntry") } var r0 error @@ -993,27 +950,27 @@ func (_m *Service) UnassignUser(ctx context.Context, token string, id string, us return r0 } -// UpdateDescription provides a mock function with given fields: ctx, token, patID, description -func (_m *Service) UpdateDescription(ctx context.Context, token string, patID string, description string) (auth.PAT, error) { - ret := _m.Called(ctx, token, patID, description) +// UpdateDomain provides a mock function with given fields: ctx, token, id, d +func (_m *Service) UpdateDomain(ctx context.Context, token string, id string, d auth.DomainReq) (auth.Domain, error) { + ret := _m.Called(ctx, token, id, d) if len(ret) == 0 { - panic("no return value specified for UpdateDescription") + panic("no return value specified for UpdateDomain") } - var r0 auth.PAT + var r0 auth.Domain var r1 error - if rf, ok := ret.Get(0).(func(context.Context, string, string, string) (auth.PAT, error)); ok { - return rf(ctx, token, patID, description) + if rf, ok := ret.Get(0).(func(context.Context, string, string, auth.DomainReq) (auth.Domain, error)); ok { + return rf(ctx, token, id, d) } - if rf, ok := ret.Get(0).(func(context.Context, string, string, string) auth.PAT); ok { - r0 = rf(ctx, token, patID, description) + if rf, ok := ret.Get(0).(func(context.Context, string, string, auth.DomainReq) auth.Domain); ok { + r0 = rf(ctx, token, id, d) } else { - r0 = ret.Get(0).(auth.PAT) + r0 = ret.Get(0).(auth.Domain) } - if rf, ok := ret.Get(1).(func(context.Context, string, string, string) error); ok { - r1 = rf(ctx, token, patID, description) + if rf, ok := ret.Get(1).(func(context.Context, string, string, auth.DomainReq) error); ok { + r1 = rf(ctx, token, id, d) } else { r1 = ret.Error(1) } @@ -1021,27 +978,27 @@ func (_m *Service) UpdateDescription(ctx context.Context, token string, patID st return r0, r1 } -// UpdateDomain provides a mock function with given fields: ctx, token, id, d -func (_m *Service) UpdateDomain(ctx context.Context, token string, id string, d auth.DomainReq) (auth.Domain, error) { - ret := _m.Called(ctx, token, id, d) +// UpdatePATDescription provides a mock function with given fields: ctx, token, patID, description +func (_m *Service) UpdatePATDescription(ctx context.Context, token string, patID string, description string) (auth.PAT, error) { + ret := _m.Called(ctx, token, patID, description) if len(ret) == 0 { - panic("no return value specified for UpdateDomain") + panic("no return value specified for UpdatePATDescription") } - var r0 auth.Domain + var r0 auth.PAT var r1 error - if rf, ok := ret.Get(0).(func(context.Context, string, string, auth.DomainReq) (auth.Domain, error)); ok { - return rf(ctx, token, id, d) + if rf, ok := ret.Get(0).(func(context.Context, string, string, string) (auth.PAT, error)); ok { + return rf(ctx, token, patID, description) } - if rf, ok := ret.Get(0).(func(context.Context, string, string, auth.DomainReq) auth.Domain); ok { - r0 = rf(ctx, token, id, d) + if rf, ok := ret.Get(0).(func(context.Context, string, string, string) auth.PAT); ok { + r0 = rf(ctx, token, patID, description) } else { - r0 = ret.Get(0).(auth.Domain) + r0 = ret.Get(0).(auth.PAT) } - if rf, ok := ret.Get(1).(func(context.Context, string, string, auth.DomainReq) error); ok { - r1 = rf(ctx, token, id, d) + if rf, ok := ret.Get(1).(func(context.Context, string, string, string) error); ok { + r1 = rf(ctx, token, patID, description) } else { r1 = ret.Error(1) } @@ -1049,12 +1006,12 @@ func (_m *Service) UpdateDomain(ctx context.Context, token string, id string, d return r0, r1 } -// UpdateName provides a mock function with given fields: ctx, token, patID, name -func (_m *Service) UpdateName(ctx context.Context, token string, patID string, name string) (auth.PAT, error) { +// UpdatePATName provides a mock function with given fields: ctx, token, patID, name +func (_m *Service) UpdatePATName(ctx context.Context, token string, patID string, name string) (auth.PAT, error) { ret := _m.Called(ctx, token, patID, name) if len(ret) == 0 { - panic("no return value specified for UpdateName") + panic("no return value specified for UpdatePATName") } var r0 auth.PAT diff --git a/auth/pat.go b/auth/pat.go index a669a27641..ddf70a0d9a 100644 --- a/auth/pat.go +++ b/auth/pat.go @@ -657,40 +657,40 @@ func (pat PAT) Expired() bool { type PATS interface { // Create function creates new PAT for given valid inputs. - Create(ctx context.Context, token, name, description string, duration time.Duration, scope Scope) (PAT, error) + CreatePAT(ctx context.Context, token, name, description string, duration time.Duration, scope Scope) (PAT, error) // UpdateName function updates the name for the given PAT ID. - UpdateName(ctx context.Context, token, patID, name string) (PAT, error) + UpdatePATName(ctx context.Context, token, patID, name string) (PAT, error) // UpdateDescription function updates the description for the given PAT ID. - UpdateDescription(ctx context.Context, token, patID, description string) (PAT, error) + UpdatePATDescription(ctx context.Context, token, patID, description string) (PAT, error) // Retrieve function retrieves the PAT for given ID. - Retrieve(ctx context.Context, token, patID string) (PAT, error) + RetrievePAT(ctx context.Context, token, patID string) (PAT, error) // List function lists all the PATs for the user. - List(ctx context.Context, token string, pm PATSPageMeta) (PATSPage, error) + ListPATS(ctx context.Context, token string, pm PATSPageMeta) (PATSPage, error) // Delete function deletes the PAT for given ID. - Delete(ctx context.Context, token, patID string) error + DeletePAT(ctx context.Context, token, patID string) error // ResetSecret function reset the secret and creates new secret for the given ID. - ResetSecret(ctx context.Context, token, patID string, duration time.Duration) (PAT, error) + ResetPATSecret(ctx context.Context, token, patID string, duration time.Duration) (PAT, error) // RevokeSecret function revokes the secret for the given ID. - RevokeSecret(ctx context.Context, token, patID string) error + RevokePATSecret(ctx context.Context, token, patID string) error // AddScope function adds a new scope entry. - AddScopeEntry(ctx context.Context, token, patID string, platformEntityType PlatformEntityType, optionalDomainID string, optionalDomainEntityType DomainEntityType, operation OperationType, entityIDs ...string) (Scope, error) + AddPATScopeEntry(ctx context.Context, token, patID string, platformEntityType PlatformEntityType, optionalDomainID string, optionalDomainEntityType DomainEntityType, operation OperationType, entityIDs ...string) (Scope, error) // RemoveScope function removes a scope entry. - RemoveScopeEntry(ctx context.Context, token, patID string, platformEntityType PlatformEntityType, optionalDomainID string, optionalDomainEntityType DomainEntityType, operation OperationType, entityIDs ...string) (Scope, error) + RemovePATScopeEntry(ctx context.Context, token, patID string, platformEntityType PlatformEntityType, optionalDomainID string, optionalDomainEntityType DomainEntityType, operation OperationType, entityIDs ...string) (Scope, error) // ClearAllScope function removes all scope entry. - ClearAllScopeEntry(ctx context.Context, token, patID string) error + ClearPATAllScopeEntry(ctx context.Context, token, patID string) error // This will be removed during PR merge. TestCheckScope will check the given scope exists. - TestCheckScopeEntry(ctx context.Context, paToken string, platformEntityType PlatformEntityType, optionalDomainID string, optionalDomainEntityType DomainEntityType, operation OperationType, entityIDs ...string) error + TestCheckPATScopeEntry(ctx context.Context, paToken string, platformEntityType PlatformEntityType, optionalDomainID string, optionalDomainEntityType DomainEntityType, operation OperationType, entityIDs ...string) error // IdentifyPAT function will valid the secret. IdentifyPAT(ctx context.Context, paToken string) (PAT, error) diff --git a/auth/service.go b/auth/service.go index e42fee9697..eedad5ac36 100644 --- a/auth/service.go +++ b/auth/service.go @@ -1083,7 +1083,7 @@ func (svc service) DeleteEntityPolicies(ctx context.Context, entityType, id stri } } -func (svc service) Create(ctx context.Context, token, name, description string, duration time.Duration, scope Scope) (PAT, error) { +func (svc service) CreatePAT(ctx context.Context, token, name, description string, duration time.Duration, scope Scope) (PAT, error) { key, err := svc.Identify(ctx, token) if err != nil { return PAT{}, err @@ -1098,7 +1098,7 @@ func (svc service) Create(ctx context.Context, token, name, description string, return pat, nil } -func (svc service) UpdateName(ctx context.Context, token, patID, name string) (PAT, error) { +func (svc service) UpdatePATName(ctx context.Context, token, patID, name string) (PAT, error) { key, err := svc.Identify(ctx, token) if err != nil { return PAT{}, err @@ -1109,7 +1109,7 @@ func (svc service) UpdateName(ctx context.Context, token, patID, name string) (P } return pat, nil } -func (svc service) UpdateDescription(ctx context.Context, token, patID, description string) (PAT, error) { +func (svc service) UpdatePATDescription(ctx context.Context, token, patID, description string) (PAT, error) { key, err := svc.Identify(ctx, token) if err != nil { return PAT{}, err @@ -1120,7 +1120,7 @@ func (svc service) UpdateDescription(ctx context.Context, token, patID, descript } return pat, nil } -func (svc service) Retrieve(ctx context.Context, token, patID string) (PAT, error) { +func (svc service) RetrievePAT(ctx context.Context, token, patID string) (PAT, error) { key, err := svc.Identify(ctx, token) if err != nil { return PAT{}, err @@ -1132,7 +1132,7 @@ func (svc service) Retrieve(ctx context.Context, token, patID string) (PAT, erro } return pat, nil } -func (svc service) List(ctx context.Context, token string, pm PATSPageMeta) (PATSPage, error) { +func (svc service) ListPATS(ctx context.Context, token string, pm PATSPageMeta) (PATSPage, error) { key, err := svc.Identify(ctx, token) if err != nil { return PATSPage{}, err @@ -1143,7 +1143,7 @@ func (svc service) List(ctx context.Context, token string, pm PATSPageMeta) (PAT } return patsPage, nil } -func (svc service) Delete(ctx context.Context, token, patID string) error { +func (svc service) DeletePAT(ctx context.Context, token, patID string) error { key, err := svc.Identify(ctx, token) if err != nil { return err @@ -1153,7 +1153,7 @@ func (svc service) Delete(ctx context.Context, token, patID string) error { } return nil } -func (svc service) ResetSecret(ctx context.Context, token, patID string, duration time.Duration) (PAT, error) { +func (svc service) ResetPATSecret(ctx context.Context, token, patID string, duration time.Duration) (PAT, error) { key, err := svc.Identify(ctx, token) if err != nil { return PAT{}, err @@ -1169,7 +1169,7 @@ func (svc service) ResetSecret(ctx context.Context, token, patID string, duratio } return pat, nil } -func (svc service) RevokeSecret(ctx context.Context, token, patID string) error { +func (svc service) RevokePATSecret(ctx context.Context, token, patID string) error { key, err := svc.Identify(ctx, token) if err != nil { return err @@ -1181,7 +1181,7 @@ func (svc service) RevokeSecret(ctx context.Context, token, patID string) error return nil } -func (svc service) AddScopeEntry(ctx context.Context, token, patID string, platformEntityType PlatformEntityType, optionalDomainID string, optionalDomainEntityType DomainEntityType, operation OperationType, entityIDs ...string) (Scope, error) { +func (svc service) AddPATScopeEntry(ctx context.Context, token, patID string, platformEntityType PlatformEntityType, optionalDomainID string, optionalDomainEntityType DomainEntityType, operation OperationType, entityIDs ...string) (Scope, error) { key, err := svc.Identify(ctx, token) if err != nil { return Scope{}, err @@ -1193,7 +1193,7 @@ func (svc service) AddScopeEntry(ctx context.Context, token, patID string, platf } return scope, nil } -func (svc service) RemoveScopeEntry(ctx context.Context, token, patID string, platformEntityType PlatformEntityType, optionalDomainID string, optionalDomainEntityType DomainEntityType, operation OperationType, entityIDs ...string) (Scope, error) { +func (svc service) RemovePATScopeEntry(ctx context.Context, token, patID string, platformEntityType PlatformEntityType, optionalDomainID string, optionalDomainEntityType DomainEntityType, operation OperationType, entityIDs ...string) (Scope, error) { key, err := svc.Identify(ctx, token) if err != nil { return Scope{}, err @@ -1204,7 +1204,7 @@ func (svc service) RemoveScopeEntry(ctx context.Context, token, patID string, pl } return scope, nil } -func (svc service) ClearAllScopeEntry(ctx context.Context, token, patID string) error { +func (svc service) ClearPATAllScopeEntry(ctx context.Context, token, patID string) error { key, err := svc.Identify(ctx, token) if err != nil { return err @@ -1215,7 +1215,7 @@ func (svc service) ClearAllScopeEntry(ctx context.Context, token, patID string) return nil } -func (svc service) TestCheckScopeEntry(ctx context.Context, paToken string, platformEntityType PlatformEntityType, optionalDomainID string, optionalDomainEntityType DomainEntityType, operation OperationType, entityIDs ...string) error { +func (svc service) TestCheckPATScopeEntry(ctx context.Context, paToken string, platformEntityType PlatformEntityType, optionalDomainID string, optionalDomainEntityType DomainEntityType, operation OperationType, entityIDs ...string) error { res, err := svc.IdentifyPAT(ctx, paToken) if err != nil { return err diff --git a/auth/tracing/tracing.go b/auth/tracing/tracing.go index 455258342f..3419abd759 100644 --- a/auth/tracing/tracing.go +++ b/auth/tracing/tracing.go @@ -314,7 +314,7 @@ func (tm *tracingMiddleware) DeleteEntityPolicies(ctx context.Context, entityTyp return tm.svc.DeleteEntityPolicies(ctx, entityType, id) } -func (tm *tracingMiddleware) Create(ctx context.Context, token, name, description string, duration time.Duration, scope auth.Scope) (auth.PAT, error) { +func (tm *tracingMiddleware) CreatePAT(ctx context.Context, token, name, description string, duration time.Duration, scope auth.Scope) (auth.PAT, error) { ctx, span := tm.tracer.Start(ctx, "create_pat", trace.WithAttributes( attribute.String("name", name), attribute.String("description", description), @@ -322,70 +322,70 @@ func (tm *tracingMiddleware) Create(ctx context.Context, token, name, descriptio attribute.String("scope", scope.String()), )) defer span.End() - return tm.svc.Create(ctx, token, name, description, duration, scope) + return tm.svc.CreatePAT(ctx, token, name, description, duration, scope) } -func (tm *tracingMiddleware) UpdateName(ctx context.Context, token, patID, name string) (auth.PAT, error) { +func (tm *tracingMiddleware) UpdatePATName(ctx context.Context, token, patID, name string) (auth.PAT, error) { ctx, span := tm.tracer.Start(ctx, "update_pat_name", trace.WithAttributes( attribute.String("pat_id", patID), attribute.String("name", name), )) defer span.End() - return tm.svc.UpdateName(ctx, token, patID, name) + return tm.svc.UpdatePATName(ctx, token, patID, name) } -func (tm *tracingMiddleware) UpdateDescription(ctx context.Context, token, patID, description string) (auth.PAT, error) { +func (tm *tracingMiddleware) UpdatePATDescription(ctx context.Context, token, patID, description string) (auth.PAT, error) { ctx, span := tm.tracer.Start(ctx, "update_pat_description", trace.WithAttributes( attribute.String("pat_id", patID), attribute.String("description", description), )) defer span.End() - return tm.svc.UpdateDescription(ctx, token, patID, description) + return tm.svc.UpdatePATDescription(ctx, token, patID, description) } -func (tm *tracingMiddleware) Retrieve(ctx context.Context, token, patID string) (auth.PAT, error) { +func (tm *tracingMiddleware) RetrievePAT(ctx context.Context, token, patID string) (auth.PAT, error) { ctx, span := tm.tracer.Start(ctx, "retrieve_pat", trace.WithAttributes( attribute.String("pat_id", patID), )) defer span.End() - return tm.svc.Retrieve(ctx, token, patID) + return tm.svc.RetrievePAT(ctx, token, patID) } -func (tm *tracingMiddleware) List(ctx context.Context, token string, pm auth.PATSPageMeta) (auth.PATSPage, error) { +func (tm *tracingMiddleware) ListPATS(ctx context.Context, token string, pm auth.PATSPageMeta) (auth.PATSPage, error) { ctx, span := tm.tracer.Start(ctx, "list_pat", trace.WithAttributes( attribute.Int64("limit", int64(pm.Limit)), attribute.Int64("offset", int64(pm.Offset)), )) defer span.End() - return tm.svc.List(ctx, token, pm) + return tm.svc.ListPATS(ctx, token, pm) } -func (tm *tracingMiddleware) Delete(ctx context.Context, token, patID string) error { +func (tm *tracingMiddleware) DeletePAT(ctx context.Context, token, patID string) error { ctx, span := tm.tracer.Start(ctx, "delete_pat", trace.WithAttributes( attribute.String("pat_id", patID), )) defer span.End() - return tm.svc.Delete(ctx, token, patID) + return tm.svc.DeletePAT(ctx, token, patID) } -func (tm *tracingMiddleware) ResetSecret(ctx context.Context, token, patID string, duration time.Duration) (auth.PAT, error) { +func (tm *tracingMiddleware) ResetPATSecret(ctx context.Context, token, patID string, duration time.Duration) (auth.PAT, error) { ctx, span := tm.tracer.Start(ctx, "reset_pat_secret", trace.WithAttributes( attribute.String("pat_id", patID), attribute.String("duration", duration.String()), )) defer span.End() - return tm.svc.ResetSecret(ctx, token, patID, duration) + return tm.svc.ResetPATSecret(ctx, token, patID, duration) } -func (tm *tracingMiddleware) RevokeSecret(ctx context.Context, token, patID string) error { +func (tm *tracingMiddleware) RevokePATSecret(ctx context.Context, token, patID string) error { ctx, span := tm.tracer.Start(ctx, "revoke_pat_secret", trace.WithAttributes( attribute.String("pat_id", patID), )) defer span.End() - return tm.svc.RevokeSecret(ctx, token, patID) + return tm.svc.RevokePATSecret(ctx, token, patID) } -func (tm *tracingMiddleware) AddScopeEntry(ctx context.Context, token, patID string, platformEntityType auth.PlatformEntityType, optionalDomainID string, optionalDomainEntityType auth.DomainEntityType, operation auth.OperationType, entityIDs ...string) (auth.Scope, error) { +func (tm *tracingMiddleware) AddPATScopeEntry(ctx context.Context, token, patID string, platformEntityType auth.PlatformEntityType, optionalDomainID string, optionalDomainEntityType auth.DomainEntityType, operation auth.OperationType, entityIDs ...string) (auth.Scope, error) { ctx, span := tm.tracer.Start(ctx, "add_pat_scope_entry", trace.WithAttributes( attribute.String("pat_id", patID), attribute.String("platform_entity", platformEntityType.String()), @@ -395,10 +395,10 @@ func (tm *tracingMiddleware) AddScopeEntry(ctx context.Context, token, patID str attribute.StringSlice("entities", entityIDs), )) defer span.End() - return tm.svc.AddScopeEntry(ctx, token, patID, platformEntityType, optionalDomainID, optionalDomainEntityType, operation, entityIDs...) + return tm.svc.AddPATScopeEntry(ctx, token, patID, platformEntityType, optionalDomainID, optionalDomainEntityType, operation, entityIDs...) } -func (tm *tracingMiddleware) RemoveScopeEntry(ctx context.Context, token, patID string, platformEntityType auth.PlatformEntityType, optionalDomainID string, optionalDomainEntityType auth.DomainEntityType, operation auth.OperationType, entityIDs ...string) (auth.Scope, error) { +func (tm *tracingMiddleware) RemovePATScopeEntry(ctx context.Context, token, patID string, platformEntityType auth.PlatformEntityType, optionalDomainID string, optionalDomainEntityType auth.DomainEntityType, operation auth.OperationType, entityIDs ...string) (auth.Scope, error) { ctx, span := tm.tracer.Start(ctx, "remove_pat_scope_entry", trace.WithAttributes( attribute.String("pat_id", patID), attribute.String("platform_entity", platformEntityType.String()), @@ -408,18 +408,18 @@ func (tm *tracingMiddleware) RemoveScopeEntry(ctx context.Context, token, patID attribute.StringSlice("entities", entityIDs), )) defer span.End() - return tm.svc.RemoveScopeEntry(ctx, token, patID, platformEntityType, optionalDomainID, optionalDomainEntityType, operation, entityIDs...) + return tm.svc.RemovePATScopeEntry(ctx, token, patID, platformEntityType, optionalDomainID, optionalDomainEntityType, operation, entityIDs...) } -func (tm *tracingMiddleware) ClearAllScopeEntry(ctx context.Context, token, patID string) error { +func (tm *tracingMiddleware) ClearPATAllScopeEntry(ctx context.Context, token, patID string) error { ctx, span := tm.tracer.Start(ctx, "clear_pat_all_scope_entry", trace.WithAttributes( attribute.String("pat_id", patID), )) defer span.End() - return tm.svc.ClearAllScopeEntry(ctx, token, patID) + return tm.svc.ClearPATAllScopeEntry(ctx, token, patID) } -func (tm *tracingMiddleware) TestCheckScopeEntry(ctx context.Context, paToken string, platformEntityType auth.PlatformEntityType, optionalDomainID string, optionalDomainEntityType auth.DomainEntityType, operation auth.OperationType, entityIDs ...string) error { +func (tm *tracingMiddleware) TestCheckPATScopeEntry(ctx context.Context, paToken string, platformEntityType auth.PlatformEntityType, optionalDomainID string, optionalDomainEntityType auth.DomainEntityType, operation auth.OperationType, entityIDs ...string) error { ctx, span := tm.tracer.Start(ctx, "test_check_pat_scope_entry", trace.WithAttributes( attribute.String("personal_access_token", paToken), attribute.String("platform_entity", platformEntityType.String()), @@ -429,7 +429,7 @@ func (tm *tracingMiddleware) TestCheckScopeEntry(ctx context.Context, paToken st attribute.StringSlice("entities", entityIDs), )) defer span.End() - return tm.svc.TestCheckScopeEntry(ctx, paToken, platformEntityType, optionalDomainID, optionalDomainEntityType, operation, entityIDs...) + return tm.svc.TestCheckPATScopeEntry(ctx, paToken, platformEntityType, optionalDomainID, optionalDomainEntityType, operation, entityIDs...) } func (tm *tracingMiddleware) IdentifyPAT(ctx context.Context, paToken string) (auth.PAT, error) { diff --git a/bootstrap/postgres/configs.go b/bootstrap/postgres/configs.go index c8b90c09a5..e3760b6816 100644 --- a/bootstrap/postgres/configs.go +++ b/bootstrap/postgres/configs.go @@ -281,8 +281,8 @@ func (cr configRepository) Update(ctx context.Context, cfg bootstrap.Config) err return nil } -func (cr configRepository) UpdateCert(ctx context.Context, domainID, thingID, clientCert, clientKey, caCert string) (bootstrap.Config, error) { - q := `UPDATE configs SET client_cert = :client_cert, client_key = :client_key, ca_cert = :ca_cert WHERE magistrala_thing = :magistrala_thing AND domain_id = :domain_id +func (cr configRepository) UpdateCert(ctx context.Context, owner, thingID, clientCert, clientKey, caCert string) (bootstrap.Config, error) { + q := `UPDATE configs SET client_cert = :client_cert, client_key = :client_key, ca_cert = :ca_cert WHERE magistrala_thing = :magistrala_thing AND owner = :owner RETURNING magistrala_thing, client_cert, client_key, ca_cert` dbcfg := dbConfig{ @@ -443,7 +443,7 @@ func (cr configRepository) UpdateChannel(ctx context.Context, c bootstrap.Channe return errors.Wrap(repoerr.ErrUpdateEntity, err) } - q := `UPDATE channels SET name = :name, metadata = :metadata, updated_at = :updated_at, updated_by = :updated_by + q := `UPDATE channels SET name = :name, metadata = :metadata, updated_at = :updated_at, updated_by = :updated_by WHERE magistrala_channel = :magistrala_channel` if _, err = cr.db.NamedExecContext(ctx, q, dbch); err != nil { return errors.Wrap(errUpdateChannels, err) diff --git a/cmd/auth/main.go b/cmd/auth/main.go index a1f103d360..f53f41d93c 100644 --- a/cmd/auth/main.go +++ b/cmd/auth/main.go @@ -25,11 +25,6 @@ import ( "github.com/absmach/magistrala/auth/spicedb" "github.com/absmach/magistrala/auth/tracing" boltclient "github.com/absmach/magistrala/internal/clients/bolt" - pgclient "github.com/absmach/magistrala/internal/clients/postgres" - "github.com/absmach/magistrala/internal/postgres" - "github.com/absmach/magistrala/internal/server" - grpcserver "github.com/absmach/magistrala/internal/server/grpc" - httpserver "github.com/absmach/magistrala/internal/server/http" mglog "github.com/absmach/magistrala/logger" "github.com/absmach/magistrala/pkg/jaeger" "github.com/absmach/magistrala/pkg/postgres" @@ -149,7 +144,7 @@ func main() { patsRepo := bolt.NewPATSRepository(client, boltDBConfig.Bucket) - svc := newService(db, tracer, cfg, dbConfig, logger, spicedbclient, patsRepo) + svc := newService(ctx, db, tracer, cfg, dbConfig, logger, spicedbclient, patsRepo) httpServerConfig := server.Config{Port: defSvcHTTPPort} if err := env.ParseWithOptions(&httpServerConfig, env.Options{Prefix: envPrefixHTTP}); err != nil { @@ -223,7 +218,7 @@ func initSchema(ctx context.Context, client *authzed.ClientWithExperimental, sch return nil } -func newService(db *sqlx.DB, tracer trace.Tracer, cfg config, dbConfig pgclient.Config, logger *slog.Logger, spicedbClient *authzed.ClientWithExperimental, pats auth.PATSRepository) auth.Service { +func newService(ctx context.Context, db *sqlx.DB, tracer trace.Tracer, cfg config, dbConfig pgclient.Config, logger *slog.Logger, spicedbClient *authzed.ClientWithExperimental, pats auth.PATSRepository) auth.Service { database := postgres.NewDatabase(db, dbConfig, tracer) keysRepo := apostgres.New(database) domainsRepo := apostgres.NewDomainRepository(database) diff --git a/go.sum b/go.sum index 7cbeb9862d..d8155e1c2b 100644 --- a/go.sum +++ b/go.sum @@ -76,7 +76,6 @@ github.com/dgraph-io/badger/v4 v4.2.0 h1:kJrlajbXXL9DFTNuhhu9yCx7JJa4qpYWxtE8Bzu github.com/dgraph-io/badger/v4 v4.2.0/go.mod h1:qfCqhPoWDFJRx1gp5QwwyGo8xk1lbHUxvK9nK0OGAak= github.com/dgraph-io/ristretto v0.1.1 h1:6CWw5tJNgpegArSHpNHJKldNeq03FQCwYvfMVWajOK8= github.com/dgraph-io/ristretto v0.1.1/go.mod h1:S1GPSBCYCIhmVNfcth17y2zZtQT6wzkzgwUve0VDWWA= -github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2 h1:tdlZCpZ/P9DhczCTSixgIKmwPv6+wP5DGjqLYw5SUiA= github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw= github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78= @@ -180,7 +179,6 @@ github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMyw github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= @@ -600,8 +598,6 @@ golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/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-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.0.0-20201201195509-5d6afe98e0b7/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -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-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= @@ -669,8 +665,6 @@ 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.12.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= -golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4= golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI= @@ -709,6 +703,7 @@ google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7 google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= google.golang.org/genproto v0.0.0-20200423170343-7949de9c1215/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= google.golang.org/genproto/googleapis/api v0.0.0-20240528184218-531527333157 h1:7whR9kGa5LUwFtpLm2ArCEejtnxlGeLbAyjFY8sGNFw= google.golang.org/genproto/googleapis/api v0.0.0-20240528184218-531527333157/go.mod h1:99sLkeliLXfdj2J75X3Ho+rrVCaJze0uwN7zDDkjPVU= google.golang.org/genproto/googleapis/rpc v0.0.0-20240701130421-f6361c86f094 h1:BwIjyKYGsK9dMCBOorzRri8MQwmi7mT9rGHsCEinZkA= @@ -718,8 +713,18 @@ google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyac google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= +google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= google.golang.org/grpc v1.65.0 h1:bs/cUb4lp1G5iImFFd3u5ixQzweKizoZJAwBNLR42lc= google.golang.org/grpc v1.65.0/go.mod h1:WgYC2ypjlB0EiQi6wdKixMqukr6lBc0Vo+oOgjrM5ZQ= +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= +google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= +google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= +google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg= google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw= gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc h1:2gGKlE2+asNV9m7xrywl36YYNnBG5ZQ0r/BOOxqPpmk= From c539b38fb202e10b7c5674a2d2154df5eb0ccbe2 Mon Sep 17 00:00:00 2001 From: Arvindh Date: Wed, 10 Jul 2024 17:02:27 +0530 Subject: [PATCH 14/24] fix bugs Signed-off-by: Arvindh --- auth/api/http/pats/requests.go | 4 ---- auth/api/http/pats/transport.go | 3 --- auth/api/http/transport.go | 2 ++ auth/bolt/pat.go | 12 ++++++++---- auth/service.go | 5 +++++ 5 files changed, 15 insertions(+), 11 deletions(-) diff --git a/auth/api/http/pats/requests.go b/auth/api/http/pats/requests.go index 44ce84c988..6552725a1c 100644 --- a/auth/api/http/pats/requests.go +++ b/auth/api/http/pats/requests.go @@ -10,7 +10,6 @@ import ( "github.com/absmach/magistrala/auth" "github.com/absmach/magistrala/pkg/apiutil" - "github.com/absmach/magistrala/pkg/errors" ) type createPatReq struct { @@ -51,9 +50,6 @@ func (req createPatReq) validate() (err error) { return apiutil.ErrMissingName } - if _, err := time.ParseDuration(req.Description); err != nil { - return errors.Wrap(apiutil.ErrInvalidInterval, err) - } return nil } diff --git a/auth/api/http/pats/transport.go b/auth/api/http/pats/transport.go index e32180ebd1..4cf8691f90 100644 --- a/auth/api/http/pats/transport.go +++ b/auth/api/http/pats/transport.go @@ -180,9 +180,6 @@ func decodeListPATSRequest(_ context.Context, r *http.Request) (interface{}, err limit: l, offset: o, } - if err := json.NewDecoder(r.Body).Decode(&req); err != nil { - return nil, errors.Wrap(errors.ErrMalformedEntity, err) - } return req, nil } diff --git a/auth/api/http/transport.go b/auth/api/http/transport.go index 5e31ee553f..d3d8306871 100644 --- a/auth/api/http/transport.go +++ b/auth/api/http/transport.go @@ -10,6 +10,7 @@ import ( "github.com/absmach/magistrala/auth" "github.com/absmach/magistrala/auth/api/http/domains" "github.com/absmach/magistrala/auth/api/http/keys" + "github.com/absmach/magistrala/auth/api/http/pats" "github.com/go-chi/chi/v5" "github.com/prometheus/client_golang/prometheus/promhttp" ) @@ -20,6 +21,7 @@ func MakeHandler(svc auth.Service, logger *slog.Logger, instanceID string) http. mux = keys.MakeHandler(svc, mux, logger) mux = domains.MakeHandler(svc, mux, logger) + mux = pats.MakeHandler(svc, mux, logger) mux.Get("/health", magistrala.Health("auth", instanceID)) mux.Handle("/metrics", promhttp.Handler()) diff --git a/auth/bolt/pat.go b/auth/bolt/pat.go index 940b6cc8a1..ffee6eff51 100644 --- a/auth/bolt/pat.go +++ b/auth/bolt/pat.go @@ -87,7 +87,7 @@ func (pr *patRepo) Save(ctx context.Context, pat auth.PAT) error { func (pr *patRepo) Retrieve(ctx context.Context, userID, patID string) (auth.PAT, error) { prefix := []byte(patID + keySeparator) kv := map[string][]byte{} - if err := pr.db.Update(func(tx *bolt.Tx) error { + if err := pr.db.View(func(tx *bolt.Tx) error { b, err := pr.retrieveUserBucket(tx, userID) if err != nil { return errors.Wrap(repoerr.ErrRemoveEntity, err) @@ -141,10 +141,10 @@ func (pr *patRepo) RetrieveAll(ctx context.Context, userID string, pm auth.PATSP prefix := []byte(userID + keySeparator + patKey + keySeparator) patIDs := []string{} - if err := pr.db.Update(func(tx *bolt.Tx) error { + if err := pr.db.View(func(tx *bolt.Tx) error { b, err := pr.retrieveUserBucket(tx, userID) if err != nil { - return errors.Wrap(repoerr.ErrRemoveEntity, err) + return errors.Wrap(repoerr.ErrViewEntity, err) } c := b.Cursor() for k, v := c.Seek(prefix); k != nil && bytes.HasPrefix(k, prefix); k, v = c.Next() { @@ -342,7 +342,11 @@ func (pr *patRepo) retrieveUserBucket(tx *bolt.Tx, userID string) (*bolt.Bucket, return nil, fmt.Errorf("bucket %s not found", pr.bucketName) } - return rootBucket.Bucket([]byte(userID)), nil + userBucket := rootBucket.Bucket([]byte(userID)) + if userBucket == nil { + return nil, fmt.Errorf("user %s not found", userID) + } + return userBucket, nil } func (pr *patRepo) updatePATField(_ context.Context, userID, patID, key string, value []byte) (auth.PAT, error) { diff --git a/auth/service.go b/auth/service.go index eedad5ac36..1efca5ce6a 100644 --- a/auth/service.go +++ b/auth/service.go @@ -1089,7 +1089,12 @@ func (svc service) CreatePAT(ctx context.Context, token, name, description strin return PAT{}, err } + id, err := svc.idProvider.ID() + if err != nil { + return PAT{}, errors.Wrap(svcerr.ErrCreateEntity, err) + } pat := PAT{ + ID: id, User: key.User, } if err := svc.pats.Save(ctx, pat); err != nil { From 7f9bdecdc2736b598fd1e2c64ca8e14db5d59498 Mon Sep 17 00:00:00 2001 From: Arvindh Date: Wed, 10 Jul 2024 17:22:57 +0530 Subject: [PATCH 15/24] go modules Signed-off-by: Arvindh --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 3c3a42932f..404376c584 100644 --- a/go.mod +++ b/go.mod @@ -103,7 +103,7 @@ require ( github.com/go-logr/stdr v1.2.2 // indirect github.com/goccy/go-json v0.10.2 // indirect github.com/gogo/protobuf v1.3.2 // indirect - github.com/golang/glog v1.2.0 // indirect + github.com/golang/glog v1.2.1 // indirect github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect github.com/golang/protobuf v1.5.4 // indirect github.com/golang/snappy v0.0.4 // indirect diff --git a/go.sum b/go.sum index d8155e1c2b..5d2afbc245 100644 --- a/go.sum +++ b/go.sum @@ -150,8 +150,8 @@ github.com/gofrs/uuid v4.4.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRx 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.2.0 h1:uCdmnmatrKCgMBlM4rMuJZWOkPDqdbZPnrMXDY4gI68= -github.com/golang/glog v1.2.0/go.mod h1:6AhwSGph0fcJtXVM/PEHPqZlFeoLxhs7/t5UDAwmO+w= +github.com/golang/glog v1.2.1 h1:OptwRhECazUx5ix5TTWC3EZhsZEHWcYWY4FQHTIubm4= +github.com/golang/glog v1.2.1/go.mod h1:6AhwSGph0fcJtXVM/PEHPqZlFeoLxhs7/t5UDAwmO+w= github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE= github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= From 1c13071d62463f5852efc316b6e3e02e6daa67a1 Mon Sep 17 00:00:00 2001 From: Arvindh Date: Wed, 10 Jul 2024 17:24:19 +0530 Subject: [PATCH 16/24] revert bs repo function Signed-off-by: Arvindh --- bootstrap/postgres/configs.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bootstrap/postgres/configs.go b/bootstrap/postgres/configs.go index e3760b6816..66cbe7d00d 100644 --- a/bootstrap/postgres/configs.go +++ b/bootstrap/postgres/configs.go @@ -281,8 +281,8 @@ func (cr configRepository) Update(ctx context.Context, cfg bootstrap.Config) err return nil } -func (cr configRepository) UpdateCert(ctx context.Context, owner, thingID, clientCert, clientKey, caCert string) (bootstrap.Config, error) { - q := `UPDATE configs SET client_cert = :client_cert, client_key = :client_key, ca_cert = :ca_cert WHERE magistrala_thing = :magistrala_thing AND owner = :owner +func (cr configRepository) UpdateCert(ctx context.Context, domainID, thingID, clientCert, clientKey, caCert string) (bootstrap.Config, error) { + q := `UPDATE configs SET client_cert = :client_cert, client_key = :client_key, ca_cert = :ca_cert WHERE magistrala_thing = :magistrala_thing AND domain_id = :domain_id RETURNING magistrala_thing, client_cert, client_key, ca_cert` dbcfg := dbConfig{ From 61890889fab88238bcc95b69308811bb327e24b3 Mon Sep 17 00:00:00 2001 From: Arvindh Date: Wed, 10 Jul 2024 17:25:41 +0530 Subject: [PATCH 17/24] remove unwanted files Signed-off-by: Arvindh --- cmd/test/badger/main.go | 205 -------------------- cmd/test/badgergob/benchmark.go | 54 ------ cmd/test/badgergob/main.go | 187 ------------------ cmd/test/bbolt/main.go | 170 ---------------- cmd/test/bboltgob/main.go | 166 ---------------- cmd/test/patrepo/main.go | 79 -------- cmd/test/patunmarshal/main.go | 172 ----------------- cmd/test/patunmarshal/temp.json | 71 ------- cmd/test/redis/main.go | 277 --------------------------- cmd/test/redisgob/main.go | 143 -------------- cmd/test/second/main.go | 98 ---------- cmd/test/stringsbench/concat_test.go | 34 ---- cmd/test/test.go | 76 -------- 13 files changed, 1732 deletions(-) delete mode 100644 cmd/test/badger/main.go delete mode 100644 cmd/test/badgergob/benchmark.go delete mode 100644 cmd/test/badgergob/main.go delete mode 100644 cmd/test/bbolt/main.go delete mode 100644 cmd/test/bboltgob/main.go delete mode 100644 cmd/test/patrepo/main.go delete mode 100644 cmd/test/patunmarshal/main.go delete mode 100644 cmd/test/patunmarshal/temp.json delete mode 100644 cmd/test/redis/main.go delete mode 100644 cmd/test/redisgob/main.go delete mode 100644 cmd/test/second/main.go delete mode 100644 cmd/test/stringsbench/concat_test.go delete mode 100644 cmd/test/test.go diff --git a/cmd/test/badger/main.go b/cmd/test/badger/main.go deleted file mode 100644 index f9d18d3a2b..0000000000 --- a/cmd/test/badger/main.go +++ /dev/null @@ -1,205 +0,0 @@ -package main - -import ( - "fmt" - "log" - "strings" - "time" - - "github.com/absmach/magistrala/auth" - badger "github.com/dgraph-io/badger/v4" - "github.com/google/uuid" -) - -const timeLayout = "2006-01-02 15:04:05.999999999 -0700 MST" - -func main() { - // Open Badger database - db, err := badger.Open(badger.DefaultOptions("./badger")) - if err != nil { - log.Fatal(err) - } - defer db.Close() - - // Create a new PAT - pat := auth.PAT{ - ID: uuid.New().String(), - User: "user123", - Name: "Test Token", - IssuedAt: time.Now(), - ExpiresAt: time.Now().Add(24 * time.Hour), - } - - // Set scope - pat.Scope.Users = auth.OperationScope{ - Operations: map[auth.OperationType]auth.ScopeValue{ - auth.CreateOp: auth.SelectedIDs{"entity1": {}, "entity2": {}}, - }, - } - - // Store PAT - if err := storePAT(db, pat); err != nil { - log.Fatal(err) - } - - // Retrieve PAT - retrievedPAT, err := getPAT(db, pat.ID) - if err != nil { - log.Fatal(err) - } - fmt.Printf("Retrieved PAT: %+v\n", retrievedPAT) -} - -func storePAT(db *badger.DB, pat auth.PAT) error { - return db.Update(func(txn *badger.Txn) error { - if err := txn.Set([]byte(fmt.Sprintf("pat:%s:id", pat.ID)), []byte(pat.ID)); err != nil { - return err - } - if err := txn.Set([]byte(fmt.Sprintf("pat:%s:user", pat.ID)), []byte(pat.User)); err != nil { - return err - } - if err := txn.Set([]byte(fmt.Sprintf("pat:%s:name", pat.ID)), []byte(pat.Name)); err != nil { - return err - } - if err := txn.Set([]byte(fmt.Sprintf("pat:%s:issued_at", pat.ID)), []byte(pat.IssuedAt.String())); err != nil { - return err - } - if err := txn.Set([]byte(fmt.Sprintf("pat:%s:expires_at", pat.ID)), []byte(pat.ExpiresAt.String())); err != nil { - return err - } - // Store scope - scopeKeyPrefix := fmt.Sprintf("pat:%s:scope", pat.ID) - if err := storeOperationScope(txn, scopeKeyPrefix, pat.Scope.Users); err != nil { - return err - } - return nil - }) -} - -func storeOperationScope(txn *badger.Txn, keyPrefix string, os auth.OperationScope) error { - for opType, scopeValue := range os.Operations { - scopeKey := fmt.Sprintf("%s:%d", keyPrefix, opType) - switch v := scopeValue.(type) { - case auth.AnyIDs: - if err := txn.Set([]byte(scopeKey), []byte("*")); err != nil { - return err - } - case auth.SelectedIDs: - for id := range v { - if err := txn.Set([]byte(fmt.Sprintf("%s:%s", scopeKey, id)), []byte(id)); err != nil { - return err - } - } - } - } - return nil -} - -func getPAT(db *badger.DB, id string) (auth.PAT, error) { - var pat auth.PAT - err := db.View(func(txn *badger.Txn) error { - patID, err := txn.Get([]byte(fmt.Sprintf("pat:%s:id", id))) - if err != nil { - return err - } - err = patID.Value(func(val []byte) error { - pat.ID = string(val) - return nil - }) - if err != nil { - return err - } - - user, err := txn.Get([]byte(fmt.Sprintf("pat:%s:user", id))) - if err != nil { - return err - } - err = user.Value(func(val []byte) error { - pat.User = string(val) - return nil - }) - if err != nil { - return err - } - - name, err := txn.Get([]byte(fmt.Sprintf("pat:%s:name", id))) - if err != nil { - return err - } - err = name.Value(func(val []byte) error { - pat.Name = string(val) - return nil - }) - if err != nil { - return err - } - - issuedAt, err := txn.Get([]byte(fmt.Sprintf("pat:%s:issued_at", id))) - if err != nil { - return err - } - err = issuedAt.Value(func(val []byte) error { - pat.IssuedAt, err = time.Parse(timeLayout, strings.Split(string(val), " m=")[0]) - return err - }) - if err != nil { - return err - } - - expiresAt, err := txn.Get([]byte(fmt.Sprintf("pat:%s:expires_at", id))) - if err != nil { - return err - } - err = expiresAt.Value(func(val []byte) error { - pat.ExpiresAt, err = time.Parse(timeLayout, strings.Split(string(val), " m=")[0]) - return err - }) - if err != nil { - return err - } - - // Retrieve scope - scopeKeyPrefix := fmt.Sprintf("pat:%s:scope", id) - scope, err := getOperationScope(txn, scopeKeyPrefix) - if err != nil { - return err - } - pat.Scope.Users = scope - - return nil - }) - return pat, err -} - -func getOperationScope(txn *badger.Txn, keyPrefix string) (auth.OperationScope, error) { - os := auth.OperationScope{Operations: make(map[auth.OperationType]auth.ScopeValue)} - opt := badger.DefaultIteratorOptions - opt.Prefix = []byte(keyPrefix) - it := txn.NewIterator(opt) - defer it.Close() - - for it.Rewind(); it.Valid(); it.Next() { - item := it.Item() - k := item.Key() - opTypeStr := string(k[len(keyPrefix)+1:]) - var opType auth.OperationType - _, err := fmt.Sscanf(opTypeStr, "%d", &opType) - if err != nil { - return os, err - } - - item.Value(func(val []byte) error { - if string(val) == "*" { - os.Operations[opType] = auth.AnyIDs{} - } else { - if os.Operations[opType] == nil { - os.Operations[opType] = auth.SelectedIDs{} - } - os.Operations[opType].(auth.SelectedIDs)[string(val)] = struct{}{} - } - return nil - }) - } - - return os, nil -} diff --git a/cmd/test/badgergob/benchmark.go b/cmd/test/badgergob/benchmark.go deleted file mode 100644 index 596afe14a8..0000000000 --- a/cmd/test/badgergob/benchmark.go +++ /dev/null @@ -1,54 +0,0 @@ -package badgergob - -import ( - "log" - "testing" - "time" - - "github.com/absmach/magistrala/auth" - badger "github.com/dgraph-io/badger/v4" - "github.com/google/uuid" -) - -var num = 1000 - -func generateTestPAT() auth.PAT { - return auth.PAT{ - ID: uuid.New().String(), - User: "user123", - Name: "Test Token", - IssuedAt: time.Now(), - ExpiresAt: time.Now().Add(24 * time.Hour), - Scope: auth.Scope{ - Users: auth.OperationScope{ - Operations: map[auth.OperationType]auth.ScopeValue{ - auth.CreateOp: auth.SelectedIDs{"entity1": {}, "entity2": {}}, - }, - }, - }, - } -} - -func BenchmarkGetPAT(b *testing.B) { - // Open Badger database - db, err := badger.Open(badger.DefaultOptions("./badger")) - if err != nil { - log.Fatal(err) - } - defer db.Close() - - // Store a PAT to retrieve - pat := generateTestPAT() - err = StorePAT(db, pat) - if err != nil { - log.Fatal(err) - } - - b.ResetTimer() - for i := 0; i < b.N; i++ { - _, err := GetPAT(db, pat.ID) - if err != nil { - log.Fatal(err) - } - } -} diff --git a/cmd/test/badgergob/main.go b/cmd/test/badgergob/main.go deleted file mode 100644 index 4727d84a94..0000000000 --- a/cmd/test/badgergob/main.go +++ /dev/null @@ -1,187 +0,0 @@ -package badgergob - -import ( - "bytes" - "encoding/gob" - "fmt" - "log" - "strings" - "time" - - "github.com/absmach/magistrala/auth" - badger "github.com/dgraph-io/badger/v4" - "github.com/google/uuid" -) - -const timeLayout = "2006-01-02 15:04:05.999999999 -0700 MST" - -func init() { - gob.Register(auth.SelectedIDs{}) - gob.Register(auth.AnyIDs{}) -} -func main() { - // Open Badger database - db, err := badger.Open(badger.DefaultOptions("./badger")) - if err != nil { - log.Fatal(err) - } - defer db.Close() - - // Create a new PAT - pat := auth.PAT{ - ID: uuid.New().String(), - User: "user123", - Name: "Test Token", - IssuedAt: time.Now(), - ExpiresAt: time.Now().Add(24 * time.Hour), - } - - // Set scope - pat.Scope.Users = auth.OperationScope{ - Operations: map[auth.OperationType]auth.ScopeValue{ - auth.CreateOp: auth.SelectedIDs{"entity1": {}, "entity2": {}}, - }, - } - - // Store PAT - if err := StorePAT(db, pat); err != nil { - log.Fatal(err) - } - - // Retrieve PAT - retrievedPAT, err := GetPAT(db, pat.ID) - if err != nil { - log.Fatal(err) - } - fmt.Printf("Retrieved PAT: %+v\n", retrievedPAT) -} - -func StorePAT(db *badger.DB, pat auth.PAT) error { - return db.Update(func(txn *badger.Txn) error { - if err := txn.Set([]byte(fmt.Sprintf("pat:%s:id", pat.ID)), []byte(pat.ID)); err != nil { - return err - } - if err := txn.Set([]byte(fmt.Sprintf("pat:%s:user", pat.ID)), []byte(pat.User)); err != nil { - return err - } - if err := txn.Set([]byte(fmt.Sprintf("pat:%s:name", pat.ID)), []byte(pat.Name)); err != nil { - return err - } - if err := txn.Set([]byte(fmt.Sprintf("pat:%s:issued_at", pat.ID)), []byte(pat.IssuedAt.String())); err != nil { - return err - } - if err := txn.Set([]byte(fmt.Sprintf("pat:%s:expires_at", pat.ID)), []byte(pat.ExpiresAt.String())); err != nil { - return err - } - // Store scope - scopeBytes, err := EncodeScopeToGob(pat.Scope) - if err != nil { - return err - } - if err := txn.Set([]byte(fmt.Sprintf("pat:%s:scope", pat.ID)), scopeBytes); err != nil { - return err - } - return nil - }) -} - -func GetPAT(db *badger.DB, id string) (auth.PAT, error) { - var pat auth.PAT - err := db.View(func(txn *badger.Txn) error { - patID, err := txn.Get([]byte(fmt.Sprintf("pat:%s:id", id))) - if err != nil { - return err - } - err = patID.Value(func(val []byte) error { - pat.ID = string(val) - return nil - }) - if err != nil { - return err - } - - user, err := txn.Get([]byte(fmt.Sprintf("pat:%s:user", id))) - if err != nil { - return err - } - err = user.Value(func(val []byte) error { - pat.User = string(val) - return nil - }) - if err != nil { - return err - } - - name, err := txn.Get([]byte(fmt.Sprintf("pat:%s:name", id))) - if err != nil { - return err - } - err = name.Value(func(val []byte) error { - pat.Name = string(val) - return nil - }) - if err != nil { - return err - } - - issuedAt, err := txn.Get([]byte(fmt.Sprintf("pat:%s:issued_at", id))) - if err != nil { - return err - } - err = issuedAt.Value(func(val []byte) error { - pat.IssuedAt, err = time.Parse(timeLayout, strings.Split(string(val), " m=")[0]) - return err - }) - if err != nil { - return err - } - - expiresAt, err := txn.Get([]byte(fmt.Sprintf("pat:%s:expires_at", id))) - if err != nil { - return err - } - err = expiresAt.Value(func(val []byte) error { - pat.ExpiresAt, err = time.Parse(timeLayout, strings.Split(string(val), " m=")[0]) - return err - }) - if err != nil { - return err - } - - // Retrieve scope - scope, err := txn.Get([]byte(fmt.Sprintf("pat:%s:scope", id))) - if err != nil { - return err - } - - err = scope.Value(func(val []byte) error { - pat.Scope, err = DecodeGobToScope(val) - return err - }) - if err != nil { - return err - } - - return nil - }) - return pat, err -} - -func EncodeScopeToGob(scope auth.Scope) ([]byte, error) { - buf := bytes.NewBuffer([]byte{}) - enc := gob.NewEncoder(buf) - if err := enc.Encode(scope); err != nil { - return nil, err - } - return buf.Bytes(), nil -} - -func DecodeGobToScope(scopeBytes []byte) (auth.Scope, error) { - buf := bytes.NewBuffer(scopeBytes) - var scope auth.Scope - dec := gob.NewDecoder(buf) - if err := dec.Decode(&scope); err != nil { - return auth.Scope{}, err - } - return scope, nil -} diff --git a/cmd/test/bbolt/main.go b/cmd/test/bbolt/main.go deleted file mode 100644 index 52351dbd52..0000000000 --- a/cmd/test/bbolt/main.go +++ /dev/null @@ -1,170 +0,0 @@ -package main - -import ( - "fmt" - "log" - "time" - - "github.com/absmach/magistrala/auth" - "github.com/google/uuid" - "go.etcd.io/bbolt" -) - -func main() { - // Open bbolt database - db, err := bbolt.Open("./bbolt.db", 0600, nil) - if err != nil { - log.Fatal(err) - } - defer db.Close() - - // Create a new PAT - pat := auth.PAT{ - ID: uuid.New().String(), - User: "user123", - Name: "Test Token", - IssuedAt: time.Now(), - ExpiresAt: time.Now().Add(24 * time.Hour), - } - - // Set scope - pat.Scope.Users = auth.OperationScope{ - Operations: map[auth.OperationType]auth.ScopeValue{ - auth.CreateOp: auth.SelectedIDs{"entity1": {}, "entity2": {}}, - }, - } - - // Store PAT - if err := storePAT(db, pat); err != nil { - log.Fatal(err) - } - - // Retrieve PAT - retrievedPAT, err := getPAT(db, pat.ID) - if err != nil { - log.Fatal(err) - } - fmt.Printf("Retrieved PAT: %+v\n", retrievedPAT) -} - -func storePAT(db *bbolt.DB, pat auth.PAT) error { - return db.Update(func(tx *bbolt.Tx) error { - bucket, err := tx.CreateBucketIfNotExists([]byte("pats")) - if err != nil { - return err - } - - if err := bucket.Put([]byte(fmt.Sprintf("pat:%s:id", pat.ID)), []byte(pat.ID)); err != nil { - return err - } - if err := bucket.Put([]byte(fmt.Sprintf("pat:%s:user", pat.ID)), []byte(pat.User)); err != nil { - return err - } - if err := bucket.Put([]byte(fmt.Sprintf("pat:%s:name", pat.ID)), []byte(pat.Name)); err != nil { - return err - } - if err := bucket.Put([]byte(fmt.Sprintf("pat:%s:issued_at", pat.ID)), []byte(pat.IssuedAt.String())); err != nil { - return err - } - if err := bucket.Put([]byte(fmt.Sprintf("pat:%s:expires_at", pat.ID)), []byte(pat.ExpiresAt.String())); err != nil { - return err - } - // Store scope - scopeKeyPrefix := fmt.Sprintf("pat:%s:scope", pat.ID) - if err := storeOperationScope(bucket, scopeKeyPrefix, pat.Scope.Users); err != nil { - return err - } - return nil - }) -} - -func storeOperationScope(bucket *bbolt.Bucket, keyPrefix string, os auth.OperationScope) error { - for opType, scopeValue := range os.Operations { - scopeKey := []byte(fmt.Sprintf("%s:%d", keyPrefix, opType)) - switch v := scopeValue.(type) { - case auth.AnyIDs: - if err := bucket.Put(scopeKey, []byte("*")); err != nil { - return err - } - case auth.SelectedIDs: - for id := range v { - if err := bucket.Put([]byte(fmt.Sprintf("%s:%s", scopeKey, id)), []byte(id)); err != nil { - return err - } - } - } - } - return nil -} - -func getPAT(db *bbolt.DB, id string) (auth.PAT, error) { - var pat auth.PAT - err := db.View(func(tx *bbolt.Tx) error { - bucket := tx.Bucket([]byte("pats")) - if bucket == nil { - return fmt.Errorf("bucket not found") - } - - patID := bucket.Get([]byte(fmt.Sprintf("pat:%s:id", id))) - if patID == nil { - return fmt.Errorf("pat with ID %s not found", id) - } - pat.ID = string(patID) - - user := bucket.Get([]byte(fmt.Sprintf("pat:%s:user", id))) - if user != nil { - pat.User = string(user) - } - - name := bucket.Get([]byte(fmt.Sprintf("pat:%s:name", id))) - if name != nil { - pat.Name = string(name) - } - - issuedAt := bucket.Get([]byte(fmt.Sprintf("pat:%s:issued_at", id))) - if issuedAt != nil { - pat.IssuedAt, _ = time.Parse(time.RFC3339, string(issuedAt)) - } - - expiresAt := bucket.Get([]byte(fmt.Sprintf("pat:%s:expires_at", id))) - if expiresAt != nil { - pat.ExpiresAt, _ = time.Parse(time.RFC3339, string(expiresAt)) - } - - // Retrieve scope - scopeKeyPrefix := fmt.Sprintf("pat:%s:scope", id) - scope, err := getOperationScope(bucket, scopeKeyPrefix) - if err != nil { - return err - } - pat.Scope.Users = scope - - return nil - }) - return pat, err -} - -func getOperationScope(bucket *bbolt.Bucket, keyPrefix string) (auth.OperationScope, error) { - os := auth.OperationScope{Operations: make(map[auth.OperationType]auth.ScopeValue)} - c := bucket.Cursor() - prefix := []byte(keyPrefix) - for k, v := c.Seek(prefix); k != nil && len(k) > len(prefix) && string(k[:len(prefix)]) == keyPrefix; k, v = c.Next() { - opTypeStr := string(k[len(prefix)+1:]) - var opType auth.OperationType - _, err := fmt.Sscanf(opTypeStr, "%d", &opType) - if err != nil { - return os, err - } - - if string(v) == "*" { - os.Operations[opType] = auth.AnyIDs{} - } else { - if os.Operations[opType] == nil { - os.Operations[opType] = auth.SelectedIDs{} - } - os.Operations[opType].(auth.SelectedIDs)[string(v)] = struct{}{} - } - } - - return os, nil -} diff --git a/cmd/test/bboltgob/main.go b/cmd/test/bboltgob/main.go deleted file mode 100644 index e440876c65..0000000000 --- a/cmd/test/bboltgob/main.go +++ /dev/null @@ -1,166 +0,0 @@ -package main - -import ( - "bytes" - "encoding/gob" - "fmt" - "log" - "strings" - "time" - - "github.com/absmach/magistrala/auth" - "github.com/google/uuid" - "go.etcd.io/bbolt" -) - -const timeLayout = "2006-01-02 15:04:05.999999999 -0700 MST" - -func init() { - gob.Register(auth.SelectedIDs{}) - gob.Register(auth.AnyIDs{}) -} -func main() { - // Open bbolt database - db, err := bbolt.Open("./bbolt.db", 0600, nil) - if err != nil { - log.Fatal(err) - } - defer db.Close() - - // Create a new PAT - pat := auth.PAT{ - ID: uuid.New().String(), - User: "user123", - Name: "Test Token", - IssuedAt: time.Now(), - ExpiresAt: time.Now().Add(24 * time.Hour), - } - - // Set scope - pat.Scope.Users = auth.OperationScope{ - Operations: map[auth.OperationType]auth.ScopeValue{ - auth.CreateOp: auth.SelectedIDs{"entity1": {}, "entity2": {}}, - }, - } - - // Store PAT - if err := storePATGob(db, pat); err != nil { - log.Fatal(err) - } - - // Retrieve PAT - retrievedPAT, err := getPATGob(db, pat.ID) - if err != nil { - log.Fatal(err) - } - fmt.Printf("Retrieved PAT: %+v\n", retrievedPAT) -} - -func storePATGob(db *bbolt.DB, pat auth.PAT) error { - return db.Update(func(tx *bbolt.Tx) error { - bucket, err := tx.CreateBucketIfNotExists([]byte("pats")) - if err != nil { - return err - } - - if err := bucket.Put([]byte(fmt.Sprintf("pat:%s:id", pat.ID)), []byte(pat.ID)); err != nil { - return err - } - if err := bucket.Put([]byte(fmt.Sprintf("pat:%s:user", pat.ID)), []byte(pat.User)); err != nil { - return err - } - if err := bucket.Put([]byte(fmt.Sprintf("pat:%s:name", pat.ID)), []byte(pat.Name)); err != nil { - return err - } - if err := bucket.Put([]byte(fmt.Sprintf("pat:%s:issued_at", pat.ID)), []byte(pat.IssuedAt.String())); err != nil { - return err - } - if err := bucket.Put([]byte(fmt.Sprintf("pat:%s:expires_at", pat.ID)), []byte(pat.ExpiresAt.String())); err != nil { - return err - } - // Store scope - scopeBytes, err := EncodeScopeToGob(pat.Scope) - if err != nil { - return err - } - if err := bucket.Put([]byte(fmt.Sprintf("pat:%s:scope", pat.ID)), scopeBytes); err != nil { - return err - } - - return nil - }) -} - -func getPATGob(db *bbolt.DB, id string) (auth.PAT, error) { - var pat auth.PAT - err := db.View(func(tx *bbolt.Tx) (err error) { - bucket := tx.Bucket([]byte("pats")) - if bucket == nil { - return fmt.Errorf("bucket not found") - } - - patID := bucket.Get([]byte(fmt.Sprintf("pat:%s:id", id))) - if patID == nil { - return fmt.Errorf("pat with ID %s not found", id) - } - pat.ID = string(patID) - - user := bucket.Get([]byte(fmt.Sprintf("pat:%s:user", id))) - if user != nil { - pat.User = string(user) - } - - name := bucket.Get([]byte(fmt.Sprintf("pat:%s:name", id))) - if name != nil { - pat.Name = string(name) - } - - issuedAt := bucket.Get([]byte(fmt.Sprintf("pat:%s:issued_at", id))) - if issuedAt != nil { - pat.IssuedAt, err = time.Parse(timeLayout, strings.Split(string(issuedAt), " m=")[0]) - if err != nil { - return err - } - } - - expiresAt := bucket.Get([]byte(fmt.Sprintf("pat:%s:expires_at", id))) - if expiresAt != nil { - pat.ExpiresAt, err = time.Parse(timeLayout, strings.Split(string(expiresAt), " m=")[0]) - if err != nil { - return err - } - } - - // Retrieve scope - scopeBytes := bucket.Get([]byte(fmt.Sprintf("pat:%s:scope", id))) - if scopeBytes != nil { - scope, err := DecodeGobToScope(scopeBytes) - if err != nil { - return err - } - pat.Scope = scope - } - - return nil - }) - return pat, err -} - -func EncodeScopeToGob(scope auth.Scope) ([]byte, error) { - buf := bytes.NewBuffer([]byte{}) - enc := gob.NewEncoder(buf) - if err := enc.Encode(scope); err != nil { - return nil, err - } - return buf.Bytes(), nil -} - -func DecodeGobToScope(scopeBytes []byte) (auth.Scope, error) { - buf := bytes.NewBuffer(scopeBytes) - var scope auth.Scope - dec := gob.NewDecoder(buf) - if err := dec.Decode(&scope); err != nil { - return auth.Scope{}, err - } - return scope, nil -} diff --git a/cmd/test/patrepo/main.go b/cmd/test/patrepo/main.go deleted file mode 100644 index 4e27041ab3..0000000000 --- a/cmd/test/patrepo/main.go +++ /dev/null @@ -1,79 +0,0 @@ -package main - -import ( - "context" - "fmt" - "time" - - "github.com/absmach/magistrala/auth" - "github.com/absmach/magistrala/auth/bolt" - boltclient "github.com/absmach/magistrala/internal/clients/bolt" - "github.com/caarlos0/env/v10" - "github.com/google/uuid" -) - -func main() { - - boltDBConfig := boltclient.Config{} - if err := env.ParseWithOptions(&boltDBConfig, env.Options{}); err != nil { - panic(err) - } - - client, err := boltclient.Connect(boltDBConfig, bolt.Init) - if err != nil { - panic(err) - } - defer client.Close() - - patrepo := bolt.NewPATSRepository(client, boltDBConfig.Bucket) - - pat := auth.PAT{ - ID: uuid.New().String(), - User: "user123", - Name: "user 123", - IssuedAt: time.Now(), - ExpiresAt: time.Now().Add(24 * time.Hour), - Scope: auth.Scope{ - Users: auth.OperationScope{ - Operations: map[auth.OperationType]auth.ScopeValue{ - auth.ReadOp: &auth.AnyIDs{}, - }, - }, - Domains: map[string]auth.DomainScope{ - "domain_1": { - DomainManagement: auth.OperationScope{ - Operations: map[auth.OperationType]auth.ScopeValue{ - auth.ReadOp: &auth.AnyIDs{}, - }, - }, - Entities: map[auth.DomainEntityType]auth.OperationScope{ - auth.DomainGroupsScope: { - Operations: map[auth.OperationType]auth.ScopeValue{ - auth.ReadOp: &auth.SelectedIDs{"group_1": {}, "group_2": {}}, - }, - }, - }, - }, - }, - }, - } - - if err := patrepo.Save(context.Background(), pat); err != nil { - panic(err) - } - - rPAT, err := patrepo.Retrieve(context.Background(), pat.User, pat.ID) - if err != nil { - panic(err) - } - fmt.Println(rPAT.String()) - - fmt.Println(patrepo.CheckScopeEntry(context.Background(), pat.User, pat.ID, auth.PlatformDomainsScope, "domain_1", auth.DomainGroupsScope, auth.ReadOp, "group_1")) - fmt.Println(patrepo.CheckScopeEntry(context.Background(), pat.User, pat.ID, auth.PlatformDomainsScope, "domain_1", auth.DomainGroupsScope, auth.ReadOp, "group_2")) - fmt.Println(patrepo.CheckScopeEntry(context.Background(), pat.User, pat.ID, auth.PlatformDomainsScope, "domain_1", auth.DomainGroupsScope, auth.UpdateOp, "group_1")) - fmt.Println(patrepo.CheckScopeEntry(context.Background(), pat.User, pat.ID, auth.PlatformDomainsScope, "domain_1", auth.DomainGroupsScope, auth.UpdateOp, "group_2")) - fmt.Println(patrepo.CheckScopeEntry(context.Background(), pat.User, pat.ID, auth.PlatformDomainsScope, "domain_1", auth.DomainThingsScope, auth.UpdateOp, "group_2")) - fmt.Println(patrepo.CheckScopeEntry(context.Background(), pat.User, pat.ID, auth.PlatformUsersScope, "", auth.DomainNullScope, auth.ReadOp, "user_123")) - fmt.Println(patrepo.CheckScopeEntry(context.Background(), pat.User, pat.ID, auth.PlatformUsersScope, "", auth.DomainNullScope, auth.UpdateOp, "user_123")) - -} diff --git a/cmd/test/patunmarshal/main.go b/cmd/test/patunmarshal/main.go deleted file mode 100644 index 352f34cacf..0000000000 --- a/cmd/test/patunmarshal/main.go +++ /dev/null @@ -1,172 +0,0 @@ -package main - -import ( - "encoding/json" - "fmt" - - "github.com/absmach/magistrala/auth" -) - -func main() { - var pat auth.PAT - - jsonData := ` - { - "id": "id_1", - "user": "user_1", - "name": "user 1 PAT", - "Description": "user 1 pat 1 description", - "Token": "hashed token", - "scope": { - "users": { - "operations": { - "update": [ - "123", - "123123" - ], - "create": "123", - "read": "*" - } - }, - "domains": { - "domain_1": { - "domain_management": { - "operations": { - "update": [ - "123", - "123123" - ], - "create": "123", - "read": "*" - } - }, - "entities": { - "groups": { - "operations": { - "update": [ - "123", - "123123" - ], - "create": "123", - "read": "*" - } - }, - "things": { - "operations": { - "update": [ - "123", - "123123" - ], - "create": "123", - "read": "*" - } - }, - "channels": { - "operations": { - "update": [ - "123", - "123123" - ], - "create": "123", - "read": "*" - } - } - } - } - } - }, - "issued_at": "2024-01-01T00:00:00Z", - "expires_at": "2024-01-04T00:00:00Z", - "updated_at": "2024-01-02T00:00:00Z", - "last_used_at": "2024-01-02T00:00:00Z", - "revoked": true, - "revoked_at": "2024-01-03T00:00:00Z" - } - ` - - if err := json.Unmarshal([]byte(jsonData), &pat); err != nil { - fmt.Println("Error:", err) - return - } - - fmt.Printf("pat: %s\n", pat.String()) - - scopeByte := ` - { - "domain_management": { - "operations": { - "update": [ - "123", - "123123" - ], - "create": "123", - "read": "*" - } - }, - "entities": { - "groups": { - "operations": { - "update": [ - "123", - "123123" - ], - "create": "123", - "read": "*" - } - }, - "things": { - "operations": { - "update": [ - "123", - "123123" - ], - "create": "123", - "read": "*" - } - }, - "channels": { - "operations": { - "update": [ - "123", - "123123" - ], - "create": "123", - "read": "*" - } - } - } -} - ` - var domainscope auth.DomainScope - - if err := json.Unmarshal([]byte(scopeByte), &domainscope); err != nil { - panic(err) - } - - domBytes, _ := json.MarshalIndent(domainscope, "", " ") - fmt.Println("\n\n", string(domBytes)) - - operationsByte := ` - { - "operations": { - "update": [ - "123", - "123123" - ], - "create": "123", - "read": "*" - } - } - - ` - - var operation auth.OperationScope - - if err := json.Unmarshal([]byte(operationsByte), &operation); err != nil { - panic(err) - } - - opbytes, _ := json.MarshalIndent(operation, "", " ") - fmt.Println("\n\n", string(opbytes)) - -} diff --git a/cmd/test/patunmarshal/temp.json b/cmd/test/patunmarshal/temp.json deleted file mode 100644 index ed45c5ebf7..0000000000 --- a/cmd/test/patunmarshal/temp.json +++ /dev/null @@ -1,71 +0,0 @@ -{ - "id": "id_1", - "user": "user_1", - "name": "user 1 PAT", - "Description": "user 1 pat 1 description", - "Token": "hashed token", - "scope": { - "users": { - "operations": { - "update": [ - "123", - "123123" - ], - "create": "123", - "read": "*" - } - }, - "domains": { - "domain_1": { - "domain_management": { - "operations": { - "update": [ - "123", - "123123" - ], - "create": "123", - "read": "*" - } - }, - "entites": { - "groups": { - "operations": { - "update": [ - "123", - "123123" - ], - "create": "123", - "read": "*" - } - }, - "things": { - "operations": { - "update": [ - "123", - "123123" - ], - "create": "123", - "read": "*" - } - }, - "channels": { - "operations": { - "update": [ - "123", - "123123" - ], - "create": "123", - "read": "*" - } - } - } - } - } - }, - "issued_at": "2024-01-01T00:00:00Z", - "expires_at": "2024-01-04T00:00:00Z", - "updated_at": "2024-01-02T00:00:00Z", - "last_used_at": "2024-01-02T00:00:00Z", - "revoked": true, - "revoked_at": "2024-01-03T00:00:00Z" -} diff --git a/cmd/test/redis/main.go b/cmd/test/redis/main.go deleted file mode 100644 index 8ac4450f98..0000000000 --- a/cmd/test/redis/main.go +++ /dev/null @@ -1,277 +0,0 @@ -package main - -import ( - "context" - "fmt" - "strconv" - "time" - - "github.com/absmach/magistrala/auth" - "github.com/go-redis/redis/v8" - "github.com/google/uuid" -) - -var ctx = context.Background() - -func main() { - rdb := redis.NewClient(&redis.Options{ - Addr: "localhost:6379", - Password: "", // no password set - DB: 0, // use default DB - }) - // Create a new PAT - pat := auth.PAT{ - ID: uuid.New().String(), - User: "user123", - Name: "Test Token", - IssuedAt: time.Now(), - ExpiresAt: time.Now().Add(24 * time.Hour), - } - - // Set scope - pat.Scope.Users = auth.OperationScope{ - Operations: map[auth.OperationType]auth.ScopeValue{ - auth.CreateOp: auth.SelectedIDs{"entity1": {}, "entity2": {}}, - }, - } - - if err := StorePAT(rdb, pat); err != nil { - panic(err) - } - rPAT, err := RetrievePAT(rdb, pat.ID) - if err != nil { - panic(err) - } - - fmt.Println(rPAT.String()) -} - -func StorePAT(client *redis.Client, pat auth.PAT) error { - // Create a hash to store PAT fields - key := "pat:" + pat.ID - - // Convert time.Time fields to Unix timestamps - issuedAt := strconv.FormatInt(pat.IssuedAt.Unix(), 10) - expiresAt := strconv.FormatInt(pat.ExpiresAt.Unix(), 10) - updatedAt := strconv.FormatInt(pat.UpdatedAt.Unix(), 10) - lastUsedAt := strconv.FormatInt(pat.LastUsedAt.Unix(), 10) - revokedAt := strconv.FormatInt(pat.RevokedAt.Unix(), 10) - - // Store basic PAT fields - err := client.HMSet(ctx, key, map[string]interface{}{ - "user": pat.User, - "name": pat.Name, - "issued_at": issuedAt, - "expires_at": expiresAt, - "updated_at": updatedAt, - "last_used_at": lastUsedAt, - "revoked": pat.Revoked, - "revoked_at": revokedAt, - }).Err() - if err != nil { - return err - } - - // Store Scope - err = StoreScope(client, key+":scope", pat.Scope) - if err != nil { - return err - } - - return nil -} - -func StoreScope(client *redis.Client, key string, scope auth.Scope) error { - // Store Users OperationScope - err := StoreOperationScope(client, key+":users", scope.Users) - if err != nil { - return err - } - - // Store Domains - for domainID, domainScope := range scope.Domains { - domainKey := key + ":domains:" + domainID - err = StoreDomainScope(client, domainKey, domainScope) - if err != nil { - return err - } - } - - return nil -} - -func StoreOperationScope(client *redis.Client, key string, os auth.OperationScope) error { - for operation, scopeValue := range os.Operations { - operationKey := key + ":" + operation.String() - switch value := scopeValue.(type) { - case auth.AnyIDs: - err := client.Set(ctx, operationKey, "*", 0).Err() - if err != nil { - return err - } - case auth.SelectedIDs: - for id := range value { - err := client.HSet(ctx, operationKey, id, "").Err() - if err != nil { - return err - } - } - } - } - return nil -} - -func StoreDomainScope(client *redis.Client, key string, ds auth.DomainScope) error { - // Store DomainManagement OperationScope - err := StoreOperationScope(client, key+":domain_management", ds.DomainManagement) - if err != nil { - return err - } - - // Store Entities - for entityType, operationScope := range ds.Entities { - entityKey := key + ":entities:" + entityType.String() - err = StoreOperationScope(client, entityKey, operationScope) - if err != nil { - return err - } - } - - return nil -} - -func RetrievePAT(client *redis.Client, patID string) (*auth.PAT, error) { - key := "pat:" + patID - - fields, err := client.HGetAll(ctx, key).Result() - if err != nil { - return nil, err - } - - issuedAt, _ := strconv.ParseInt(fields["issued_at"], 10, 64) - expiresAt, _ := strconv.ParseInt(fields["expires_at"], 10, 64) - updatedAt, _ := strconv.ParseInt(fields["updated_at"], 10, 64) - lastUsedAt, _ := strconv.ParseInt(fields["last_used_at"], 10, 64) - revokedAt, _ := strconv.ParseInt(fields["revoked_at"], 10, 64) - - pat := &auth.PAT{ - ID: patID, - User: fields["user"], - Name: fields["name"], - IssuedAt: time.Unix(issuedAt, 0), - ExpiresAt: time.Unix(expiresAt, 0), - UpdatedAt: time.Unix(updatedAt, 0), - LastUsedAt: time.Unix(lastUsedAt, 0), - Revoked: fields["revoked"] == "true", - RevokedAt: time.Unix(revokedAt, 0), - } - - // Retrieve Scope - scope, err := RetrieveScope(client, key+":scope") - if err != nil { - return nil, err - } - pat.Scope = *scope - - return pat, nil -} - -func RetrieveScope(client *redis.Client, key string) (*auth.Scope, error) { - scope := &auth.Scope{} - - // Retrieve Users OperationScope - users, err := RetrieveOperationScope(client, key+":users") - if err != nil { - return nil, err - } - scope.Users = *users - - // Retrieve Domains - domainKeys, err := client.Keys(ctx, key+":domains:*").Result() - if err != nil { - return nil, err - } - - scope.Domains = make(map[string]auth.DomainScope) - for _, domainKey := range domainKeys { - domainID := domainKey[len(key+":domains:"):] - domainScope, err := RetrieveDomainScope(client, domainKey) - if err != nil { - return nil, err - } - scope.Domains[domainID] = *domainScope - } - - return scope, nil -} - -func RetrieveOperationScope(client *redis.Client, key string) (*auth.OperationScope, error) { - os := &auth.OperationScope{ - Operations: make(map[auth.OperationType]auth.ScopeValue), - } - - operationKeys, err := client.Keys(ctx, key+":*").Result() - if err != nil { - return nil, err - } - - for _, operationKey := range operationKeys { - operationStr := operationKey[len(key+":"):] - operation, err := auth.ParseOperationType(operationStr) // You'll need to implement this function to convert string to OperationType - if err != nil { - return nil, err - } - - if wildcard, err := client.Get(ctx, operationKey).Result(); err == nil && wildcard == "*" { - os.Operations[operation] = auth.AnyIDs{} - } else { - ids, err := client.HKeys(ctx, operationKey).Result() - if err != nil { - return nil, err - } - - selectedIDs := auth.SelectedIDs{} - for _, id := range ids { - selectedIDs[id] = struct{}{} - } - os.Operations[operation] = selectedIDs - } - } - - return os, nil -} - -func RetrieveDomainScope(client *redis.Client, key string) (*auth.DomainScope, error) { - ds := &auth.DomainScope{ - Entities: make(map[auth.DomainEntityType]auth.OperationScope), - } - - // Retrieve DomainManagement OperationScope - domainManagement, err := RetrieveOperationScope(client, key+":domain_management") - if err != nil { - return nil, err - } - ds.DomainManagement = *domainManagement - - // Retrieve Entities - entityKeys, err := client.Keys(ctx, key+":entities:*").Result() - if err != nil { - return nil, err - } - - for _, entityKey := range entityKeys { - entityTypeStr := entityKey[len(key+":entities:")] - entityType, err := auth.ParseDomainEntityType(string(entityTypeStr)) // You'll need to implement this function to convert string to DomainEntityType - if err != nil { - return nil, err - } - - operationScope, err := RetrieveOperationScope(client, entityKey) - if err != nil { - return nil, err - } - ds.Entities[entityType] = *operationScope - } - - return ds, nil -} diff --git a/cmd/test/redisgob/main.go b/cmd/test/redisgob/main.go deleted file mode 100644 index ce11d51bff..0000000000 --- a/cmd/test/redisgob/main.go +++ /dev/null @@ -1,143 +0,0 @@ -package main - -import ( - "bytes" - "context" - "encoding/gob" - "fmt" - "strconv" - "time" - - "github.com/absmach/magistrala/auth" - "github.com/go-redis/redis/v8" - "github.com/google/uuid" -) - -var ctx = context.Background() - -func init() { - gob.Register(auth.SelectedIDs{}) - gob.Register(auth.AnyIDs{}) -} -func main() { - rdb := redis.NewClient(&redis.Options{ - Addr: "localhost:6379", - Password: "", // no password set - DB: 0, // use default DB - }) - // Create a new PAT - pat := auth.PAT{ - ID: uuid.New().String(), - User: "user123", - Name: "Test Token", - IssuedAt: time.Now(), - ExpiresAt: time.Now().Add(24 * time.Hour), - } - - // Set scope - pat.Scope.Users = auth.OperationScope{ - Operations: map[auth.OperationType]auth.ScopeValue{ - auth.CreateOp: auth.SelectedIDs{"entity1": {}, "entity2": {}}, - }, - } - - if err := StorePATGob(rdb, pat); err != nil { - panic(err) - } - rPAT, err := RetrievePATGob(rdb, pat.ID) - if err != nil { - panic(err) - } - - fmt.Println(rPAT.String()) -} - -func StorePATGob(client *redis.Client, pat auth.PAT) error { - scopeByte, err := EncodeScopeToGob(pat.Scope) - if err != nil { - return err - } - // Create a hash to store PAT fields - key := "pat:" + pat.ID - - // Convert time.Time fields to Unix timestamps - issuedAt := strconv.FormatInt(pat.IssuedAt.Unix(), 10) - expiresAt := strconv.FormatInt(pat.ExpiresAt.Unix(), 10) - updatedAt := strconv.FormatInt(pat.UpdatedAt.Unix(), 10) - lastUsedAt := strconv.FormatInt(pat.LastUsedAt.Unix(), 10) - revokedAt := strconv.FormatInt(pat.RevokedAt.Unix(), 10) - - // Store basic PAT fields - err = client.HMSet(ctx, key, map[string]interface{}{ - "user": pat.User, - "name": pat.Name, - "issued_at": issuedAt, - "expires_at": expiresAt, - "updated_at": updatedAt, - "last_used_at": lastUsedAt, - "revoked": pat.Revoked, - "revoked_at": revokedAt, - "scope": scopeByte, - }).Err() - if err != nil { - return err - } - return nil -} - -func RetrievePATGob(client *redis.Client, patID string) (*auth.PAT, error) { - key := "pat:" + patID - - fields, err := client.HGetAll(ctx, key).Result() - if err != nil { - return nil, err - } - - issuedAt, _ := strconv.ParseInt(fields["issued_at"], 10, 64) - expiresAt, _ := strconv.ParseInt(fields["expires_at"], 10, 64) - updatedAt, _ := strconv.ParseInt(fields["updated_at"], 10, 64) - lastUsedAt, _ := strconv.ParseInt(fields["last_used_at"], 10, 64) - revokedAt, _ := strconv.ParseInt(fields["revoked_at"], 10, 64) - - // Decode scope from bytes - scopeBytes := []byte(fields["scope"]) - - scope, err := DecodeGobToScope(scopeBytes) - if err != nil { - return nil, err - } - - pat := &auth.PAT{ - ID: patID, - User: fields["user"], - Name: fields["name"], - IssuedAt: time.Unix(issuedAt, 0), - ExpiresAt: time.Unix(expiresAt, 0), - UpdatedAt: time.Unix(updatedAt, 0), - LastUsedAt: time.Unix(lastUsedAt, 0), - Revoked: fields["revoked"] == "true", - RevokedAt: time.Unix(revokedAt, 0), - Scope: scope, // Assign decoded scope to PAT's Scope field - } - - return pat, nil -} - -func EncodeScopeToGob(scope auth.Scope) ([]byte, error) { - buf := bytes.NewBuffer([]byte{}) - enc := gob.NewEncoder(buf) - if err := enc.Encode(scope); err != nil { - return nil, err - } - return buf.Bytes(), nil -} - -func DecodeGobToScope(scopeBytes []byte) (auth.Scope, error) { - buf := bytes.NewBuffer(scopeBytes) - var scope auth.Scope - dec := gob.NewDecoder(buf) - if err := dec.Decode(&scope); err != nil { - return auth.Scope{}, err - } - return scope, nil -} diff --git a/cmd/test/second/main.go b/cmd/test/second/main.go deleted file mode 100644 index e84af018d6..0000000000 --- a/cmd/test/second/main.go +++ /dev/null @@ -1,98 +0,0 @@ -package main - -import ( - "encoding/json" - "fmt" -) - -type OperationType uint32 - -const ( - CreateOp OperationType = iota - ReadOp - ListOp - UpdateOp - DeleteOp -) - -func (ot OperationType) String() string { - switch ot { - case CreateOp: - return "create" - case ReadOp: - return "read" - case ListOp: - return "list" - case UpdateOp: - return "update" - case DeleteOp: - return "delete" - default: - return fmt.Sprintf("unknown operation type %d", ot) - } -} - -func (ot OperationType) MarshalText() ([]byte, error) { - return []byte(ot.String()), nil -} - -type OperationScope struct { - Operations map[OperationType]string `json:"operations,omitempty"` -} - -type DomainEntityType uint32 - -const ( - DomainManagementScope DomainEntityType = iota - DomainGroupsScope - DomainChannelsScope - DomainThingsScope - DomainNullScope -) - -func (det DomainEntityType) String() string { - switch det { - case DomainManagementScope: - return "domain_management" - case DomainGroupsScope: - return "groups" - case DomainChannelsScope: - return "channels" - case DomainThingsScope: - return "things" - case DomainNullScope: - return "null" - default: - return fmt.Sprintf("unknown domain entity type %d", det) - } -} - -func (det DomainEntityType) MarshalText() ([]byte, error) { - return []byte(det.String()), nil -} - -type DomainScope struct { - Entities map[DomainEntityType]string `json:"entities,omitempty"` -} - -func main() { - // OperationScope works because map keys are encoded correctly - os := &OperationScope{ - Operations: map[OperationType]string{ - CreateOp: "allowed", - ReadOp: "allowed", - }, - } - osJSON, _ := json.MarshalIndent(os, "", " ") - fmt.Println("OperationScope:", string(osJSON)) - - // DomainScope does not work as intended for map keys - ds := &DomainScope{ - Entities: map[DomainEntityType]string{ - DomainManagementScope: "allowed", - DomainGroupsScope: "allowed", - }, - } - dsJSON, _ := json.MarshalIndent(ds, "", " ") - fmt.Println("DomainScope (incorrect):", string(dsJSON)) -} diff --git a/cmd/test/stringsbench/concat_test.go b/cmd/test/stringsbench/concat_test.go deleted file mode 100644 index fdda6684fb..0000000000 --- a/cmd/test/stringsbench/concat_test.go +++ /dev/null @@ -1,34 +0,0 @@ -package main - -import ( - "strings" - "testing" -) - -func concatUsingPlus(n int) string { - s := "" - for i := 0; i < n; i++ { - s += "a" - } - return s -} - -func concatUsingBuilder(n int) string { - var builder strings.Builder - for i := 0; i < n; i++ { - builder.WriteString("a") - } - return builder.String() -} - -func BenchmarkConcatUsingPlus(b *testing.B) { - for i := 0; i < b.N; i++ { - concatUsingPlus(1000) - } -} - -func BenchmarkConcatUsingBuilder(b *testing.B) { - for i := 0; i < b.N; i++ { - concatUsingBuilder(1000) - } -} diff --git a/cmd/test/test.go b/cmd/test/test.go deleted file mode 100644 index 773d69f5a8..0000000000 --- a/cmd/test/test.go +++ /dev/null @@ -1,76 +0,0 @@ -package main - -import ( - "fmt" - "log" - - "github.com/dgraph-io/badger/v4" -) - -func main() { - // Open the Badger database located in the /tmp/badger directory. - // It will be created if it doesn't exist. - opts := badger.DefaultOptions("./badger") - db, err := badger.Open(opts) - if err != nil { - log.Fatal(err) - } - defer db.Close() - - // Insert some data into the database - // err = db.Update(func(txn *badger.Txn) error { - // for i := 0; i < 10; i++ { - // key := fmt.Sprintf("key%d", i) - // value := fmt.Sprintf("value%d", i) - // err := txn.Set([]byte(key), []byte(value)) - // if err != nil { - // return err - // } - // } - // return nil - // }) - // if err != nil { - // log.Fatal(err) - // } - - // Read and filter the data - // err = db.View(func(txn *badger.Txn) error { - // it := txn.NewIterator(badger.DefaultIteratorOptions) - // defer it.Close() - // for it.Rewind(); it.Valid(); it.Next() { - // item := it.Item() - // k := item.Key() - // err := item.Value(func(v []byte) error { - // // Filter: Only print keys with even numbers - // if k[len(k)-1]%2 == 0 { - // fmt.Printf("key=%s, value=%s\n", k, v) - // } - // return nil - // }) - // if err != nil { - // return err - // } - // } - // return nil - // }) - - err = db.View(func(txn *badger.Txn) error { - item, err := txn.Get([]byte("pat:b24bff46-07f2-4d33-a5f0-08447cfc20ca:scope")) - if err != nil { - log.Fatal(err) - } - err = item.Value(func(val []byte) error { - fmt.Printf("The answer is: %s\n", val) - return nil - }) - if err != nil { - log.Fatal(err) - } - - return nil - }) - - if err != nil { - log.Fatal(err) - } -} From b4d46c5b46e4ffd82c97b78c83070cb31822dc14 Mon Sep 17 00:00:00 2001 From: Arvindh Date: Wed, 10 Jul 2024 19:24:47 +0530 Subject: [PATCH 18/24] lint Signed-off-by: Arvindh --- auth/api/http/pats/endpoint.go | 1 - auth/api/http/pats/requests.go | 3 +-- auth/api/logging.go | 13 +++++++++++++ auth/api/metrics.go | 2 ++ auth/bolt/pat.go | 6 +++--- auth/events/streams.go | 13 +++++++++++++ auth/pat.go | 5 ++--- auth/service.go | 9 ++++++++- internal/clients/bolt/bolt.go | 3 +-- 9 files changed, 43 insertions(+), 12 deletions(-) diff --git a/auth/api/http/pats/endpoint.go b/auth/api/http/pats/endpoint.go index 3c35241078..d2f1002a02 100644 --- a/auth/api/http/pats/endpoint.go +++ b/auth/api/http/pats/endpoint.go @@ -169,7 +169,6 @@ func removePATScopeEntryEndpoint(svc auth.Service) endpoint.Endpoint { } return removePatScopeEntryRes{scope}, nil } - } func clearPATAllScopeEntryEndpoint(svc auth.Service) endpoint.Endpoint { diff --git a/auth/api/http/pats/requests.go b/auth/api/http/pats/requests.go index 6552725a1c..77678d5bb2 100644 --- a/auth/api/http/pats/requests.go +++ b/auth/api/http/pats/requests.go @@ -222,8 +222,8 @@ func (apser *addPatScopeEntryReq) UnmarshalJSON(data []byte) error { apser.Operation = op apser.EntityIDs = temp.EntityIDs return nil - } + func (req addPatScopeEntryReq) validate() (err error) { if req.token == "" { return apiutil.ErrBearerToken @@ -275,7 +275,6 @@ func (rpser *removePatScopeEntryReq) UnmarshalJSON(data []byte) error { rpser.Operation = op rpser.EntityIDs = temp.EntityIDs return nil - } func (req removePatScopeEntryReq) validate() (err error) { diff --git a/auth/api/logging.go b/auth/api/logging.go index 210af37907..da2c320405 100644 --- a/auth/api/logging.go +++ b/auth/api/logging.go @@ -526,6 +526,7 @@ func (lm *loggingMiddleware) CreatePAT(ctx context.Context, token, name, descrip }(time.Now()) return lm.svc.CreatePAT(ctx, token, name, description, duration, scope) } + func (lm *loggingMiddleware) UpdatePATName(ctx context.Context, token, patID, name string) (pa auth.PAT, err error) { defer func(begin time.Time) { args := []any{ @@ -542,6 +543,7 @@ func (lm *loggingMiddleware) UpdatePATName(ctx context.Context, token, patID, na }(time.Now()) return lm.svc.UpdatePATName(ctx, token, patID, name) } + func (lm *loggingMiddleware) UpdatePATDescription(ctx context.Context, token, patID, description string) (pa auth.PAT, err error) { defer func(begin time.Time) { args := []any{ @@ -558,6 +560,7 @@ func (lm *loggingMiddleware) UpdatePATDescription(ctx context.Context, token, pa }(time.Now()) return lm.svc.UpdatePATDescription(ctx, token, patID, description) } + func (lm *loggingMiddleware) RetrievePAT(ctx context.Context, token, patID string) (pa auth.PAT, err error) { defer func(begin time.Time) { args := []any{ @@ -573,6 +576,7 @@ func (lm *loggingMiddleware) RetrievePAT(ctx context.Context, token, patID strin }(time.Now()) return lm.svc.RetrievePAT(ctx, token, patID) } + func (lm *loggingMiddleware) ListPATS(ctx context.Context, token string, pm auth.PATSPageMeta) (pp auth.PATSPage, err error) { defer func(begin time.Time) { args := []any{ @@ -589,6 +593,7 @@ func (lm *loggingMiddleware) ListPATS(ctx context.Context, token string, pm auth }(time.Now()) return lm.svc.ListPATS(ctx, token, pm) } + func (lm *loggingMiddleware) DeletePAT(ctx context.Context, token, patID string) (err error) { defer func(begin time.Time) { args := []any{ @@ -604,6 +609,7 @@ func (lm *loggingMiddleware) DeletePAT(ctx context.Context, token, patID string) }(time.Now()) return lm.svc.DeletePAT(ctx, token, patID) } + func (lm *loggingMiddleware) ResetPATSecret(ctx context.Context, token, patID string, duration time.Duration) (pa auth.PAT, err error) { defer func(begin time.Time) { args := []any{ @@ -620,6 +626,7 @@ func (lm *loggingMiddleware) ResetPATSecret(ctx context.Context, token, patID st }(time.Now()) return lm.svc.ResetPATSecret(ctx, token, patID, duration) } + func (lm *loggingMiddleware) RevokePATSecret(ctx context.Context, token, patID string) (err error) { defer func(begin time.Time) { args := []any{ @@ -635,6 +642,7 @@ func (lm *loggingMiddleware) RevokePATSecret(ctx context.Context, token, patID s }(time.Now()) return lm.svc.RevokePATSecret(ctx, token, patID) } + func (lm *loggingMiddleware) AddPATScopeEntry(ctx context.Context, token, patID string, platformEntityType auth.PlatformEntityType, optionalDomainID string, optionalDomainEntityType auth.DomainEntityType, operation auth.OperationType, entityIDs ...string) (sc auth.Scope, err error) { defer func(begin time.Time) { args := []any{ @@ -655,6 +663,7 @@ func (lm *loggingMiddleware) AddPATScopeEntry(ctx context.Context, token, patID }(time.Now()) return lm.svc.AddPATScopeEntry(ctx, token, patID, platformEntityType, optionalDomainID, optionalDomainEntityType, operation, entityIDs...) } + func (lm *loggingMiddleware) RemovePATScopeEntry(ctx context.Context, token, patID string, platformEntityType auth.PlatformEntityType, optionalDomainID string, optionalDomainEntityType auth.DomainEntityType, operation auth.OperationType, entityIDs ...string) (sc auth.Scope, err error) { defer func(begin time.Time) { args := []any{ @@ -675,6 +684,7 @@ func (lm *loggingMiddleware) RemovePATScopeEntry(ctx context.Context, token, pat }(time.Now()) return lm.svc.RemovePATScopeEntry(ctx, token, patID, platformEntityType, optionalDomainID, optionalDomainEntityType, operation, entityIDs...) } + func (lm *loggingMiddleware) ClearPATAllScopeEntry(ctx context.Context, token, patID string) (err error) { defer func(begin time.Time) { args := []any{ @@ -690,6 +700,7 @@ func (lm *loggingMiddleware) ClearPATAllScopeEntry(ctx context.Context, token, p }(time.Now()) return lm.svc.ClearPATAllScopeEntry(ctx, token, patID) } + func (lm *loggingMiddleware) TestCheckPATScopeEntry(ctx context.Context, paToken string, platformEntityType auth.PlatformEntityType, optionalDomainID string, optionalDomainEntityType auth.DomainEntityType, operation auth.OperationType, entityIDs ...string) (err error) { defer func(begin time.Time) { args := []any{ @@ -709,6 +720,7 @@ func (lm *loggingMiddleware) TestCheckPATScopeEntry(ctx context.Context, paToken }(time.Now()) return lm.svc.TestCheckPATScopeEntry(ctx, paToken, platformEntityType, optionalDomainID, optionalDomainEntityType, operation, entityIDs...) } + func (lm *loggingMiddleware) IdentifyPAT(ctx context.Context, paToken string) (pa auth.PAT, err error) { defer func(begin time.Time) { args := []any{ @@ -723,6 +735,7 @@ func (lm *loggingMiddleware) IdentifyPAT(ctx context.Context, paToken string) (p }(time.Now()) return lm.svc.IdentifyPAT(ctx, paToken) } + func (lm *loggingMiddleware) AuthorizePAT(ctx context.Context, paToken string) (pa auth.PAT, err error) { defer func(begin time.Time) { args := []any{ diff --git a/auth/api/metrics.go b/auth/api/metrics.go index 29922329c0..511e782442 100644 --- a/auth/api/metrics.go +++ b/auth/api/metrics.go @@ -287,6 +287,7 @@ func (ms *metricsMiddleware) ListPATS(ctx context.Context, token string, pm auth }(time.Now()) return ms.svc.ListPATS(ctx, token, pm) } + func (ms *metricsMiddleware) DeletePAT(ctx context.Context, token, patID string) error { defer func(begin time.Time) { ms.counter.With("method", "delete_pat").Add(1) @@ -310,6 +311,7 @@ func (ms *metricsMiddleware) RevokePATSecret(ctx context.Context, token, patID s }(time.Now()) return ms.svc.RevokePATSecret(ctx, token, patID) } + func (ms *metricsMiddleware) AddPATScopeEntry(ctx context.Context, token, patID string, platformEntityType auth.PlatformEntityType, optionalDomainID string, optionalDomainEntityType auth.DomainEntityType, operation auth.OperationType, entityIDs ...string) (auth.Scope, error) { defer func(begin time.Time) { ms.counter.With("method", "add_pat_scope_entry").Add(1) diff --git a/auth/bolt/pat.go b/auth/bolt/pat.go index ffee6eff51..dfcde5a7ca 100644 --- a/auth/bolt/pat.go +++ b/auth/bolt/pat.go @@ -14,7 +14,6 @@ import ( "github.com/absmach/magistrala/auth" "github.com/absmach/magistrala/pkg/errors" repoerr "github.com/absmach/magistrala/pkg/errors/repository" - bolt "go.etcd.io/bbolt" ) @@ -161,7 +160,7 @@ func (pr *patRepo) RetrieveAll(ctx context.Context, userID string, pm auth.PATSP var pats []auth.PAT - var patsPage = auth.PATSPage{ + patsPage := auth.PATSPage{ Total: uint64(total), Limit: pm.Limit, Offset: pm.Offset, @@ -276,7 +275,7 @@ func (pr *patRepo) RemoveScopeEntry(ctx context.Context, userID, patID string, p if err != nil { return err } - for key, _ := range kv { + for key := range kv { fullKey := []byte(patID + keySeparator + key) if err := b.Delete(fullKey); err != nil { return errors.Wrap(repoerr.ErrRemoveEntity, err) @@ -430,6 +429,7 @@ func scopeToKeyValue(scope auth.Scope) (map[string][]byte, error) { } return kv, nil } + func scopeEntryToKeyValue(platformEntityType auth.PlatformEntityType, optionalDomainID string, optionalDomainEntityType auth.DomainEntityType, operation auth.OperationType, entityIDs ...string) (map[string][]byte, error) { if len(entityIDs) == 0 { return nil, repoerr.ErrMalformedEntity diff --git a/auth/events/streams.go b/auth/events/streams.go index e2d971cdad..c839153f10 100644 --- a/auth/events/streams.go +++ b/auth/events/streams.go @@ -267,42 +267,55 @@ func (es *eventStore) ListPermissions(ctx context.Context, pr auth.PolicyReq, fi func (es *eventStore) CreatePAT(ctx context.Context, token, name, description string, duration time.Duration, scope auth.Scope) (auth.PAT, error) { return es.svc.CreatePAT(ctx, token, name, description, duration, scope) } + func (es *eventStore) UpdatePATName(ctx context.Context, token, patID, name string) (auth.PAT, error) { return es.svc.UpdatePATName(ctx, token, patID, name) } + func (es *eventStore) UpdatePATDescription(ctx context.Context, token, patID, description string) (auth.PAT, error) { return es.svc.UpdatePATDescription(ctx, token, patID, description) } + func (es *eventStore) RetrievePAT(ctx context.Context, token, patID string) (auth.PAT, error) { return es.svc.RetrievePAT(ctx, token, patID) } + func (es *eventStore) ListPATS(ctx context.Context, token string, pm auth.PATSPageMeta) (auth.PATSPage, error) { return es.svc.ListPATS(ctx, token, pm) } + func (es *eventStore) DeletePAT(ctx context.Context, token, patID string) error { return es.svc.DeletePAT(ctx, token, patID) } + func (es *eventStore) ResetPATSecret(ctx context.Context, token, patID string, duration time.Duration) (auth.PAT, error) { return es.svc.ResetPATSecret(ctx, token, patID, duration) } + func (es *eventStore) RevokePATSecret(ctx context.Context, token, patID string) error { return es.svc.RevokePATSecret(ctx, token, patID) } + func (es *eventStore) AddPATScopeEntry(ctx context.Context, token, patID string, platformEntityType auth.PlatformEntityType, optionalDomainID string, optionalDomainEntityType auth.DomainEntityType, operation auth.OperationType, entityIDs ...string) (auth.Scope, error) { return es.svc.AddPATScopeEntry(ctx, token, patID, platformEntityType, optionalDomainID, optionalDomainEntityType, operation, entityIDs...) } + func (es *eventStore) RemovePATScopeEntry(ctx context.Context, token, patID string, platformEntityType auth.PlatformEntityType, optionalDomainID string, optionalDomainEntityType auth.DomainEntityType, operation auth.OperationType, entityIDs ...string) (auth.Scope, error) { return es.svc.RemovePATScopeEntry(ctx, token, patID, platformEntityType, optionalDomainID, optionalDomainEntityType, operation, entityIDs...) } + func (es *eventStore) ClearPATAllScopeEntry(ctx context.Context, token, patID string) error { return es.svc.ClearPATAllScopeEntry(ctx, token, patID) } + func (es *eventStore) TestCheckPATScopeEntry(ctx context.Context, paToken string, platformEntityType auth.PlatformEntityType, optionalDomainID string, optionalDomainEntityType auth.DomainEntityType, operation auth.OperationType, entityIDs ...string) error { return es.svc.TestCheckPATScopeEntry(ctx, paToken, platformEntityType, optionalDomainID, optionalDomainEntityType, operation, entityIDs...) } + func (es *eventStore) IdentifyPAT(ctx context.Context, paToken string) (auth.PAT, error) { return es.svc.IdentifyPAT(ctx, paToken) } + func (es *eventStore) AuthorizePAT(ctx context.Context, paToken string) (auth.PAT, error) { return es.svc.AuthorizePAT(ctx, paToken) } diff --git a/auth/pat.go b/auth/pat.go index ddf70a0d9a..c8fca89022 100644 --- a/auth/pat.go +++ b/auth/pat.go @@ -12,9 +12,7 @@ import ( "github.com/absmach/magistrala/pkg/errors" ) -var ( - errAddEntityToAnyIDs = errors.New("could not add entity id to any ID scope value") -) +var errAddEntityToAnyIDs = errors.New("could not add entity id to any ID scope value") // Define OperationType. type OperationType uint32 @@ -241,6 +239,7 @@ func (s SelectedIDs) Values() []string { } return values } + func (s *SelectedIDs) AddValues(ids ...string) error { if *s == nil { *s = make(SelectedIDs) diff --git a/auth/service.go b/auth/service.go index 1efca5ce6a..7790c46f97 100644 --- a/auth/service.go +++ b/auth/service.go @@ -1114,6 +1114,7 @@ func (svc service) UpdatePATName(ctx context.Context, token, patID, name string) } return pat, nil } + func (svc service) UpdatePATDescription(ctx context.Context, token, patID, description string) (PAT, error) { key, err := svc.Identify(ctx, token) if err != nil { @@ -1125,6 +1126,7 @@ func (svc service) UpdatePATDescription(ctx context.Context, token, patID, descr } return pat, nil } + func (svc service) RetrievePAT(ctx context.Context, token, patID string) (PAT, error) { key, err := svc.Identify(ctx, token) if err != nil { @@ -1137,6 +1139,7 @@ func (svc service) RetrievePAT(ctx context.Context, token, patID string) (PAT, e } return pat, nil } + func (svc service) ListPATS(ctx context.Context, token string, pm PATSPageMeta) (PATSPage, error) { key, err := svc.Identify(ctx, token) if err != nil { @@ -1148,6 +1151,7 @@ func (svc service) ListPATS(ctx context.Context, token string, pm PATSPageMeta) } return patsPage, nil } + func (svc service) DeletePAT(ctx context.Context, token, patID string) error { key, err := svc.Identify(ctx, token) if err != nil { @@ -1158,6 +1162,7 @@ func (svc service) DeletePAT(ctx context.Context, token, patID string) error { } return nil } + func (svc service) ResetPATSecret(ctx context.Context, token, patID string, duration time.Duration) (PAT, error) { key, err := svc.Identify(ctx, token) if err != nil { @@ -1174,6 +1179,7 @@ func (svc service) ResetPATSecret(ctx context.Context, token, patID string, dura } return pat, nil } + func (svc service) RevokePATSecret(ctx context.Context, token, patID string) error { key, err := svc.Identify(ctx, token) if err != nil { @@ -1193,11 +1199,11 @@ func (svc service) AddPATScopeEntry(ctx context.Context, token, patID string, pl } scope, err := svc.pats.AddScopeEntry(ctx, key.User, patID, platformEntityType, optionalDomainID, optionalDomainEntityType, operation, entityIDs...) if err != nil { - return Scope{}, errors.Wrap(errRevokePAT, err) } return scope, nil } + func (svc service) RemovePATScopeEntry(ctx context.Context, token, patID string, platformEntityType PlatformEntityType, optionalDomainID string, optionalDomainEntityType DomainEntityType, operation OperationType, entityIDs ...string) (Scope, error) { key, err := svc.Identify(ctx, token) if err != nil { @@ -1209,6 +1215,7 @@ func (svc service) RemovePATScopeEntry(ctx context.Context, token, patID string, } return scope, nil } + func (svc service) ClearPATAllScopeEntry(ctx context.Context, token, patID string) error { key, err := svc.Identify(ctx, token) if err != nil { diff --git a/internal/clients/bolt/bolt.go b/internal/clients/bolt/bolt.go index 845e6f395e..12e22efadf 100644 --- a/internal/clients/bolt/bolt.go +++ b/internal/clients/bolt/bolt.go @@ -10,7 +10,6 @@ import ( "github.com/absmach/magistrala/pkg/errors" "github.com/caarlos0/env/v10" - bolt "go.etcd.io/bbolt" ) @@ -44,7 +43,7 @@ func Setup(envPrefix string, initFn func(*bolt.Tx, string) error) (*bolt.DB, err return SetupDB(envPrefix, initFn) } -// SetupDB load configuration from environment, +// SetupDB load configuration from environment,. func SetupDB(envPrefix string, initFn func(*bolt.Tx, string) error) (*bolt.DB, error) { cfg := Config{} if err := env.ParseWithOptions(&cfg, env.Options{Prefix: envPrefix}); err != nil { From 20f0d7f3473ce00ba33466cd3cd7cba70b03b514 Mon Sep 17 00:00:00 2001 From: Arvindh Date: Wed, 10 Jul 2024 19:40:29 +0530 Subject: [PATCH 19/24] fix lint errors Signed-off-by: Arvindh --- auth/api/http/pats/endpoint.go | 2 +- auth/bolt/pat.go | 289 --------------------------------- auth/pat.go | 69 +++++--- auth/service.go | 2 - 4 files changed, 43 insertions(+), 319 deletions(-) diff --git a/auth/api/http/pats/endpoint.go b/auth/api/http/pats/endpoint.go index d2f1002a02..36b7ac1b75 100644 --- a/auth/api/http/pats/endpoint.go +++ b/auth/api/http/pats/endpoint.go @@ -197,6 +197,6 @@ func testCheckPATScopeEntryEndpoint(svc auth.Service) endpoint.Endpoint { return nil, err } - return revokePatSecretRes{}, nil + return testCheckPatScopeRes{}, nil } } diff --git a/auth/bolt/pat.go b/auth/bolt/pat.go index dfcde5a7ca..9c8e8f2eed 100644 --- a/auth/bolt/pat.go +++ b/auth/bolt/pat.go @@ -42,8 +42,6 @@ var ( entityValue = []byte{0x02} anyIDValue = []byte{0x03} selectedIDsValue = []byte{0x04} - - errBucketNotFound = errors.New("bucket not found") ) type patRepo struct { @@ -525,269 +523,6 @@ func keyValueToPAT(kv map[string][]byte) (auth.PAT, error) { return pat, nil } -// 129eab11-681b-4c62-89e4-7a9ce0eda29d:scope:users:read -// 129eab11-681b-4c62-89e4-7a9ce0eda29d:scope:domains:domain_1:groups:read -// 129eab11-681b-4c62-89e4-7a9ce0eda29d:scope:domains:domain_1:groups:read:group_2 -// 129eab11-681b-4c62-89e4-7a9ce0eda29d:scope:domains:domain_1:domain_management:read -// 129eab11-681b-4c62-89e4-7a9ce0eda29d:scope:domains:domain_1:groups:read:group_1 - -func parseKeyValueToScopeLegacy(kv map[string][]byte) (auth.Scope, error) { - scope := auth.Scope{ - Domains: make(map[string]auth.DomainScope), - } - for key, value := range kv { - if strings.Index(key, keySeparator+scopeKey+keySeparator) > 0 { - keyParts := strings.Split(key, keySeparator) - - platformEntityType, err := auth.ParsePlatformEntityType(keyParts[2]) - if err != nil { - return auth.Scope{}, errors.Wrap(repoerr.ErrViewEntity, err) - } - - switch platformEntityType { - case auth.PlatformUsersScope: - switch string(value) { - case string(entityValue): - if len(keyParts) != 5 { - return auth.Scope{}, fmt.Errorf("invalid scope key format: %s", key) - } - opType, err := auth.ParseOperationType(keyParts[len(keyParts)-2]) - if err != nil { - return auth.Scope{}, errors.Wrap(repoerr.ErrViewEntity, err) - } - entityID := keyParts[len(keyParts)-1] - if scope.Users.Operations == nil { - scope.Users.Operations = make(map[auth.OperationType]auth.ScopeValue) - } - - if _, oValueExists := scope.Users.Operations[opType]; !oValueExists { - scope.Users.Operations[opType] = &auth.SelectedIDs{} - } - oValue := scope.Users.Operations[opType] - if err := oValue.AddValues(entityID); err != nil { - return auth.Scope{}, fmt.Errorf("failed to add scope key %s with entity value %v : %w", key, entityID, err) - } - scope.Users.Operations[opType] = oValue - case string(anyIDValue): - if len(keyParts) != 4 { - return auth.Scope{}, fmt.Errorf("invalid scope key format: %s", key) - } - opType, err := auth.ParseOperationType(keyParts[len(keyParts)-1]) - if err != nil { - return auth.Scope{}, errors.Wrap(repoerr.ErrViewEntity, err) - } - - if scope.Users.Operations == nil { - scope.Users.Operations = make(map[auth.OperationType]auth.ScopeValue) - } - - if oValue, oValueExists := scope.Users.Operations[opType]; oValueExists && oValue != nil { - if _, ok := oValue.(*auth.AnyIDs); !ok { - return auth.Scope{}, fmt.Errorf("failed to add scope key %s with entity anyIDs scope value : key already initialized with different type", key) - } - } - scope.Users.Operations[opType] = &auth.AnyIDs{} - case string(selectedIDsValue): - if len(keyParts) != 4 { - return auth.Scope{}, fmt.Errorf("invalid scope key format: %s", key) - } - opType, err := auth.ParseOperationType(keyParts[len(keyParts)-1]) - if err != nil { - return auth.Scope{}, errors.Wrap(repoerr.ErrViewEntity, err) - } - - if scope.Users.Operations == nil { - scope.Users.Operations = make(map[auth.OperationType]auth.ScopeValue) - } - - oValue, oValueExists := scope.Users.Operations[opType] - if oValueExists && oValue != nil { - if _, ok := oValue.(*auth.SelectedIDs); !ok { - return auth.Scope{}, fmt.Errorf("failed to add scope key %s with entity selectedIDs scope value : key already initialized with different type", key) - } - } - if !oValueExists { - scope.Users.Operations[opType] = &auth.SelectedIDs{} - } - default: - return auth.Scope{}, fmt.Errorf("key %s have invalid value %v", key, value) - } - - case auth.PlatformDomainsScope: - if len(keyParts) < 6 { - return auth.Scope{}, fmt.Errorf("invalid scope key format: %s", key) - } - domainID := keyParts[3] - if scope.Domains == nil { - scope.Domains = make(map[string]auth.DomainScope) - } - if _, ok := scope.Domains[domainID]; !ok { - scope.Domains[domainID] = auth.DomainScope{} - } - domainScope := scope.Domains[domainID] - - entityType := keyParts[4] - - switch entityType { - case auth.DomainManagementScope.String(): - switch string(value) { - case string(entityValue): - if len(keyParts) != 7 { - return auth.Scope{}, fmt.Errorf("invalid scope key format: %s", key) - } - opType, err := auth.ParseOperationType(keyParts[len(keyParts)-2]) - if err != nil { - return auth.Scope{}, errors.Wrap(repoerr.ErrViewEntity, err) - } - entityID := keyParts[len(keyParts)-1] - - if domainScope.DomainManagement.Operations == nil { - domainScope.DomainManagement.Operations = make(map[auth.OperationType]auth.ScopeValue) - } - - if _, oValueExists := domainScope.DomainManagement.Operations[opType]; !oValueExists { - domainScope.DomainManagement.Operations[opType] = &auth.SelectedIDs{} - } - oValue := domainScope.DomainManagement.Operations[opType] - if err := oValue.AddValues(entityID); err != nil { - return auth.Scope{}, fmt.Errorf("failed to add scope key %s with entity value %v : %w", key, entityID, err) - } - domainScope.DomainManagement.Operations[opType] = oValue - case string(anyIDValue): - if len(keyParts) != 6 { - return auth.Scope{}, fmt.Errorf("invalid scope key format: %s", key) - } - opType, err := auth.ParseOperationType(keyParts[len(keyParts)-1]) - if err != nil { - return auth.Scope{}, errors.Wrap(repoerr.ErrViewEntity, err) - } - - if domainScope.DomainManagement.Operations == nil { - domainScope.DomainManagement.Operations = make(map[auth.OperationType]auth.ScopeValue) - } - - if oValue, oValueExists := domainScope.DomainManagement.Operations[opType]; oValueExists && oValue != nil { - if _, ok := oValue.(*auth.AnyIDs); !ok { - return auth.Scope{}, fmt.Errorf("failed to add scope key %s with entity anyIDs scope value : key already initialized with different type", key) - } - } - domainScope.DomainManagement.Operations[opType] = &auth.AnyIDs{} - case string(selectedIDsValue): - if len(keyParts) != 6 { - return auth.Scope{}, fmt.Errorf("invalid scope key format: %s", key) - } - opType, err := auth.ParseOperationType(keyParts[len(keyParts)-1]) - if err != nil { - return auth.Scope{}, errors.Wrap(repoerr.ErrViewEntity, err) - } - - if domainScope.DomainManagement.Operations == nil { - domainScope.DomainManagement.Operations = make(map[auth.OperationType]auth.ScopeValue) - } - - oValue, oValueExists := domainScope.DomainManagement.Operations[opType] - if oValueExists && oValue != nil { - if _, ok := oValue.(*auth.SelectedIDs); !ok { - return auth.Scope{}, fmt.Errorf("failed to add scope key %s with entity selectedIDs scope value : key already initialized with different type", key) - } - } - if !oValueExists { - domainScope.DomainManagement.Operations[opType] = &auth.SelectedIDs{} - } - default: - return auth.Scope{}, fmt.Errorf("key %s have invalid value %v", key, value) - } - default: - etype, err := auth.ParseDomainEntityType(entityType) - if err != nil { - return auth.Scope{}, fmt.Errorf("key %s invalid entity type %s : %w", key, entityType, err) - } - if domainScope.Entities == nil { - domainScope.Entities = make(map[auth.DomainEntityType]auth.OperationScope) - } - if _, ok := domainScope.Entities[etype]; !ok { - domainScope.Entities[etype] = auth.OperationScope{} - } - entityOperationScope := domainScope.Entities[etype] - switch string(value) { - case string(entityValue): - if len(keyParts) != 7 { - return auth.Scope{}, fmt.Errorf("invalid scope key format: %s", key) - } - opType, err := auth.ParseOperationType(keyParts[len(keyParts)-2]) - if err != nil { - return auth.Scope{}, errors.Wrap(repoerr.ErrViewEntity, err) - } - entityID := keyParts[len(keyParts)-1] - if entityOperationScope.Operations == nil { - entityOperationScope.Operations = make(map[auth.OperationType]auth.ScopeValue) - } - - if _, oValueExists := entityOperationScope.Operations[opType]; !oValueExists { - entityOperationScope.Operations[opType] = &auth.SelectedIDs{} - } - oValue := entityOperationScope.Operations[opType] - if err := oValue.AddValues(entityID); err != nil { - return auth.Scope{}, fmt.Errorf("failed to add scope key %s with entity value %v : %w", key, entityID, err) - } - entityOperationScope.Operations[opType] = oValue - case string(anyIDValue): - if len(keyParts) != 6 { - return auth.Scope{}, fmt.Errorf("invalid scope key format: %s", key) - } - opType, err := auth.ParseOperationType(keyParts[len(keyParts)-1]) - if err != nil { - return auth.Scope{}, errors.Wrap(repoerr.ErrViewEntity, err) - } - - if entityOperationScope.Operations == nil { - entityOperationScope.Operations = make(map[auth.OperationType]auth.ScopeValue) - } - - if oValue, oValueExists := entityOperationScope.Operations[opType]; oValueExists && oValue != nil { - if _, ok := oValue.(*auth.AnyIDs); !ok { - return auth.Scope{}, fmt.Errorf("failed to add scope key %s with entity anyIDs scope value : key already initialized with different type", key) - } - } - entityOperationScope.Operations[opType] = &auth.AnyIDs{} - case string(selectedIDsValue): - if len(keyParts) != 6 { - return auth.Scope{}, fmt.Errorf("invalid scope key format: %s", key) - } - opType, err := auth.ParseOperationType(keyParts[len(keyParts)-1]) - if err != nil { - return auth.Scope{}, errors.Wrap(repoerr.ErrViewEntity, err) - } - - if entityOperationScope.Operations == nil { - entityOperationScope.Operations = make(map[auth.OperationType]auth.ScopeValue) - } - - oValue, oValueExists := entityOperationScope.Operations[opType] - if oValueExists && oValue != nil { - if _, ok := oValue.(*auth.SelectedIDs); !ok { - return auth.Scope{}, fmt.Errorf("failed to add scope key %s with entity selectedIDs scope value : key already initialized with different type", key) - } - } - if !oValueExists { - entityOperationScope.Operations[opType] = &auth.SelectedIDs{} - } - default: - return auth.Scope{}, fmt.Errorf("key %s have invalid value %v", key, value) - } - - domainScope.Entities[etype] = entityOperationScope - } - - scope.Domains[domainID] = domainScope - default: - return auth.Scope{}, errors.Wrap(repoerr.ErrViewEntity, fmt.Errorf("invalid platform entity type : %s", platformEntityType.String())) - } - } - } - return scope, nil -} - func parseKeyValueToScope(kv map[string][]byte) (auth.Scope, error) { scope := auth.Scope{ Domains: make(map[string]auth.DomainScope), @@ -942,30 +677,6 @@ func validateOperation(platformEntityType auth.PlatformEntityType, opScope auth. return nil } -func getString(b *bolt.Bucket, patID, key string) string { - value := b.Get([]byte(patID + keySeparator + key)) - if value != nil { - return string(value) - } - return "" -} - -func getTime(b *bolt.Bucket, patID, key string) time.Time { - value := b.Get([]byte(patID + keySeparator + key)) - if value != nil { - return bytesToTime(value) - } - return time.Time{} -} - -func getBool(b *bolt.Bucket, patID, key string) bool { - value := b.Get([]byte(patID + keySeparator + key)) - if value != nil { - return bytesToBoolean(value) - } - return false -} - func timeToBytes(t time.Time) []byte { timeBytes := make([]byte, 8) binary.BigEndian.PutUint64(timeBytes, uint64(t.Nanosecond())) diff --git a/auth/pat.go b/auth/pat.go index c8fca89022..1ea4ee09cd 100644 --- a/auth/pat.go +++ b/auth/pat.go @@ -25,18 +25,26 @@ const ( DeleteOp ) +const ( + createOpStr = "create" + readOpStr = "read" + listOpStr = "list" + updateOpStr = "update" + deleteOpStr = "delete" +) + func (ot OperationType) ValidString() (string, error) { switch ot { case CreateOp: - return "create", nil + return createOpStr, nil case ReadOp: - return "read", nil + return readOpStr, nil case ListOp: - return "list", nil + return listOpStr, nil case UpdateOp: - return "update", nil + return updateOpStr, nil case DeleteOp: - return "delete", nil + return deleteOpStr, nil default: return "", fmt.Errorf("unknown operation type %d", ot) } @@ -45,15 +53,15 @@ func (ot OperationType) ValidString() (string, error) { func (ot OperationType) String() string { switch ot { case CreateOp: - return "create" + return createOpStr case ReadOp: - return "read" + return readOpStr case ListOp: - return "list" + return listOpStr case UpdateOp: - return "update" + return updateOpStr case DeleteOp: - return "delete" + return deleteOpStr default: return fmt.Sprintf("unknown operation type %d", ot) } @@ -61,15 +69,15 @@ func (ot OperationType) String() string { func ParseOperationType(ot string) (OperationType, error) { switch ot { - case "create": + case createOpStr: return CreateOp, nil - case "read": + case readOpStr: return ReadOp, nil - case "list": + case listOpStr: return ListOp, nil - case "update": + case updateOpStr: return UpdateOp, nil - case "delete": + case deleteOpStr: return DeleteOp, nil default: return 0, fmt.Errorf("unknown operation type %s", ot) @@ -100,16 +108,23 @@ const ( DomainNullScope ) +const ( + domainManagementScopeStr = "domain_management" + domainGroupsScopeStr = "groups" + domainChannelsScopeStr = "channels" + domainThingsScopeStr = "things" +) + func (det DomainEntityType) ValidString() (string, error) { switch det { case DomainManagementScope: - return "domain_management", nil + return domainManagementScopeStr, nil case DomainGroupsScope: - return "groups", nil + return domainGroupsScopeStr, nil case DomainChannelsScope: - return "channels", nil + return domainChannelsScopeStr, nil case DomainThingsScope: - return "things", nil + return domainThingsScopeStr, nil default: return "", fmt.Errorf("unknown domain entity type %d", det) } @@ -118,13 +133,13 @@ func (det DomainEntityType) ValidString() (string, error) { func (det DomainEntityType) String() string { switch det { case DomainManagementScope: - return "domain_management" + return domainManagementScopeStr case DomainGroupsScope: - return "groups" + return domainGroupsScopeStr case DomainChannelsScope: - return "channels" + return domainChannelsScopeStr case DomainThingsScope: - return "things" + return domainThingsScopeStr default: return fmt.Sprintf("unknown domain entity type %d", det) } @@ -132,13 +147,13 @@ func (det DomainEntityType) String() string { func ParseDomainEntityType(det string) (DomainEntityType, error) { switch det { - case "domain_management": + case domainManagementScopeStr: return DomainManagementScope, nil - case "groups": + case domainGroupsScopeStr: return DomainGroupsScope, nil - case "channels": + case domainChannelsScopeStr: return DomainChannelsScope, nil - case "things": + case domainThingsScopeStr: return DomainThingsScope, nil default: return 0, fmt.Errorf("unknown domain entity type %s", det) diff --git a/auth/service.go b/auth/service.go index 7790c46f97..070ddbae8a 100644 --- a/auth/service.go +++ b/auth/service.go @@ -77,8 +77,6 @@ var ( errRetrievePAT = errors.New("failed to retrieve PAT") errDeletePAT = errors.New("failed to delete PAT") errRevokePAT = errors.New("failed to revoke PAT") - errAddScope = errors.New("failed to add entry in scope") - errRemoveScope = errors.New("failed to remove entry in scope") errClearAllScope = errors.New("failed to clear all entry in scope") ) From a900094b674b1b815b5ddc18d8c5a6f73c752829 Mon Sep 17 00:00:00 2001 From: Arvindh Date: Wed, 10 Jul 2024 19:45:35 +0530 Subject: [PATCH 20/24] fix lint errors Signed-off-by: Arvindh --- auth/pat.go | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/auth/pat.go b/auth/pat.go index 1ea4ee09cd..48a405624b 100644 --- a/auth/pat.go +++ b/auth/pat.go @@ -181,12 +181,17 @@ const ( PlatformDomainsScope ) +const ( + platformUsersScopeStr = "users" + platformDomainsScopeStr = "domains" +) + func (pet PlatformEntityType) ValidString() (string, error) { switch pet { case PlatformUsersScope: - return "users", nil + return platformUsersScopeStr, nil case PlatformDomainsScope: - return "domains", nil + return platformDomainsScopeStr, nil default: return "", fmt.Errorf("unknown platform entity type %d", pet) } @@ -195,9 +200,9 @@ func (pet PlatformEntityType) ValidString() (string, error) { func (pet PlatformEntityType) String() string { switch pet { case PlatformUsersScope: - return "users" + return platformUsersScopeStr case PlatformDomainsScope: - return "domains" + return platformDomainsScopeStr default: return fmt.Sprintf("unknown platform entity type %d", pet) } @@ -205,9 +210,9 @@ func (pet PlatformEntityType) String() string { func ParsePlatformEntityType(pet string) (PlatformEntityType, error) { switch pet { - case "users": + case platformUsersScopeStr: return PlatformUsersScope, nil - case "domains": + case platformDomainsScopeStr: return PlatformDomainsScope, nil default: return 0, fmt.Errorf("unknown platform entity type %s", pet) From f811a66422adb065e8f020523903f75422403fe7 Mon Sep 17 00:00:00 2001 From: Arvindh Date: Wed, 10 Jul 2024 19:47:31 +0530 Subject: [PATCH 21/24] go mod tidy Signed-off-by: Arvindh --- go.mod | 10 +--------- go.sum | 44 -------------------------------------------- 2 files changed, 1 insertion(+), 53 deletions(-) diff --git a/go.mod b/go.mod index 404376c584..165f34793e 100644 --- a/go.mod +++ b/go.mod @@ -11,7 +11,6 @@ require ( github.com/authzed/grpcutil v0.0.0-20240123194739-2ea1e3d2d98b github.com/caarlos0/env/v10 v10.0.0 github.com/cenkalti/backoff/v4 v4.3.0 - github.com/dgraph-io/badger/v4 v4.2.0 github.com/eclipse/paho.mqtt.golang v1.4.3 github.com/fatih/color v1.17.0 github.com/fiorix/go-smpp v0.0.0-20210403173735-2894b96e70ba @@ -20,7 +19,6 @@ require ( github.com/go-redis/redis/v8 v8.11.5 github.com/gocql/gocql v1.6.0 github.com/gofrs/uuid v4.4.0+incompatible - github.com/google/uuid v1.6.0 github.com/gookit/color v1.5.4 github.com/gopcua/opcua v0.1.6 github.com/gorilla/websocket v1.5.3 @@ -83,14 +81,12 @@ require ( github.com/containerd/continuity v0.4.3 // indirect github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/decred/dcrd/dcrec/secp256k1/v4 v4.3.0 // indirect - github.com/dgraph-io/ristretto v0.1.1 // indirect github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect github.com/docker/cli v26.0.0+incompatible // indirect github.com/docker/docker v26.0.2+incompatible // indirect github.com/docker/go-connections v0.5.0 // indirect github.com/docker/go-units v0.5.0 // indirect github.com/dsnet/golib/memfile v1.0.0 // indirect - github.com/dustin/go-humanize v1.0.0 // indirect github.com/envoyproxy/protoc-gen-validate v1.0.4 // indirect github.com/felixge/httpsnoop v1.0.4 // indirect github.com/fsnotify/fsnotify v1.7.0 // indirect @@ -103,12 +99,9 @@ require ( github.com/go-logr/stdr v1.2.2 // indirect github.com/goccy/go-json v0.10.2 // indirect github.com/gogo/protobuf v1.3.2 // indirect - github.com/golang/glog v1.2.1 // indirect - github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect - github.com/golang/protobuf v1.5.4 // indirect github.com/golang/snappy v0.0.4 // indirect - github.com/google/flatbuffers v1.12.1 // indirect github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect + github.com/google/uuid v1.6.0 // indirect github.com/grpc-ecosystem/go-grpc-middleware v1.4.0 // indirect github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0 // indirect github.com/hailocab/go-hostpool v0.0.0-20160125115350-e80d13ce29ed // indirect @@ -181,7 +174,6 @@ require ( github.com/xeipuuv/gojsonschema v1.2.0 // indirect github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect github.com/youmark/pkcs8 v0.0.0-20201027041543-1326539a0a0a // indirect - go.opencensus.io v0.24.0 // indirect go.opentelemetry.io/otel/metric v1.28.0 // indirect go.opentelemetry.io/proto/otlp v1.2.0 // indirect go.uber.org/atomic v1.11.0 // indirect diff --git a/go.sum b/go.sum index 5d2afbc245..d3828dc2b2 100644 --- a/go.sum +++ b/go.sum @@ -50,7 +50,6 @@ github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyY 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.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= @@ -72,12 +71,6 @@ github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1 github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/decred/dcrd/dcrec/secp256k1/v4 v4.3.0 h1:rpfIENRNNilwHwZeG5+P150SMrnNEcHYvcCuK6dPZSg= github.com/decred/dcrd/dcrec/secp256k1/v4 v4.3.0/go.mod h1:v57UDF4pDQJcEfFUCRop3lJL149eHGSe9Jvczhzjo/0= -github.com/dgraph-io/badger/v4 v4.2.0 h1:kJrlajbXXL9DFTNuhhu9yCx7JJa4qpYWxtE8BzuWsEs= -github.com/dgraph-io/badger/v4 v4.2.0/go.mod h1:qfCqhPoWDFJRx1gp5QwwyGo8xk1lbHUxvK9nK0OGAak= -github.com/dgraph-io/ristretto v0.1.1 h1:6CWw5tJNgpegArSHpNHJKldNeq03FQCwYvfMVWajOK8= -github.com/dgraph-io/ristretto v0.1.1/go.mod h1:S1GPSBCYCIhmVNfcth17y2zZtQT6wzkzgwUve0VDWWA= -github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2 h1:tdlZCpZ/P9DhczCTSixgIKmwPv6+wP5DGjqLYw5SUiA= -github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw= 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 v26.0.0+incompatible h1:90BKrx1a1HKYpSnnBFR6AgDq/FqkHxwlUyzJVPxD30I= @@ -90,8 +83,6 @@ github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4 github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= 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/dustin/go-humanize v1.0.0 h1:VSnTsYCnlFHaM2/igO1h6X3HA71jcobQuxemgkq4zYo= -github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= 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= @@ -150,41 +141,21 @@ github.com/gofrs/uuid v4.4.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRx 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.2.1 h1:OptwRhECazUx5ix5TTWC3EZhsZEHWcYWY4FQHTIubm4= -github.com/golang/glog v1.2.1/go.mod h1:6AhwSGph0fcJtXVM/PEHPqZlFeoLxhs7/t5UDAwmO+w= -github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE= -github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= -github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= -github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= -github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= -github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= -github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= -github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= -github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= 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/google/flatbuffers v1.12.1 h1:MVlul7pQNoDzWRLTw5imwYsl+usrS1TXG2H4jg6ImGw= -github.com/google/flatbuffers v1.12.1/go.mod h1:1AeVuKshWv4vARoZatz6mlQ0JxURH0Kv5+zNeJKJCa8= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= -github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= -github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= -github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= 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.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/gookit/color v1.5.4 h1:FZmqs7XOyGgCAxmWyPslpiok1k05wmY3SJTytgvYFs0= @@ -515,8 +486,6 @@ go.etcd.io/bbolt v1.3.10 h1:+BqfJTcCzTItrop8mq/lbzL8wSGtj94UO/3U31shqG0= go.etcd.io/bbolt v1.3.10/go.mod h1:bK3UQLPJZly7IlNmV7uVHJDxfe5aK9Ll93e/74Y9oEQ= go.mongodb.org/mongo-driver v1.15.0 h1:rJCKC8eEliewXjZGf0ddURtl7tTVy1TK3bfl0gkUSLc= go.mongodb.org/mongo-driver v1.15.0/go.mod h1:Vzb0Mk/pa7e6cWw85R4F/endUC3u0U9jGcNU603k65c= -go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0= -go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo= go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.53.0 h1:9G6E0TXzGFVfTnawRzrPl83iHOAV7L8NJiR8RSGYV1g= go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.53.0/go.mod h1:azvtTADFQJA8mX80jIH/akaE7h+dbm/sVuaHqN13w74= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.53.0 h1:4K4tsIXefpVJtvA/8srF4V4y0akAoPHkIslgAkjixJA= @@ -597,7 +566,6 @@ golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLL golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= 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= @@ -641,7 +609,6 @@ golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20221010170243-090e33056c14/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 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.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -703,7 +670,6 @@ google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7 google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= google.golang.org/genproto v0.0.0-20200423170343-7949de9c1215/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= google.golang.org/genproto/googleapis/api v0.0.0-20240528184218-531527333157 h1:7whR9kGa5LUwFtpLm2ArCEejtnxlGeLbAyjFY8sGNFw= google.golang.org/genproto/googleapis/api v0.0.0-20240528184218-531527333157/go.mod h1:99sLkeliLXfdj2J75X3Ho+rrVCaJze0uwN7zDDkjPVU= google.golang.org/genproto/googleapis/rpc v0.0.0-20240701130421-f6361c86f094 h1:BwIjyKYGsK9dMCBOorzRri8MQwmi7mT9rGHsCEinZkA= @@ -713,18 +679,8 @@ google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyac google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= -google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= google.golang.org/grpc v1.65.0 h1:bs/cUb4lp1G5iImFFd3u5ixQzweKizoZJAwBNLR42lc= google.golang.org/grpc v1.65.0/go.mod h1:WgYC2ypjlB0EiQi6wdKixMqukr6lBc0Vo+oOgjrM5ZQ= -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= -google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= -google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= -google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg= google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw= gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc h1:2gGKlE2+asNV9m7xrywl36YYNnBG5ZQ0r/BOOxqPpmk= From 9d4f0e3dbc79f665e21c66aed992562161b11361 Mon Sep 17 00:00:00 2001 From: Arvindh Date: Wed, 10 Jul 2024 23:18:18 +0530 Subject: [PATCH 22/24] fix compose and list pats Signed-off-by: Arvindh --- auth/bolt/pat.go | 33 ++++++++++++++++++++------------- cmd/auth/main.go | 11 ++++++++--- docker/docker-compose.yml | 2 ++ internal/clients/bolt/bolt.go | 12 +++++++----- 4 files changed, 37 insertions(+), 21 deletions(-) diff --git a/auth/bolt/pat.go b/auth/bolt/pat.go index 9c8e8f2eed..9c7d2ea247 100644 --- a/auth/bolt/pat.go +++ b/auth/bolt/pat.go @@ -64,7 +64,11 @@ func (pr *patRepo) Save(ctx context.Context, pat auth.PAT) error { return err } return pr.db.Update(func(tx *bolt.Tx) error { - b, err := pr.createRetrieveUserBucket(tx, pat.User) + rootBucket, err := pr.retrieveRootBucket(tx) + if err != nil { + return errors.Wrap(repoerr.ErrCreateEntity, err) + } + b, err := pr.createUserBucket(rootBucket, pat.User) if err != nil { return errors.Wrap(repoerr.ErrCreateEntity, err) } @@ -74,7 +78,7 @@ func (pr *patRepo) Save(ctx context.Context, pat auth.PAT) error { return errors.Wrap(repoerr.ErrCreateEntity, err) } } - if err := b.Put([]byte(pat.User+keySeparator+patKey+pat.ID), []byte(pat.ID)); err != nil { + if err := rootBucket.Put([]byte(pat.User+keySeparator+patKey+keySeparator+pat.ID), []byte(pat.ID)); err != nil { return errors.Wrap(repoerr.ErrCreateEntity, err) } return nil @@ -139,7 +143,7 @@ func (pr *patRepo) RetrieveAll(ctx context.Context, userID string, pm auth.PATSP patIDs := []string{} if err := pr.db.View(func(tx *bolt.Tx) error { - b, err := pr.retrieveUserBucket(tx, userID) + b, err := pr.retrieveRootBucket(tx) if err != nil { return errors.Wrap(repoerr.ErrViewEntity, err) } @@ -175,7 +179,7 @@ func (pr *patRepo) RetrieveAll(ctx context.Context, userID string, pm auth.PATSP } for i := pm.Offset; i < aLimit; i++ { - if total < int(i) { + if int(i) < total { pat, err := pr.Retrieve(ctx, userID, patIDs[i]) if err != nil { return patsPage, err @@ -319,12 +323,7 @@ func (pr *patRepo) RemoveAllScopeEntry(ctx context.Context, userID, patID string return nil } -func (pr *patRepo) createRetrieveUserBucket(tx *bolt.Tx, userID string) (*bolt.Bucket, error) { - rootBucket := tx.Bucket([]byte(pr.bucketName)) - if rootBucket == nil { - return nil, errors.Wrap(repoerr.ErrCreateEntity, fmt.Errorf("bucket %s not found", pr.bucketName)) - } - +func (pr *patRepo) createUserBucket(rootBucket *bolt.Bucket, userID string) (*bolt.Bucket, error) { userBucket, err := rootBucket.CreateBucketIfNotExists([]byte(userID)) if err != nil { return nil, errors.Wrap(repoerr.ErrCreateEntity, fmt.Errorf("failed to retrieve or create bucket for user %s : %w", userID, err)) @@ -334,9 +333,9 @@ func (pr *patRepo) createRetrieveUserBucket(tx *bolt.Tx, userID string) (*bolt.B } func (pr *patRepo) retrieveUserBucket(tx *bolt.Tx, userID string) (*bolt.Bucket, error) { - rootBucket := tx.Bucket([]byte(pr.bucketName)) - if rootBucket == nil { - return nil, fmt.Errorf("bucket %s not found", pr.bucketName) + rootBucket, err := pr.retrieveRootBucket(tx) + if err != nil { + return nil, err } userBucket := rootBucket.Bucket([]byte(userID)) @@ -346,6 +345,14 @@ func (pr *patRepo) retrieveUserBucket(tx *bolt.Tx, userID string) (*bolt.Bucket, return userBucket, nil } +func (pr *patRepo) retrieveRootBucket(tx *bolt.Tx) (*bolt.Bucket, error) { + rootBucket := tx.Bucket([]byte(pr.bucketName)) + if rootBucket == nil { + return nil, fmt.Errorf("bucket %s not found", pr.bucketName) + } + return rootBucket, nil +} + func (pr *patRepo) updatePATField(_ context.Context, userID, patID, key string, value []byte) (auth.PAT, error) { prefix := []byte(patID + keySeparator) kv := map[string][]byte{} diff --git a/cmd/auth/main.go b/cmd/auth/main.go index f53f41d93c..5c44e0e54e 100644 --- a/cmd/auth/main.go +++ b/cmd/auth/main.go @@ -51,6 +51,7 @@ const ( envPrefixHTTP = "MG_AUTH_HTTP_" envPrefixGrpc = "MG_AUTH_GRPC_" envPrefixDB = "MG_AUTH_DB_" + envPrefixPATDB = "MG_AUTH_PAT_DB_" defDB = "auth" defSvcHTTPPort = "8189" defSvcGRPCPort = "8181" @@ -132,13 +133,17 @@ func main() { } boltDBConfig := boltclient.Config{} - if err := env.ParseWithOptions(&boltDBConfig, env.Options{}); err != nil { - panic(err) + if err := env.ParseWithOptions(&boltDBConfig, env.Options{Prefix: envPrefixPATDB}); err != nil { + logger.Error(fmt.Sprintf("failed to parse bolt db config : %s\n", err.Error())) + exitCode = 1 + return } client, err := boltclient.Connect(boltDBConfig, bolt.Init) if err != nil { - panic(err) + logger.Error(fmt.Sprintf("failed to connect to bolt db : %s\n", err.Error())) + exitCode = 1 + return } defer client.Close() diff --git a/docker/docker-compose.yml b/docker/docker-compose.yml index c206e7529c..ab333e37da 100644 --- a/docker/docker-compose.yml +++ b/docker/docker-compose.yml @@ -15,6 +15,7 @@ volumes: magistrala-mqtt-broker-volume: magistrala-spicedb-db-volume: magistrala-auth-db-volume: + magistrala-pat-db-volume: magistrala-invitations-db-volume: magistrala-ui-db-volume: @@ -132,6 +133,7 @@ services: - magistrala-base-net volumes: - ./spicedb/schema.zed:${MG_SPICEDB_SCHEMA_FILE} + - magistrala-pat-db-volume:/magistrala-data # Auth gRPC mTLS server certificates - type: bind source: ${MG_AUTH_GRPC_SERVER_CERT:-ssl/certs/dummy/server_cert} diff --git a/internal/clients/bolt/bolt.go b/internal/clients/bolt/bolt.go index 12e22efadf..6db2d1a276 100644 --- a/internal/clients/bolt/bolt.go +++ b/internal/clients/bolt/bolt.go @@ -32,10 +32,11 @@ func (fm *FileMode) UnmarshalText(text []byte) error { // Config contains BoltDB specific parameters. type Config struct { - FilePath string `env:"FILE_PATH" envDefault:"./bolt.db"` - FileMode FileMode `env:"FILE_MODE" envDefault:"0600"` - Bucket string `env:"BUCKET" envDefault:"magistrala"` - Timeout time.Duration `env:"TIMEOUT" envDefault:"0"` + FileDirPath string `env:"FILE_DIR_PATH" envDefault:"./magistrala-data"` + FileName string `env:"FILE_NAME" envDefault:"magistrala-pat.db"` + FileMode FileMode `env:"FILE_MODE" envDefault:"0600"` + Bucket string `env:"BUCKET" envDefault:"magistrala"` + Timeout time.Duration `env:"TIMEOUT" envDefault:"0"` } // Setup load configuration from environment and creates new BoltDB. @@ -59,7 +60,8 @@ func SetupDB(envPrefix string, initFn func(*bolt.Tx, string) error) (*bolt.DB, e // Connect establishes connection to the BoltDB. func Connect(cfg Config, initFn func(*bolt.Tx, string) error) (*bolt.DB, error) { - db, err := bolt.Open(cfg.FilePath, fs.FileMode(cfg.FileMode), nil) + filePath := cfg.FileDirPath + "/" + cfg.FileName + db, err := bolt.Open(filePath, fs.FileMode(cfg.FileMode), nil) if err != nil { return nil, errors.Wrap(errConnect, err) } From b6b762ae21aae7310b0e67b48de92fa3b18d3f0e Mon Sep 17 00:00:00 2001 From: Arvindh Date: Fri, 12 Jul 2024 20:27:39 +0530 Subject: [PATCH 23/24] add http api endpoints Signed-off-by: Arvindh --- auth/api/http/keys/endpoint_test.go | 3 +- auth/api/http/pats/endpoint.go | 8 +- auth/api/http/pats/requests.go | 36 +++--- auth/api/http/pats/responses.go | 10 +- auth/api/http/pats/transport.go | 18 +-- auth/api/logging.go | 41 ++++--- auth/api/metrics.go | 20 +-- auth/bolt/pat.go | 182 +++++++++++++++++++--------- auth/events/streams.go | 12 +- auth/hasher.go | 17 +++ auth/hasher/doc.go | 6 + auth/hasher/hasher.go | 86 +++++++++++++ auth/mocks/hasher.go | 72 +++++++++++ auth/mocks/pats.go | 77 ++++++------ auth/mocks/patsrepo.go | 53 ++++++++ auth/mocks/service.go | 83 ++++++------- auth/pat.go | 144 +++++++++++----------- auth/service.go | 136 ++++++++++++++++++--- auth/service_test.go | 4 +- auth/tracing/tracing.go | 32 +++-- cmd/auth/main.go | 16 +-- 21 files changed, 732 insertions(+), 324 deletions(-) create mode 100644 auth/hasher.go create mode 100644 auth/hasher/doc.go create mode 100644 auth/hasher/hasher.go create mode 100644 auth/mocks/hasher.go diff --git a/auth/api/http/keys/endpoint_test.go b/auth/api/http/keys/endpoint_test.go index fd31e58588..64f5e872c9 100644 --- a/auth/api/http/keys/endpoint_test.go +++ b/auth/api/http/keys/endpoint_test.go @@ -71,11 +71,12 @@ func newService() (auth.Service, *mocks.KeyRepository) { prepo := new(mocks.PolicyAgent) drepo := new(mocks.DomainsRepository) patsRepo := new(mocks.PATSRepository) + hasher := new(mocks.Hasher) idProvider := uuid.NewMock() t := jwt.New([]byte(secret)) - return auth.New(krepo, drepo, patsRepo, idProvider, t, prepo, loginDuration, refreshDuration, invalidDuration), krepo + return auth.New(krepo, drepo, patsRepo, hasher, idProvider, t, prepo, loginDuration, refreshDuration, invalidDuration), krepo } func newServer(svc auth.Service) *httptest.Server { diff --git a/auth/api/http/pats/endpoint.go b/auth/api/http/pats/endpoint.go index 36b7ac1b75..9e83bf1581 100644 --- a/auth/api/http/pats/endpoint.go +++ b/auth/api/http/pats/endpoint.go @@ -186,17 +186,17 @@ func clearPATAllScopeEntryEndpoint(svc auth.Service) endpoint.Endpoint { } } -func testCheckPATScopeEntryEndpoint(svc auth.Service) endpoint.Endpoint { +func authorizePATEndpoint(svc auth.Service) endpoint.Endpoint { return func(ctx context.Context, request interface{}) (interface{}, error) { - req := request.(testCheckPatScopeReq) + req := request.(authorizePATReq) if err := req.validate(); err != nil { return nil, err } - if err := svc.TestCheckPATScopeEntry(ctx, req.token, req.PlatformEntityType, req.OptionalDomainID, req.OptionalDomainEntityType, req.Operation, req.EntityIDs...); err != nil { + if err := svc.AuthorizePAT(ctx, req.token, req.PlatformEntityType, req.OptionalDomainID, req.OptionalDomainEntityType, req.Operation, req.EntityIDs...); err != nil { return nil, err } - return testCheckPatScopeRes{}, nil + return authorizePATRes{}, nil } } diff --git a/auth/api/http/pats/requests.go b/auth/api/http/pats/requests.go index 77678d5bb2..61584021fd 100644 --- a/auth/api/http/pats/requests.go +++ b/auth/api/http/pats/requests.go @@ -302,7 +302,7 @@ func (req clearAllScopeEntryReq) validate() (err error) { return nil } -type testCheckPatScopeReq struct { +type authorizePATReq struct { token string PlatformEntityType auth.PlatformEntityType `json:"platform_entity_type,omitempty"` OptionalDomainID string `json:"optional_domain_id,omitempty"` @@ -311,7 +311,7 @@ type testCheckPatScopeReq struct { EntityIDs []string `json:"entity_ids,omitempty"` } -func (tcpsr *testCheckPatScopeReq) UnmarshalJSON(data []byte) error { +func (tcpsr *authorizePATReq) UnmarshalJSON(data []byte) error { var temp struct { PlatformEntityType string `json:"platform_entity_type,omitempty"` OptionalDomainID string `json:"optional_domain_id,omitempty"` @@ -324,27 +324,35 @@ func (tcpsr *testCheckPatScopeReq) UnmarshalJSON(data []byte) error { return err } + tcpsr.OptionalDomainID = temp.OptionalDomainID + tcpsr.EntityIDs = temp.EntityIDs + pet, err := auth.ParsePlatformEntityType(temp.PlatformEntityType) if err != nil { return err } - odt, err := auth.ParseDomainEntityType(temp.OptionalDomainEntityType) - if err != nil { - return err + tcpsr.PlatformEntityType = pet + + if temp.OptionalDomainEntityType != "" { + odt, err := auth.ParseDomainEntityType(temp.OptionalDomainEntityType) + if err != nil { + return err + } + tcpsr.OptionalDomainEntityType = odt } - op, err := auth.ParseOperationType(temp.Operation) - if err != nil { - return err + + if temp.OptionalDomainID != "" { + op, err := auth.ParseOperationType(temp.Operation) + if err != nil { + return err + } + tcpsr.Operation = op } - tcpsr.PlatformEntityType = pet - tcpsr.OptionalDomainID = temp.OptionalDomainID - tcpsr.OptionalDomainEntityType = odt - tcpsr.Operation = op - tcpsr.EntityIDs = temp.EntityIDs + return nil } -func (req testCheckPatScopeReq) validate() (err error) { +func (req authorizePATReq) validate() (err error) { if req.token == "" { return apiutil.ErrBearerToken } diff --git a/auth/api/http/pats/responses.go b/auth/api/http/pats/responses.go index 2f9020fc0f..01a18722d2 100644 --- a/auth/api/http/pats/responses.go +++ b/auth/api/http/pats/responses.go @@ -193,16 +193,16 @@ func (res clearAllScopeEntryRes) Empty() bool { return true } -type testCheckPatScopeRes struct{} +type authorizePATRes struct{} -func (res testCheckPatScopeRes) Code() int { - return http.StatusOK +func (res authorizePATRes) Code() int { + return http.StatusNoContent } -func (res testCheckPatScopeRes) Headers() map[string]string { +func (res authorizePATRes) Headers() map[string]string { return map[string]string{} } -func (res testCheckPatScopeRes) Empty() bool { +func (res authorizePATRes) Empty() bool { return true } diff --git a/auth/api/http/pats/transport.go b/auth/api/http/pats/transport.go index 4cf8691f90..0c7dda9cc3 100644 --- a/auth/api/http/pats/transport.go +++ b/auth/api/http/pats/transport.go @@ -106,9 +106,9 @@ func MakeHandler(svc auth.Service, mux *chi.Mux, logger *slog.Logger) *chi.Mux { opts..., ).ServeHTTP) - r.Get("/check", kithttp.NewServer( - (testCheckPATScopeEntryEndpoint(svc)), - decodeTestCheckPATScopeEntryRequest, + r.Post("/authorize", kithttp.NewServer( + (authorizePATEndpoint(svc)), + decodeAuthorizePATRequest, api.EncodeResponse, opts..., ).ServeHTTP) @@ -184,10 +184,6 @@ func decodeListPATSRequest(_ context.Context, r *http.Request) (interface{}, err } func decodeDeletePATRequest(_ context.Context, r *http.Request) (interface{}, error) { - if !strings.Contains(r.Header.Get("Content-Type"), contentType) { - return nil, apiutil.ErrUnsupportedContentType - } - return deletePatReq{ token: apiutil.ExtractBearerToken(r), id: chi.URLParam(r, "id"), @@ -210,10 +206,6 @@ func decodeResetPATSecretRequest(_ context.Context, r *http.Request) (interface{ } func decodeRevokePATSecretRequest(_ context.Context, r *http.Request) (interface{}, error) { - if !strings.Contains(r.Header.Get("Content-Type"), contentType) { - return nil, apiutil.ErrUnsupportedContentType - } - return revokePatSecretReq{ token: apiutil.ExtractBearerToken(r), id: chi.URLParam(r, "id"), @@ -261,12 +253,12 @@ func decodeClearPATAllScopeEntryRequest(_ context.Context, r *http.Request) (int }, nil } -func decodeTestCheckPATScopeEntryRequest(_ context.Context, r *http.Request) (interface{}, error) { +func decodeAuthorizePATRequest(_ context.Context, r *http.Request) (interface{}, error) { if !strings.Contains(r.Header.Get("Content-Type"), contentType) { return nil, apiutil.ErrUnsupportedContentType } - req := testCheckPatScopeReq{token: apiutil.ExtractBearerToken(r)} + req := authorizePATReq{token: apiutil.ExtractBearerToken(r)} if err := json.NewDecoder(r.Body).Decode(&req); err != nil { return nil, errors.Wrap(errors.ErrMalformedEntity, err) } diff --git a/auth/api/logging.go b/auth/api/logging.go index da2c320405..046bc778b7 100644 --- a/auth/api/logging.go +++ b/auth/api/logging.go @@ -701,52 +701,59 @@ func (lm *loggingMiddleware) ClearPATAllScopeEntry(ctx context.Context, token, p return lm.svc.ClearPATAllScopeEntry(ctx, token, patID) } -func (lm *loggingMiddleware) TestCheckPATScopeEntry(ctx context.Context, paToken string, platformEntityType auth.PlatformEntityType, optionalDomainID string, optionalDomainEntityType auth.DomainEntityType, operation auth.OperationType, entityIDs ...string) (err error) { +func (lm *loggingMiddleware) IdentifyPAT(ctx context.Context, paToken string) (pa auth.PAT, err error) { defer func(begin time.Time) { args := []any{ slog.String("duration", time.Since(begin).String()), - slog.String("platform_entity_type", platformEntityType.String()), - slog.String("optional_domain_id", optionalDomainID), - slog.String("optional_domain_entity_type", optionalDomainEntityType.String()), - slog.String("operation", operation.String()), - slog.Any("entities", entityIDs), } if err != nil { args = append(args, slog.Any("error", err)) - lm.logger.Warn("Test Check entry in PAT scope failed complete successfully", args...) + lm.logger.Warn("Identify PAT failed to complete successfully", args...) return } - lm.logger.Info("Test Check entry in PAT scope completed successfully", args...) + lm.logger.Info("Identify PAT completed successfully", args...) }(time.Now()) - return lm.svc.TestCheckPATScopeEntry(ctx, paToken, platformEntityType, optionalDomainID, optionalDomainEntityType, operation, entityIDs...) + return lm.svc.IdentifyPAT(ctx, paToken) } -func (lm *loggingMiddleware) IdentifyPAT(ctx context.Context, paToken string) (pa auth.PAT, err error) { +func (lm *loggingMiddleware) AuthorizePAT(ctx context.Context, paToken string, platformEntityType auth.PlatformEntityType, optionalDomainID string, optionalDomainEntityType auth.DomainEntityType, operation auth.OperationType, entityIDs ...string) (err error) { defer func(begin time.Time) { args := []any{ slog.String("duration", time.Since(begin).String()), + slog.String("platform_entity_type", platformEntityType.String()), + slog.String("optional_domain_id", optionalDomainID), + slog.String("optional_domain_entity_type", optionalDomainEntityType.String()), + slog.String("operation", operation.String()), + slog.Any("entities", entityIDs), } if err != nil { args = append(args, slog.Any("error", err)) - lm.logger.Warn("Identify PAT failed to complete successfully", args...) + lm.logger.Warn("Authorize PAT failed complete successfully", args...) return } - lm.logger.Info("Identify PAT completed successfully", args...) + lm.logger.Info("Authorize PAT completed successfully", args...) }(time.Now()) - return lm.svc.IdentifyPAT(ctx, paToken) + return lm.svc.AuthorizePAT(ctx, paToken, platformEntityType, optionalDomainID, optionalDomainEntityType, operation, entityIDs...) } -func (lm *loggingMiddleware) AuthorizePAT(ctx context.Context, paToken string) (pa auth.PAT, err error) { +func (lm *loggingMiddleware) CheckPAT(ctx context.Context, userID, patID string, platformEntityType auth.PlatformEntityType, optionalDomainID string, optionalDomainEntityType auth.DomainEntityType, operation auth.OperationType, entityIDs ...string) (err error) { defer func(begin time.Time) { args := []any{ slog.String("duration", time.Since(begin).String()), + slog.String("user_id", userID), + slog.String("pat_id", patID), + slog.String("platform_entity_type", platformEntityType.String()), + slog.String("optional_domain_id", optionalDomainID), + slog.String("optional_domain_entity_type", optionalDomainEntityType.String()), + slog.String("operation", operation.String()), + slog.Any("entities", entityIDs), } if err != nil { args = append(args, slog.Any("error", err)) - lm.logger.Warn("Authorize PAT failed to complete successfully", args...) + lm.logger.Warn("Check PAT failed complete successfully", args...) return } - lm.logger.Info("Authorize PAT completed successfully", args...) + lm.logger.Info("Check PAT completed successfully", args...) }(time.Now()) - return lm.svc.AuthorizePAT(ctx, paToken) + return lm.svc.CheckPAT(ctx, userID, patID, platformEntityType, optionalDomainID, optionalDomainEntityType, operation, entityIDs...) } diff --git a/auth/api/metrics.go b/auth/api/metrics.go index 511e782442..3808efc096 100644 --- a/auth/api/metrics.go +++ b/auth/api/metrics.go @@ -336,14 +336,6 @@ func (ms *metricsMiddleware) ClearPATAllScopeEntry(ctx context.Context, token, p return ms.svc.ClearPATAllScopeEntry(ctx, token, patID) } -func (ms *metricsMiddleware) TestCheckPATScopeEntry(ctx context.Context, paToken string, platformEntityType auth.PlatformEntityType, optionalDomainID string, optionalDomainEntityType auth.DomainEntityType, operation auth.OperationType, entityIDs ...string) error { - defer func(begin time.Time) { - ms.counter.With("method", "test_check_pat_scope_entry").Add(1) - ms.latency.With("method", "test_check_pat_scope_entry").Observe(time.Since(begin).Seconds()) - }(time.Now()) - return ms.svc.TestCheckPATScopeEntry(ctx, paToken, platformEntityType, optionalDomainID, optionalDomainEntityType, operation, entityIDs...) -} - func (ms *metricsMiddleware) IdentifyPAT(ctx context.Context, paToken string) (auth.PAT, error) { defer func(begin time.Time) { ms.counter.With("method", "identify_pat").Add(1) @@ -352,10 +344,18 @@ func (ms *metricsMiddleware) IdentifyPAT(ctx context.Context, paToken string) (a return ms.svc.IdentifyPAT(ctx, paToken) } -func (ms *metricsMiddleware) AuthorizePAT(ctx context.Context, paToken string) (auth.PAT, error) { +func (ms *metricsMiddleware) AuthorizePAT(ctx context.Context, paToken string, platformEntityType auth.PlatformEntityType, optionalDomainID string, optionalDomainEntityType auth.DomainEntityType, operation auth.OperationType, entityIDs ...string) error { defer func(begin time.Time) { ms.counter.With("method", "authorize_pat").Add(1) ms.latency.With("method", "authorize_pat").Observe(time.Since(begin).Seconds()) }(time.Now()) - return ms.svc.AuthorizePAT(ctx, paToken) + return ms.svc.AuthorizePAT(ctx, paToken, platformEntityType, optionalDomainID, optionalDomainEntityType, operation, entityIDs...) +} + +func (ms *metricsMiddleware) CheckPAT(ctx context.Context, userID, patID string, platformEntityType auth.PlatformEntityType, optionalDomainID string, optionalDomainEntityType auth.DomainEntityType, operation auth.OperationType, entityIDs ...string) error { + defer func(begin time.Time) { + ms.counter.With("method", "check_pat").Add(1) + ms.latency.With("method", "check_pat").Observe(time.Since(begin).Seconds()) + }(time.Now()) + return ms.svc.CheckPAT(ctx, userID, patID, platformEntityType, optionalDomainID, optionalDomainEntityType, operation, entityIDs...) } diff --git a/auth/bolt/pat.go b/auth/bolt/pat.go index 9c7d2ea247..4534dc4e85 100644 --- a/auth/bolt/pat.go +++ b/auth/bolt/pat.go @@ -38,6 +38,7 @@ const ( ) var ( + activateValue = []byte{0x00} revokedValue = []byte{0x01} entityValue = []byte{0x02} anyIDValue = []byte{0x03} @@ -59,6 +60,7 @@ func NewPATSRepository(db *bolt.DB, bucketName string) auth.PATSRepository { } func (pr *patRepo) Save(ctx context.Context, pat auth.PAT) error { + idxKey := []byte(pat.User + keySeparator + patKey + keySeparator + pat.ID) kv, err := patToKeyValue(pat) if err != nil { return err @@ -78,7 +80,7 @@ func (pr *patRepo) Save(ctx context.Context, pat auth.PAT) error { return errors.Wrap(repoerr.ErrCreateEntity, err) } } - if err := rootBucket.Put([]byte(pat.User+keySeparator+patKey+keySeparator+pat.ID), []byte(pat.ID)); err != nil { + if err := rootBucket.Put(idxKey, []byte(pat.ID)); err != nil { return errors.Wrap(repoerr.ErrCreateEntity, err) } return nil @@ -89,9 +91,9 @@ func (pr *patRepo) Retrieve(ctx context.Context, userID, patID string) (auth.PAT prefix := []byte(patID + keySeparator) kv := map[string][]byte{} if err := pr.db.View(func(tx *bolt.Tx) error { - b, err := pr.retrieveUserBucket(tx, userID) + b, err := pr.retrieveUserBucket(tx, userID, patID, repoerr.ErrViewEntity) if err != nil { - return errors.Wrap(repoerr.ErrRemoveEntity, err) + return err } c := b.Cursor() for k, v := c.Seek(prefix); k != nil && bytes.HasPrefix(k, prefix); k, v = c.Next() { @@ -105,6 +107,25 @@ func (pr *patRepo) Retrieve(ctx context.Context, userID, patID string) (auth.PAT return keyValueToPAT(kv) } +func (pr *patRepo) RetrieveSecretAndRevokeStatus(ctx context.Context, userID, patID string) (string, bool, error) { + revoked := true + keySecret := patID + keySeparator + secretKey + keyRevoked := patID + keySeparator + revokedKey + var secretHash string + if err := pr.db.View(func(tx *bolt.Tx) error { + b, err := pr.retrieveUserBucket(tx, userID, patID, repoerr.ErrViewEntity) + if err != nil { + return err + } + secretHash = string(b.Get([]byte(keySecret))) + revoked = bytesToBoolean(b.Get([]byte(keyRevoked))) + return nil + }); err != nil { + return "", true, err + } + return secretHash, revoked, nil +} + func (pr *patRepo) UpdateName(ctx context.Context, userID, patID, name string) (auth.PAT, error) { return pr.updatePATField(ctx, userID, patID, nameKey, []byte(name)) } @@ -117,9 +138,9 @@ func (pr *patRepo) UpdateTokenHash(ctx context.Context, userID, patID, tokenHash prefix := []byte(patID + keySeparator) kv := map[string][]byte{} if err := pr.db.Update(func(tx *bolt.Tx) error { - b, err := pr.retrieveUserBucket(tx, userID) + b, err := pr.retrieveUserBucket(tx, userID, patID, repoerr.ErrUpdateEntity) if err != nil { - return errors.Wrap(repoerr.ErrUpdateEntity, err) + return err } if err := b.Put([]byte(patID+keySeparator+secretKey), []byte(tokenHash)); err != nil { return errors.Wrap(repoerr.ErrUpdateEntity, err) @@ -127,6 +148,9 @@ func (pr *patRepo) UpdateTokenHash(ctx context.Context, userID, patID, tokenHash if err := b.Put([]byte(patID+keySeparator+expiresAtKey), timeToBytes(expiryAt)); err != nil { return errors.Wrap(repoerr.ErrUpdateEntity, err) } + if err := b.Put([]byte(patID+keySeparator+updatedAtKey), timeToBytes(time.Now())); err != nil { + return errors.Wrap(repoerr.ErrUpdateEntity, err) + } c := b.Cursor() for k, v := c.Seek(prefix); k != nil && bytes.HasPrefix(k, prefix); k, v = c.Next() { kv[string(k)] = v @@ -178,7 +202,7 @@ func (pr *patRepo) RetrieveAll(ctx context.Context, userID string, pm auth.PATSP aLimit = uint64(rLimit) } - for i := pm.Offset; i < aLimit; i++ { + for i := pm.Offset; i < pm.Offset+aLimit; i++ { if int(i) < total { pat, err := pr.Retrieve(ctx, userID, patIDs[i]) if err != nil { @@ -193,9 +217,9 @@ func (pr *patRepo) RetrieveAll(ctx context.Context, userID string, pm auth.PATSP func (pr *patRepo) Revoke(ctx context.Context, userID, patID string) error { if err := pr.db.Update(func(tx *bolt.Tx) error { - b, err := pr.retrieveUserBucket(tx, userID) + b, err := pr.retrieveUserBucket(tx, userID, patID, repoerr.ErrUpdateEntity) if err != nil { - return errors.Wrap(repoerr.ErrUpdateEntity, err) + return err } if err := b.Put([]byte(patID+keySeparator+revokedKey), revokedValue); err != nil { return errors.Wrap(repoerr.ErrUpdateEntity, err) @@ -210,13 +234,32 @@ func (pr *patRepo) Revoke(ctx context.Context, userID, patID string) error { return nil } +func (pr *patRepo) Reactivate(ctx context.Context, userID, patID string) error { + if err := pr.db.Update(func(tx *bolt.Tx) error { + b, err := pr.retrieveUserBucket(tx, userID, patID, repoerr.ErrUpdateEntity) + if err != nil { + return err + } + if err := b.Put([]byte(patID+keySeparator+revokedKey), activateValue); err != nil { + return errors.Wrap(repoerr.ErrUpdateEntity, err) + } + if err := b.Put([]byte(patID+keySeparator+revokedAtKey), []byte{}); err != nil { + return errors.Wrap(repoerr.ErrUpdateEntity, err) + } + return nil + }); err != nil { + return err + } + return nil +} + func (pr *patRepo) Remove(ctx context.Context, userID, patID string) error { prefix := []byte(patID + keySeparator) - + idxKey := []byte(userID + keySeparator + patKey + keySeparator + patID) if err := pr.db.Update(func(tx *bolt.Tx) error { - b, err := pr.retrieveUserBucket(tx, userID) + b, err := pr.retrieveUserBucket(tx, userID, patID, repoerr.ErrRemoveEntity) if err != nil { - return errors.Wrap(repoerr.ErrRemoveEntity, err) + return err } c := b.Cursor() for k, _ := c.Seek(prefix); k != nil && bytes.HasPrefix(k, prefix); k, _ = c.Next() { @@ -224,6 +267,13 @@ func (pr *patRepo) Remove(ctx context.Context, userID, patID string) error { return errors.Wrap(repoerr.ErrRemoveEntity, err) } } + rb, err := pr.retrieveRootBucket(tx) + if err != nil { + return err + } + if err := rb.Delete(idxKey); err != nil { + return errors.Wrap(repoerr.ErrRemoveEntity, err) + } return nil }); err != nil { return err @@ -236,9 +286,9 @@ func (pr *patRepo) AddScopeEntry(ctx context.Context, userID, patID string, plat prefix := []byte(patID + keySeparator + scopeKey) var rKV map[string][]byte if err := pr.db.Update(func(tx *bolt.Tx) error { - b, err := pr.retrieveUserBucket(tx, userID) + b, err := pr.retrieveUserBucket(tx, userID, patID, repoerr.ErrCreateEntity) if err != nil { - return errors.Wrap(repoerr.ErrCreateEntity, err) + return err } kv, err := scopeEntryToKeyValue(platformEntityType, optionalDomainID, optionalDomainEntityType, operation, entityIDs...) if err != nil { @@ -269,9 +319,9 @@ func (pr *patRepo) RemoveScopeEntry(ctx context.Context, userID, patID string, p prefix := []byte(patID + keySeparator + scopeKey) var rKV map[string][]byte if err := pr.db.Update(func(tx *bolt.Tx) error { - b, err := pr.retrieveUserBucket(tx, userID) + b, err := pr.retrieveUserBucket(tx, userID, patID, repoerr.ErrRemoveEntity) if err != nil { - return errors.Wrap(repoerr.ErrRemoveEntity, err) + return err } kv, err := scopeEntryToKeyValue(platformEntityType, optionalDomainID, optionalDomainEntityType, operation, entityIDs...) if err != nil { @@ -296,7 +346,7 @@ func (pr *patRepo) RemoveScopeEntry(ctx context.Context, userID, patID string, p func (pr *patRepo) CheckScopeEntry(ctx context.Context, userID, patID string, platformEntityType auth.PlatformEntityType, optionalDomainID string, optionalDomainEntityType auth.DomainEntityType, operation auth.OperationType, entityIDs ...string) error { return pr.db.Update(func(tx *bolt.Tx) error { - b, err := pr.retrieveUserBucket(tx, userID) + b, err := pr.retrieveUserBucket(tx, userID, patID, repoerr.ErrViewEntity) if err != nil { return errors.Wrap(repoerr.ErrViewEntity, err) } @@ -323,6 +373,31 @@ func (pr *patRepo) RemoveAllScopeEntry(ctx context.Context, userID, patID string return nil } +func (pr *patRepo) updatePATField(_ context.Context, userID, patID, key string, value []byte) (auth.PAT, error) { + prefix := []byte(patID + keySeparator) + kv := map[string][]byte{} + if err := pr.db.Update(func(tx *bolt.Tx) error { + b, err := pr.retrieveUserBucket(tx, userID, patID, repoerr.ErrUpdateEntity) + if err != nil { + return err + } + if err := b.Put([]byte(patID+keySeparator+key), value); err != nil { + return errors.Wrap(repoerr.ErrUpdateEntity, err) + } + if err := b.Put([]byte(patID+keySeparator+updatedAtKey), timeToBytes(time.Now())); err != nil { + return errors.Wrap(repoerr.ErrUpdateEntity, err) + } + c := b.Cursor() + for k, v := c.Seek(prefix); k != nil && bytes.HasPrefix(k, prefix); k, v = c.Next() { + kv[string(k)] = v + } + return nil + }); err != nil { + return auth.PAT{}, err + } + return keyValueToPAT(kv) +} + func (pr *patRepo) createUserBucket(rootBucket *bolt.Bucket, userID string) (*bolt.Bucket, error) { userBucket, err := rootBucket.CreateBucketIfNotExists([]byte(userID)) if err != nil { @@ -332,15 +407,20 @@ func (pr *patRepo) createUserBucket(rootBucket *bolt.Bucket, userID string) (*bo return userBucket, nil } -func (pr *patRepo) retrieveUserBucket(tx *bolt.Tx, userID string) (*bolt.Bucket, error) { +func (pr *patRepo) retrieveUserBucket(tx *bolt.Tx, userID, patID string, wrap error) (*bolt.Bucket, error) { rootBucket, err := pr.retrieveRootBucket(tx) if err != nil { - return nil, err + return nil, errors.Wrap(wrap, err) + } + + vPatID := rootBucket.Get([]byte(userID + keySeparator + patKey + keySeparator + patID)) + if vPatID == nil { + return nil, repoerr.ErrNotFound } userBucket := rootBucket.Bucket([]byte(userID)) if userBucket == nil { - return nil, fmt.Errorf("user %s not found", userID) + return nil, errors.Wrap(wrap, fmt.Errorf("user %s not found", userID)) } return userBucket, nil } @@ -353,28 +433,6 @@ func (pr *patRepo) retrieveRootBucket(tx *bolt.Tx) (*bolt.Bucket, error) { return rootBucket, nil } -func (pr *patRepo) updatePATField(_ context.Context, userID, patID, key string, value []byte) (auth.PAT, error) { - prefix := []byte(patID + keySeparator) - kv := map[string][]byte{} - if err := pr.db.Update(func(tx *bolt.Tx) error { - b, err := pr.retrieveUserBucket(tx, userID) - if err != nil { - return errors.Wrap(repoerr.ErrUpdateEntity, err) - } - if err := b.Put([]byte(patID+keySeparator+key), value); err != nil { - return errors.Wrap(repoerr.ErrUpdateEntity, err) - } - c := b.Cursor() - for k, v := c.Seek(prefix); k != nil && bytes.HasPrefix(k, prefix); k, v = c.Next() { - kv[string(k)] = v - } - return nil - }); err != nil { - return auth.PAT{}, err - } - return keyValueToPAT(kv) -} - func patToKeyValue(pat auth.PAT) (map[string][]byte, error) { kv := map[string][]byte{ idKey: []byte(pat.ID), @@ -401,7 +459,7 @@ func patToKeyValue(pat auth.PAT) (map[string][]byte, error) { func scopeToKeyValue(scope auth.Scope) (map[string][]byte, error) { kv := map[string][]byte{} - for opType, scopeValue := range scope.Users.Operations { + for opType, scopeValue := range scope.Users { tempKV, err := scopeEntryToKeyValue(auth.PlatformUsersScope, "", auth.DomainNullScope, opType, scopeValue.Values()...) if err != nil { return nil, err @@ -411,7 +469,7 @@ func scopeToKeyValue(scope auth.Scope) (map[string][]byte, error) { } } for domainID, domainScope := range scope.Domains { - for opType, scopeValue := range domainScope.DomainManagement.Operations { + for opType, scopeValue := range domainScope.DomainManagement { tempKV, err := scopeEntryToKeyValue(auth.PlatformDomainsScope, domainID, auth.DomainManagementScope, opType, scopeValue.Values()...) if err != nil { return nil, errors.Wrap(repoerr.ErrCreateEntity, err) @@ -421,7 +479,7 @@ func scopeToKeyValue(scope auth.Scope) (map[string][]byte, error) { } } for entityType, scope := range domainScope.Entities { - for opType, scopeValue := range scope.Operations { + for opType, scopeValue := range scope { tempKV, err := scopeEntryToKeyValue(auth.PlatformDomainsScope, domainID, entityType, opType, scopeValue.Values()...) if err != nil { return nil, errors.Wrap(repoerr.ErrCreateEntity, err) @@ -496,7 +554,7 @@ func scopeRootKey(platformEntityType auth.PlatformEntityType, optionalDomainID s return rootKey.String(), nil } -func keyValueToPAT(kv map[string][]byte) (auth.PAT, error) { +func keyValueToBasicPAT(kv map[string][]byte) auth.PAT { var pat auth.PAT for k, v := range kv { switch { @@ -522,6 +580,11 @@ func keyValueToPAT(kv map[string][]byte) (auth.PAT, error) { pat.RevokedAt = bytesToTime(v) } } + return pat +} + +func keyValueToPAT(kv map[string][]byte) (auth.PAT, error) { + pat := keyValueToBasicPAT(kv) scope, err := parseKeyValueToScope(kv) if err != nil { return auth.PAT{}, err @@ -599,8 +662,8 @@ func parseKeyValueToScope(kv map[string][]byte) (auth.Scope, error) { } func parseOperation(platformEntityType auth.PlatformEntityType, opScope auth.OperationScope, key string, keyParts []string, value []byte) (auth.OperationScope, error) { - if opScope.Operations == nil { - opScope.Operations = make(map[auth.OperationType]auth.ScopeValue) + if opScope == nil { + opScope = make(map[auth.OperationType]auth.ScopeValue) } if err := validateOperation(platformEntityType, opScope, key, keyParts, value); err != nil { @@ -615,38 +678,38 @@ func parseOperation(platformEntityType auth.PlatformEntityType, opScope auth.Ope } entityID := keyParts[len(keyParts)-1] - if _, oValueExists := opScope.Operations[opType]; !oValueExists { - opScope.Operations[opType] = &auth.SelectedIDs{} + if _, oValueExists := opScope[opType]; !oValueExists { + opScope[opType] = &auth.SelectedIDs{} } - oValue := opScope.Operations[opType] + oValue := opScope[opType] if err := oValue.AddValues(entityID); err != nil { return auth.OperationScope{}, fmt.Errorf("failed to add scope key %s with entity value %v : %w", key, entityID, err) } - opScope.Operations[opType] = oValue + opScope[opType] = oValue case string(anyIDValue): opType, err := auth.ParseOperationType(keyParts[len(keyParts)-1]) if err != nil { return auth.OperationScope{}, errors.Wrap(repoerr.ErrViewEntity, err) } - if oValue, oValueExists := opScope.Operations[opType]; oValueExists && oValue != nil { + if oValue, oValueExists := opScope[opType]; oValueExists && oValue != nil { if _, ok := oValue.(*auth.AnyIDs); !ok { return auth.OperationScope{}, fmt.Errorf("failed to add scope key %s with entity anyIDs scope value : key already initialized with different type", key) } } - opScope.Operations[opType] = &auth.AnyIDs{} + opScope[opType] = &auth.AnyIDs{} case string(selectedIDsValue): opType, err := auth.ParseOperationType(keyParts[len(keyParts)-1]) if err != nil { return auth.OperationScope{}, errors.Wrap(repoerr.ErrViewEntity, err) } - oValue, oValueExists := opScope.Operations[opType] + oValue, oValueExists := opScope[opType] if oValueExists && oValue != nil { if _, ok := oValue.(*auth.SelectedIDs); !ok { return auth.OperationScope{}, fmt.Errorf("failed to add scope key %s with entity selectedIDs scope value : key already initialized with different type", key) } } if !oValueExists { - opScope.Operations[opType] = &auth.SelectedIDs{} + opScope[opType] = &auth.SelectedIDs{} } default: return auth.OperationScope{}, fmt.Errorf("key %s have invalid value %v", key, value) @@ -686,14 +749,13 @@ func validateOperation(platformEntityType auth.PlatformEntityType, opScope auth. func timeToBytes(t time.Time) []byte { timeBytes := make([]byte, 8) - binary.BigEndian.PutUint64(timeBytes, uint64(t.Nanosecond())) + binary.BigEndian.PutUint64(timeBytes, uint64(t.Unix())) return timeBytes } func bytesToTime(b []byte) time.Time { - var timeAtNs uint64 - binary.BigEndian.AppendUint64(b, timeAtNs) - return time.Unix(0, int64(timeAtNs)) + timeAtSeconds := binary.BigEndian.Uint64(b) + return time.Unix(int64(timeAtSeconds), 0) } func booleanToBytes(b bool) []byte { @@ -704,7 +766,7 @@ func booleanToBytes(b bool) []byte { } func bytesToBoolean(b []byte) bool { - if len(b) > 0 && b[0] == 1 { + if len(b) > 1 || b[0] != activateValue[0] { return true } return false diff --git a/auth/events/streams.go b/auth/events/streams.go index c839153f10..f2e87f5783 100644 --- a/auth/events/streams.go +++ b/auth/events/streams.go @@ -308,14 +308,14 @@ func (es *eventStore) ClearPATAllScopeEntry(ctx context.Context, token, patID st return es.svc.ClearPATAllScopeEntry(ctx, token, patID) } -func (es *eventStore) TestCheckPATScopeEntry(ctx context.Context, paToken string, platformEntityType auth.PlatformEntityType, optionalDomainID string, optionalDomainEntityType auth.DomainEntityType, operation auth.OperationType, entityIDs ...string) error { - return es.svc.TestCheckPATScopeEntry(ctx, paToken, platformEntityType, optionalDomainID, optionalDomainEntityType, operation, entityIDs...) -} - func (es *eventStore) IdentifyPAT(ctx context.Context, paToken string) (auth.PAT, error) { return es.svc.IdentifyPAT(ctx, paToken) } -func (es *eventStore) AuthorizePAT(ctx context.Context, paToken string) (auth.PAT, error) { - return es.svc.AuthorizePAT(ctx, paToken) +func (es *eventStore) AuthorizePAT(ctx context.Context, paToken string, platformEntityType auth.PlatformEntityType, optionalDomainID string, optionalDomainEntityType auth.DomainEntityType, operation auth.OperationType, entityIDs ...string) error { + return es.svc.AuthorizePAT(ctx, paToken, platformEntityType, optionalDomainID, optionalDomainEntityType, operation, entityIDs...) +} + +func (es *eventStore) CheckPAT(ctx context.Context, userID, patID string, platformEntityType auth.PlatformEntityType, optionalDomainID string, optionalDomainEntityType auth.DomainEntityType, operation auth.OperationType, entityIDs ...string) error { + return es.svc.CheckPAT(ctx, userID, patID, platformEntityType, optionalDomainID, optionalDomainEntityType, operation, entityIDs...) } diff --git a/auth/hasher.go b/auth/hasher.go new file mode 100644 index 0000000000..ada2352bbe --- /dev/null +++ b/auth/hasher.go @@ -0,0 +1,17 @@ +// Copyright (c) Abstract Machines +// SPDX-License-Identifier: Apache-2.0 + +package auth + +// Hasher specifies an API for generating hashes of an arbitrary textual +// content. +// +//go:generate mockery --name Hasher --output=./mocks --filename hasher.go --quiet --note "Copyright (c) Abstract Machines" +type Hasher interface { + // Hash generates the hashed string from plain-text. + Hash(string) (string, error) + + // Compare compares plain-text version to the hashed one. An error should + // indicate failed comparison. + Compare(string, string) error +} diff --git a/auth/hasher/doc.go b/auth/hasher/doc.go new file mode 100644 index 0000000000..98be992262 --- /dev/null +++ b/auth/hasher/doc.go @@ -0,0 +1,6 @@ +// Copyright (c) Abstract Machines +// SPDX-License-Identifier: Apache-2.0 + +// Package hasher contains the domain concept definitions needed to +// support Magistrala users password hasher sub-service functionality. +package hasher diff --git a/auth/hasher/hasher.go b/auth/hasher/hasher.go new file mode 100644 index 0000000000..c417bf7b80 --- /dev/null +++ b/auth/hasher/hasher.go @@ -0,0 +1,86 @@ +// Copyright (c) Abstract Machines +// SPDX-License-Identifier: Apache-2.0 + +package hasher + +import ( + "encoding/base64" + "fmt" + "math/rand" + "strings" + "time" + + "github.com/absmach/magistrala/auth" + "github.com/absmach/magistrala/pkg/errors" + "golang.org/x/crypto/scrypt" +) + +var ( + errHashToken = errors.New("failed to generate hash for token") + errHashCompare = errors.New("failed to generate hash for given compare string") + errToken = errors.New("given token and hash are not same") + errSalt = errors.New("failed to generate salt") + errInvalidHashStore = errors.New("invalid stored hash format") + errDecode = errors.New("failed to decode") +) + +var _ auth.Hasher = (*bcryptHasher)(nil) + +type bcryptHasher struct{} + +// New instantiates a bcrypt-based hasher implementation. +func New() auth.Hasher { + return &bcryptHasher{} +} + +func (bh *bcryptHasher) Hash(token string) (string, error) { + salt, err := generateSalt(25) + if err != nil { + return "", err + } + // N is kept 16384 to make faster and added large salt, since PAT will be access by automation scripts in high frequency. + hash, err := scrypt.Key([]byte(token), salt, 16384, 8, 1, 32) + if err != nil { + return "", errors.Wrap(errHashToken, err) + } + + return fmt.Sprintf("%s.%s", base64.StdEncoding.EncodeToString(hash), base64.StdEncoding.EncodeToString(salt)), nil +} + +func (bh *bcryptHasher) Compare(plain, hashed string) error { + parts := strings.Split(hashed, ".") + if len(parts) != 2 { + return errInvalidHashStore + } + + actHash, err := base64.StdEncoding.DecodeString(parts[0]) + if err != nil { + return errors.Wrap(errDecode, err) + } + + salt, err := base64.StdEncoding.DecodeString(parts[1]) + if err != nil { + return errors.Wrap(errDecode, err) + } + + derivedHash, err := scrypt.Key([]byte(plain), salt, 16384, 8, 1, 32) + if err != nil { + return errors.Wrap(errHashCompare, err) + } + + if string(derivedHash) == string(actHash) { + return nil + } + + return errToken +} + +func generateSalt(length int) ([]byte, error) { + rand.New(rand.NewSource(time.Now().UnixNano())) + salt := make([]byte, length) + _, err := rand.Read(salt) + if err != nil { + return nil, errors.Wrap(errSalt, err) + } + return salt, nil +} diff --git a/auth/mocks/hasher.go b/auth/mocks/hasher.go new file mode 100644 index 0000000000..4c4425b257 --- /dev/null +++ b/auth/mocks/hasher.go @@ -0,0 +1,72 @@ +// Code generated by mockery v2.43.2. DO NOT EDIT. + +// Copyright (c) Abstract Machines + +package mocks + +import mock "github.com/stretchr/testify/mock" + +// Hasher is an autogenerated mock type for the Hasher type +type Hasher struct { + mock.Mock +} + +// Compare provides a mock function with given fields: _a0, _a1 +func (_m *Hasher) Compare(_a0 string, _a1 string) error { + ret := _m.Called(_a0, _a1) + + if len(ret) == 0 { + panic("no return value specified for Compare") + } + + var r0 error + if rf, ok := ret.Get(0).(func(string, string) error); ok { + r0 = rf(_a0, _a1) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// Hash provides a mock function with given fields: _a0 +func (_m *Hasher) Hash(_a0 string) (string, error) { + ret := _m.Called(_a0) + + if len(ret) == 0 { + panic("no return value specified for Hash") + } + + var r0 string + var r1 error + if rf, ok := ret.Get(0).(func(string) (string, error)); ok { + return rf(_a0) + } + if rf, ok := ret.Get(0).(func(string) string); ok { + r0 = rf(_a0) + } else { + r0 = ret.Get(0).(string) + } + + if rf, ok := ret.Get(1).(func(string) error); ok { + r1 = rf(_a0) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// NewHasher creates a new instance of Hasher. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +// The first argument is typically a *testing.T value. +func NewHasher(t interface { + mock.TestingT + Cleanup(func()) +}) *Hasher { + mock := &Hasher{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/auth/mocks/pats.go b/auth/mocks/pats.go index 3b53eaf647..c18cddcef0 100644 --- a/auth/mocks/pats.go +++ b/auth/mocks/pats.go @@ -54,32 +54,54 @@ func (_m *PATS) AddPATScopeEntry(ctx context.Context, token string, patID string return r0, r1 } -// AuthorizePAT provides a mock function with given fields: ctx, paToken -func (_m *PATS) AuthorizePAT(ctx context.Context, paToken string) (auth.PAT, error) { - ret := _m.Called(ctx, paToken) +// AuthorizePAT provides a mock function with given fields: ctx, paToken, platformEntityType, optionalDomainID, optionalDomainEntityType, operation, entityIDs +func (_m *PATS) AuthorizePAT(ctx context.Context, paToken string, platformEntityType auth.PlatformEntityType, optionalDomainID string, optionalDomainEntityType auth.DomainEntityType, operation auth.OperationType, entityIDs ...string) error { + _va := make([]interface{}, len(entityIDs)) + for _i := range entityIDs { + _va[_i] = entityIDs[_i] + } + var _ca []interface{} + _ca = append(_ca, ctx, paToken, platformEntityType, optionalDomainID, optionalDomainEntityType, operation) + _ca = append(_ca, _va...) + ret := _m.Called(_ca...) if len(ret) == 0 { panic("no return value specified for AuthorizePAT") } - var r0 auth.PAT - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, string) (auth.PAT, error)); ok { - return rf(ctx, paToken) - } - if rf, ok := ret.Get(0).(func(context.Context, string) auth.PAT); ok { - r0 = rf(ctx, paToken) + var r0 error + if rf, ok := ret.Get(0).(func(context.Context, string, auth.PlatformEntityType, string, auth.DomainEntityType, auth.OperationType, ...string) error); ok { + r0 = rf(ctx, paToken, platformEntityType, optionalDomainID, optionalDomainEntityType, operation, entityIDs...) } else { - r0 = ret.Get(0).(auth.PAT) + r0 = ret.Error(0) } - if rf, ok := ret.Get(1).(func(context.Context, string) error); ok { - r1 = rf(ctx, paToken) + return r0 +} + +// CheckPAT provides a mock function with given fields: ctx, userID, patID, platformEntityType, optionalDomainID, optionalDomainEntityType, operation, entityIDs +func (_m *PATS) CheckPAT(ctx context.Context, userID string, patID string, platformEntityType auth.PlatformEntityType, optionalDomainID string, optionalDomainEntityType auth.DomainEntityType, operation auth.OperationType, entityIDs ...string) error { + _va := make([]interface{}, len(entityIDs)) + for _i := range entityIDs { + _va[_i] = entityIDs[_i] + } + var _ca []interface{} + _ca = append(_ca, ctx, userID, patID, platformEntityType, optionalDomainID, optionalDomainEntityType, operation) + _ca = append(_ca, _va...) + ret := _m.Called(_ca...) + + if len(ret) == 0 { + panic("no return value specified for CheckPAT") + } + + var r0 error + if rf, ok := ret.Get(0).(func(context.Context, string, string, auth.PlatformEntityType, string, auth.DomainEntityType, auth.OperationType, ...string) error); ok { + r0 = rf(ctx, userID, patID, platformEntityType, optionalDomainID, optionalDomainEntityType, operation, entityIDs...) } else { - r1 = ret.Error(1) + r0 = ret.Error(0) } - return r0, r1 + return r0 } // ClearPATAllScopeEntry provides a mock function with given fields: ctx, token, patID @@ -311,31 +333,6 @@ func (_m *PATS) RevokePATSecret(ctx context.Context, token string, patID string) return r0 } -// TestCheckPATScopeEntry provides a mock function with given fields: ctx, paToken, platformEntityType, optionalDomainID, optionalDomainEntityType, operation, entityIDs -func (_m *PATS) TestCheckPATScopeEntry(ctx context.Context, paToken string, platformEntityType auth.PlatformEntityType, optionalDomainID string, optionalDomainEntityType auth.DomainEntityType, operation auth.OperationType, entityIDs ...string) error { - _va := make([]interface{}, len(entityIDs)) - for _i := range entityIDs { - _va[_i] = entityIDs[_i] - } - var _ca []interface{} - _ca = append(_ca, ctx, paToken, platformEntityType, optionalDomainID, optionalDomainEntityType, operation) - _ca = append(_ca, _va...) - ret := _m.Called(_ca...) - - if len(ret) == 0 { - panic("no return value specified for TestCheckPATScopeEntry") - } - - var r0 error - if rf, ok := ret.Get(0).(func(context.Context, string, auth.PlatformEntityType, string, auth.DomainEntityType, auth.OperationType, ...string) error); ok { - r0 = rf(ctx, paToken, platformEntityType, optionalDomainID, optionalDomainEntityType, operation, entityIDs...) - } else { - r0 = ret.Error(0) - } - - return r0 -} - // UpdatePATDescription provides a mock function with given fields: ctx, token, patID, description func (_m *PATS) UpdatePATDescription(ctx context.Context, token string, patID string, description string) (auth.PAT, error) { ret := _m.Called(ctx, token, patID, description) diff --git a/auth/mocks/patsrepo.go b/auth/mocks/patsrepo.go index 10cc48612a..323baca097 100644 --- a/auth/mocks/patsrepo.go +++ b/auth/mocks/patsrepo.go @@ -79,6 +79,24 @@ func (_m *PATSRepository) CheckScopeEntry(ctx context.Context, userID string, pa return r0 } +// Reactivate provides a mock function with given fields: ctx, userID, patID +func (_m *PATSRepository) Reactivate(ctx context.Context, userID string, patID string) error { + ret := _m.Called(ctx, userID, patID) + + if len(ret) == 0 { + panic("no return value specified for Reactivate") + } + + var r0 error + if rf, ok := ret.Get(0).(func(context.Context, string, string) error); ok { + r0 = rf(ctx, userID, patID) + } else { + r0 = ret.Error(0) + } + + return r0 +} + // Remove provides a mock function with given fields: ctx, userID, patID func (_m *PATSRepository) Remove(ctx context.Context, userID string, patID string) error { ret := _m.Called(ctx, userID, patID) @@ -206,6 +224,41 @@ func (_m *PATSRepository) RetrieveAll(ctx context.Context, userID string, pm aut return r0, r1 } +// RetrieveSecretAndRevokeStatus provides a mock function with given fields: ctx, userID, patID +func (_m *PATSRepository) RetrieveSecretAndRevokeStatus(ctx context.Context, userID string, patID string) (string, bool, error) { + ret := _m.Called(ctx, userID, patID) + + if len(ret) == 0 { + panic("no return value specified for RetrieveSecretAndRevokeStatus") + } + + var r0 string + var r1 bool + var r2 error + if rf, ok := ret.Get(0).(func(context.Context, string, string) (string, bool, error)); ok { + return rf(ctx, userID, patID) + } + if rf, ok := ret.Get(0).(func(context.Context, string, string) string); ok { + r0 = rf(ctx, userID, patID) + } else { + r0 = ret.Get(0).(string) + } + + if rf, ok := ret.Get(1).(func(context.Context, string, string) bool); ok { + r1 = rf(ctx, userID, patID) + } else { + r1 = ret.Get(1).(bool) + } + + if rf, ok := ret.Get(2).(func(context.Context, string, string) error); ok { + r2 = rf(ctx, userID, patID) + } else { + r2 = ret.Error(2) + } + + return r0, r1, r2 +} + // Revoke provides a mock function with given fields: ctx, userID, patID func (_m *PATSRepository) Revoke(ctx context.Context, userID string, patID string) error { ret := _m.Called(ctx, userID, patID) diff --git a/auth/mocks/service.go b/auth/mocks/service.go index ccf50a34f2..2700de304e 100644 --- a/auth/mocks/service.go +++ b/auth/mocks/service.go @@ -126,32 +126,29 @@ func (_m *Service) Authorize(ctx context.Context, pr auth.PolicyReq) error { return r0 } -// AuthorizePAT provides a mock function with given fields: ctx, paToken -func (_m *Service) AuthorizePAT(ctx context.Context, paToken string) (auth.PAT, error) { - ret := _m.Called(ctx, paToken) +// AuthorizePAT provides a mock function with given fields: ctx, paToken, platformEntityType, optionalDomainID, optionalDomainEntityType, operation, entityIDs +func (_m *Service) AuthorizePAT(ctx context.Context, paToken string, platformEntityType auth.PlatformEntityType, optionalDomainID string, optionalDomainEntityType auth.DomainEntityType, operation auth.OperationType, entityIDs ...string) error { + _va := make([]interface{}, len(entityIDs)) + for _i := range entityIDs { + _va[_i] = entityIDs[_i] + } + var _ca []interface{} + _ca = append(_ca, ctx, paToken, platformEntityType, optionalDomainID, optionalDomainEntityType, operation) + _ca = append(_ca, _va...) + ret := _m.Called(_ca...) if len(ret) == 0 { panic("no return value specified for AuthorizePAT") } - var r0 auth.PAT - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, string) (auth.PAT, error)); ok { - return rf(ctx, paToken) - } - if rf, ok := ret.Get(0).(func(context.Context, string) auth.PAT); ok { - r0 = rf(ctx, paToken) - } else { - r0 = ret.Get(0).(auth.PAT) - } - - if rf, ok := ret.Get(1).(func(context.Context, string) error); ok { - r1 = rf(ctx, paToken) + var r0 error + if rf, ok := ret.Get(0).(func(context.Context, string, auth.PlatformEntityType, string, auth.DomainEntityType, auth.OperationType, ...string) error); ok { + r0 = rf(ctx, paToken, platformEntityType, optionalDomainID, optionalDomainEntityType, operation, entityIDs...) } else { - r1 = ret.Error(1) + r0 = ret.Error(0) } - return r0, r1 + return r0 } // ChangeDomainStatus provides a mock function with given fields: ctx, token, id, d @@ -182,6 +179,31 @@ func (_m *Service) ChangeDomainStatus(ctx context.Context, token string, id stri return r0, r1 } +// CheckPAT provides a mock function with given fields: ctx, userID, patID, platformEntityType, optionalDomainID, optionalDomainEntityType, operation, entityIDs +func (_m *Service) CheckPAT(ctx context.Context, userID string, patID string, platformEntityType auth.PlatformEntityType, optionalDomainID string, optionalDomainEntityType auth.DomainEntityType, operation auth.OperationType, entityIDs ...string) error { + _va := make([]interface{}, len(entityIDs)) + for _i := range entityIDs { + _va[_i] = entityIDs[_i] + } + var _ca []interface{} + _ca = append(_ca, ctx, userID, patID, platformEntityType, optionalDomainID, optionalDomainEntityType, operation) + _ca = append(_ca, _va...) + ret := _m.Called(_ca...) + + if len(ret) == 0 { + panic("no return value specified for CheckPAT") + } + + var r0 error + if rf, ok := ret.Get(0).(func(context.Context, string, string, auth.PlatformEntityType, string, auth.DomainEntityType, auth.OperationType, ...string) error); ok { + r0 = rf(ctx, userID, patID, platformEntityType, optionalDomainID, optionalDomainEntityType, operation, entityIDs...) + } else { + r0 = ret.Error(0) + } + + return r0 +} + // ClearPATAllScopeEntry provides a mock function with given fields: ctx, token, patID func (_m *Service) ClearPATAllScopeEntry(ctx context.Context, token string, patID string) error { ret := _m.Called(ctx, token, patID) @@ -907,31 +929,6 @@ func (_m *Service) RevokePATSecret(ctx context.Context, token string, patID stri return r0 } -// TestCheckPATScopeEntry provides a mock function with given fields: ctx, paToken, platformEntityType, optionalDomainID, optionalDomainEntityType, operation, entityIDs -func (_m *Service) TestCheckPATScopeEntry(ctx context.Context, paToken string, platformEntityType auth.PlatformEntityType, optionalDomainID string, optionalDomainEntityType auth.DomainEntityType, operation auth.OperationType, entityIDs ...string) error { - _va := make([]interface{}, len(entityIDs)) - for _i := range entityIDs { - _va[_i] = entityIDs[_i] - } - var _ca []interface{} - _ca = append(_ca, ctx, paToken, platformEntityType, optionalDomainID, optionalDomainEntityType, operation) - _ca = append(_ca, _va...) - ret := _m.Called(_ca...) - - if len(ret) == 0 { - panic("no return value specified for TestCheckPATScopeEntry") - } - - var r0 error - if rf, ok := ret.Get(0).(func(context.Context, string, auth.PlatformEntityType, string, auth.DomainEntityType, auth.OperationType, ...string) error); ok { - r0 = rf(ctx, paToken, platformEntityType, optionalDomainID, optionalDomainEntityType, operation, entityIDs...) - } else { - r0 = ret.Error(0) - } - - return r0 -} - // UnassignUser provides a mock function with given fields: ctx, token, id, userID func (_m *Service) UnassignUser(ctx context.Context, token string, id string, userID string) error { ret := _m.Called(ctx, token, id, userID) diff --git a/auth/pat.go b/auth/pat.go index 48a405624b..7e26065264 100644 --- a/auth/pat.go +++ b/auth/pat.go @@ -33,23 +33,6 @@ const ( deleteOpStr = "delete" ) -func (ot OperationType) ValidString() (string, error) { - switch ot { - case CreateOp: - return createOpStr, nil - case ReadOp: - return readOpStr, nil - case ListOp: - return listOpStr, nil - case UpdateOp: - return updateOpStr, nil - case DeleteOp: - return deleteOpStr, nil - default: - return "", fmt.Errorf("unknown operation type %d", ot) - } -} - func (ot OperationType) String() string { switch ot { case CreateOp: @@ -67,6 +50,14 @@ func (ot OperationType) String() string { } } +func (ot OperationType) ValidString() (string, error) { + str := ot.String() + if str == fmt.Sprintf("unknown operation type %d", ot) { + return "", errors.New(str) + } + return str, nil +} + func ParseOperationType(ot string) (OperationType, error) { switch ot { case createOpStr: @@ -115,21 +106,6 @@ const ( domainThingsScopeStr = "things" ) -func (det DomainEntityType) ValidString() (string, error) { - switch det { - case DomainManagementScope: - return domainManagementScopeStr, nil - case DomainGroupsScope: - return domainGroupsScopeStr, nil - case DomainChannelsScope: - return domainChannelsScopeStr, nil - case DomainThingsScope: - return domainThingsScopeStr, nil - default: - return "", fmt.Errorf("unknown domain entity type %d", det) - } -} - func (det DomainEntityType) String() string { switch det { case DomainManagementScope: @@ -145,6 +121,14 @@ func (det DomainEntityType) String() string { } } +func (det DomainEntityType) ValidString() (string, error) { + str := det.String() + if str == fmt.Sprintf("unknown operation type %d", det) { + return "", errors.New(str) + } + return str, nil +} + func ParseDomainEntityType(det string) (DomainEntityType, error) { switch det { case domainManagementScopeStr: @@ -186,17 +170,6 @@ const ( platformDomainsScopeStr = "domains" ) -func (pet PlatformEntityType) ValidString() (string, error) { - switch pet { - case PlatformUsersScope: - return platformUsersScopeStr, nil - case PlatformDomainsScope: - return platformDomainsScopeStr, nil - default: - return "", fmt.Errorf("unknown platform entity type %d", pet) - } -} - func (pet PlatformEntityType) String() string { switch pet { case PlatformUsersScope: @@ -208,6 +181,14 @@ func (pet PlatformEntityType) String() string { } } +func (pet PlatformEntityType) ValidString() (string, error) { + str := pet.String() + if str == fmt.Sprintf("unknown platform entity type %d", pet) { + return "", errors.New(str) + } + return str, nil +} + func ParsePlatformEntityType(pet string) (PlatformEntityType, error) { switch pet { case platformUsersScopeStr: @@ -281,44 +262,35 @@ func (s *SelectedIDs) RemoveValues(ids ...string) error { } // OperationScope contains map of OperationType with value of AnyIDs or SelectedIDs. -type OperationScope struct { - Operations map[OperationType]ScopeValue `json:"operations,omitempty"` -} +type OperationScope map[OperationType]ScopeValue func (os *OperationScope) UnmarshalJSON(data []byte) error { - type tempOperationScope struct { - Operations map[OperationType]json.RawMessage `json:"operations"` - } + type tempOperationScope map[OperationType]json.RawMessage var tempScope tempOperationScope if err := json.Unmarshal(data, &tempScope); err != nil { return err } // Initialize the Operations map - os.Operations = make(map[OperationType]ScopeValue) + *os = OperationScope{} - for opType, rawMessage := range tempScope.Operations { + for opType, rawMessage := range tempScope { var stringValue string var stringArrayValue []string // Try to unmarshal as string if err := json.Unmarshal(rawMessage, &stringValue); err == nil { - switch { - case stringValue == "*": - os.Operations[opType] = &AnyIDs{} - default: - os.Operations[opType] = &SelectedIDs{stringValue: {}} + if err := os.Add(opType, stringValue); err != nil { + return err } continue } // Try to unmarshal as []string if err := json.Unmarshal(rawMessage, &stringArrayValue); err == nil { - sids := make(SelectedIDs) - for _, stringVal := range stringArrayValue { - sids[stringVal] = struct{}{} + if err := os.Add(opType, stringArrayValue...); err != nil { + return err } - os.Operations[opType] = &sids continue } @@ -329,11 +301,29 @@ func (os *OperationScope) UnmarshalJSON(data []byte) error { return nil } +func (os OperationScope) MarshalJSON() ([]byte, error) { + tempOperationScope := make(map[OperationType]interface{}) + for oType, scope := range os { + value := scope.Values() + if len(value) == 1 && value[0] == "*" { + tempOperationScope[oType] = "*" + continue + } + tempOperationScope[oType] = value + } + + b, err := json.Marshal(tempOperationScope) + if err != nil { + return nil, err + } + return b, nil +} + func (os *OperationScope) Add(operation OperationType, entityIDs ...string) error { var value ScopeValue - if os == nil || os.Operations == nil { - os.Operations = make(map[OperationType]ScopeValue) + if os == nil { + os = &OperationScope{} } if len(entityIDs) == 0 { @@ -355,16 +345,16 @@ func (os *OperationScope) Add(operation OperationType, entityIDs ...string) erro } value = &sids } - os.Operations[operation] = value + (*os)[operation] = value return nil } func (os *OperationScope) Delete(operation OperationType, entityIDs ...string) error { - if os == nil || os.Operations == nil { + if os == nil { return nil } - opEntityIDs, exists := os.Operations[operation] + opEntityIDs, exists := (*os)[operation] if !exists { return nil } @@ -378,7 +368,7 @@ func (os *OperationScope) Delete(operation OperationType, entityIDs ...string) e if !(len(entityIDs) == 1 && entityIDs[0] == "*") { return fmt.Errorf("failed to delete operation %s: invalid list", operation.String()) } - delete(os.Operations, operation) + delete((*os), operation) return nil case *SelectedIDs: for _, entityID := range entityIDs { @@ -389,7 +379,7 @@ func (os *OperationScope) Delete(operation OperationType, entityIDs ...string) e for _, entityID := range entityIDs { delete(*eIDs, entityID) if len(*eIDs) == 0 { - delete(os.Operations, operation) + delete((*os), operation) } } return nil @@ -399,11 +389,11 @@ func (os *OperationScope) Delete(operation OperationType, entityIDs ...string) e } func (os *OperationScope) Check(operation OperationType, entityIDs ...string) bool { - if os == nil || os.Operations == nil { + if os == nil { return false } - if scopeValue, ok := os.Operations[operation]; ok { + if scopeValue, ok := (*os)[operation]; ok { if len(entityIDs) == 0 { _, ok := scopeValue.(*AnyIDs) return ok @@ -483,7 +473,7 @@ func (ds *DomainScope) Delete(domainEntityType DomainEntityType, operation Opera return fmt.Errorf("failed to delete domain %s scope: %w", domainEntityType.String(), err) } - if len(os.Operations) == 0 { + if len(os) == 0 { delete(ds.Entities, domainEntityType) } return nil @@ -708,14 +698,14 @@ type PATS interface { // ClearAllScope function removes all scope entry. ClearPATAllScopeEntry(ctx context.Context, token, patID string) error - // This will be removed during PR merge. TestCheckScope will check the given scope exists. - TestCheckPATScopeEntry(ctx context.Context, paToken string, platformEntityType PlatformEntityType, optionalDomainID string, optionalDomainEntityType DomainEntityType, operation OperationType, entityIDs ...string) error - // IdentifyPAT function will valid the secret. IdentifyPAT(ctx context.Context, paToken string) (PAT, error) // AuthorizePAT function will valid the secret and check the given scope exists. - AuthorizePAT(ctx context.Context, paToken string) (PAT, error) + AuthorizePAT(ctx context.Context, paToken string, platformEntityType PlatformEntityType, optionalDomainID string, optionalDomainEntityType DomainEntityType, operation OperationType, entityIDs ...string) error + + // CheckPAT function will check the given scope exists. + CheckPAT(ctx context.Context, userID, patID string, platformEntityType PlatformEntityType, optionalDomainID string, optionalDomainEntityType DomainEntityType, operation OperationType, entityIDs ...string) error } // PATSRepository specifies PATS persistence API. @@ -728,6 +718,9 @@ type PATSRepository interface { // Retrieve retrieves users PAT by its unique identifier. Retrieve(ctx context.Context, userID, patID string) (pat PAT, err error) + // RetrieveSecretAndRevokeStatus retrieves secret and revoke status of PAT by its unique identifier. + RetrieveSecretAndRevokeStatus(ctx context.Context, userID, patID string) (string, bool, error) + // UpdateName updates the name of a PAT. UpdateName(ctx context.Context, userID, patID, name string) (PAT, error) @@ -743,6 +736,9 @@ type PATSRepository interface { // Revoke PAT with provided ID. Revoke(ctx context.Context, userID, patID string) error + // Reactivate PAT with provided ID. + Reactivate(ctx context.Context, userID, patID string) error + // Remove removes Key with provided ID. Remove(ctx context.Context, userID, patID string) error diff --git a/auth/service.go b/auth/service.go index 070ddbae8a..26965a40f0 100644 --- a/auth/service.go +++ b/auth/service.go @@ -5,18 +5,24 @@ package auth import ( "context" + "encoding/base64" "fmt" + "math/rand" "strings" "time" "github.com/absmach/magistrala" "github.com/absmach/magistrala/pkg/errors" svcerr "github.com/absmach/magistrala/pkg/errors/service" + "github.com/google/uuid" ) const ( - recoveryDuration = 5 * time.Minute - defLimit = 100 + recoveryDuration = 5 * time.Minute + defLimit = 100 + randStr = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890!@#$%^&&*|+-=" + patPrefix = "pat" + patSecretSeparator = "_" ) var ( @@ -36,7 +42,11 @@ var ( errRemoveLocalPolicy = errors.New("failed to remove from local policy copy") errRemovePolicyEngine = errors.New("failed to remove from policy engine") // errInvalidEntityType indicates invalid entity type. - errInvalidEntityType = errors.New("invalid entity type") + errInvalidEntityType = errors.New("invalid entity type") + errMalformedPAT = errors.New("malformed personal access token") + errFailedToParseUUID = errors.New("failed to parse string to UUID") + errInvalidLenFor2UUIDs = errors.New("invalid input length for 2 UUID, excepted 32 byte") + errRevokedPAT = errors.New("revoked pat") ) var ( @@ -121,6 +131,7 @@ type service struct { keys KeyRepository domains DomainsRepository pats PATSRepository + hasher Hasher idProvider magistrala.IDProvider agent PolicyAgent tokenizer Tokenizer @@ -130,12 +141,13 @@ type service struct { } // New instantiates the auth service implementation. -func New(keys KeyRepository, domains DomainsRepository, pats PATSRepository, idp magistrala.IDProvider, tokenizer Tokenizer, policyAgent PolicyAgent, loginDuration, refreshDuration, invitationDuration time.Duration) Service { +func New(keys KeyRepository, domains DomainsRepository, pats PATSRepository, hasher Hasher, idp magistrala.IDProvider, tokenizer Tokenizer, policyAgent PolicyAgent, loginDuration, refreshDuration, invitationDuration time.Duration) Service { return &service{ tokenizer: tokenizer, domains: domains, keys: keys, pats: pats, + hasher: hasher, idProvider: idp, agent: policyAgent, loginDuration: loginDuration, @@ -1091,13 +1103,26 @@ func (svc service) CreatePAT(ctx context.Context, token, name, description strin if err != nil { return PAT{}, errors.Wrap(svcerr.ErrCreateEntity, err) } + secret, hash, err := svc.generateSecretAndHash(key.User, id) + if err != nil { + return PAT{}, errors.Wrap(svcerr.ErrCreateEntity, err) + } + + now := time.Now() pat := PAT{ - ID: id, - User: key.User, + ID: id, + User: key.User, + Name: name, + Description: description, + Secret: hash, + IssuedAt: now, + ExpiresAt: now.Add(duration), + Scope: scope, } if err := svc.pats.Save(ctx, pat); err != nil { return PAT{}, errors.Wrap(errCreatePAT, err) } + pat.Secret = secret return pat, nil } @@ -1167,14 +1192,23 @@ func (svc service) ResetPATSecret(ctx context.Context, token, patID string, dura return PAT{}, err } - var paTokenHash string - var newExpiry time.Time // Generate new HashToken take place here + secret, hash, err := svc.generateSecretAndHash(key.User, patID) + if err != nil { + return PAT{}, errors.Wrap(svcerr.ErrUpdateEntity, err) + } - pat, err := svc.pats.UpdateTokenHash(ctx, key.User, patID, paTokenHash, newExpiry) + pat, err := svc.pats.UpdateTokenHash(ctx, key.User, patID, hash, time.Now().Add(duration)) if err != nil { - return PAT{}, errors.Wrap(errUpdatePAT, err) + return PAT{}, errors.Wrap(svcerr.ErrUpdateEntity, err) + } + + if err := svc.pats.Reactivate(ctx, key.User, patID); err != nil { + return PAT{}, errors.Wrap(svcerr.ErrUpdateEntity, err) } + pat.Secret = secret + pat.Revoked = false + pat.RevokedAt = time.Time{} return pat, nil } @@ -1225,21 +1259,89 @@ func (svc service) ClearPATAllScopeEntry(ctx context.Context, token, patID strin return nil } -func (svc service) TestCheckPATScopeEntry(ctx context.Context, paToken string, platformEntityType PlatformEntityType, optionalDomainID string, optionalDomainEntityType DomainEntityType, operation OperationType, entityIDs ...string) error { +func (svc service) IdentifyPAT(ctx context.Context, secret string) (PAT, error) { + parts := strings.Split(secret, patSecretSeparator) + if len(parts) != 3 && parts[0] != patPrefix { + return PAT{}, errors.Wrap(svcerr.ErrAuthentication, errMalformedPAT) + } + userID, patID, err := decode(parts[1]) + if err != nil { + return PAT{}, errors.Wrap(svcerr.ErrAuthentication, errMalformedPAT) + } + secretHash, revoked, err := svc.pats.RetrieveSecretAndRevokeStatus(ctx, userID.String(), patID.String()) + if err != nil { + return PAT{}, errors.Wrap(svcerr.ErrAuthentication, err) + } + if revoked { + return PAT{}, errors.Wrap(svcerr.ErrAuthentication, errRevokedPAT) + } + if err := svc.hasher.Compare(secret, secretHash); err != nil { + return PAT{}, errors.Wrap(svcerr.ErrAuthentication, err) + } + return PAT{ID: patID.String(), User: userID.String()}, nil +} + +func (svc service) AuthorizePAT(ctx context.Context, paToken string, platformEntityType PlatformEntityType, optionalDomainID string, optionalDomainEntityType DomainEntityType, operation OperationType, entityIDs ...string) error { res, err := svc.IdentifyPAT(ctx, paToken) if err != nil { return err } - if err := svc.pats.CheckScopeEntry(ctx, res.User, paToken, platformEntityType, optionalDomainID, optionalDomainEntityType, operation, entityIDs...); err != nil { - return err + if err := svc.pats.CheckScopeEntry(ctx, res.User, res.ID, platformEntityType, optionalDomainID, optionalDomainEntityType, operation, entityIDs...); err != nil { + return errors.Wrap(svcerr.ErrAuthorization, err) } return nil } -func (svc service) IdentifyPAT(ctx context.Context, paToken string) (PAT, error) { - return PAT{}, nil +func (svc service) CheckPAT(ctx context.Context, userID, patID string, platformEntityType PlatformEntityType, optionalDomainID string, optionalDomainEntityType DomainEntityType, operation OperationType, entityIDs ...string) error { + if err := svc.pats.CheckScopeEntry(ctx, userID, patID, platformEntityType, optionalDomainID, optionalDomainEntityType, operation, entityIDs...); err != nil { + return errors.Wrap(svcerr.ErrAuthorization, err) + } + return nil +} + +func (svc service) generateSecretAndHash(userID, patID string) (string, string, error) { + uID, err := uuid.Parse(userID) + if err != nil { + return "", "", errors.Wrap(errFailedToParseUUID, err) + } + pID, err := uuid.Parse(patID) + if err != nil { + return "", "", errors.Wrap(errFailedToParseUUID, err) + } + + secret := patPrefix + patSecretSeparator + encode(uID, pID) + patSecretSeparator + generateRandomString(100) + secretHash, err := svc.hasher.Hash(secret) + return secret, secretHash, err +} + +func encode(userID, patID uuid.UUID) string { + c := append(userID[:], patID[:]...) + return base64.StdEncoding.EncodeToString(c) +} + +func decode(encoded string) (uuid.UUID, uuid.UUID, error) { + data, err := base64.StdEncoding.DecodeString(encoded) + if err != nil { + return uuid.Nil, uuid.Nil, err + } + + if len(data) != 32 { + return uuid.Nil, uuid.Nil, errInvalidLenFor2UUIDs + } + + var userID, patID uuid.UUID + copy(userID[:], data[:16]) + copy(patID[:], data[16:]) + + return userID, patID, nil } -func (svc service) AuthorizePAT(ctx context.Context, paToken string) (PAT, error) { - return PAT{}, nil +func generateRandomString(n int) string { + letterRunes := []rune(randStr) + rand.New(rand.NewSource(time.Now().UnixNano())) + b := make([]rune, n) + for i := range b { + b[i] = letterRunes[rand.Intn(len(letterRunes))] + } + return string(b) } diff --git a/auth/service_test.go b/auth/service_test.go index 09cb07dbee..4987683e47 100644 --- a/auth/service_test.go +++ b/auth/service_test.go @@ -62,6 +62,7 @@ var ( prepo *mocks.PolicyAgent drepo *mocks.DomainsRepository patsrepo *mocks.PATSRepository + hasher *mocks.Hasher ) func newService() (auth.Service, string) { @@ -69,6 +70,7 @@ func newService() (auth.Service, string) { prepo = new(mocks.PolicyAgent) drepo = new(mocks.DomainsRepository) patsrepo = new(mocks.PATSRepository) + hasher = new(mocks.Hasher) idProvider := uuid.NewMock() t := jwt.New([]byte(secret)) @@ -82,7 +84,7 @@ func newService() (auth.Service, string) { } token, _ := t.Issue(key) - return auth.New(krepo, drepo, patsrepo, idProvider, t, prepo, loginDuration, refreshDuration, invalidDuration), token + return auth.New(krepo, drepo, patsrepo, hasher, idProvider, t, prepo, loginDuration, refreshDuration, invalidDuration), token } func TestIssue(t *testing.T) { diff --git a/auth/tracing/tracing.go b/auth/tracing/tracing.go index 3419abd759..2be137e71a 100644 --- a/auth/tracing/tracing.go +++ b/auth/tracing/tracing.go @@ -419,8 +419,14 @@ func (tm *tracingMiddleware) ClearPATAllScopeEntry(ctx context.Context, token, p return tm.svc.ClearPATAllScopeEntry(ctx, token, patID) } -func (tm *tracingMiddleware) TestCheckPATScopeEntry(ctx context.Context, paToken string, platformEntityType auth.PlatformEntityType, optionalDomainID string, optionalDomainEntityType auth.DomainEntityType, operation auth.OperationType, entityIDs ...string) error { - ctx, span := tm.tracer.Start(ctx, "test_check_pat_scope_entry", trace.WithAttributes( +func (tm *tracingMiddleware) IdentifyPAT(ctx context.Context, paToken string) (auth.PAT, error) { + ctx, span := tm.tracer.Start(ctx, "identity_pat") + defer span.End() + return tm.svc.IdentifyPAT(ctx, paToken) +} + +func (tm *tracingMiddleware) AuthorizePAT(ctx context.Context, paToken string, platformEntityType auth.PlatformEntityType, optionalDomainID string, optionalDomainEntityType auth.DomainEntityType, operation auth.OperationType, entityIDs ...string) error { + ctx, span := tm.tracer.Start(ctx, "authorize_pat", trace.WithAttributes( attribute.String("personal_access_token", paToken), attribute.String("platform_entity", platformEntityType.String()), attribute.String("optional_domain_id", optionalDomainID), @@ -429,17 +435,19 @@ func (tm *tracingMiddleware) TestCheckPATScopeEntry(ctx context.Context, paToken attribute.StringSlice("entities", entityIDs), )) defer span.End() - return tm.svc.TestCheckPATScopeEntry(ctx, paToken, platformEntityType, optionalDomainID, optionalDomainEntityType, operation, entityIDs...) + return tm.svc.AuthorizePAT(ctx, paToken, platformEntityType, optionalDomainID, optionalDomainEntityType, operation, entityIDs...) } -func (tm *tracingMiddleware) IdentifyPAT(ctx context.Context, paToken string) (auth.PAT, error) { - ctx, span := tm.tracer.Start(ctx, "identity_pat") - defer span.End() - return tm.svc.IdentifyPAT(ctx, paToken) -} - -func (tm *tracingMiddleware) AuthorizePAT(ctx context.Context, paToken string) (auth.PAT, error) { - ctx, span := tm.tracer.Start(ctx, "authorize_pat") +func (tm *tracingMiddleware) CheckPAT(ctx context.Context, userID, patID string, platformEntityType auth.PlatformEntityType, optionalDomainID string, optionalDomainEntityType auth.DomainEntityType, operation auth.OperationType, entityIDs ...string) error { + ctx, span := tm.tracer.Start(ctx, "check_pat", trace.WithAttributes( + attribute.String("user_id", userID), + attribute.String("patID", patID), + attribute.String("platform_entity", platformEntityType.String()), + attribute.String("optional_domain_id", optionalDomainID), + attribute.String("optional_domain_entity", optionalDomainEntityType.String()), + attribute.String("operation", operation.String()), + attribute.StringSlice("entities", entityIDs), + )) defer span.End() - return tm.svc.AuthorizePAT(ctx, paToken) + return tm.svc.CheckPAT(ctx, userID, patID, platformEntityType, optionalDomainID, optionalDomainEntityType, operation, entityIDs...) } diff --git a/cmd/auth/main.go b/cmd/auth/main.go index 5c44e0e54e..67f2c254ff 100644 --- a/cmd/auth/main.go +++ b/cmd/auth/main.go @@ -20,6 +20,7 @@ import ( httpapi "github.com/absmach/magistrala/auth/api/http" "github.com/absmach/magistrala/auth/bolt" "github.com/absmach/magistrala/auth/events" + "github.com/absmach/magistrala/auth/hasher" "github.com/absmach/magistrala/auth/jwt" apostgres "github.com/absmach/magistrala/auth/postgres" "github.com/absmach/magistrala/auth/spicedb" @@ -39,6 +40,7 @@ import ( "github.com/authzed/grpcutil" "github.com/caarlos0/env/v10" "github.com/jmoiron/sqlx" + "go.etcd.io/bbolt" "go.opentelemetry.io/otel/trace" "golang.org/x/sync/errgroup" "google.golang.org/grpc" @@ -139,17 +141,15 @@ func main() { return } - client, err := boltclient.Connect(boltDBConfig, bolt.Init) + bClient, err := boltclient.Connect(boltDBConfig, bolt.Init) if err != nil { logger.Error(fmt.Sprintf("failed to connect to bolt db : %s\n", err.Error())) exitCode = 1 return } - defer client.Close() + defer bClient.Close() - patsRepo := bolt.NewPATSRepository(client, boltDBConfig.Bucket) - - svc := newService(ctx, db, tracer, cfg, dbConfig, logger, spicedbclient, patsRepo) + svc := newService(ctx, db, tracer, cfg, dbConfig, logger, spicedbclient, bClient, boltDBConfig) httpServerConfig := server.Config{Port: defSvcHTTPPort} if err := env.ParseWithOptions(&httpServerConfig, env.Options{Prefix: envPrefixHTTP}); err != nil { @@ -223,16 +223,18 @@ func initSchema(ctx context.Context, client *authzed.ClientWithExperimental, sch return nil } -func newService(ctx context.Context, db *sqlx.DB, tracer trace.Tracer, cfg config, dbConfig pgclient.Config, logger *slog.Logger, spicedbClient *authzed.ClientWithExperimental, pats auth.PATSRepository) auth.Service { +func newService(ctx context.Context, db *sqlx.DB, tracer trace.Tracer, cfg config, dbConfig pgclient.Config, logger *slog.Logger, spicedbClient *authzed.ClientWithExperimental, bClient *bbolt.DB, bConfig boltclient.Config) auth.Service { database := postgres.NewDatabase(db, dbConfig, tracer) keysRepo := apostgres.New(database) domainsRepo := apostgres.NewDomainRepository(database) + patsRepo := bolt.NewPATSRepository(bClient, bConfig.Bucket) + hasher := hasher.New() pa := spicedb.NewPolicyAgent(spicedbClient, logger) idProvider := uuid.New() t := jwt.New([]byte(cfg.SecretKey)) - svc := auth.New(keysRepo, domainsRepo, pats, idProvider, t, pa, cfg.AccessDuration, cfg.RefreshDuration, cfg.InvitationDuration) + svc := auth.New(keysRepo, domainsRepo, patsRepo, hasher, idProvider, t, pa, cfg.AccessDuration, cfg.RefreshDuration, cfg.InvitationDuration) svc, err := events.NewEventStoreMiddleware(ctx, svc, cfg.ESURL) if err != nil { logger.Error(fmt.Sprintf("failed to init event store middleware : %s", err)) From 0edc405a07f97df49724f8e083fa3817f4984d93 Mon Sep 17 00:00:00 2001 From: Arvindh Date: Fri, 12 Jul 2024 20:29:13 +0530 Subject: [PATCH 24/24] go mod tidy Signed-off-by: Arvindh --- go.mod | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/go.mod b/go.mod index 165f34793e..f81a340d75 100644 --- a/go.mod +++ b/go.mod @@ -19,6 +19,7 @@ require ( github.com/go-redis/redis/v8 v8.11.5 github.com/gocql/gocql v1.6.0 github.com/gofrs/uuid v4.4.0+incompatible + github.com/google/uuid v1.6.0 github.com/gookit/color v1.5.4 github.com/gopcua/opcua v0.1.6 github.com/gorilla/websocket v1.5.3 @@ -101,7 +102,6 @@ require ( github.com/gogo/protobuf v1.3.2 // indirect github.com/golang/snappy v0.0.4 // indirect github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect - github.com/google/uuid v1.6.0 // indirect github.com/grpc-ecosystem/go-grpc-middleware v1.4.0 // indirect github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0 // indirect github.com/hailocab/go-hostpool v0.0.0-20160125115350-e80d13ce29ed // indirect