From 9a3a710f8b40bd354ff514d0db968baceb0ead4d Mon Sep 17 00:00:00 2001 From: nyagamunene Date: Mon, 28 Oct 2024 15:09:07 +0300 Subject: [PATCH 01/72] First implementation Signed-off-by: nyagamunene --- go.mod | 1 + pat/bolt/doc.go | 6 + pat/bolt/init.go | 21 + pat/bolt/pat.go | 773 ++++++++++++++++++++++++++++++++ pat/hasher.go | 17 + pat/hasher/doc.go | 6 + pat/hasher/hasher.go | 86 ++++ pat/middleware/authorization.go | 4 + pat/middleware/logging.go | 275 ++++++++++++ pat/middleware/metrics.go | 4 + pat/pat.go | 752 +++++++++++++++++++++++++++++++ pat/service.go | 302 +++++++++++++ 12 files changed, 2247 insertions(+) create mode 100644 pat/bolt/doc.go create mode 100644 pat/bolt/init.go create mode 100644 pat/bolt/pat.go create mode 100644 pat/hasher.go create mode 100644 pat/hasher/doc.go create mode 100644 pat/hasher/hasher.go create mode 100644 pat/middleware/authorization.go create mode 100644 pat/middleware/logging.go create mode 100644 pat/middleware/metrics.go create mode 100644 pat/pat.go create mode 100644 pat/service.go diff --git a/go.mod b/go.mod index f79cb10647..072a6485ac 100644 --- a/go.mod +++ b/go.mod @@ -169,6 +169,7 @@ require ( github.com/xeipuuv/gojsonschema v1.2.0 // indirect github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect go.opentelemetry.io/auto/sdk v1.1.0 // indirect + go.etcd.io/bbolt v1.3.11 // indirect go.opentelemetry.io/otel/metric v1.33.0 // indirect go.opentelemetry.io/proto/otlp v1.4.0 // indirect go.uber.org/atomic v1.11.0 // indirect diff --git a/pat/bolt/doc.go b/pat/bolt/doc.go new file mode 100644 index 0000000000..dcd06ac566 --- /dev/null +++ b/pat/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/pat/bolt/init.go b/pat/bolt/init.go new file mode 100644 index 0000000000..9d496e65ca --- /dev/null +++ b/pat/bolt/init.go @@ -0,0 +1,21 @@ +// Copyright (c) Abstract Machines +// SPDX-License-Identifier: Apache-2.0 + +// 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/pat/bolt/pat.go b/pat/bolt/pat.go new file mode 100644 index 0000000000..4534dc4e85 --- /dev/null +++ b/pat/bolt/pat.go @@ -0,0 +1,773 @@ +// Copyright (c) Abstract Machines +// SPDX-License-Identifier: Apache-2.0 + +package bolt + +import ( + "bytes" + "context" + "encoding/binary" + "fmt" + "strings" + "time" + + "github.com/absmach/magistrala/auth" + "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" + secretKey = "secret_key" + scopeKey = "scope" + issuedAtKey = "issued_at" + expiresAtKey = "expires_at" + updatedAtKey = "updated_at" + lastUsedAtKey = "last_used_at" + revokedKey = "revoked" + revokedAtKey = "revoked_at" + platformEntitiesKey = "platform_entities" + patKey = "pat" + + keySeparator = ":" + anyID = "*" +) + +var ( + activateValue = []byte{0x00} + revokedValue = []byte{0x01} + entityValue = []byte{0x02} + anyIDValue = []byte{0x03} + selectedIDsValue = []byte{0x04} +) + +type patRepo struct { + db *bolt.DB + bucketName string +} + +// NewPATSRepository instantiates a bolt +// implementation of PAT repository. +func NewPATSRepository(db *bolt.DB, bucketName string) auth.PATSRepository { + return &patRepo{ + db: db, + bucketName: bucketName, + } +} + +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 + } + return pr.db.Update(func(tx *bolt.Tx) error { + 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) + } + 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) + } + } + if err := rootBucket.Put(idxKey, []byte(pat.ID)); err != nil { + return errors.Wrap(repoerr.ErrCreateEntity, err) + } + return nil + }) +} + +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.View(func(tx *bolt.Tx) error { + b, err := pr.retrieveUserBucket(tx, userID, patID, repoerr.ErrViewEntity) + 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 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)) +} + +func (pr *patRepo) UpdateDescription(ctx context.Context, userID, patID, description string) (auth.PAT, error) { + 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) { + 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+secretKey), []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) + } + 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) RetrieveAll(ctx context.Context, userID string, pm auth.PATSPageMeta) (auth.PATSPage, error) { + prefix := []byte(userID + keySeparator + patKey + keySeparator) + + patIDs := []string{} + if err := pr.db.View(func(tx *bolt.Tx) error { + b, err := pr.retrieveRootBucket(tx) + if err != nil { + 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() { + if v != nil { + patIDs = append(patIDs, string(v)) + } + } + return nil + }); err != nil { + return auth.PATSPage{}, err + } + + total := len(patIDs) + + var pats []auth.PAT + + 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 < pm.Offset+aLimit; i++ { + if int(i) < total { + 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 { + 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), 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) 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, patID, repoerr.ErrRemoveEntity) + if err != nil { + return 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) + } + } + 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 + } + + 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) { + 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, patID, repoerr.ErrCreateEntity) + if err != nil { + return 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) + } + } + 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 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, patID, repoerr.ErrRemoveEntity) + if err != nil { + return 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) + } + } + 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 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 { + return pr.db.Update(func(tx *bolt.Tx) error { + b, err := pr.retrieveUserBucket(tx, userID, patID, repoerr.ErrViewEntity) + 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) 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 { + 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, patID string, wrap error) (*bolt.Bucket, error) { + rootBucket, err := pr.retrieveRootBucket(tx) + if err != nil { + 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, errors.Wrap(wrap, fmt.Errorf("user %s not found", userID)) + } + 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 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), + secretKey: []byte(pat.Secret), + 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 { + 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 { + 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 { + 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 keyValueToBasicPAT(kv map[string][]byte) auth.PAT { + 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 +} + +func keyValueToPAT(kv map[string][]byte) (auth.PAT, error) { + pat := keyValueToBasicPAT(kv) + 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) { + 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: + scope.Users, err = parseOperation(platformEntityType, scope.Users, key, keyParts, value) + if err != nil { + return auth.Scope{}, errors.Wrap(repoerr.ErrViewEntity, err) + } + + 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(): + 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] = domainScope + default: + 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 == nil { + opScope = make(map[auth.OperationType]auth.ScopeValue) + } + + 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[opType]; !oValueExists { + opScope[opType] = &auth.SelectedIDs{} + } + 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[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[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[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[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[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 timeToBytes(t time.Time) []byte { + timeBytes := make([]byte, 8) + binary.BigEndian.PutUint64(timeBytes, uint64(t.Unix())) + return timeBytes +} + +func bytesToTime(b []byte) time.Time { + timeAtSeconds := binary.BigEndian.Uint64(b) + return time.Unix(int64(timeAtSeconds), 0) +} + +func booleanToBytes(b bool) []byte { + if b { + return []byte{1} + } + return []byte{0} +} + +func bytesToBoolean(b []byte) bool { + if len(b) > 1 || b[0] != activateValue[0] { + return true + } + return false +} diff --git a/pat/hasher.go b/pat/hasher.go new file mode 100644 index 0000000000..b0452c1e1d --- /dev/null +++ b/pat/hasher.go @@ -0,0 +1,17 @@ +// Copyright (c) Abstract Machines +// SPDX-License-Identifier: Apache-2.0 + +package pat + +// 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/pat/hasher/doc.go b/pat/hasher/doc.go new file mode 100644 index 0000000000..98be992262 --- /dev/null +++ b/pat/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/pat/hasher/hasher.go b/pat/hasher/hasher.go new file mode 100644 index 0000000000..c417bf7b80 --- /dev/null +++ b/pat/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/pat/middleware/authorization.go b/pat/middleware/authorization.go new file mode 100644 index 0000000000..71995a25ab --- /dev/null +++ b/pat/middleware/authorization.go @@ -0,0 +1,4 @@ +// Copyright (c) Abstract Machines +// SPDX-License-Identifier: Apache-2.0 + +package middleware \ No newline at end of file diff --git a/pat/middleware/logging.go b/pat/middleware/logging.go new file mode 100644 index 0000000000..650eab5115 --- /dev/null +++ b/pat/middleware/logging.go @@ -0,0 +1,275 @@ +// Copyright (c) Abstract Machines +// SPDX-License-Identifier: Apache-2.0 + +package middleware + +import ( + "context" + "fmt" + "log/slog" + "time" + + "github.com/absmach/magistrala/pat" +) + +var _ pat.Service = (*loggingMiddleware)(nil) + +type loggingMiddleware struct { + logger *slog.Logger + svc pat.Service +} + +// LoggingMiddleware adds logging facilities to the core service. +func LoggingMiddleware(svc pat.Service, logger *slog.Logger) pat.Service { + return &loggingMiddleware{logger, svc} +} + +func (lm *loggingMiddleware) CreatePAT(ctx context.Context, token, name, description string, duration time.Duration, scope pat.Scope) (pa pat.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.CreatePAT(ctx, token, name, description, duration, scope) +} + +func (lm *loggingMiddleware) UpdatePATName(ctx context.Context, token, patID, name string) (pa pat.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.UpdatePATName(ctx, token, patID, name) +} + +func (lm *loggingMiddleware) UpdatePATDescription(ctx context.Context, token, patID, description string) (pa pat.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.UpdatePATDescription(ctx, token, patID, description) +} + +func (lm *loggingMiddleware) RetrievePAT(ctx context.Context, token, patID string) (pa pat.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.RetrievePAT(ctx, token, patID) +} + +func (lm *loggingMiddleware) ListPATS(ctx context.Context, token string, pm pat.PATSPageMeta) (pp pat.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.ListPATS(ctx, token, pm) +} + +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()), + 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.DeletePAT(ctx, token, patID) +} + +func (lm *loggingMiddleware) ResetPATSecret(ctx context.Context, token, patID string, duration time.Duration) (pa pat.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.ResetPATSecret(ctx, token, patID, duration) +} + +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()), + 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.RevokePATSecret(ctx, token, patID) +} + +func (lm *loggingMiddleware) AddPATScopeEntry(ctx context.Context, token, patID string, platformEntityType pat.PlatformEntityType, optionalDomainID string, optionalDomainEntityType pat.DomainEntityType, operation pat.OperationType, entityIDs ...string) (sc pat.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.AddPATScopeEntry(ctx, token, patID, platformEntityType, optionalDomainID, optionalDomainEntityType, operation, entityIDs...) +} + +func (lm *loggingMiddleware) RemovePATScopeEntry(ctx context.Context, token, patID string, platformEntityType pat.PlatformEntityType, optionalDomainID string, optionalDomainEntityType pat.DomainEntityType, operation pat.OperationType, entityIDs ...string) (sc pat.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.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{ + 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.ClearPATAllScopeEntry(ctx, token, patID) +} + +func (lm *loggingMiddleware) IdentifyPAT(ctx context.Context, paToken string) (pa pat.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, platformEntityType pat.PlatformEntityType, optionalDomainID string, optionalDomainEntityType pat.DomainEntityType, operation pat.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("Authorize PAT failed complete successfully", args...) + return + } + lm.logger.Info("Authorize PAT completed successfully", args...) + }(time.Now()) + return lm.svc.AuthorizePAT(ctx, paToken, platformEntityType, optionalDomainID, optionalDomainEntityType, operation, entityIDs...) +} + +func (lm *loggingMiddleware) CheckPAT(ctx context.Context, userID, patID string, platformEntityType pat.PlatformEntityType, optionalDomainID string, optionalDomainEntityType pat.DomainEntityType, operation pat.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("Check PAT failed complete successfully", args...) + return + } + lm.logger.Info("Check PAT completed successfully", args...) + }(time.Now()) + return lm.svc.CheckPAT(ctx, userID, patID, platformEntityType, optionalDomainID, optionalDomainEntityType, operation, entityIDs...) +} \ No newline at end of file diff --git a/pat/middleware/metrics.go b/pat/middleware/metrics.go new file mode 100644 index 0000000000..b41a88a838 --- /dev/null +++ b/pat/middleware/metrics.go @@ -0,0 +1,4 @@ +// Copyright (c) Abstract Machines +// SPDX-License-Identifier: Apache-2.0 + +package middleware diff --git a/pat/pat.go b/pat/pat.go new file mode 100644 index 0000000000..8e427b4332 --- /dev/null +++ b/pat/pat.go @@ -0,0 +1,752 @@ +// Copyright (c) Abstract Machines +// SPDX-License-Identifier: Apache-2.0 + +package pat + +import ( + "context" + "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. +type OperationType uint32 + +const ( + CreateOp OperationType = iota + ReadOp + ListOp + UpdateOp + DeleteOp +) + +const ( + createOpStr = "create" + readOpStr = "read" + listOpStr = "list" + updateOpStr = "update" + deleteOpStr = "delete" +) + +func (ot OperationType) String() string { + switch ot { + case CreateOp: + return createOpStr + case ReadOp: + return readOpStr + case ListOp: + return listOpStr + case UpdateOp: + return updateOpStr + case DeleteOp: + return deleteOpStr + default: + return fmt.Sprintf("unknown operation type %d", ot) + } +} + +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: + return CreateOp, nil + case readOpStr: + return ReadOp, nil + case listOpStr: + return ListOp, nil + case updateOpStr: + return UpdateOp, nil + case deleteOpStr: + return DeleteOp, nil + default: + return 0, fmt.Errorf("unknown operation type %s", 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 +} + +func (ot *OperationType) UnmarshalText(data []byte) (err error) { + *ot, err = ParseOperationType(string(data)) + return err +} + +// Define DomainEntityType. +type DomainEntityType uint32 + +const ( + DomainManagementScope DomainEntityType = iota + DomainGroupsScope + DomainChannelsScope + DomainThingsScope + DomainNullScope +) + +const ( + domainManagementScopeStr = "domain_management" + domainGroupsScopeStr = "groups" + domainChannelsScopeStr = "channels" + domainThingsScopeStr = "things" +) + +func (det DomainEntityType) String() string { + switch det { + case DomainManagementScope: + return domainManagementScopeStr + case DomainGroupsScope: + return domainGroupsScopeStr + case DomainChannelsScope: + return domainChannelsScopeStr + case DomainThingsScope: + return domainThingsScopeStr + default: + return fmt.Sprintf("unknown domain entity type %d", det) + } +} + +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: + return DomainManagementScope, nil + case domainGroupsScopeStr: + return DomainGroupsScope, nil + case domainChannelsScopeStr: + return DomainChannelsScope, nil + case domainThingsScopeStr: + 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() ([]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 + +const ( + PlatformUsersScope PlatformEntityType = iota + PlatformDomainsScope +) + +const ( + platformUsersScopeStr = "users" + platformDomainsScopeStr = "domains" +) + +func (pet PlatformEntityType) String() string { + switch pet { + case PlatformUsersScope: + return platformUsersScopeStr + case PlatformDomainsScope: + return platformDomainsScopeStr + default: + return fmt.Sprintf("unknown platform entity type %d", pet) + } +} + +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: + return PlatformUsersScope, nil + case platformDomainsScopeStr: + 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 +} + +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 + 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) 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{} + +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 +} + +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 map[OperationType]ScopeValue + +func (os *OperationScope) UnmarshalJSON(data []byte) error { + type tempOperationScope map[OperationType]json.RawMessage + + var tempScope tempOperationScope + if err := json.Unmarshal(data, &tempScope); err != nil { + return err + } + // Initialize the Operations map + *os = OperationScope{} + + for opType, rawMessage := range tempScope { + var stringValue string + var stringArrayValue []string + + // Try to unmarshal as string + if err := json.Unmarshal(rawMessage, &stringValue); err == nil { + if err := os.Add(opType, stringValue); err != nil { + return err + } + continue + } + + // Try to unmarshal as []string + if err := json.Unmarshal(rawMessage, &stringArrayValue); err == nil { + if err := os.Add(opType, stringArrayValue...); err != nil { + return err + } + continue + } + + // If neither unmarshalling succeeded, return an error + return fmt.Errorf("invalid ScopeValue for OperationType %v", opType) + } + + 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 = &OperationScope{} + } + + if len(entityIDs) == 0 { + return fmt.Errorf("entity ID is missing") + } + switch { + 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) + } + sids[entityID] = struct{}{} + } + value = &sids + } + (*os)[operation] = value + return nil +} + +func (os *OperationScope) Delete(operation OperationType, entityIDs ...string) error { + if os == nil { + return nil + } + + opEntityIDs, exists := (*os)[operation] + if !exists { + return nil + } + + if len(entityIDs) == 0 { + return fmt.Errorf("failed to delete operation %s: entity ID is missing", operation.String()) + } + + switch eIDs := opEntityIDs.(type) { + case *AnyIDs: + if !(len(entityIDs) == 1 && entityIDs[0] == "*") { + return fmt.Errorf("failed to delete operation %s: invalid list", operation.String()) + } + delete((*os), operation) + return nil + 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((*os), operation) + } + } + return nil + default: + return fmt.Errorf("failed to delete operation: invalid entity id type %d", operation) + } +} + +func (os *OperationScope) Check(operation OperationType, entityIDs ...string) bool { + if os == nil { + return false + } + + if scopeValue, ok := (*os)[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) == 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 + } + if domainEntityType == DomainManagementScope { + return ds.DomainManagement.Check(operation, ids...) + } + os, exists := ds.Entities[domainEntityType] + if !exists { + return false + } + + return os.Check(operation, ids...) +} + +// Example Scope as JSON +// +// { +// "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.Marshal(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"` + Name string `json:"name,omitempty"` + Description string `json:"description,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"` + 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 PATSPageMeta struct { + Offset uint64 `json:"offset"` + Limit uint64 `json:"limit"` +} +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()) + } + return string(str) +} + +// Expired verifies if the key is expired. +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. + 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. + UpdatePATName(ctx context.Context, token, patID, name string) (PAT, error) + + // UpdateDescription function updates the description for the given PAT ID. + UpdatePATDescription(ctx context.Context, token, patID, description string) (PAT, error) + + // Retrieve function retrieves the PAT for given ID. + RetrievePAT(ctx context.Context, token, patID string) (PAT, error) + + // List function lists all the PATs for the user. + ListPATS(ctx context.Context, token string, pm PATSPageMeta) (PATSPage, error) + + // Delete function deletes the PAT for given ID. + DeletePAT(ctx context.Context, token, patID string) error + + // ResetSecret function reset the secret and creates new secret for the given ID. + ResetPATSecret(ctx context.Context, token, patID string, duration time.Duration) (PAT, error) + + // RevokeSecret function revokes the secret for the given ID. + RevokePATSecret(ctx context.Context, token, patID string) error + + // AddScope function adds a new scope entry. + 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. + 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. + ClearPATAllScopeEntry(ctx context.Context, token, patID 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, 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. +// +//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) (err error) + + // 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) + + // 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) + + // RetrieveAll retrieves all PATs belongs to userID. + 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 + + // 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 + + 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) + + 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/pat/service.go b/pat/service.go new file mode 100644 index 0000000000..5161e3cb57 --- /dev/null +++ b/pat/service.go @@ -0,0 +1,302 @@ +// Copyright (c) Abstract Machines +// SPDX-License-Identifier: Apache-2.0 + +package pat + +import ( + "context" + "encoding/base64" + "math/rand" + "strings" + "time" + + "github.com/absmach/magistrala/pkg/errors" + svcerr "github.com/absmach/magistrala/pkg/errors/service" +) + +const ( + randStr = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890!@#$%^&&*|+-=" + patPrefix = "pat" + patSecretSeparator = "_" +) + +var ( + 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") + 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") + errClearAllScope = errors.New("failed to clear all entry in scope") +) + +type Service struct { + pats PATSRepository + hasher Hasher +} + +var _ Service = (*Service)(nil) + +// New instantiates the auth service implementation. +func New(pats PATSRepository, hasher Hasher) Service { + return &Service{ + pats: pats, + hasher: hasher, + } +} + +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 + } + + id, err := svc.idProvider.ID() + 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, + 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 +} + +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 + } + 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) UpdatePATDescription(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) RetrievePAT(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) ListPATS(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, pm) + if err != nil { + return PATSPage{}, errors.Wrap(errRetrievePAT, err) + } + return patsPage, nil +} + +func (svc Service) DeletePAT(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) ResetPATSecret(ctx context.Context, token, patID string, duration time.Duration) (PAT, error) { + key, err := svc.Identify(ctx, token) + if err != nil { + return PAT{}, err + } + + // 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, hash, time.Now().Add(duration)) + if err != nil { + 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 +} + +func (svc Service) RevokePATSecret(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) 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 + } + 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 { + return Scope{}, err + } + 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) ClearPATAllScopeEntry(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) 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, res.ID, platformEntityType, optionalDomainID, optionalDomainEntityType, operation, entityIDs...); err != nil { + return errors.Wrap(svcerr.ErrAuthorization, err) + } + return 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 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) +} From 33db8b32d5a9cca3aee13172d12098d9ca7dcea6 Mon Sep 17 00:00:00 2001 From: nyagamunene Date: Mon, 28 Oct 2024 20:29:11 +0300 Subject: [PATCH 02/72] Update middleware, service and api Signed-off-by: nyagamunene --- pat/api/endpoint.go | 259 +++++++++++++++++++++++ pat/api/requests.go | 361 ++++++++++++++++++++++++++++++++ pat/api/responses.go | 208 ++++++++++++++++++ pat/api/transport.go | 271 ++++++++++++++++++++++++ pat/bolt/pat.go | 158 +++++++------- pat/hasher/hasher.go | 6 +- pat/middleware/authorization.go | 82 +++++++- pat/middleware/logging.go | 48 ++--- pat/middleware/metrics.go | 138 ++++++++++++ pat/pat.go | 27 +-- pat/service.go | 127 ++++------- pat/tracing/tracing.go | 164 +++++++++++++++ 12 files changed, 1644 insertions(+), 205 deletions(-) create mode 100644 pat/api/endpoint.go create mode 100644 pat/api/requests.go create mode 100644 pat/api/responses.go create mode 100644 pat/api/transport.go create mode 100644 pat/tracing/tracing.go diff --git a/pat/api/endpoint.go b/pat/api/endpoint.go new file mode 100644 index 0000000000..94cbdf5e80 --- /dev/null +++ b/pat/api/endpoint.go @@ -0,0 +1,259 @@ +// Copyright (c) Abstract Machines +// SPDX-License-Identifier: Apache-2.0 + +package pats + +import ( + "context" + + "github.com/absmach/magistrala/internal/api" + "github.com/absmach/magistrala/pat" + "github.com/absmach/magistrala/pkg/authn" + svcerr "github.com/absmach/magistrala/pkg/errors/service" + "github.com/go-kit/kit/endpoint" +) + +func createPATEndpoint(svc pat.Service) endpoint.Endpoint { + return func(ctx context.Context, request interface{}) (interface{}, error) { + req := request.(createPatReq) + if err := req.validate(); err != nil { + return nil, err + } + + session, ok := ctx.Value(api.SessionKey).(authn.Session) + if !ok { + return nil, svcerr.ErrAuthentication + } + + pat, err := svc.CreatePAT(ctx, session, req.Name, req.Description, req.Duration, req.Scope) + if err != nil { + return nil, err + } + + return createPatRes{pat}, nil + } +} + +func retrievePATEndpoint(svc pat.Service) endpoint.Endpoint { + return func(ctx context.Context, request interface{}) (interface{}, error) { + req := request.(retrievePatReq) + if err := req.validate(); err != nil { + return nil, err + } + + session, ok := ctx.Value(api.SessionKey).(authn.Session) + if !ok { + return nil, svcerr.ErrAuthentication + } + + pat, err := svc.RetrievePAT(ctx, session, req.id) + if err != nil { + return nil, err + } + + return retrievePatRes{pat}, nil + } +} + +func updatePATNameEndpoint(svc pat.Service) endpoint.Endpoint { + return func(ctx context.Context, request interface{}) (interface{}, error) { + req := request.(updatePatNameReq) + if err := req.validate(); err != nil { + return nil, err + } + + session, ok := ctx.Value(api.SessionKey).(authn.Session) + if !ok { + return nil, svcerr.ErrAuthentication + } + pat, err := svc.UpdatePATName(ctx, session, req.id, req.Name) + if err != nil { + return nil, err + } + + return updatePatNameRes{pat}, nil + } +} + +func updatePATDescriptionEndpoint(svc pat.Service) endpoint.Endpoint { + return func(ctx context.Context, request interface{}) (interface{}, error) { + req := request.(updatePatDescriptionReq) + if err := req.validate(); err != nil { + return nil, err + } + + session, ok := ctx.Value(api.SessionKey).(authn.Session) + if !ok { + return nil, svcerr.ErrAuthentication + } + + pat, err := svc.UpdatePATDescription(ctx, session, req.id, req.Description) + if err != nil { + return nil, err + } + + return updatePatDescriptionRes{pat}, nil + } +} + +func listPATSEndpoint(svc pat.Service) endpoint.Endpoint { + return func(ctx context.Context, request interface{}) (interface{}, error) { + req := request.(listPatsReq) + if err := req.validate(); err != nil { + return nil, err + } + + session, ok := ctx.Value(api.SessionKey).(authn.Session) + if !ok { + return nil, svcerr.ErrAuthentication + } + + pm := pat.PATSPageMeta{ + Limit: req.limit, + Offset: req.offset, + } + patsPage, err := svc.ListPATS(ctx, session, pm) + if err != nil { + return nil, err + } + + return listPatsRes{patsPage}, nil + } +} + +func deletePATEndpoint(svc pat.Service) endpoint.Endpoint { + return func(ctx context.Context, request interface{}) (interface{}, error) { + req := request.(deletePatReq) + if err := req.validate(); err != nil { + return nil, err + } + + session, ok := ctx.Value(api.SessionKey).(authn.Session) + if !ok { + return nil, svcerr.ErrAuthentication + } + + if err := svc.DeletePAT(ctx, session, req.id); err != nil { + return nil, err + } + + return deletePatRes{}, nil + } +} + +func resetPATSecretEndpoint(svc pat.Service) endpoint.Endpoint { + return func(ctx context.Context, request interface{}) (interface{}, error) { + req := request.(resetPatSecretReq) + if err := req.validate(); err != nil { + return nil, err + } + + session, ok := ctx.Value(api.SessionKey).(authn.Session) + if !ok { + return nil, svcerr.ErrAuthentication + } + + pat, err := svc.ResetPATSecret(ctx, session, req.id, req.Duration) + if err != nil { + return nil, err + } + + return resetPatSecretRes{pat}, nil + } +} + +func revokePATSecretEndpoint(svc pat.Service) endpoint.Endpoint { + return func(ctx context.Context, request interface{}) (interface{}, error) { + req := request.(revokePatSecretReq) + if err := req.validate(); err != nil { + return nil, err + } + + session, ok := ctx.Value(api.SessionKey).(authn.Session) + if !ok { + return nil, svcerr.ErrAuthentication + } + + if err := svc.RevokePATSecret(ctx, session, req.id); err != nil { + return nil, err + } + + return revokePatSecretRes{}, nil + } +} + +func addPATScopeEntryEndpoint(svc pat.Service) endpoint.Endpoint { + return func(ctx context.Context, request interface{}) (interface{}, error) { + req := request.(addPatScopeEntryReq) + if err := req.validate(); err != nil { + return nil, err + } + + session, ok := ctx.Value(api.SessionKey).(authn.Session) + if !ok { + return nil, svcerr.ErrAuthentication + } + + scope, err := svc.AddPATScopeEntry(ctx, session, req.id, req.PlatformEntityType, req.OptionalDomainID, req.OptionalDomainEntityType, req.Operation, req.EntityIDs...) + if err != nil { + return nil, err + } + + return addPatScopeEntryRes{scope}, nil + } +} + +func removePATScopeEntryEndpoint(svc pat.Service) endpoint.Endpoint { + return func(ctx context.Context, request interface{}) (interface{}, error) { + req := request.(removePatScopeEntryReq) + if err := req.validate(); err != nil { + return nil, err + } + + session, ok := ctx.Value(api.SessionKey).(authn.Session) + if !ok { + return nil, svcerr.ErrAuthentication + } + + scope, err := svc.RemovePATScopeEntry(ctx, session, req.id, req.PlatformEntityType, req.OptionalDomainID, req.OptionalDomainEntityType, req.Operation, req.EntityIDs...) + if err != nil { + return nil, err + } + return removePatScopeEntryRes{scope}, nil + } +} + +func clearPATAllScopeEntryEndpoint(svc pat.Service) endpoint.Endpoint { + return func(ctx context.Context, request interface{}) (interface{}, error) { + req := request.(clearAllScopeEntryReq) + if err := req.validate(); err != nil { + return nil, err + } + + session, ok := ctx.Value(api.SessionKey).(authn.Session) + if !ok { + return nil, svcerr.ErrAuthentication + } + + if err := svc.ClearPATAllScopeEntry(ctx, session, req.id); err != nil { + return nil, err + } + + return clearAllScopeEntryRes{}, nil + } +} + +func authorizePATEndpoint(svc pat.Service) endpoint.Endpoint { + return func(ctx context.Context, request interface{}) (interface{}, error) { + req := request.(authorizePATReq) + if err := req.validate(); err != nil { + return nil, err + } + + if err := svc.AuthorizePAT(ctx, req.token, req.PlatformEntityType, req.OptionalDomainID, req.OptionalDomainEntityType, req.Operation, req.EntityIDs...); err != nil { + return nil, err + } + + return authorizePATRes{}, nil + } +} diff --git a/pat/api/requests.go b/pat/api/requests.go new file mode 100644 index 0000000000..1822ec457e --- /dev/null +++ b/pat/api/requests.go @@ -0,0 +1,361 @@ +// Copyright (c) Abstract Machines +// SPDX-License-Identifier: Apache-2.0 + +package pats + +import ( + "encoding/json" + "strings" + "time" + + "github.com/absmach/magistrala/pat" + "github.com/absmach/magistrala/pkg/apiutil" +) + +type createPatReq struct { + token string + Name string `json:"name,omitempty"` + Description string `json:"description,omitempty"` + Duration time.Duration `json:"duration,omitempty"` + Scope pat.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 pat.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 + } + + 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 pat.PlatformEntityType `json:"platform_entity_type,omitempty"` + OptionalDomainID string `json:"optional_domain_id,omitempty"` + OptionalDomainEntityType pat.DomainEntityType `json:"optional_domain_entity_type,omitempty"` + Operation pat.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 := pat.ParsePlatformEntityType(temp.PlatformEntityType) + if err != nil { + return err + } + odt, err := pat.ParseDomainEntityType(temp.OptionalDomainEntityType) + if err != nil { + return err + } + op, err := pat.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 pat.PlatformEntityType `json:"platform_entity_type,omitempty"` + OptionalDomainID string `json:"optional_domain_id,omitempty"` + OptionalDomainEntityType pat.DomainEntityType `json:"optional_domain_entity_type,omitempty"` + Operation pat.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 := pat.ParsePlatformEntityType(temp.PlatformEntityType) + if err != nil { + return err + } + odt, err := pat.ParseDomainEntityType(temp.OptionalDomainEntityType) + if err != nil { + return err + } + op, err := pat.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 authorizePATReq struct { + token string + PlatformEntityType pat.PlatformEntityType `json:"platform_entity_type,omitempty"` + OptionalDomainID string `json:"optional_domain_id,omitempty"` + OptionalDomainEntityType pat.DomainEntityType `json:"optional_domain_entity_type,omitempty"` + Operation pat.OperationType `json:"operation,omitempty"` + EntityIDs []string `json:"entity_ids,omitempty"` +} + +func (tcpsr *authorizePATReq) 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 + } + + tcpsr.OptionalDomainID = temp.OptionalDomainID + tcpsr.EntityIDs = temp.EntityIDs + + pet, err := pat.ParsePlatformEntityType(temp.PlatformEntityType) + if err != nil { + return err + } + tcpsr.PlatformEntityType = pet + + if temp.OptionalDomainEntityType != "" { + odt, err := pat.ParseDomainEntityType(temp.OptionalDomainEntityType) + if err != nil { + return err + } + tcpsr.OptionalDomainEntityType = odt + } + + if temp.OptionalDomainID != "" { + op, err := pat.ParseOperationType(temp.Operation) + if err != nil { + return err + } + tcpsr.Operation = op + } + + return nil +} + +func (req authorizePATReq) validate() (err error) { + if req.token == "" { + return apiutil.ErrBearerToken + } + + return nil +} diff --git a/pat/api/responses.go b/pat/api/responses.go new file mode 100644 index 0000000000..b2d0fe57de --- /dev/null +++ b/pat/api/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/pat" +) + +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 { + pat.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 { + pat.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 { + pat.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 { + pat.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 { + pat.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 { + pat.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 { + pat.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 { + pat.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 authorizePATRes struct{} + +func (res authorizePATRes) Code() int { + return http.StatusNoContent +} + +func (res authorizePATRes) Headers() map[string]string { + return map[string]string{} +} + +func (res authorizePATRes) Empty() bool { + return true +} diff --git a/pat/api/transport.go b/pat/api/transport.go new file mode 100644 index 0000000000..95d0f45efa --- /dev/null +++ b/pat/api/transport.go @@ -0,0 +1,271 @@ +// 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/internal/api" + "github.com/absmach/magistrala/pat" + "github.com/absmach/magistrala/pkg/apiutil" + mgauthn "github.com/absmach/magistrala/pkg/authn" + "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 pat.Service, mux *chi.Mux, authn mgauthn.Authentication, logger *slog.Logger) *chi.Mux { + opts := []kithttp.ServerOption{ + kithttp.ServerErrorEncoder(apiutil.LoggingErrorEncoder(logger, api.EncodeError)), + } + mux.Group(func(r chi.Router) { + mux.Use(api.AuthenticateMiddleware(authn, true)) + + mux.Route("/pats", func(r chi.Router) { + r.Post("/", kithttp.NewServer( + createPATEndpoint(svc), + decodeCreatePATRequest, + api.EncodeResponse, + opts..., + ).ServeHTTP) + + r.Get("/{id}", kithttp.NewServer( + (retrievePATEndpoint(svc)), + decodeRetrievePATRequest, + api.EncodeResponse, + opts..., + ).ServeHTTP) + + r.Put("/{id}/name", kithttp.NewServer( + (updatePATNameEndpoint(svc)), + decodeUpdatePATNameRequest, + api.EncodeResponse, + opts..., + ).ServeHTTP) + + r.Put("/{id}/description", kithttp.NewServer( + (updatePATDescriptionEndpoint(svc)), + decodeUpdatePATDescriptionRequest, + api.EncodeResponse, + opts..., + ).ServeHTTP) + + r.Get("/", kithttp.NewServer( + (listPATSEndpoint(svc)), + decodeListPATSRequest, + api.EncodeResponse, + opts..., + ).ServeHTTP) + + r.Delete("/{id}", kithttp.NewServer( + (deletePATEndpoint(svc)), + decodeDeletePATRequest, + api.EncodeResponse, + opts..., + ).ServeHTTP) + + r.Put("/{id}/secret/reset", kithttp.NewServer( + (resetPATSecretEndpoint(svc)), + decodeResetPATSecretRequest, + api.EncodeResponse, + opts..., + ).ServeHTTP) + + r.Put("/{id}/secret/revoke", kithttp.NewServer( + (revokePATSecretEndpoint(svc)), + decodeRevokePATSecretRequest, + api.EncodeResponse, + opts..., + ).ServeHTTP) + + r.Put("/{id}/scope/add", kithttp.NewServer( + (addPATScopeEntryEndpoint(svc)), + decodeAddPATScopeEntryRequest, + api.EncodeResponse, + opts..., + ).ServeHTTP) + + r.Put("/{id}/scope/remove", kithttp.NewServer( + (removePATScopeEntryEndpoint(svc)), + decodeRemovePATScopeEntryRequest, + api.EncodeResponse, + opts..., + ).ServeHTTP) + + r.Delete("/{id}/scope", kithttp.NewServer( + (clearPATAllScopeEntryEndpoint(svc)), + decodeClearPATAllScopeEntryRequest, + api.EncodeResponse, + opts..., + ).ServeHTTP) + + r.Post("/authorize", kithttp.NewServer( + (authorizePATEndpoint(svc)), + decodeAuthorizePATRequest, + 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 decodeUpdatePATNameRequest(_ 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 decodeUpdatePATDescriptionRequest(_ 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 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) + } + 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, + } + return req, nil +} + +func decodeDeletePATRequest(_ context.Context, r *http.Request) (interface{}, error) { + return deletePatReq{ + token: apiutil.ExtractBearerToken(r), + id: chi.URLParam(r, "id"), + }, nil +} + +func decodeResetPATSecretRequest(_ 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 decodeRevokePATSecretRequest(_ context.Context, r *http.Request) (interface{}, error) { + return revokePatSecretReq{ + token: apiutil.ExtractBearerToken(r), + id: chi.URLParam(r, "id"), + }, nil +} + +func decodeAddPATScopeEntryRequest(_ 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 decodeRemovePATScopeEntryRequest(_ 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 decodeClearPATAllScopeEntryRequest(_ 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 decodeAuthorizePATRequest(_ context.Context, r *http.Request) (interface{}, error) { + if !strings.Contains(r.Header.Get("Content-Type"), contentType) { + return nil, apiutil.ErrUnsupportedContentType + } + + req := authorizePATReq{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/pat/bolt/pat.go b/pat/bolt/pat.go index 4534dc4e85..b3af87890b 100644 --- a/pat/bolt/pat.go +++ b/pat/bolt/pat.go @@ -11,7 +11,7 @@ import ( "strings" "time" - "github.com/absmach/magistrala/auth" + "github.com/absmach/magistrala/pat" "github.com/absmach/magistrala/pkg/errors" repoerr "github.com/absmach/magistrala/pkg/errors/repository" bolt "go.etcd.io/bbolt" @@ -52,14 +52,14 @@ type patRepo struct { // NewPATSRepository instantiates a bolt // implementation of PAT repository. -func NewPATSRepository(db *bolt.DB, bucketName string) auth.PATSRepository { +func NewPATSRepository(db *bolt.DB, bucketName string) pat.PATSRepository { return &patRepo{ db: db, bucketName: bucketName, } } -func (pr *patRepo) Save(ctx context.Context, pat auth.PAT) error { +func (pr *patRepo) Save(ctx context.Context, pat pat.PAT) error { idxKey := []byte(pat.User + keySeparator + patKey + keySeparator + pat.ID) kv, err := patToKeyValue(pat) if err != nil { @@ -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) { +func (pr *patRepo) Retrieve(ctx context.Context, userID, patID string) (pat.PAT, error) { prefix := []byte(patID + keySeparator) kv := map[string][]byte{} if err := pr.db.View(func(tx *bolt.Tx) error { @@ -101,7 +101,7 @@ func (pr *patRepo) Retrieve(ctx context.Context, userID, patID string) (auth.PAT } return nil }); err != nil { - return auth.PAT{}, err + return pat.PAT{}, err } return keyValueToPAT(kv) @@ -126,15 +126,15 @@ func (pr *patRepo) RetrieveSecretAndRevokeStatus(ctx context.Context, userID, pa return secretHash, revoked, nil } -func (pr *patRepo) UpdateName(ctx context.Context, userID, patID, name string) (auth.PAT, error) { +func (pr *patRepo) UpdateName(ctx context.Context, userID, patID, name string) (pat.PAT, error) { return pr.updatePATField(ctx, userID, patID, nameKey, []byte(name)) } -func (pr *patRepo) UpdateDescription(ctx context.Context, userID, patID, description string) (auth.PAT, error) { +func (pr *patRepo) UpdateDescription(ctx context.Context, userID, patID, description string) (pat.PAT, error) { 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) { +func (pr *patRepo) UpdateTokenHash(ctx context.Context, userID, patID, tokenHash string, expiryAt time.Time) (pat.PAT, error) { prefix := []byte(patID + keySeparator) kv := map[string][]byte{} if err := pr.db.Update(func(tx *bolt.Tx) error { @@ -157,12 +157,12 @@ func (pr *patRepo) UpdateTokenHash(ctx context.Context, userID, patID, tokenHash } return nil }); err != nil { - return auth.PAT{}, err + return pat.PAT{}, err } return keyValueToPAT(kv) } -func (pr *patRepo) RetrieveAll(ctx context.Context, userID string, pm auth.PATSPageMeta) (auth.PATSPage, error) { +func (pr *patRepo) RetrieveAll(ctx context.Context, userID string, pm pat.PATSPageMeta) (pat.PATSPage, error) { prefix := []byte(userID + keySeparator + patKey + keySeparator) patIDs := []string{} @@ -179,14 +179,14 @@ func (pr *patRepo) RetrieveAll(ctx context.Context, userID string, pm auth.PATSP } return nil }); err != nil { - return auth.PATSPage{}, err + return pat.PATSPage{}, err } total := len(patIDs) - var pats []auth.PAT + var pats []pat.PAT - patsPage := auth.PATSPage{ + patsPage := pat.PATSPage{ Total: uint64(total), Limit: pm.Limit, Offset: pm.Offset, @@ -282,7 +282,7 @@ 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) { +func (pr *patRepo) AddScopeEntry(ctx context.Context, userID, patID string, platformEntityType pat.PlatformEntityType, optionalDomainID string, optionalDomainEntityType pat.DomainEntityType, operation pat.OperationType, entityIDs ...string) (pat.Scope, error) { prefix := []byte(patID + keySeparator + scopeKey) var rKV map[string][]byte if err := pr.db.Update(func(tx *bolt.Tx) error { @@ -306,15 +306,15 @@ func (pr *patRepo) AddScopeEntry(ctx context.Context, userID, patID string, plat } return nil }); err != nil { - return auth.Scope{}, err + return pat.Scope{}, err } 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) { +func (pr *patRepo) RemoveScopeEntry(ctx context.Context, userID, patID string, platformEntityType pat.PlatformEntityType, optionalDomainID string, optionalDomainEntityType pat.DomainEntityType, operation pat.OperationType, entityIDs ...string) (pat.Scope, error) { if len(entityIDs) == 0 { - return auth.Scope{}, repoerr.ErrMalformedEntity + return pat.Scope{}, repoerr.ErrMalformedEntity } prefix := []byte(patID + keySeparator + scopeKey) var rKV map[string][]byte @@ -339,12 +339,12 @@ func (pr *patRepo) RemoveScopeEntry(ctx context.Context, userID, patID string, p } return nil }); err != nil { - return auth.Scope{}, err + return pat.Scope{}, err } 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 { +func (pr *patRepo) CheckScopeEntry(ctx context.Context, userID, patID string, platformEntityType pat.PlatformEntityType, optionalDomainID string, optionalDomainEntityType pat.DomainEntityType, operation pat.OperationType, entityIDs ...string) error { return pr.db.Update(func(tx *bolt.Tx) error { b, err := pr.retrieveUserBucket(tx, userID, patID, repoerr.ErrViewEntity) if err != nil { @@ -373,7 +373,7 @@ 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) { +func (pr *patRepo) updatePATField(_ context.Context, userID, patID, key string, value []byte) (pat.PAT, error) { prefix := []byte(patID + keySeparator) kv := map[string][]byte{} if err := pr.db.Update(func(tx *bolt.Tx) error { @@ -393,7 +393,7 @@ func (pr *patRepo) updatePATField(_ context.Context, userID, patID, key string, } return nil }); err != nil { - return auth.PAT{}, err + return pat.PAT{}, err } return keyValueToPAT(kv) } @@ -433,7 +433,7 @@ func (pr *patRepo) retrieveRootBucket(tx *bolt.Tx) (*bolt.Bucket, error) { return rootBucket, nil } -func patToKeyValue(pat auth.PAT) (map[string][]byte, error) { +func patToKeyValue(pat pat.PAT) (map[string][]byte, error) { kv := map[string][]byte{ idKey: []byte(pat.ID), userKey: []byte(pat.User), @@ -457,10 +457,10 @@ func patToKeyValue(pat auth.PAT) (map[string][]byte, error) { return kv, nil } -func scopeToKeyValue(scope auth.Scope) (map[string][]byte, error) { +func scopeToKeyValue(scope pat.Scope) (map[string][]byte, error) { kv := map[string][]byte{} for opType, scopeValue := range scope.Users { - tempKV, err := scopeEntryToKeyValue(auth.PlatformUsersScope, "", auth.DomainNullScope, opType, scopeValue.Values()...) + tempKV, err := scopeEntryToKeyValue(pat.PlatformUsersScope, "", pat.DomainNullScope, opType, scopeValue.Values()...) if err != nil { return nil, err } @@ -470,7 +470,7 @@ func scopeToKeyValue(scope auth.Scope) (map[string][]byte, error) { } for domainID, domainScope := range scope.Domains { for opType, scopeValue := range domainScope.DomainManagement { - tempKV, err := scopeEntryToKeyValue(auth.PlatformDomainsScope, domainID, auth.DomainManagementScope, opType, scopeValue.Values()...) + tempKV, err := scopeEntryToKeyValue(pat.PlatformDomainsScope, domainID, pat.DomainManagementScope, opType, scopeValue.Values()...) if err != nil { return nil, errors.Wrap(repoerr.ErrCreateEntity, err) } @@ -480,7 +480,7 @@ func scopeToKeyValue(scope auth.Scope) (map[string][]byte, error) { } for entityType, scope := range domainScope.Entities { for opType, scopeValue := range scope { - tempKV, err := scopeEntryToKeyValue(auth.PlatformDomainsScope, domainID, entityType, opType, scopeValue.Values()...) + tempKV, err := scopeEntryToKeyValue(pat.PlatformDomainsScope, domainID, entityType, opType, scopeValue.Values()...) if err != nil { return nil, errors.Wrap(repoerr.ErrCreateEntity, err) } @@ -493,7 +493,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) { +func scopeEntryToKeyValue(platformEntityType pat.PlatformEntityType, optionalDomainID string, optionalDomainEntityType pat.DomainEntityType, operation pat.OperationType, entityIDs ...string) (map[string][]byte, error) { if len(entityIDs) == 0 { return nil, repoerr.ErrMalformedEntity } @@ -518,7 +518,7 @@ func scopeEntryToKeyValue(platformEntityType auth.PlatformEntityType, optionalDo return kv, nil } -func scopeRootKey(platformEntityType auth.PlatformEntityType, optionalDomainID string, optionalDomainEntityType auth.DomainEntityType, operation auth.OperationType) (string, error) { +func scopeRootKey(platformEntityType pat.PlatformEntityType, optionalDomainID string, optionalDomainEntityType pat.DomainEntityType, operation pat.OperationType) (string, error) { op, err := operation.ValidString() if err != nil { return "", errors.Wrap(repoerr.ErrMalformedEntity, err) @@ -532,9 +532,9 @@ func scopeRootKey(platformEntityType auth.PlatformEntityType, optionalDomainID s rootKey.WriteString(keySeparator) switch platformEntityType { - case auth.PlatformUsersScope: + case pat.PlatformUsersScope: rootKey.WriteString(op) - case auth.PlatformDomainsScope: + case pat.PlatformDomainsScope: if optionalDomainID == "" { return "", fmt.Errorf("failed to add platform %s scope: invalid domain id", platformEntityType.String()) } @@ -554,8 +554,8 @@ func scopeRootKey(platformEntityType auth.PlatformEntityType, optionalDomainID s return rootKey.String(), nil } -func keyValueToBasicPAT(kv map[string][]byte) auth.PAT { - var pat auth.PAT +func keyValueToBasicPAT(kv map[string][]byte) pat.PAT { + var pat pat.PAT for k, v := range kv { switch { case strings.HasSuffix(k, keySeparator+idKey): @@ -583,157 +583,157 @@ func keyValueToBasicPAT(kv map[string][]byte) auth.PAT { return pat } -func keyValueToPAT(kv map[string][]byte) (auth.PAT, error) { - pat := keyValueToBasicPAT(kv) +func keyValueToPAT(kv map[string][]byte) (pat.PAT, error) { + res := keyValueToBasicPAT(kv) scope, err := parseKeyValueToScope(kv) if err != nil { - return auth.PAT{}, err + return pat.PAT{}, err } - pat.Scope = scope - return pat, nil + res.Scope = scope + return res, nil } -func parseKeyValueToScope(kv map[string][]byte) (auth.Scope, error) { - scope := auth.Scope{ - Domains: make(map[string]auth.DomainScope), +func parseKeyValueToScope(kv map[string][]byte) (pat.Scope, error) { + scope := pat.Scope{ + Domains: make(map[string]pat.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]) + platformEntityType, err := pat.ParsePlatformEntityType(keyParts[2]) if err != nil { - return auth.Scope{}, errors.Wrap(repoerr.ErrViewEntity, err) + return pat.Scope{}, errors.Wrap(repoerr.ErrViewEntity, err) } switch platformEntityType { - case auth.PlatformUsersScope: + case pat.PlatformUsersScope: scope.Users, err = parseOperation(platformEntityType, scope.Users, key, keyParts, value) if err != nil { - return auth.Scope{}, errors.Wrap(repoerr.ErrViewEntity, err) + return pat.Scope{}, errors.Wrap(repoerr.ErrViewEntity, err) } - case auth.PlatformDomainsScope: + case pat.PlatformDomainsScope: if len(keyParts) < 6 { - return auth.Scope{}, fmt.Errorf("invalid scope key format: %s", key) + return pat.Scope{}, fmt.Errorf("invalid scope key format: %s", key) } domainID := keyParts[3] if scope.Domains == nil { - scope.Domains = make(map[string]auth.DomainScope) + scope.Domains = make(map[string]pat.DomainScope) } if _, ok := scope.Domains[domainID]; !ok { - scope.Domains[domainID] = auth.DomainScope{} + scope.Domains[domainID] = pat.DomainScope{} } domainScope := scope.Domains[domainID] entityType := keyParts[4] switch entityType { - case auth.DomainManagementScope.String(): + case pat.DomainManagementScope.String(): domainScope.DomainManagement, err = parseOperation(platformEntityType, domainScope.DomainManagement, key, keyParts, value) if err != nil { - return auth.Scope{}, errors.Wrap(repoerr.ErrViewEntity, err) + return pat.Scope{}, errors.Wrap(repoerr.ErrViewEntity, err) } default: - etype, err := auth.ParseDomainEntityType(entityType) + etype, err := pat.ParseDomainEntityType(entityType) if err != nil { - return auth.Scope{}, fmt.Errorf("key %s invalid entity type %s : %w", key, entityType, err) + return pat.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) + domainScope.Entities = make(map[pat.DomainEntityType]pat.OperationScope) } if _, ok := domainScope.Entities[etype]; !ok { - domainScope.Entities[etype] = auth.OperationScope{} + domainScope.Entities[etype] = pat.OperationScope{} } entityOperationScope := domainScope.Entities[etype] entityOperationScope, err = parseOperation(platformEntityType, entityOperationScope, key, keyParts, value) if err != nil { - return auth.Scope{}, errors.Wrap(repoerr.ErrViewEntity, err) + return pat.Scope{}, errors.Wrap(repoerr.ErrViewEntity, err) } 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 pat.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) { +func parseOperation(platformEntityType pat.PlatformEntityType, opScope pat.OperationScope, key string, keyParts []string, value []byte) (pat.OperationScope, error) { if opScope == nil { - opScope = make(map[auth.OperationType]auth.ScopeValue) + opScope = make(map[pat.OperationType]pat.ScopeValue) } if err := validateOperation(platformEntityType, opScope, key, keyParts, value); err != nil { - return auth.OperationScope{}, err + return pat.OperationScope{}, err } switch string(value) { case string(entityValue): - opType, err := auth.ParseOperationType(keyParts[len(keyParts)-2]) + opType, err := pat.ParseOperationType(keyParts[len(keyParts)-2]) if err != nil { - return auth.OperationScope{}, errors.Wrap(repoerr.ErrViewEntity, err) + return pat.OperationScope{}, errors.Wrap(repoerr.ErrViewEntity, err) } entityID := keyParts[len(keyParts)-1] if _, oValueExists := opScope[opType]; !oValueExists { - opScope[opType] = &auth.SelectedIDs{} + opScope[opType] = &pat.SelectedIDs{} } 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) + return pat.OperationScope{}, fmt.Errorf("failed to add scope key %s with entity value %v : %w", key, entityID, err) } opScope[opType] = oValue case string(anyIDValue): - opType, err := auth.ParseOperationType(keyParts[len(keyParts)-1]) + opType, err := pat.ParseOperationType(keyParts[len(keyParts)-1]) if err != nil { - return auth.OperationScope{}, errors.Wrap(repoerr.ErrViewEntity, err) + return pat.OperationScope{}, errors.Wrap(repoerr.ErrViewEntity, err) } 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) + if _, ok := oValue.(*pat.AnyIDs); !ok { + return pat.OperationScope{}, fmt.Errorf("failed to add scope key %s with entity anyIDs scope value : key already initialized with different type", key) } } - opScope[opType] = &auth.AnyIDs{} + opScope[opType] = &pat.AnyIDs{} case string(selectedIDsValue): - opType, err := auth.ParseOperationType(keyParts[len(keyParts)-1]) + opType, err := pat.ParseOperationType(keyParts[len(keyParts)-1]) if err != nil { - return auth.OperationScope{}, errors.Wrap(repoerr.ErrViewEntity, err) + return pat.OperationScope{}, errors.Wrap(repoerr.ErrViewEntity, err) } 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 _, ok := oValue.(*pat.SelectedIDs); !ok { + return pat.OperationScope{}, fmt.Errorf("failed to add scope key %s with entity selectedIDs scope value : key already initialized with different type", key) } } if !oValueExists { - opScope[opType] = &auth.SelectedIDs{} + opScope[opType] = &pat.SelectedIDs{} } default: - return auth.OperationScope{}, fmt.Errorf("key %s have invalid value %v", key, value) + return pat.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 { +func validateOperation(platformEntityType pat.PlatformEntityType, _ pat.OperationScope, key string, keyParts []string, value []byte) error { expectedKeyPartsLength := 0 switch string(value) { case string(entityValue): switch platformEntityType { - case auth.PlatformDomainsScope: + case pat.PlatformDomainsScope: expectedKeyPartsLength = 7 - case auth.PlatformUsersScope: + case pat.PlatformUsersScope: expectedKeyPartsLength = 5 default: return fmt.Errorf("invalid platform entity type : %s", platformEntityType.String()) } case string(selectedIDsValue), string(anyIDValue): switch platformEntityType { - case auth.PlatformDomainsScope: + case pat.PlatformDomainsScope: expectedKeyPartsLength = 6 - case auth.PlatformUsersScope: + case pat.PlatformUsersScope: expectedKeyPartsLength = 4 default: return fmt.Errorf("invalid platform entity type : %s", platformEntityType.String()) diff --git a/pat/hasher/hasher.go b/pat/hasher/hasher.go index c417bf7b80..4d74669f02 100644 --- a/pat/hasher/hasher.go +++ b/pat/hasher/hasher.go @@ -10,7 +10,7 @@ import ( "strings" "time" - "github.com/absmach/magistrala/auth" + "github.com/absmach/magistrala/pat" "github.com/absmach/magistrala/pkg/errors" "golang.org/x/crypto/scrypt" ) @@ -24,12 +24,12 @@ var ( errDecode = errors.New("failed to decode") ) -var _ auth.Hasher = (*bcryptHasher)(nil) +var _ pat.Hasher = (*bcryptHasher)(nil) type bcryptHasher struct{} // New instantiates a bcrypt-based hasher implementation. -func New() auth.Hasher { +func New() pat.Hasher { return &bcryptHasher{} } diff --git a/pat/middleware/authorization.go b/pat/middleware/authorization.go index 71995a25ab..1064590088 100644 --- a/pat/middleware/authorization.go +++ b/pat/middleware/authorization.go @@ -1,4 +1,84 @@ // Copyright (c) Abstract Machines // SPDX-License-Identifier: Apache-2.0 -package middleware \ No newline at end of file +package middleware + +import ( + "context" + "time" + + "github.com/absmach/magistrala/pat" + "github.com/absmach/magistrala/pkg/authn" + mgauthz "github.com/absmach/magistrala/pkg/authz" +) + +var _ pat.Service = (*authorizationMiddleware)(nil) + +type authorizationMiddleware struct { + svc pat.Service + authz mgauthz.Authorization +} + +// AuthorizationMiddleware adds authorization to the clients service. +func AuthorizationMiddleware(entityType string, svc pat.Service, authz mgauthz.Authorization) (pat.Service, error) { + return &authorizationMiddleware{ + svc: svc, + authz: authz, + }, nil +} + +func (am *authorizationMiddleware) CreatePAT(ctx context.Context, session authn.Session, name, description string, duration time.Duration, scope pat.Scope) (pat.PAT, error) { + return am.svc.CreatePAT(ctx, session, name, description, duration, scope) +} + +func (am *authorizationMiddleware) UpdatePATName(ctx context.Context, session authn.Session, patID, name string) (pat.PAT, error) { + return am.svc.UpdatePATName(ctx, session, patID, name) +} + +func (am *authorizationMiddleware) UpdatePATDescription(ctx context.Context, session authn.Session, patID, description string) (pat.PAT, error) { + return am.svc.UpdatePATDescription(ctx, session, patID, description) +} + +func (am *authorizationMiddleware) RetrievePAT(ctx context.Context, session authn.Session, patID string) (pat.PAT, error) { + return am.svc.RetrievePAT(ctx, session, patID) +} + +func (am *authorizationMiddleware) ListPATS(ctx context.Context, session authn.Session, pm pat.PATSPageMeta) (pat.PATSPage, error) { + return am.svc.ListPATS(ctx, session, pm) +} + +func (am *authorizationMiddleware) DeletePAT(ctx context.Context, session authn.Session, patID string) error { + return am.svc.DeletePAT(ctx, session, patID) +} + +func (am *authorizationMiddleware) ResetPATSecret(ctx context.Context, session authn.Session, patID string, duration time.Duration) (pat.PAT, error) { + return am.svc.ResetPATSecret(ctx, session, patID, duration) +} + +func (am *authorizationMiddleware) RevokePATSecret(ctx context.Context, session authn.Session, patID string) error { + return am.svc.RevokePATSecret(ctx, session, patID) +} + +func (am *authorizationMiddleware) AddPATScopeEntry(ctx context.Context, session authn.Session, patID string, platformEntityType pat.PlatformEntityType, optionalDomainID string, optionalDomainEntityType pat.DomainEntityType, operation pat.OperationType, entityIDs ...string) (pat.Scope, error) { + return am.svc.AddPATScopeEntry(ctx, session, patID, platformEntityType, optionalDomainID, optionalDomainEntityType, operation, entityIDs...) +} + +func (am *authorizationMiddleware) RemovePATScopeEntry(ctx context.Context, session authn.Session, patID string, platformEntityType pat.PlatformEntityType, optionalDomainID string, optionalDomainEntityType pat.DomainEntityType, operation pat.OperationType, entityIDs ...string) (pat.Scope, error) { + return am.svc.RemovePATScopeEntry(ctx, session, patID, platformEntityType, optionalDomainID, optionalDomainEntityType, operation, entityIDs...) +} + +func (am *authorizationMiddleware) ClearPATAllScopeEntry(ctx context.Context, session authn.Session, patID string) error { + return am.svc.ClearPATAllScopeEntry(ctx, session, patID) +} + +func (am *authorizationMiddleware) IdentifyPAT(ctx context.Context, secret string) (pat.PAT, error) { + return am.svc.IdentifyPAT(ctx, secret) +} + +func (am *authorizationMiddleware) AuthorizePAT(ctx context.Context, paToken string, platformEntityType pat.PlatformEntityType, optionalDomainID string, optionalDomainEntityType pat.DomainEntityType, operation pat.OperationType, entityIDs ...string) error { + return am.svc.AuthorizePAT(ctx, paToken, platformEntityType, optionalDomainID, optionalDomainEntityType, operation, entityIDs...) +} + +func (am *authorizationMiddleware) CheckPAT(ctx context.Context, userID, patID string, platformEntityType pat.PlatformEntityType, optionalDomainID string, optionalDomainEntityType pat.DomainEntityType, operation pat.OperationType, entityIDs ...string) error { + return am.svc.CheckPAT(ctx, userID, patID, platformEntityType, optionalDomainID, optionalDomainEntityType, operation, entityIDs...) +} diff --git a/pat/middleware/logging.go b/pat/middleware/logging.go index 650eab5115..51767d39c1 100644 --- a/pat/middleware/logging.go +++ b/pat/middleware/logging.go @@ -5,11 +5,11 @@ package middleware import ( "context" - "fmt" "log/slog" "time" "github.com/absmach/magistrala/pat" + "github.com/absmach/magistrala/pkg/authn" ) var _ pat.Service = (*loggingMiddleware)(nil) @@ -24,7 +24,7 @@ func LoggingMiddleware(svc pat.Service, logger *slog.Logger) pat.Service { return &loggingMiddleware{logger, svc} } -func (lm *loggingMiddleware) CreatePAT(ctx context.Context, token, name, description string, duration time.Duration, scope pat.Scope) (pa pat.PAT, err error) { +func (lm *loggingMiddleware) CreatePAT(ctx context.Context, session authn.Session, name, description string, duration time.Duration, scope pat.Scope) (pa pat.PAT, err error) { defer func(begin time.Time) { args := []any{ slog.String("duration", time.Since(begin).String()), @@ -40,10 +40,10 @@ func (lm *loggingMiddleware) CreatePAT(ctx context.Context, token, name, descrip } lm.logger.Info("Create PAT completed successfully", args...) }(time.Now()) - return lm.svc.CreatePAT(ctx, token, name, description, duration, scope) + return lm.svc.CreatePAT(ctx, session, name, description, duration, scope) } -func (lm *loggingMiddleware) UpdatePATName(ctx context.Context, token, patID, name string) (pa pat.PAT, err error) { +func (lm *loggingMiddleware) UpdatePATName(ctx context.Context, session authn.Session, patID, name string) (pa pat.PAT, err error) { defer func(begin time.Time) { args := []any{ slog.String("duration", time.Since(begin).String()), @@ -57,10 +57,10 @@ func (lm *loggingMiddleware) UpdatePATName(ctx context.Context, token, patID, na } lm.logger.Info("Update PAT name completed successfully", args...) }(time.Now()) - return lm.svc.UpdatePATName(ctx, token, patID, name) + return lm.svc.UpdatePATName(ctx, session, patID, name) } -func (lm *loggingMiddleware) UpdatePATDescription(ctx context.Context, token, patID, description string) (pa pat.PAT, err error) { +func (lm *loggingMiddleware) UpdatePATDescription(ctx context.Context, session authn.Session, patID, description string) (pa pat.PAT, err error) { defer func(begin time.Time) { args := []any{ slog.String("duration", time.Since(begin).String()), @@ -74,10 +74,10 @@ func (lm *loggingMiddleware) UpdatePATDescription(ctx context.Context, token, pa } lm.logger.Info("Update PAT description completed successfully", args...) }(time.Now()) - return lm.svc.UpdatePATDescription(ctx, token, patID, description) + return lm.svc.UpdatePATDescription(ctx, session, patID, description) } -func (lm *loggingMiddleware) RetrievePAT(ctx context.Context, token, patID string) (pa pat.PAT, err error) { +func (lm *loggingMiddleware) RetrievePAT(ctx context.Context, session authn.Session, patID string) (pa pat.PAT, err error) { defer func(begin time.Time) { args := []any{ slog.String("duration", time.Since(begin).String()), @@ -90,10 +90,10 @@ func (lm *loggingMiddleware) RetrievePAT(ctx context.Context, token, patID strin } lm.logger.Info("Retrieve PAT completed successfully", args...) }(time.Now()) - return lm.svc.RetrievePAT(ctx, token, patID) + return lm.svc.RetrievePAT(ctx, session, patID) } -func (lm *loggingMiddleware) ListPATS(ctx context.Context, token string, pm pat.PATSPageMeta) (pp pat.PATSPage, err error) { +func (lm *loggingMiddleware) ListPATS(ctx context.Context, session authn.Session, pm pat.PATSPageMeta) (pp pat.PATSPage, err error) { defer func(begin time.Time) { args := []any{ slog.String("duration", time.Since(begin).String()), @@ -107,10 +107,10 @@ func (lm *loggingMiddleware) ListPATS(ctx context.Context, token string, pm pat. } lm.logger.Info("List PATS completed successfully", args...) }(time.Now()) - return lm.svc.ListPATS(ctx, token, pm) + return lm.svc.ListPATS(ctx, session, pm) } -func (lm *loggingMiddleware) DeletePAT(ctx context.Context, token, patID string) (err error) { +func (lm *loggingMiddleware) DeletePAT(ctx context.Context, session authn.Session, patID string) (err error) { defer func(begin time.Time) { args := []any{ slog.String("duration", time.Since(begin).String()), @@ -123,10 +123,10 @@ func (lm *loggingMiddleware) DeletePAT(ctx context.Context, token, patID string) } lm.logger.Info("Delete PAT completed successfully", args...) }(time.Now()) - return lm.svc.DeletePAT(ctx, token, patID) + return lm.svc.DeletePAT(ctx, session, patID) } -func (lm *loggingMiddleware) ResetPATSecret(ctx context.Context, token, patID string, duration time.Duration) (pa pat.PAT, err error) { +func (lm *loggingMiddleware) ResetPATSecret(ctx context.Context, session authn.Session, patID string, duration time.Duration) (pa pat.PAT, err error) { defer func(begin time.Time) { args := []any{ slog.String("duration", time.Since(begin).String()), @@ -140,10 +140,10 @@ func (lm *loggingMiddleware) ResetPATSecret(ctx context.Context, token, patID st } lm.logger.Info("Reset PAT secret completed successfully", args...) }(time.Now()) - return lm.svc.ResetPATSecret(ctx, token, patID, duration) + return lm.svc.ResetPATSecret(ctx, session, patID, duration) } -func (lm *loggingMiddleware) RevokePATSecret(ctx context.Context, token, patID string) (err error) { +func (lm *loggingMiddleware) RevokePATSecret(ctx context.Context, session authn.Session, patID string) (err error) { defer func(begin time.Time) { args := []any{ slog.String("duration", time.Since(begin).String()), @@ -156,10 +156,10 @@ func (lm *loggingMiddleware) RevokePATSecret(ctx context.Context, token, patID s } lm.logger.Info("Revoke PAT secret completed successfully", args...) }(time.Now()) - return lm.svc.RevokePATSecret(ctx, token, patID) + return lm.svc.RevokePATSecret(ctx, session, patID) } -func (lm *loggingMiddleware) AddPATScopeEntry(ctx context.Context, token, patID string, platformEntityType pat.PlatformEntityType, optionalDomainID string, optionalDomainEntityType pat.DomainEntityType, operation pat.OperationType, entityIDs ...string) (sc pat.Scope, err error) { +func (lm *loggingMiddleware) AddPATScopeEntry(ctx context.Context, session authn.Session, patID string, platformEntityType pat.PlatformEntityType, optionalDomainID string, optionalDomainEntityType pat.DomainEntityType, operation pat.OperationType, entityIDs ...string) (sc pat.Scope, err error) { defer func(begin time.Time) { args := []any{ slog.String("duration", time.Since(begin).String()), @@ -177,10 +177,10 @@ func (lm *loggingMiddleware) AddPATScopeEntry(ctx context.Context, token, patID } lm.logger.Info("Add entry to PAT scope completed successfully", args...) }(time.Now()) - return lm.svc.AddPATScopeEntry(ctx, token, patID, platformEntityType, optionalDomainID, optionalDomainEntityType, operation, entityIDs...) + return lm.svc.AddPATScopeEntry(ctx, session, patID, platformEntityType, optionalDomainID, optionalDomainEntityType, operation, entityIDs...) } -func (lm *loggingMiddleware) RemovePATScopeEntry(ctx context.Context, token, patID string, platformEntityType pat.PlatformEntityType, optionalDomainID string, optionalDomainEntityType pat.DomainEntityType, operation pat.OperationType, entityIDs ...string) (sc pat.Scope, err error) { +func (lm *loggingMiddleware) RemovePATScopeEntry(ctx context.Context, session authn.Session, patID string, platformEntityType pat.PlatformEntityType, optionalDomainID string, optionalDomainEntityType pat.DomainEntityType, operation pat.OperationType, entityIDs ...string) (sc pat.Scope, err error) { defer func(begin time.Time) { args := []any{ slog.String("duration", time.Since(begin).String()), @@ -198,10 +198,10 @@ func (lm *loggingMiddleware) RemovePATScopeEntry(ctx context.Context, token, pat } lm.logger.Info("Remove entry from PAT scope completed successfully", args...) }(time.Now()) - return lm.svc.RemovePATScopeEntry(ctx, token, patID, platformEntityType, optionalDomainID, optionalDomainEntityType, operation, entityIDs...) + return lm.svc.RemovePATScopeEntry(ctx, session, patID, platformEntityType, optionalDomainID, optionalDomainEntityType, operation, entityIDs...) } -func (lm *loggingMiddleware) ClearPATAllScopeEntry(ctx context.Context, token, patID string) (err error) { +func (lm *loggingMiddleware) ClearPATAllScopeEntry(ctx context.Context, session authn.Session, patID string) (err error) { defer func(begin time.Time) { args := []any{ slog.String("duration", time.Since(begin).String()), @@ -214,7 +214,7 @@ func (lm *loggingMiddleware) ClearPATAllScopeEntry(ctx context.Context, token, p } lm.logger.Info("Clear all entry from PAT scope completed successfully", args...) }(time.Now()) - return lm.svc.ClearPATAllScopeEntry(ctx, token, patID) + return lm.svc.ClearPATAllScopeEntry(ctx, session, patID) } func (lm *loggingMiddleware) IdentifyPAT(ctx context.Context, paToken string) (pa pat.PAT, err error) { @@ -272,4 +272,4 @@ func (lm *loggingMiddleware) CheckPAT(ctx context.Context, userID, patID string, lm.logger.Info("Check PAT completed successfully", args...) }(time.Now()) return lm.svc.CheckPAT(ctx, userID, patID, platformEntityType, optionalDomainID, optionalDomainEntityType, operation, entityIDs...) -} \ No newline at end of file +} diff --git a/pat/middleware/metrics.go b/pat/middleware/metrics.go index b41a88a838..4a44bb0aa1 100644 --- a/pat/middleware/metrics.go +++ b/pat/middleware/metrics.go @@ -2,3 +2,141 @@ // SPDX-License-Identifier: Apache-2.0 package middleware + +import ( + "context" + "time" + + "github.com/absmach/magistrala/pat" + "github.com/absmach/magistrala/pkg/authn" + "github.com/go-kit/kit/metrics" +) + +var _ pat.Service = (*metricsMiddleware)(nil) + +type metricsMiddleware struct { + counter metrics.Counter + latency metrics.Histogram + svc pat.Service +} + +// MetricsMiddleware instruments core service by tracking request count and latency. +func MetricsMiddleware(svc pat.Service, counter metrics.Counter, latency metrics.Histogram) pat.Service { + return &metricsMiddleware{ + counter: counter, + latency: latency, + svc: svc, + } +} + +func (ms *metricsMiddleware) CreatePAT(ctx context.Context, session authn.Session, name, description string, duration time.Duration, scope pat.Scope) (pat.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.CreatePAT(ctx, session, name, description, duration, scope) +} + +func (ms *metricsMiddleware) UpdatePATName(ctx context.Context, session authn.Session, patID, name string) (pat.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.UpdatePATName(ctx, session, patID, name) +} + +func (ms *metricsMiddleware) UpdatePATDescription(ctx context.Context, session authn.Session, patID, description string) (pat.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.UpdatePATDescription(ctx, session, patID, description) +} + +func (ms *metricsMiddleware) RetrievePAT(ctx context.Context, session authn.Session, patID string) (pat.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.RetrievePAT(ctx, session, patID) +} + +func (ms *metricsMiddleware) ListPATS(ctx context.Context, session authn.Session, pm pat.PATSPageMeta) (pat.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.ListPATS(ctx, session, pm) +} + +func (ms *metricsMiddleware) DeletePAT(ctx context.Context, session authn.Session, 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.DeletePAT(ctx, session, patID) +} + +func (ms *metricsMiddleware) ResetPATSecret(ctx context.Context, session authn.Session, patID string, duration time.Duration) (pat.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.ResetPATSecret(ctx, session, patID, duration) +} + +func (ms *metricsMiddleware) RevokePATSecret(ctx context.Context, session authn.Session, 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.RevokePATSecret(ctx, session, patID) +} + +func (ms *metricsMiddleware) AddPATScopeEntry(ctx context.Context, session authn.Session, patID string, platformEntityType pat.PlatformEntityType, optionalDomainID string, optionalDomainEntityType pat.DomainEntityType, operation pat.OperationType, entityIDs ...string) (pat.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.AddPATScopeEntry(ctx, session, patID, platformEntityType, optionalDomainID, optionalDomainEntityType, operation, entityIDs...) +} + +func (ms *metricsMiddleware) RemovePATScopeEntry(ctx context.Context, session authn.Session, patID string, platformEntityType pat.PlatformEntityType, optionalDomainID string, optionalDomainEntityType pat.DomainEntityType, operation pat.OperationType, entityIDs ...string) (pat.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.RemovePATScopeEntry(ctx, session, patID, platformEntityType, optionalDomainID, optionalDomainEntityType, operation, entityIDs...) +} + +func (ms *metricsMiddleware) ClearPATAllScopeEntry(ctx context.Context, session authn.Session, 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.ClearPATAllScopeEntry(ctx, session, patID) +} + +func (ms *metricsMiddleware) IdentifyPAT(ctx context.Context, paToken string) (pat.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, platformEntityType pat.PlatformEntityType, optionalDomainID string, optionalDomainEntityType pat.DomainEntityType, operation pat.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, platformEntityType, optionalDomainID, optionalDomainEntityType, operation, entityIDs...) +} + +func (ms *metricsMiddleware) CheckPAT(ctx context.Context, userID, patID string, platformEntityType pat.PlatformEntityType, optionalDomainID string, optionalDomainEntityType pat.DomainEntityType, operation pat.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/pat/pat.go b/pat/pat.go index 8e427b4332..5b9cb08045 100644 --- a/pat/pat.go +++ b/pat/pat.go @@ -9,6 +9,7 @@ import ( "fmt" "time" + "github.com/absmach/magistrala/pkg/authn" "github.com/absmach/magistrala/pkg/errors" ) @@ -662,41 +663,41 @@ func (pat PAT) Expired() bool { } // 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" +//go:generate mockery --name Service --output=./mocks --filename pats.go --quiet --note "Copyright (c) Abstract Machines" -type PATS interface { +type Service interface { // Create function creates new PAT for given valid inputs. - CreatePAT(ctx context.Context, token, name, description string, duration time.Duration, scope Scope) (PAT, error) + CreatePAT(ctx context.Context, session authn.Session, name, description string, duration time.Duration, scope Scope) (PAT, error) // UpdateName function updates the name for the given PAT ID. - UpdatePATName(ctx context.Context, token, patID, name string) (PAT, error) + UpdatePATName(ctx context.Context, session authn.Session, patID, name string) (PAT, error) // UpdateDescription function updates the description for the given PAT ID. - UpdatePATDescription(ctx context.Context, token, patID, description string) (PAT, error) + UpdatePATDescription(ctx context.Context, session authn.Session, patID, description string) (PAT, error) // Retrieve function retrieves the PAT for given ID. - RetrievePAT(ctx context.Context, token, patID string) (PAT, error) + RetrievePAT(ctx context.Context, session authn.Session, patID string) (PAT, error) // List function lists all the PATs for the user. - ListPATS(ctx context.Context, token string, pm PATSPageMeta) (PATSPage, error) + ListPATS(ctx context.Context, session authn.Session, pm PATSPageMeta) (PATSPage, error) // Delete function deletes the PAT for given ID. - DeletePAT(ctx context.Context, token, patID string) error + DeletePAT(ctx context.Context, session authn.Session, patID string) error // ResetSecret function reset the secret and creates new secret for the given ID. - ResetPATSecret(ctx context.Context, token, patID string, duration time.Duration) (PAT, error) + ResetPATSecret(ctx context.Context, session authn.Session, patID string, duration time.Duration) (PAT, error) // RevokeSecret function revokes the secret for the given ID. - RevokePATSecret(ctx context.Context, token, patID string) error + RevokePATSecret(ctx context.Context, session authn.Session, patID string) error // AddScope function adds a new scope entry. - AddPATScopeEntry(ctx context.Context, token, patID string, platformEntityType PlatformEntityType, optionalDomainID string, optionalDomainEntityType DomainEntityType, operation OperationType, entityIDs ...string) (Scope, error) + AddPATScopeEntry(ctx context.Context, session authn.Session, patID string, platformEntityType PlatformEntityType, optionalDomainID string, optionalDomainEntityType DomainEntityType, operation OperationType, entityIDs ...string) (Scope, error) // RemoveScope function removes a scope entry. - RemovePATScopeEntry(ctx context.Context, token, patID string, platformEntityType PlatformEntityType, optionalDomainID string, optionalDomainEntityType DomainEntityType, operation OperationType, entityIDs ...string) (Scope, error) + RemovePATScopeEntry(ctx context.Context, session authn.Session, patID string, platformEntityType PlatformEntityType, optionalDomainID string, optionalDomainEntityType DomainEntityType, operation OperationType, entityIDs ...string) (Scope, error) // ClearAllScope function removes all scope entry. - ClearPATAllScopeEntry(ctx context.Context, token, patID string) error + ClearPATAllScopeEntry(ctx context.Context, session authn.Session, patID string) error // IdentifyPAT function will valid the secret. IdentifyPAT(ctx context.Context, paToken string) (PAT, error) diff --git a/pat/service.go b/pat/service.go index 5161e3cb57..9c995914e4 100644 --- a/pat/service.go +++ b/pat/service.go @@ -10,8 +10,11 @@ import ( "strings" "time" + "github.com/absmach/magistrala" + "github.com/absmach/magistrala/pkg/authn" "github.com/absmach/magistrala/pkg/errors" svcerr "github.com/absmach/magistrala/pkg/errors/service" + "github.com/google/uuid" ) const ( @@ -33,32 +36,29 @@ var ( errClearAllScope = errors.New("failed to clear all entry in scope") ) -type Service struct { - pats PATSRepository - hasher Hasher +type service struct { + pats PATSRepository + hasher Hasher + idProvider magistrala.IDProvider } -var _ Service = (*Service)(nil) +var _ Service = (*service)(nil) // New instantiates the auth service implementation. -func New(pats PATSRepository, hasher Hasher) Service { - return &Service{ - pats: pats, - hasher: hasher, +func New(pats PATSRepository, hasher Hasher, idp magistrala.IDProvider) Service { + return &service{ + pats: pats, + hasher: hasher, + idProvider: idp, } } -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 - } - +func (svc service) CreatePAT(ctx context.Context, session authn.Session, name, description string, duration time.Duration, scope Scope) (PAT, error) { id, err := svc.idProvider.ID() if err != nil { return PAT{}, errors.Wrap(svcerr.ErrCreateEntity, err) } - secret, hash, err := svc.generateSecretAndHash(key.User, id) + secret, hash, err := svc.generateSecretAndHash(session.UserID, id) if err != nil { return PAT{}, errors.Wrap(svcerr.ErrCreateEntity, err) } @@ -66,7 +66,7 @@ func (svc Service) CreatePAT(ctx context.Context, token, name, description strin now := time.Now() pat := PAT{ ID: id, - User: key.User, + User: session.UserID, Name: name, Description: description, Secret: hash, @@ -81,84 +81,58 @@ func (svc Service) CreatePAT(ctx context.Context, token, name, description strin return pat, nil } -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 - } - pat, err := svc.pats.UpdateName(ctx, key.User, patID, name) +func (svc service) UpdatePATName(ctx context.Context, session authn.Session, patID, name string) (PAT, error) { + pat, err := svc.pats.UpdateName(ctx, session.UserID, patID, name) if err != nil { return PAT{}, errors.Wrap(errUpdatePAT, err) } 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 { - return PAT{}, err - } - pat, err := svc.pats.UpdateDescription(ctx, key.User, patID, description) +func (svc service) UpdatePATDescription(ctx context.Context, session authn.Session, patID, description string) (PAT, error) { + pat, err := svc.pats.UpdateDescription(ctx, session.UserID, patID, description) if err != nil { return PAT{}, errors.Wrap(errUpdatePAT, err) } return pat, nil } -func (svc Service) RetrievePAT(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) +func (svc service) RetrievePAT(ctx context.Context, session authn.Session, patID string) (PAT, error) { + pat, err := svc.pats.Retrieve(ctx, session.UserID, patID) if err != nil { return PAT{}, errors.Wrap(errRetrievePAT, err) } 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 { - return PATSPage{}, err - } - patsPage, err := svc.pats.RetrieveAll(ctx, key.User, pm) +func (svc service) ListPATS(ctx context.Context, session authn.Session, pm PATSPageMeta) (PATSPage, error) { + patsPage, err := svc.pats.RetrieveAll(ctx, session.UserID, pm) if err != nil { return PATSPage{}, errors.Wrap(errRetrievePAT, err) } return patsPage, nil } -func (svc Service) DeletePAT(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 { +func (svc service) DeletePAT(ctx context.Context, session authn.Session, patID string) error { + if err := svc.pats.Remove(ctx, session.UserID, patID); err != nil { return errors.Wrap(errDeletePAT, err) } 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 { - return PAT{}, err - } - +func (svc service) ResetPATSecret(ctx context.Context, session authn.Session, patID string, duration time.Duration) (PAT, error) { // Generate new HashToken take place here - secret, hash, err := svc.generateSecretAndHash(key.User, patID) + secret, hash, err := svc.generateSecretAndHash(session.UserID, patID) if err != nil { return PAT{}, errors.Wrap(svcerr.ErrUpdateEntity, err) } - pat, err := svc.pats.UpdateTokenHash(ctx, key.User, patID, hash, time.Now().Add(duration)) + pat, err := svc.pats.UpdateTokenHash(ctx, session.UserID, patID, hash, time.Now().Add(duration)) if err != nil { return PAT{}, errors.Wrap(svcerr.ErrUpdateEntity, err) } - if err := svc.pats.Reactivate(ctx, key.User, patID); err != nil { + if err := svc.pats.Reactivate(ctx, session.UserID, patID); err != nil { return PAT{}, errors.Wrap(svcerr.ErrUpdateEntity, err) } pat.Secret = secret @@ -167,54 +141,37 @@ 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 { - return err - } - - if err := svc.pats.Revoke(ctx, key.User, patID); err != nil { +func (svc service) RevokePATSecret(ctx context.Context, session authn.Session, patID string) error { + if err := svc.pats.Revoke(ctx, session.UserID, patID); err != nil { return errors.Wrap(errRevokePAT, err) } return nil } -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 - } - scope, err := svc.pats.AddScopeEntry(ctx, key.User, patID, platformEntityType, optionalDomainID, optionalDomainEntityType, operation, entityIDs...) +func (svc service) AddPATScopeEntry(ctx context.Context, session authn.Session, patID string, platformEntityType PlatformEntityType, optionalDomainID string, optionalDomainEntityType DomainEntityType, operation OperationType, entityIDs ...string) (Scope, error) { + scope, err := svc.pats.AddScopeEntry(ctx, session.UserID, 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 { - return Scope{}, err - } - scope, err := svc.pats.RemoveScopeEntry(ctx, key.User, patID, platformEntityType, optionalDomainID, optionalDomainEntityType, operation, entityIDs...) +func (svc service) RemovePATScopeEntry(ctx context.Context, session authn.Session, patID string, platformEntityType PlatformEntityType, optionalDomainID string, optionalDomainEntityType DomainEntityType, operation OperationType, entityIDs ...string) (Scope, error) { + scope, err := svc.pats.RemoveScopeEntry(ctx, session.UserID, patID, platformEntityType, optionalDomainID, optionalDomainEntityType, operation, entityIDs...) if err != nil { return Scope{}, err } return scope, nil } -func (svc Service) ClearPATAllScopeEntry(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 { +func (svc service) ClearPATAllScopeEntry(ctx context.Context, session authn.Session, patID string) error { + if err := svc.pats.RemoveAllScopeEntry(ctx, session.UserID, patID); err != nil { return errors.Wrap(errClearAllScope, err) } return nil } -func (svc Service) IdentifyPAT(ctx context.Context, secret string) (PAT, 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) @@ -236,7 +193,7 @@ func (svc Service) IdentifyPAT(ctx context.Context, secret string) (PAT, error) 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 { +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 @@ -247,14 +204,14 @@ func (svc Service) AuthorizePAT(ctx context.Context, paToken string, platformEnt return nil } -func (svc Service) CheckPAT(ctx context.Context, userID, patID string, platformEntityType PlatformEntityType, optionalDomainID string, optionalDomainEntityType DomainEntityType, operation OperationType, entityIDs ...string) error { +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) { +func (svc service) generateSecretAndHash(userID, patID string) (string, string, error) { uID, err := uuid.Parse(userID) if err != nil { return "", "", errors.Wrap(errFailedToParseUUID, err) diff --git a/pat/tracing/tracing.go b/pat/tracing/tracing.go new file mode 100644 index 0000000000..4fab5fae49 --- /dev/null +++ b/pat/tracing/tracing.go @@ -0,0 +1,164 @@ +// Copyright (c) Abstract Machines +// SPDX-License-Identifier: Apache-2.0 + +package tracing + +import ( + "context" + "time" + + "github.com/absmach/magistrala/pat" + "github.com/absmach/magistrala/pkg/authn" + "go.opentelemetry.io/otel/attribute" + "go.opentelemetry.io/otel/trace" +) + +var _ pat.Service = (*tracingMiddleware)(nil) + +type tracingMiddleware struct { + tracer trace.Tracer + svc pat.Service +} + +// New returns a new group service with tracing capabilities. +func New(svc pat.Service, tracer trace.Tracer) pat.Service { + return &tracingMiddleware{tracer, svc} +} + +func (tm *tracingMiddleware) CreatePAT(ctx context.Context, session authn.Session, name, description string, duration time.Duration, scope pat.Scope) (pat.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.CreatePAT(ctx, session, name, description, duration, scope) +} + +func (tm *tracingMiddleware) UpdatePATName(ctx context.Context, session authn.Session, patID, name string) (pat.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.UpdatePATName(ctx, session, patID, name) +} + +func (tm *tracingMiddleware) UpdatePATDescription(ctx context.Context, session authn.Session, patID, description string) (pat.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.UpdatePATDescription(ctx, session, patID, description) +} + +func (tm *tracingMiddleware) RetrievePAT(ctx context.Context, session authn.Session, patID string) (pat.PAT, error) { + ctx, span := tm.tracer.Start(ctx, "retrieve_pat", trace.WithAttributes( + attribute.String("pat_id", patID), + )) + defer span.End() + return tm.svc.RetrievePAT(ctx, session, patID) +} + +func (tm *tracingMiddleware) ListPATS(ctx context.Context, session authn.Session, pm pat.PATSPageMeta) (pat.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.ListPATS(ctx, session, pm) +} + +func (tm *tracingMiddleware) DeletePAT(ctx context.Context, session authn.Session, patID string) error { + ctx, span := tm.tracer.Start(ctx, "delete_pat", trace.WithAttributes( + attribute.String("pat_id", patID), + )) + defer span.End() + return tm.svc.DeletePAT(ctx, session, patID) +} + +func (tm *tracingMiddleware) ResetPATSecret(ctx context.Context, session authn.Session, patID string, duration time.Duration) (pat.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.ResetPATSecret(ctx, session, patID, duration) +} + +func (tm *tracingMiddleware) RevokePATSecret(ctx context.Context, session authn.Session, 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.RevokePATSecret(ctx, session, patID) +} + +func (tm *tracingMiddleware) AddPATScopeEntry(ctx context.Context, session authn.Session, patID string, platformEntityType pat.PlatformEntityType, optionalDomainID string, optionalDomainEntityType pat.DomainEntityType, operation pat.OperationType, entityIDs ...string) (pat.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), + attribute.String("optional_domain_entity", optionalDomainEntityType.String()), + attribute.String("operation", operation.String()), + attribute.StringSlice("entities", entityIDs), + )) + defer span.End() + return tm.svc.AddPATScopeEntry(ctx, session, patID, platformEntityType, optionalDomainID, optionalDomainEntityType, operation, entityIDs...) +} + +func (tm *tracingMiddleware) RemovePATScopeEntry(ctx context.Context, session authn.Session, patID string, platformEntityType pat.PlatformEntityType, optionalDomainID string, optionalDomainEntityType pat.DomainEntityType, operation pat.OperationType, entityIDs ...string) (pat.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), + attribute.String("optional_domain_entity", optionalDomainEntityType.String()), + attribute.String("operation", operation.String()), + attribute.StringSlice("entities", entityIDs), + )) + defer span.End() + return tm.svc.RemovePATScopeEntry(ctx, session, patID, platformEntityType, optionalDomainID, optionalDomainEntityType, operation, entityIDs...) +} + +func (tm *tracingMiddleware) ClearPATAllScopeEntry(ctx context.Context, session authn.Session, 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.ClearPATAllScopeEntry(ctx, session, patID) +} + +func (tm *tracingMiddleware) IdentifyPAT(ctx context.Context, paToken string) (pat.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 pat.PlatformEntityType, optionalDomainID string, optionalDomainEntityType pat.DomainEntityType, operation pat.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), + 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, platformEntityType, optionalDomainID, optionalDomainEntityType, operation, entityIDs...) +} + +func (tm *tracingMiddleware) CheckPAT(ctx context.Context, userID, patID string, platformEntityType pat.PlatformEntityType, optionalDomainID string, optionalDomainEntityType pat.DomainEntityType, operation pat.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.CheckPAT(ctx, userID, patID, platformEntityType, optionalDomainID, optionalDomainEntityType, operation, entityIDs...) +} From a63ab4e4aed53aa3cbef02f073c74bb5a6af55a0 Mon Sep 17 00:00:00 2001 From: nyagamunene Date: Wed, 30 Oct 2024 13:21:26 +0300 Subject: [PATCH 03/72] Add grpc authorizePAT Signed-off-by: nyagamunene --- api/grpc/auth/v1/auth.pb.go | 110 ++++++++++++++++++++++++++++---- auth/service.go | 15 +++++ cmd/auth/main.go | 2 + docker/nginx/nginx-key.conf | 7 ++ pat/api/{ => http}/endpoint.go | 0 pat/api/{ => http}/requests.go | 0 pat/api/{ => http}/responses.go | 0 pat/api/{ => http}/transport.go | 0 pat/events/streams.go | 94 +++++++++++++++++++++++++++ 9 files changed, 216 insertions(+), 12 deletions(-) rename pat/api/{ => http}/endpoint.go (100%) rename pat/api/{ => http}/requests.go (100%) rename pat/api/{ => http}/responses.go (100%) rename pat/api/{ => http}/transport.go (100%) create mode 100644 pat/events/streams.go diff --git a/api/grpc/auth/v1/auth.pb.go b/api/grpc/auth/v1/auth.pb.go index 1ee360a95f..bd6deda5e9 100644 --- a/api/grpc/auth/v1/auth.pb.go +++ b/api/grpc/auth/v1/auth.pb.go @@ -32,9 +32,16 @@ type AuthNReq struct { func (x *AuthNReq) Reset() { *x = AuthNReq{} - mi := &file_auth_v1_auth_proto_msgTypes[0] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) + if protoimpl.UnsafeEnabled { + mi := &file_auth_v1_auth_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } + if protoimpl.UnsafeEnabled { + mi := &file_auth_v1_auth_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } } func (x *AuthNReq) String() string { @@ -45,7 +52,8 @@ func (*AuthNReq) ProtoMessage() {} func (x *AuthNReq) ProtoReflect() protoreflect.Message { mi := &file_auth_v1_auth_proto_msgTypes[0] - if x != nil { + if protoimpl.UnsafeEnabled && x != nil { + if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -78,9 +86,16 @@ type AuthNRes struct { func (x *AuthNRes) Reset() { *x = AuthNRes{} - mi := &file_auth_v1_auth_proto_msgTypes[1] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) + if protoimpl.UnsafeEnabled { + mi := &file_auth_v1_auth_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } + if protoimpl.UnsafeEnabled { + mi := &file_auth_v1_auth_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } } func (x *AuthNRes) String() string { @@ -91,7 +106,8 @@ func (*AuthNRes) ProtoMessage() {} func (x *AuthNRes) ProtoReflect() protoreflect.Message { mi := &file_auth_v1_auth_proto_msgTypes[1] - if x != nil { + if protoimpl.UnsafeEnabled && x != nil { + if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -144,9 +160,16 @@ type AuthZReq struct { func (x *AuthZReq) Reset() { *x = AuthZReq{} - mi := &file_auth_v1_auth_proto_msgTypes[2] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) + if protoimpl.UnsafeEnabled { + mi := &file_auth_v1_auth_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } + if protoimpl.UnsafeEnabled { + mi := &file_auth_v1_auth_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } } func (x *AuthZReq) String() string { @@ -157,7 +180,8 @@ func (*AuthZReq) ProtoMessage() {} func (x *AuthZReq) ProtoReflect() protoreflect.Message { mi := &file_auth_v1_auth_proto_msgTypes[2] - if x != nil { + if protoimpl.UnsafeEnabled && x != nil { + if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -493,6 +517,68 @@ func file_auth_v1_auth_proto_init() { if File_auth_v1_auth_proto != nil { return } + if !protoimpl.UnsafeEnabled { + file_auth_v1_auth_proto_msgTypes[0].Exporter = func(v any, i int) any { + switch v := v.(*AuthNReq); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_auth_v1_auth_proto_msgTypes[1].Exporter = func(v any, i int) any { + switch v := v.(*AuthNRes); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_auth_v1_auth_proto_msgTypes[2].Exporter = func(v any, i int) any { + switch v := v.(*AuthZReq); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_auth_v1_auth_proto_msgTypes[3].Exporter = func(v any, i int) any { + switch v := v.(*AuthZpatReq); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_auth_v1_auth_proto_msgTypes[4].Exporter = func(v any, i int) any { + switch v := v.(*AuthZRes); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ diff --git a/auth/service.go b/auth/service.go index 579d8466fd..c266ed2fa7 100644 --- a/auth/service.go +++ b/auth/service.go @@ -167,6 +167,21 @@ func (svc service) RetrieveKey(ctx context.Context, token, id string) (Key, erro } func (svc service) Identify(ctx context.Context, token string) (Key, error) { + if strings.HasPrefix(token, "pat"+"_") { + pat, err := svc.IdentifyPAT(ctx, token) + if err != nil { + return Key{}, err + } + return Key{ + ID: pat.ID, + Type: PersonalAccessToken, + Subject: pat.User, + User: pat.User, + IssuedAt: pat.IssuedAt, + ExpiresAt: pat.ExpiresAt, + }, nil + } + key, err := svc.tokenizer.Parse(token) if errors.Contains(err, ErrExpiry) { err = svc.keys.Remove(ctx, key.Issuer, key.ID) diff --git a/cmd/auth/main.go b/cmd/auth/main.go index c5ef6d2b5e..88696086df 100644 --- a/cmd/auth/main.go +++ b/cmd/auth/main.go @@ -28,6 +28,8 @@ import ( "github.com/absmach/supermq/auth/tracing" boltclient "github.com/absmach/supermq/internal/clients/bolt" smqlog "github.com/absmach/supermq/logger" + "github.com/absmach/supermq/pat/bolt" + "github.com/absmach/supermq/pat/hasher" "github.com/absmach/supermq/pkg/jaeger" "github.com/absmach/supermq/pkg/policies/spicedb" pgclient "github.com/absmach/supermq/pkg/postgres" diff --git a/docker/nginx/nginx-key.conf b/docker/nginx/nginx-key.conf index 9779466dc2..db8a5aaad0 100644 --- a/docker/nginx/nginx-key.conf +++ b/docker/nginx/nginx-key.conf @@ -64,6 +64,13 @@ http { proxy_pass http://domains:${SMQ_DOMAINS_HTTP_PORT}; } + # Proxy pass to auth service + location ~ ^/(domains|keys|pats) { + include snippets/proxy-headers.conf; + add_header Access-Control-Expose-Headers Location; + proxy_pass http://auth:${MG_AUTH_HTTP_PORT}; + } + # Proxy pass to users service location ~ ^/(users|password|authorize|oauth/callback/[^/]+) { include snippets/proxy-headers.conf; diff --git a/pat/api/endpoint.go b/pat/api/http/endpoint.go similarity index 100% rename from pat/api/endpoint.go rename to pat/api/http/endpoint.go diff --git a/pat/api/requests.go b/pat/api/http/requests.go similarity index 100% rename from pat/api/requests.go rename to pat/api/http/requests.go diff --git a/pat/api/responses.go b/pat/api/http/responses.go similarity index 100% rename from pat/api/responses.go rename to pat/api/http/responses.go diff --git a/pat/api/transport.go b/pat/api/http/transport.go similarity index 100% rename from pat/api/transport.go rename to pat/api/http/transport.go diff --git a/pat/events/streams.go b/pat/events/streams.go new file mode 100644 index 0000000000..713ddea5a6 --- /dev/null +++ b/pat/events/streams.go @@ -0,0 +1,94 @@ +// Copyright (c) Abstract Machines +// SPDX-License-Identifier: Apache-2.0 + +package events + +import ( + "context" + "time" + + "github.com/absmach/magistrala/pat" + "github.com/absmach/magistrala/pkg/authn" + "github.com/absmach/magistrala/pkg/events" + "github.com/absmach/magistrala/pkg/events/store" +) + +const streamID = "magistrala.pat" + +var _ pat.Service = (*eventStore)(nil) + + +type eventStore struct { + events.Publisher + svc pat.Service +} + +// NewEventStoreMiddleware returns wrapper around pat service that sends +// events to event store. +func NewEventStoreMiddleware(ctx context.Context, svc pat.Service, url string) (pat.Service, error) { + publisher, err := store.NewPublisher(ctx, url, streamID) + if err != nil { + return nil, err + } + + return &eventStore{ + svc: svc, + Publisher: publisher, + }, nil +} + +func (es *eventStore) CreatePAT(ctx context.Context, session authn.Session, name, description string, duration time.Duration, scope pat.Scope) (pat.PAT, error) { + return es.svc.CreatePAT(ctx, session, name, description, duration, scope) +} + +func (es *eventStore) UpdatePATName(ctx context.Context, session authn.Session, patID, name string) (pat.PAT, error) { + return es.svc.UpdatePATName(ctx, session, patID, name) +} + +func (es *eventStore) UpdatePATDescription(ctx context.Context, session authn.Session, patID, description string) (pat.PAT, error) { + return es.svc.UpdatePATDescription(ctx, session, patID, description) +} + +func (es *eventStore) RetrievePAT(ctx context.Context, session authn.Session, patID string) (pat.PAT, error) { + return es.svc.RetrievePAT(ctx, session, patID) +} + +func (es *eventStore) ListPATS(ctx context.Context, session authn.Session, pm pat.PATSPageMeta) (pat.PATSPage, error) { + return es.svc.ListPATS(ctx, session, pm) +} + +func (es *eventStore) DeletePAT(ctx context.Context, session authn.Session, patID string) error { + return es.svc.DeletePAT(ctx, session, patID) +} + +func (es *eventStore) ResetPATSecret(ctx context.Context, session authn.Session, patID string, duration time.Duration) (pat.PAT, error) { + return es.svc.ResetPATSecret(ctx, session, patID, duration) +} + +func (es *eventStore) RevokePATSecret(ctx context.Context, session authn.Session, patID string) error { + return es.svc.RevokePATSecret(ctx, session, patID) +} + +func (es *eventStore) AddPATScopeEntry(ctx context.Context, session authn.Session, patID string, platformEntityType pat.PlatformEntityType, optionalDomainID string, optionalDomainEntityType pat.DomainEntityType, operation pat.OperationType, entityIDs ...string) (pat.Scope, error) { + return es.svc.AddPATScopeEntry(ctx, session, patID, platformEntityType, optionalDomainID, optionalDomainEntityType, operation, entityIDs...) +} + +func (es *eventStore) RemovePATScopeEntry(ctx context.Context, session authn.Session, patID string, platformEntityType pat.PlatformEntityType, optionalDomainID string, optionalDomainEntityType pat.DomainEntityType, operation pat.OperationType, entityIDs ...string) (pat.Scope, error) { + return es.svc.RemovePATScopeEntry(ctx, session, patID, platformEntityType, optionalDomainID, optionalDomainEntityType, operation, entityIDs...) +} + +func (es *eventStore) ClearPATAllScopeEntry(ctx context.Context, session authn.Session, patID string) error { + return es.svc.ClearPATAllScopeEntry(ctx, session, patID) +} + +func (es *eventStore) IdentifyPAT(ctx context.Context, paToken string) (pat.PAT, error) { + return es.svc.IdentifyPAT(ctx, paToken) +} + +func (es *eventStore) AuthorizePAT(ctx context.Context, paToken string, platformEntityType pat.PlatformEntityType, optionalDomainID string, optionalDomainEntityType pat.DomainEntityType, operation pat.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 pat.PlatformEntityType, optionalDomainID string, optionalDomainEntityType pat.DomainEntityType, operation pat.OperationType, entityIDs ...string) error { + return es.svc.CheckPAT(ctx, userID, patID, platformEntityType, optionalDomainID, optionalDomainEntityType, operation, entityIDs...) +} \ No newline at end of file From 61190f7df49db6b510d2b9ed26bfddec5286b2aa Mon Sep 17 00:00:00 2001 From: nyagamunene Date: Thu, 31 Oct 2024 11:55:27 +0300 Subject: [PATCH 04/72] Move pat to auth Signed-off-by: nyagamunene --- auth/api/http/transport.go | 2 +- auth/api/logging.go | 1 + auth/api/metrics.go | 1 + auth/mocks/service.go | 3 + auth/service.go | 2 +- auth/tracing/tracing.go | 1 + pat/api/http/endpoint.go | 259 ----------- pat/api/http/requests.go | 361 --------------- pat/api/http/responses.go | 208 --------- pat/api/http/transport.go | 271 ----------- pat/bolt/doc.go | 6 - pat/bolt/init.go | 21 - pat/bolt/pat.go | 773 -------------------------------- pat/events/streams.go | 94 ---- pat/hasher.go | 17 - pat/hasher/doc.go | 6 - pat/hasher/hasher.go | 86 ---- pat/middleware/authorization.go | 84 ---- pat/middleware/logging.go | 275 ------------ pat/middleware/metrics.go | 142 ------ pat/pat.go | 753 ------------------------------- pat/service.go | 259 ----------- pat/tracing/tracing.go | 164 ------- 23 files changed, 8 insertions(+), 3781 deletions(-) delete mode 100644 pat/api/http/endpoint.go delete mode 100644 pat/api/http/requests.go delete mode 100644 pat/api/http/responses.go delete mode 100644 pat/api/http/transport.go delete mode 100644 pat/bolt/doc.go delete mode 100644 pat/bolt/init.go delete mode 100644 pat/bolt/pat.go delete mode 100644 pat/events/streams.go delete mode 100644 pat/hasher.go delete mode 100644 pat/hasher/doc.go delete mode 100644 pat/hasher/hasher.go delete mode 100644 pat/middleware/authorization.go delete mode 100644 pat/middleware/logging.go delete mode 100644 pat/middleware/metrics.go delete mode 100644 pat/pat.go delete mode 100644 pat/service.go delete mode 100644 pat/tracing/tracing.go diff --git a/auth/api/http/transport.go b/auth/api/http/transport.go index c3a8d723c0..6f125b1622 100644 --- a/auth/api/http/transport.go +++ b/auth/api/http/transport.go @@ -15,7 +15,7 @@ import ( ) // MakeHandler returns a HTTP handler for API endpoints. -func MakeHandler(svc auth.Service, logger *slog.Logger, instanceID string) http.Handler { +func MakeHandler(svc auth.Service, authn mgauthn.Authentication, logger *slog.Logger, instanceID string) http.Handler { mux := chi.NewRouter() mux = keys.MakeHandler(svc, mux, logger) diff --git a/auth/api/logging.go b/auth/api/logging.go index 94bacfd3d7..b8a6d9c71a 100644 --- a/auth/api/logging.go +++ b/auth/api/logging.go @@ -11,6 +11,7 @@ import ( "time" "github.com/absmach/supermq/auth" + "github.com/absmach/supermq/pkg/authn" "github.com/absmach/supermq/pkg/policies" ) diff --git a/auth/api/metrics.go b/auth/api/metrics.go index 081165b3ff..ee3ff19b3b 100644 --- a/auth/api/metrics.go +++ b/auth/api/metrics.go @@ -10,6 +10,7 @@ import ( "time" "github.com/absmach/supermq/auth" + "github.com/absmach/supermq/pkg/authn" "github.com/absmach/supermq/pkg/policies" "github.com/go-kit/kit/metrics" ) diff --git a/auth/mocks/service.go b/auth/mocks/service.go index 0a01591776..9ff90b6f1e 100644 --- a/auth/mocks/service.go +++ b/auth/mocks/service.go @@ -5,6 +5,9 @@ package mocks import ( + auth "github.com/absmach/magistrala/auth" + authn "github.com/absmach/magistrala/pkg/authn" + context "context" auth "github.com/absmach/supermq/auth" diff --git a/auth/service.go b/auth/service.go index c266ed2fa7..583a784462 100644 --- a/auth/service.go +++ b/auth/service.go @@ -167,7 +167,7 @@ func (svc service) RetrieveKey(ctx context.Context, token, id string) (Key, erro } func (svc service) Identify(ctx context.Context, token string) (Key, error) { - if strings.HasPrefix(token, "pat"+"_") { + if strings.HasPrefix(token, patPrefix+patSecretSeparator) { pat, err := svc.IdentifyPAT(ctx, token) if err != nil { return Key{}, err diff --git a/auth/tracing/tracing.go b/auth/tracing/tracing.go index 0321eaf3bd..47735db6e7 100644 --- a/auth/tracing/tracing.go +++ b/auth/tracing/tracing.go @@ -9,6 +9,7 @@ import ( "time" "github.com/absmach/supermq/auth" + "github.com/absmach/supermq/pkg/authn" "github.com/absmach/supermq/pkg/policies" "go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/trace" diff --git a/pat/api/http/endpoint.go b/pat/api/http/endpoint.go deleted file mode 100644 index 94cbdf5e80..0000000000 --- a/pat/api/http/endpoint.go +++ /dev/null @@ -1,259 +0,0 @@ -// Copyright (c) Abstract Machines -// SPDX-License-Identifier: Apache-2.0 - -package pats - -import ( - "context" - - "github.com/absmach/magistrala/internal/api" - "github.com/absmach/magistrala/pat" - "github.com/absmach/magistrala/pkg/authn" - svcerr "github.com/absmach/magistrala/pkg/errors/service" - "github.com/go-kit/kit/endpoint" -) - -func createPATEndpoint(svc pat.Service) endpoint.Endpoint { - return func(ctx context.Context, request interface{}) (interface{}, error) { - req := request.(createPatReq) - if err := req.validate(); err != nil { - return nil, err - } - - session, ok := ctx.Value(api.SessionKey).(authn.Session) - if !ok { - return nil, svcerr.ErrAuthentication - } - - pat, err := svc.CreatePAT(ctx, session, req.Name, req.Description, req.Duration, req.Scope) - if err != nil { - return nil, err - } - - return createPatRes{pat}, nil - } -} - -func retrievePATEndpoint(svc pat.Service) endpoint.Endpoint { - return func(ctx context.Context, request interface{}) (interface{}, error) { - req := request.(retrievePatReq) - if err := req.validate(); err != nil { - return nil, err - } - - session, ok := ctx.Value(api.SessionKey).(authn.Session) - if !ok { - return nil, svcerr.ErrAuthentication - } - - pat, err := svc.RetrievePAT(ctx, session, req.id) - if err != nil { - return nil, err - } - - return retrievePatRes{pat}, nil - } -} - -func updatePATNameEndpoint(svc pat.Service) endpoint.Endpoint { - return func(ctx context.Context, request interface{}) (interface{}, error) { - req := request.(updatePatNameReq) - if err := req.validate(); err != nil { - return nil, err - } - - session, ok := ctx.Value(api.SessionKey).(authn.Session) - if !ok { - return nil, svcerr.ErrAuthentication - } - pat, err := svc.UpdatePATName(ctx, session, req.id, req.Name) - if err != nil { - return nil, err - } - - return updatePatNameRes{pat}, nil - } -} - -func updatePATDescriptionEndpoint(svc pat.Service) endpoint.Endpoint { - return func(ctx context.Context, request interface{}) (interface{}, error) { - req := request.(updatePatDescriptionReq) - if err := req.validate(); err != nil { - return nil, err - } - - session, ok := ctx.Value(api.SessionKey).(authn.Session) - if !ok { - return nil, svcerr.ErrAuthentication - } - - pat, err := svc.UpdatePATDescription(ctx, session, req.id, req.Description) - if err != nil { - return nil, err - } - - return updatePatDescriptionRes{pat}, nil - } -} - -func listPATSEndpoint(svc pat.Service) endpoint.Endpoint { - return func(ctx context.Context, request interface{}) (interface{}, error) { - req := request.(listPatsReq) - if err := req.validate(); err != nil { - return nil, err - } - - session, ok := ctx.Value(api.SessionKey).(authn.Session) - if !ok { - return nil, svcerr.ErrAuthentication - } - - pm := pat.PATSPageMeta{ - Limit: req.limit, - Offset: req.offset, - } - patsPage, err := svc.ListPATS(ctx, session, pm) - if err != nil { - return nil, err - } - - return listPatsRes{patsPage}, nil - } -} - -func deletePATEndpoint(svc pat.Service) endpoint.Endpoint { - return func(ctx context.Context, request interface{}) (interface{}, error) { - req := request.(deletePatReq) - if err := req.validate(); err != nil { - return nil, err - } - - session, ok := ctx.Value(api.SessionKey).(authn.Session) - if !ok { - return nil, svcerr.ErrAuthentication - } - - if err := svc.DeletePAT(ctx, session, req.id); err != nil { - return nil, err - } - - return deletePatRes{}, nil - } -} - -func resetPATSecretEndpoint(svc pat.Service) endpoint.Endpoint { - return func(ctx context.Context, request interface{}) (interface{}, error) { - req := request.(resetPatSecretReq) - if err := req.validate(); err != nil { - return nil, err - } - - session, ok := ctx.Value(api.SessionKey).(authn.Session) - if !ok { - return nil, svcerr.ErrAuthentication - } - - pat, err := svc.ResetPATSecret(ctx, session, req.id, req.Duration) - if err != nil { - return nil, err - } - - return resetPatSecretRes{pat}, nil - } -} - -func revokePATSecretEndpoint(svc pat.Service) endpoint.Endpoint { - return func(ctx context.Context, request interface{}) (interface{}, error) { - req := request.(revokePatSecretReq) - if err := req.validate(); err != nil { - return nil, err - } - - session, ok := ctx.Value(api.SessionKey).(authn.Session) - if !ok { - return nil, svcerr.ErrAuthentication - } - - if err := svc.RevokePATSecret(ctx, session, req.id); err != nil { - return nil, err - } - - return revokePatSecretRes{}, nil - } -} - -func addPATScopeEntryEndpoint(svc pat.Service) endpoint.Endpoint { - return func(ctx context.Context, request interface{}) (interface{}, error) { - req := request.(addPatScopeEntryReq) - if err := req.validate(); err != nil { - return nil, err - } - - session, ok := ctx.Value(api.SessionKey).(authn.Session) - if !ok { - return nil, svcerr.ErrAuthentication - } - - scope, err := svc.AddPATScopeEntry(ctx, session, req.id, req.PlatformEntityType, req.OptionalDomainID, req.OptionalDomainEntityType, req.Operation, req.EntityIDs...) - if err != nil { - return nil, err - } - - return addPatScopeEntryRes{scope}, nil - } -} - -func removePATScopeEntryEndpoint(svc pat.Service) endpoint.Endpoint { - return func(ctx context.Context, request interface{}) (interface{}, error) { - req := request.(removePatScopeEntryReq) - if err := req.validate(); err != nil { - return nil, err - } - - session, ok := ctx.Value(api.SessionKey).(authn.Session) - if !ok { - return nil, svcerr.ErrAuthentication - } - - scope, err := svc.RemovePATScopeEntry(ctx, session, req.id, req.PlatformEntityType, req.OptionalDomainID, req.OptionalDomainEntityType, req.Operation, req.EntityIDs...) - if err != nil { - return nil, err - } - return removePatScopeEntryRes{scope}, nil - } -} - -func clearPATAllScopeEntryEndpoint(svc pat.Service) endpoint.Endpoint { - return func(ctx context.Context, request interface{}) (interface{}, error) { - req := request.(clearAllScopeEntryReq) - if err := req.validate(); err != nil { - return nil, err - } - - session, ok := ctx.Value(api.SessionKey).(authn.Session) - if !ok { - return nil, svcerr.ErrAuthentication - } - - if err := svc.ClearPATAllScopeEntry(ctx, session, req.id); err != nil { - return nil, err - } - - return clearAllScopeEntryRes{}, nil - } -} - -func authorizePATEndpoint(svc pat.Service) endpoint.Endpoint { - return func(ctx context.Context, request interface{}) (interface{}, error) { - req := request.(authorizePATReq) - if err := req.validate(); err != nil { - return nil, err - } - - if err := svc.AuthorizePAT(ctx, req.token, req.PlatformEntityType, req.OptionalDomainID, req.OptionalDomainEntityType, req.Operation, req.EntityIDs...); err != nil { - return nil, err - } - - return authorizePATRes{}, nil - } -} diff --git a/pat/api/http/requests.go b/pat/api/http/requests.go deleted file mode 100644 index 1822ec457e..0000000000 --- a/pat/api/http/requests.go +++ /dev/null @@ -1,361 +0,0 @@ -// Copyright (c) Abstract Machines -// SPDX-License-Identifier: Apache-2.0 - -package pats - -import ( - "encoding/json" - "strings" - "time" - - "github.com/absmach/magistrala/pat" - "github.com/absmach/magistrala/pkg/apiutil" -) - -type createPatReq struct { - token string - Name string `json:"name,omitempty"` - Description string `json:"description,omitempty"` - Duration time.Duration `json:"duration,omitempty"` - Scope pat.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 pat.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 - } - - 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 pat.PlatformEntityType `json:"platform_entity_type,omitempty"` - OptionalDomainID string `json:"optional_domain_id,omitempty"` - OptionalDomainEntityType pat.DomainEntityType `json:"optional_domain_entity_type,omitempty"` - Operation pat.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 := pat.ParsePlatformEntityType(temp.PlatformEntityType) - if err != nil { - return err - } - odt, err := pat.ParseDomainEntityType(temp.OptionalDomainEntityType) - if err != nil { - return err - } - op, err := pat.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 pat.PlatformEntityType `json:"platform_entity_type,omitempty"` - OptionalDomainID string `json:"optional_domain_id,omitempty"` - OptionalDomainEntityType pat.DomainEntityType `json:"optional_domain_entity_type,omitempty"` - Operation pat.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 := pat.ParsePlatformEntityType(temp.PlatformEntityType) - if err != nil { - return err - } - odt, err := pat.ParseDomainEntityType(temp.OptionalDomainEntityType) - if err != nil { - return err - } - op, err := pat.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 authorizePATReq struct { - token string - PlatformEntityType pat.PlatformEntityType `json:"platform_entity_type,omitempty"` - OptionalDomainID string `json:"optional_domain_id,omitempty"` - OptionalDomainEntityType pat.DomainEntityType `json:"optional_domain_entity_type,omitempty"` - Operation pat.OperationType `json:"operation,omitempty"` - EntityIDs []string `json:"entity_ids,omitempty"` -} - -func (tcpsr *authorizePATReq) 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 - } - - tcpsr.OptionalDomainID = temp.OptionalDomainID - tcpsr.EntityIDs = temp.EntityIDs - - pet, err := pat.ParsePlatformEntityType(temp.PlatformEntityType) - if err != nil { - return err - } - tcpsr.PlatformEntityType = pet - - if temp.OptionalDomainEntityType != "" { - odt, err := pat.ParseDomainEntityType(temp.OptionalDomainEntityType) - if err != nil { - return err - } - tcpsr.OptionalDomainEntityType = odt - } - - if temp.OptionalDomainID != "" { - op, err := pat.ParseOperationType(temp.Operation) - if err != nil { - return err - } - tcpsr.Operation = op - } - - return nil -} - -func (req authorizePATReq) validate() (err error) { - if req.token == "" { - return apiutil.ErrBearerToken - } - - return nil -} diff --git a/pat/api/http/responses.go b/pat/api/http/responses.go deleted file mode 100644 index b2d0fe57de..0000000000 --- a/pat/api/http/responses.go +++ /dev/null @@ -1,208 +0,0 @@ -// Copyright (c) Abstract Machines -// SPDX-License-Identifier: Apache-2.0 - -package pats - -import ( - "net/http" - - "github.com/absmach/magistrala" - "github.com/absmach/magistrala/pat" -) - -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 { - pat.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 { - pat.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 { - pat.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 { - pat.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 { - pat.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 { - pat.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 { - pat.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 { - pat.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 authorizePATRes struct{} - -func (res authorizePATRes) Code() int { - return http.StatusNoContent -} - -func (res authorizePATRes) Headers() map[string]string { - return map[string]string{} -} - -func (res authorizePATRes) Empty() bool { - return true -} diff --git a/pat/api/http/transport.go b/pat/api/http/transport.go deleted file mode 100644 index 95d0f45efa..0000000000 --- a/pat/api/http/transport.go +++ /dev/null @@ -1,271 +0,0 @@ -// 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/internal/api" - "github.com/absmach/magistrala/pat" - "github.com/absmach/magistrala/pkg/apiutil" - mgauthn "github.com/absmach/magistrala/pkg/authn" - "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 pat.Service, mux *chi.Mux, authn mgauthn.Authentication, logger *slog.Logger) *chi.Mux { - opts := []kithttp.ServerOption{ - kithttp.ServerErrorEncoder(apiutil.LoggingErrorEncoder(logger, api.EncodeError)), - } - mux.Group(func(r chi.Router) { - mux.Use(api.AuthenticateMiddleware(authn, true)) - - mux.Route("/pats", func(r chi.Router) { - r.Post("/", kithttp.NewServer( - createPATEndpoint(svc), - decodeCreatePATRequest, - api.EncodeResponse, - opts..., - ).ServeHTTP) - - r.Get("/{id}", kithttp.NewServer( - (retrievePATEndpoint(svc)), - decodeRetrievePATRequest, - api.EncodeResponse, - opts..., - ).ServeHTTP) - - r.Put("/{id}/name", kithttp.NewServer( - (updatePATNameEndpoint(svc)), - decodeUpdatePATNameRequest, - api.EncodeResponse, - opts..., - ).ServeHTTP) - - r.Put("/{id}/description", kithttp.NewServer( - (updatePATDescriptionEndpoint(svc)), - decodeUpdatePATDescriptionRequest, - api.EncodeResponse, - opts..., - ).ServeHTTP) - - r.Get("/", kithttp.NewServer( - (listPATSEndpoint(svc)), - decodeListPATSRequest, - api.EncodeResponse, - opts..., - ).ServeHTTP) - - r.Delete("/{id}", kithttp.NewServer( - (deletePATEndpoint(svc)), - decodeDeletePATRequest, - api.EncodeResponse, - opts..., - ).ServeHTTP) - - r.Put("/{id}/secret/reset", kithttp.NewServer( - (resetPATSecretEndpoint(svc)), - decodeResetPATSecretRequest, - api.EncodeResponse, - opts..., - ).ServeHTTP) - - r.Put("/{id}/secret/revoke", kithttp.NewServer( - (revokePATSecretEndpoint(svc)), - decodeRevokePATSecretRequest, - api.EncodeResponse, - opts..., - ).ServeHTTP) - - r.Put("/{id}/scope/add", kithttp.NewServer( - (addPATScopeEntryEndpoint(svc)), - decodeAddPATScopeEntryRequest, - api.EncodeResponse, - opts..., - ).ServeHTTP) - - r.Put("/{id}/scope/remove", kithttp.NewServer( - (removePATScopeEntryEndpoint(svc)), - decodeRemovePATScopeEntryRequest, - api.EncodeResponse, - opts..., - ).ServeHTTP) - - r.Delete("/{id}/scope", kithttp.NewServer( - (clearPATAllScopeEntryEndpoint(svc)), - decodeClearPATAllScopeEntryRequest, - api.EncodeResponse, - opts..., - ).ServeHTTP) - - r.Post("/authorize", kithttp.NewServer( - (authorizePATEndpoint(svc)), - decodeAuthorizePATRequest, - 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 decodeUpdatePATNameRequest(_ 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 decodeUpdatePATDescriptionRequest(_ 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 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) - } - 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, - } - return req, nil -} - -func decodeDeletePATRequest(_ context.Context, r *http.Request) (interface{}, error) { - return deletePatReq{ - token: apiutil.ExtractBearerToken(r), - id: chi.URLParam(r, "id"), - }, nil -} - -func decodeResetPATSecretRequest(_ 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 decodeRevokePATSecretRequest(_ context.Context, r *http.Request) (interface{}, error) { - return revokePatSecretReq{ - token: apiutil.ExtractBearerToken(r), - id: chi.URLParam(r, "id"), - }, nil -} - -func decodeAddPATScopeEntryRequest(_ 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 decodeRemovePATScopeEntryRequest(_ 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 decodeClearPATAllScopeEntryRequest(_ 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 decodeAuthorizePATRequest(_ context.Context, r *http.Request) (interface{}, error) { - if !strings.Contains(r.Header.Get("Content-Type"), contentType) { - return nil, apiutil.ErrUnsupportedContentType - } - - req := authorizePATReq{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/pat/bolt/doc.go b/pat/bolt/doc.go deleted file mode 100644 index dcd06ac566..0000000000 --- a/pat/bolt/doc.go +++ /dev/null @@ -1,6 +0,0 @@ -// 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/pat/bolt/init.go b/pat/bolt/init.go deleted file mode 100644 index 9d496e65ca..0000000000 --- a/pat/bolt/init.go +++ /dev/null @@ -1,21 +0,0 @@ -// Copyright (c) Abstract Machines -// SPDX-License-Identifier: Apache-2.0 - -// 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/pat/bolt/pat.go b/pat/bolt/pat.go deleted file mode 100644 index b3af87890b..0000000000 --- a/pat/bolt/pat.go +++ /dev/null @@ -1,773 +0,0 @@ -// Copyright (c) Abstract Machines -// SPDX-License-Identifier: Apache-2.0 - -package bolt - -import ( - "bytes" - "context" - "encoding/binary" - "fmt" - "strings" - "time" - - "github.com/absmach/magistrala/pat" - "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" - secretKey = "secret_key" - scopeKey = "scope" - issuedAtKey = "issued_at" - expiresAtKey = "expires_at" - updatedAtKey = "updated_at" - lastUsedAtKey = "last_used_at" - revokedKey = "revoked" - revokedAtKey = "revoked_at" - platformEntitiesKey = "platform_entities" - patKey = "pat" - - keySeparator = ":" - anyID = "*" -) - -var ( - activateValue = []byte{0x00} - revokedValue = []byte{0x01} - entityValue = []byte{0x02} - anyIDValue = []byte{0x03} - selectedIDsValue = []byte{0x04} -) - -type patRepo struct { - db *bolt.DB - bucketName string -} - -// NewPATSRepository instantiates a bolt -// implementation of PAT repository. -func NewPATSRepository(db *bolt.DB, bucketName string) pat.PATSRepository { - return &patRepo{ - db: db, - bucketName: bucketName, - } -} - -func (pr *patRepo) Save(ctx context.Context, pat pat.PAT) error { - idxKey := []byte(pat.User + keySeparator + patKey + keySeparator + pat.ID) - kv, err := patToKeyValue(pat) - if err != nil { - return err - } - return pr.db.Update(func(tx *bolt.Tx) error { - 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) - } - 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) - } - } - if err := rootBucket.Put(idxKey, []byte(pat.ID)); err != nil { - return errors.Wrap(repoerr.ErrCreateEntity, err) - } - return nil - }) -} - -func (pr *patRepo) Retrieve(ctx context.Context, userID, patID string) (pat.PAT, error) { - prefix := []byte(patID + keySeparator) - kv := map[string][]byte{} - if err := pr.db.View(func(tx *bolt.Tx) error { - b, err := pr.retrieveUserBucket(tx, userID, patID, repoerr.ErrViewEntity) - 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 pat.PAT{}, err - } - - 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) (pat.PAT, error) { - return pr.updatePATField(ctx, userID, patID, nameKey, []byte(name)) -} - -func (pr *patRepo) UpdateDescription(ctx context.Context, userID, patID, description string) (pat.PAT, error) { - return pr.updatePATField(ctx, userID, patID, descriptionKey, []byte(description)) -} - -func (pr *patRepo) UpdateTokenHash(ctx context.Context, userID, patID, tokenHash string, expiryAt time.Time) (pat.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+secretKey), []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) - } - 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 pat.PAT{}, err - } - return keyValueToPAT(kv) -} - -func (pr *patRepo) RetrieveAll(ctx context.Context, userID string, pm pat.PATSPageMeta) (pat.PATSPage, error) { - prefix := []byte(userID + keySeparator + patKey + keySeparator) - - patIDs := []string{} - if err := pr.db.View(func(tx *bolt.Tx) error { - b, err := pr.retrieveRootBucket(tx) - if err != nil { - 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() { - if v != nil { - patIDs = append(patIDs, string(v)) - } - } - return nil - }); err != nil { - return pat.PATSPage{}, err - } - - total := len(patIDs) - - var pats []pat.PAT - - patsPage := pat.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 < pm.Offset+aLimit; i++ { - if int(i) < total { - 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 { - 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), 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) 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, patID, repoerr.ErrRemoveEntity) - if err != nil { - return 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) - } - } - 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 - } - - return nil -} - -func (pr *patRepo) AddScopeEntry(ctx context.Context, userID, patID string, platformEntityType pat.PlatformEntityType, optionalDomainID string, optionalDomainEntityType pat.DomainEntityType, operation pat.OperationType, entityIDs ...string) (pat.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, patID, repoerr.ErrCreateEntity) - if err != nil { - return 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) - } - } - 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 pat.Scope{}, err - } - - return parseKeyValueToScope(rKV) -} - -func (pr *patRepo) RemoveScopeEntry(ctx context.Context, userID, patID string, platformEntityType pat.PlatformEntityType, optionalDomainID string, optionalDomainEntityType pat.DomainEntityType, operation pat.OperationType, entityIDs ...string) (pat.Scope, error) { - if len(entityIDs) == 0 { - return pat.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, patID, repoerr.ErrRemoveEntity) - if err != nil { - return 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) - } - } - 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 pat.Scope{}, err - } - return parseKeyValueToScope(rKV) -} - -func (pr *patRepo) CheckScopeEntry(ctx context.Context, userID, patID string, platformEntityType pat.PlatformEntityType, optionalDomainID string, optionalDomainEntityType pat.DomainEntityType, operation pat.OperationType, entityIDs ...string) error { - return pr.db.Update(func(tx *bolt.Tx) error { - b, err := pr.retrieveUserBucket(tx, userID, patID, repoerr.ErrViewEntity) - 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) updatePATField(_ context.Context, userID, patID, key string, value []byte) (pat.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 pat.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 { - 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, patID string, wrap error) (*bolt.Bucket, error) { - rootBucket, err := pr.retrieveRootBucket(tx) - if err != nil { - 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, errors.Wrap(wrap, fmt.Errorf("user %s not found", userID)) - } - 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 patToKeyValue(pat pat.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), - secretKey: []byte(pat.Secret), - 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 pat.Scope) (map[string][]byte, error) { - kv := map[string][]byte{} - for opType, scopeValue := range scope.Users { - tempKV, err := scopeEntryToKeyValue(pat.PlatformUsersScope, "", pat.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 { - tempKV, err := scopeEntryToKeyValue(pat.PlatformDomainsScope, domainID, pat.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 { - tempKV, err := scopeEntryToKeyValue(pat.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 pat.PlatformEntityType, optionalDomainID string, optionalDomainEntityType pat.DomainEntityType, operation pat.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 pat.PlatformEntityType, optionalDomainID string, optionalDomainEntityType pat.DomainEntityType, operation pat.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 pat.PlatformUsersScope: - rootKey.WriteString(op) - case pat.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 keyValueToBasicPAT(kv map[string][]byte) pat.PAT { - var pat pat.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 -} - -func keyValueToPAT(kv map[string][]byte) (pat.PAT, error) { - res := keyValueToBasicPAT(kv) - scope, err := parseKeyValueToScope(kv) - if err != nil { - return pat.PAT{}, err - } - res.Scope = scope - return res, nil -} - -func parseKeyValueToScope(kv map[string][]byte) (pat.Scope, error) { - scope := pat.Scope{ - Domains: make(map[string]pat.DomainScope), - } - for key, value := range kv { - if strings.Index(key, keySeparator+scopeKey+keySeparator) > 0 { - keyParts := strings.Split(key, keySeparator) - - platformEntityType, err := pat.ParsePlatformEntityType(keyParts[2]) - if err != nil { - return pat.Scope{}, errors.Wrap(repoerr.ErrViewEntity, err) - } - - switch platformEntityType { - case pat.PlatformUsersScope: - scope.Users, err = parseOperation(platformEntityType, scope.Users, key, keyParts, value) - if err != nil { - return pat.Scope{}, errors.Wrap(repoerr.ErrViewEntity, err) - } - - case pat.PlatformDomainsScope: - if len(keyParts) < 6 { - return pat.Scope{}, fmt.Errorf("invalid scope key format: %s", key) - } - domainID := keyParts[3] - if scope.Domains == nil { - scope.Domains = make(map[string]pat.DomainScope) - } - if _, ok := scope.Domains[domainID]; !ok { - scope.Domains[domainID] = pat.DomainScope{} - } - domainScope := scope.Domains[domainID] - - entityType := keyParts[4] - - switch entityType { - case pat.DomainManagementScope.String(): - domainScope.DomainManagement, err = parseOperation(platformEntityType, domainScope.DomainManagement, key, keyParts, value) - if err != nil { - return pat.Scope{}, errors.Wrap(repoerr.ErrViewEntity, err) - } - default: - etype, err := pat.ParseDomainEntityType(entityType) - if err != nil { - return pat.Scope{}, fmt.Errorf("key %s invalid entity type %s : %w", key, entityType, err) - } - if domainScope.Entities == nil { - domainScope.Entities = make(map[pat.DomainEntityType]pat.OperationScope) - } - if _, ok := domainScope.Entities[etype]; !ok { - domainScope.Entities[etype] = pat.OperationScope{} - } - entityOperationScope := domainScope.Entities[etype] - entityOperationScope, err = parseOperation(platformEntityType, entityOperationScope, key, keyParts, value) - if err != nil { - return pat.Scope{}, errors.Wrap(repoerr.ErrViewEntity, err) - } - domainScope.Entities[etype] = entityOperationScope - } - scope.Domains[domainID] = domainScope - default: - return pat.Scope{}, errors.Wrap(repoerr.ErrViewEntity, fmt.Errorf("invalid platform entity type : %s", platformEntityType.String())) - } - } - } - return scope, nil -} - -func parseOperation(platformEntityType pat.PlatformEntityType, opScope pat.OperationScope, key string, keyParts []string, value []byte) (pat.OperationScope, error) { - if opScope == nil { - opScope = make(map[pat.OperationType]pat.ScopeValue) - } - - if err := validateOperation(platformEntityType, opScope, key, keyParts, value); err != nil { - return pat.OperationScope{}, err - } - - switch string(value) { - case string(entityValue): - opType, err := pat.ParseOperationType(keyParts[len(keyParts)-2]) - if err != nil { - return pat.OperationScope{}, errors.Wrap(repoerr.ErrViewEntity, err) - } - entityID := keyParts[len(keyParts)-1] - - if _, oValueExists := opScope[opType]; !oValueExists { - opScope[opType] = &pat.SelectedIDs{} - } - oValue := opScope[opType] - if err := oValue.AddValues(entityID); err != nil { - return pat.OperationScope{}, fmt.Errorf("failed to add scope key %s with entity value %v : %w", key, entityID, err) - } - opScope[opType] = oValue - case string(anyIDValue): - opType, err := pat.ParseOperationType(keyParts[len(keyParts)-1]) - if err != nil { - return pat.OperationScope{}, errors.Wrap(repoerr.ErrViewEntity, err) - } - if oValue, oValueExists := opScope[opType]; oValueExists && oValue != nil { - if _, ok := oValue.(*pat.AnyIDs); !ok { - return pat.OperationScope{}, fmt.Errorf("failed to add scope key %s with entity anyIDs scope value : key already initialized with different type", key) - } - } - opScope[opType] = &pat.AnyIDs{} - case string(selectedIDsValue): - opType, err := pat.ParseOperationType(keyParts[len(keyParts)-1]) - if err != nil { - return pat.OperationScope{}, errors.Wrap(repoerr.ErrViewEntity, err) - } - oValue, oValueExists := opScope[opType] - if oValueExists && oValue != nil { - if _, ok := oValue.(*pat.SelectedIDs); !ok { - return pat.OperationScope{}, fmt.Errorf("failed to add scope key %s with entity selectedIDs scope value : key already initialized with different type", key) - } - } - if !oValueExists { - opScope[opType] = &pat.SelectedIDs{} - } - default: - return pat.OperationScope{}, fmt.Errorf("key %s have invalid value %v", key, value) - } - return opScope, nil -} - -func validateOperation(platformEntityType pat.PlatformEntityType, _ pat.OperationScope, key string, keyParts []string, value []byte) error { - expectedKeyPartsLength := 0 - switch string(value) { - case string(entityValue): - switch platformEntityType { - case pat.PlatformDomainsScope: - expectedKeyPartsLength = 7 - case pat.PlatformUsersScope: - expectedKeyPartsLength = 5 - default: - return fmt.Errorf("invalid platform entity type : %s", platformEntityType.String()) - } - case string(selectedIDsValue), string(anyIDValue): - switch platformEntityType { - case pat.PlatformDomainsScope: - expectedKeyPartsLength = 6 - case pat.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 timeToBytes(t time.Time) []byte { - timeBytes := make([]byte, 8) - binary.BigEndian.PutUint64(timeBytes, uint64(t.Unix())) - return timeBytes -} - -func bytesToTime(b []byte) time.Time { - timeAtSeconds := binary.BigEndian.Uint64(b) - return time.Unix(int64(timeAtSeconds), 0) -} - -func booleanToBytes(b bool) []byte { - if b { - return []byte{1} - } - return []byte{0} -} - -func bytesToBoolean(b []byte) bool { - if len(b) > 1 || b[0] != activateValue[0] { - return true - } - return false -} diff --git a/pat/events/streams.go b/pat/events/streams.go deleted file mode 100644 index 713ddea5a6..0000000000 --- a/pat/events/streams.go +++ /dev/null @@ -1,94 +0,0 @@ -// Copyright (c) Abstract Machines -// SPDX-License-Identifier: Apache-2.0 - -package events - -import ( - "context" - "time" - - "github.com/absmach/magistrala/pat" - "github.com/absmach/magistrala/pkg/authn" - "github.com/absmach/magistrala/pkg/events" - "github.com/absmach/magistrala/pkg/events/store" -) - -const streamID = "magistrala.pat" - -var _ pat.Service = (*eventStore)(nil) - - -type eventStore struct { - events.Publisher - svc pat.Service -} - -// NewEventStoreMiddleware returns wrapper around pat service that sends -// events to event store. -func NewEventStoreMiddleware(ctx context.Context, svc pat.Service, url string) (pat.Service, error) { - publisher, err := store.NewPublisher(ctx, url, streamID) - if err != nil { - return nil, err - } - - return &eventStore{ - svc: svc, - Publisher: publisher, - }, nil -} - -func (es *eventStore) CreatePAT(ctx context.Context, session authn.Session, name, description string, duration time.Duration, scope pat.Scope) (pat.PAT, error) { - return es.svc.CreatePAT(ctx, session, name, description, duration, scope) -} - -func (es *eventStore) UpdatePATName(ctx context.Context, session authn.Session, patID, name string) (pat.PAT, error) { - return es.svc.UpdatePATName(ctx, session, patID, name) -} - -func (es *eventStore) UpdatePATDescription(ctx context.Context, session authn.Session, patID, description string) (pat.PAT, error) { - return es.svc.UpdatePATDescription(ctx, session, patID, description) -} - -func (es *eventStore) RetrievePAT(ctx context.Context, session authn.Session, patID string) (pat.PAT, error) { - return es.svc.RetrievePAT(ctx, session, patID) -} - -func (es *eventStore) ListPATS(ctx context.Context, session authn.Session, pm pat.PATSPageMeta) (pat.PATSPage, error) { - return es.svc.ListPATS(ctx, session, pm) -} - -func (es *eventStore) DeletePAT(ctx context.Context, session authn.Session, patID string) error { - return es.svc.DeletePAT(ctx, session, patID) -} - -func (es *eventStore) ResetPATSecret(ctx context.Context, session authn.Session, patID string, duration time.Duration) (pat.PAT, error) { - return es.svc.ResetPATSecret(ctx, session, patID, duration) -} - -func (es *eventStore) RevokePATSecret(ctx context.Context, session authn.Session, patID string) error { - return es.svc.RevokePATSecret(ctx, session, patID) -} - -func (es *eventStore) AddPATScopeEntry(ctx context.Context, session authn.Session, patID string, platformEntityType pat.PlatformEntityType, optionalDomainID string, optionalDomainEntityType pat.DomainEntityType, operation pat.OperationType, entityIDs ...string) (pat.Scope, error) { - return es.svc.AddPATScopeEntry(ctx, session, patID, platformEntityType, optionalDomainID, optionalDomainEntityType, operation, entityIDs...) -} - -func (es *eventStore) RemovePATScopeEntry(ctx context.Context, session authn.Session, patID string, platformEntityType pat.PlatformEntityType, optionalDomainID string, optionalDomainEntityType pat.DomainEntityType, operation pat.OperationType, entityIDs ...string) (pat.Scope, error) { - return es.svc.RemovePATScopeEntry(ctx, session, patID, platformEntityType, optionalDomainID, optionalDomainEntityType, operation, entityIDs...) -} - -func (es *eventStore) ClearPATAllScopeEntry(ctx context.Context, session authn.Session, patID string) error { - return es.svc.ClearPATAllScopeEntry(ctx, session, patID) -} - -func (es *eventStore) IdentifyPAT(ctx context.Context, paToken string) (pat.PAT, error) { - return es.svc.IdentifyPAT(ctx, paToken) -} - -func (es *eventStore) AuthorizePAT(ctx context.Context, paToken string, platformEntityType pat.PlatformEntityType, optionalDomainID string, optionalDomainEntityType pat.DomainEntityType, operation pat.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 pat.PlatformEntityType, optionalDomainID string, optionalDomainEntityType pat.DomainEntityType, operation pat.OperationType, entityIDs ...string) error { - return es.svc.CheckPAT(ctx, userID, patID, platformEntityType, optionalDomainID, optionalDomainEntityType, operation, entityIDs...) -} \ No newline at end of file diff --git a/pat/hasher.go b/pat/hasher.go deleted file mode 100644 index b0452c1e1d..0000000000 --- a/pat/hasher.go +++ /dev/null @@ -1,17 +0,0 @@ -// Copyright (c) Abstract Machines -// SPDX-License-Identifier: Apache-2.0 - -package pat - -// 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/pat/hasher/doc.go b/pat/hasher/doc.go deleted file mode 100644 index 98be992262..0000000000 --- a/pat/hasher/doc.go +++ /dev/null @@ -1,6 +0,0 @@ -// 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/pat/hasher/hasher.go b/pat/hasher/hasher.go deleted file mode 100644 index 4d74669f02..0000000000 --- a/pat/hasher/hasher.go +++ /dev/null @@ -1,86 +0,0 @@ -// Copyright (c) Abstract Machines -// SPDX-License-Identifier: Apache-2.0 - -package hasher - -import ( - "encoding/base64" - "fmt" - "math/rand" - "strings" - "time" - - "github.com/absmach/magistrala/pat" - "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 _ pat.Hasher = (*bcryptHasher)(nil) - -type bcryptHasher struct{} - -// New instantiates a bcrypt-based hasher implementation. -func New() pat.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/pat/middleware/authorization.go b/pat/middleware/authorization.go deleted file mode 100644 index 1064590088..0000000000 --- a/pat/middleware/authorization.go +++ /dev/null @@ -1,84 +0,0 @@ -// Copyright (c) Abstract Machines -// SPDX-License-Identifier: Apache-2.0 - -package middleware - -import ( - "context" - "time" - - "github.com/absmach/magistrala/pat" - "github.com/absmach/magistrala/pkg/authn" - mgauthz "github.com/absmach/magistrala/pkg/authz" -) - -var _ pat.Service = (*authorizationMiddleware)(nil) - -type authorizationMiddleware struct { - svc pat.Service - authz mgauthz.Authorization -} - -// AuthorizationMiddleware adds authorization to the clients service. -func AuthorizationMiddleware(entityType string, svc pat.Service, authz mgauthz.Authorization) (pat.Service, error) { - return &authorizationMiddleware{ - svc: svc, - authz: authz, - }, nil -} - -func (am *authorizationMiddleware) CreatePAT(ctx context.Context, session authn.Session, name, description string, duration time.Duration, scope pat.Scope) (pat.PAT, error) { - return am.svc.CreatePAT(ctx, session, name, description, duration, scope) -} - -func (am *authorizationMiddleware) UpdatePATName(ctx context.Context, session authn.Session, patID, name string) (pat.PAT, error) { - return am.svc.UpdatePATName(ctx, session, patID, name) -} - -func (am *authorizationMiddleware) UpdatePATDescription(ctx context.Context, session authn.Session, patID, description string) (pat.PAT, error) { - return am.svc.UpdatePATDescription(ctx, session, patID, description) -} - -func (am *authorizationMiddleware) RetrievePAT(ctx context.Context, session authn.Session, patID string) (pat.PAT, error) { - return am.svc.RetrievePAT(ctx, session, patID) -} - -func (am *authorizationMiddleware) ListPATS(ctx context.Context, session authn.Session, pm pat.PATSPageMeta) (pat.PATSPage, error) { - return am.svc.ListPATS(ctx, session, pm) -} - -func (am *authorizationMiddleware) DeletePAT(ctx context.Context, session authn.Session, patID string) error { - return am.svc.DeletePAT(ctx, session, patID) -} - -func (am *authorizationMiddleware) ResetPATSecret(ctx context.Context, session authn.Session, patID string, duration time.Duration) (pat.PAT, error) { - return am.svc.ResetPATSecret(ctx, session, patID, duration) -} - -func (am *authorizationMiddleware) RevokePATSecret(ctx context.Context, session authn.Session, patID string) error { - return am.svc.RevokePATSecret(ctx, session, patID) -} - -func (am *authorizationMiddleware) AddPATScopeEntry(ctx context.Context, session authn.Session, patID string, platformEntityType pat.PlatformEntityType, optionalDomainID string, optionalDomainEntityType pat.DomainEntityType, operation pat.OperationType, entityIDs ...string) (pat.Scope, error) { - return am.svc.AddPATScopeEntry(ctx, session, patID, platformEntityType, optionalDomainID, optionalDomainEntityType, operation, entityIDs...) -} - -func (am *authorizationMiddleware) RemovePATScopeEntry(ctx context.Context, session authn.Session, patID string, platformEntityType pat.PlatformEntityType, optionalDomainID string, optionalDomainEntityType pat.DomainEntityType, operation pat.OperationType, entityIDs ...string) (pat.Scope, error) { - return am.svc.RemovePATScopeEntry(ctx, session, patID, platformEntityType, optionalDomainID, optionalDomainEntityType, operation, entityIDs...) -} - -func (am *authorizationMiddleware) ClearPATAllScopeEntry(ctx context.Context, session authn.Session, patID string) error { - return am.svc.ClearPATAllScopeEntry(ctx, session, patID) -} - -func (am *authorizationMiddleware) IdentifyPAT(ctx context.Context, secret string) (pat.PAT, error) { - return am.svc.IdentifyPAT(ctx, secret) -} - -func (am *authorizationMiddleware) AuthorizePAT(ctx context.Context, paToken string, platformEntityType pat.PlatformEntityType, optionalDomainID string, optionalDomainEntityType pat.DomainEntityType, operation pat.OperationType, entityIDs ...string) error { - return am.svc.AuthorizePAT(ctx, paToken, platformEntityType, optionalDomainID, optionalDomainEntityType, operation, entityIDs...) -} - -func (am *authorizationMiddleware) CheckPAT(ctx context.Context, userID, patID string, platformEntityType pat.PlatformEntityType, optionalDomainID string, optionalDomainEntityType pat.DomainEntityType, operation pat.OperationType, entityIDs ...string) error { - return am.svc.CheckPAT(ctx, userID, patID, platformEntityType, optionalDomainID, optionalDomainEntityType, operation, entityIDs...) -} diff --git a/pat/middleware/logging.go b/pat/middleware/logging.go deleted file mode 100644 index 51767d39c1..0000000000 --- a/pat/middleware/logging.go +++ /dev/null @@ -1,275 +0,0 @@ -// Copyright (c) Abstract Machines -// SPDX-License-Identifier: Apache-2.0 - -package middleware - -import ( - "context" - "log/slog" - "time" - - "github.com/absmach/magistrala/pat" - "github.com/absmach/magistrala/pkg/authn" -) - -var _ pat.Service = (*loggingMiddleware)(nil) - -type loggingMiddleware struct { - logger *slog.Logger - svc pat.Service -} - -// LoggingMiddleware adds logging facilities to the core service. -func LoggingMiddleware(svc pat.Service, logger *slog.Logger) pat.Service { - return &loggingMiddleware{logger, svc} -} - -func (lm *loggingMiddleware) CreatePAT(ctx context.Context, session authn.Session, name, description string, duration time.Duration, scope pat.Scope) (pa pat.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.CreatePAT(ctx, session, name, description, duration, scope) -} - -func (lm *loggingMiddleware) UpdatePATName(ctx context.Context, session authn.Session, patID, name string) (pa pat.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.UpdatePATName(ctx, session, patID, name) -} - -func (lm *loggingMiddleware) UpdatePATDescription(ctx context.Context, session authn.Session, patID, description string) (pa pat.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.UpdatePATDescription(ctx, session, patID, description) -} - -func (lm *loggingMiddleware) RetrievePAT(ctx context.Context, session authn.Session, patID string) (pa pat.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.RetrievePAT(ctx, session, patID) -} - -func (lm *loggingMiddleware) ListPATS(ctx context.Context, session authn.Session, pm pat.PATSPageMeta) (pp pat.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.ListPATS(ctx, session, pm) -} - -func (lm *loggingMiddleware) DeletePAT(ctx context.Context, session authn.Session, 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.DeletePAT(ctx, session, patID) -} - -func (lm *loggingMiddleware) ResetPATSecret(ctx context.Context, session authn.Session, patID string, duration time.Duration) (pa pat.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.ResetPATSecret(ctx, session, patID, duration) -} - -func (lm *loggingMiddleware) RevokePATSecret(ctx context.Context, session authn.Session, 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.RevokePATSecret(ctx, session, patID) -} - -func (lm *loggingMiddleware) AddPATScopeEntry(ctx context.Context, session authn.Session, patID string, platformEntityType pat.PlatformEntityType, optionalDomainID string, optionalDomainEntityType pat.DomainEntityType, operation pat.OperationType, entityIDs ...string) (sc pat.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.AddPATScopeEntry(ctx, session, patID, platformEntityType, optionalDomainID, optionalDomainEntityType, operation, entityIDs...) -} - -func (lm *loggingMiddleware) RemovePATScopeEntry(ctx context.Context, session authn.Session, patID string, platformEntityType pat.PlatformEntityType, optionalDomainID string, optionalDomainEntityType pat.DomainEntityType, operation pat.OperationType, entityIDs ...string) (sc pat.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.RemovePATScopeEntry(ctx, session, patID, platformEntityType, optionalDomainID, optionalDomainEntityType, operation, entityIDs...) -} - -func (lm *loggingMiddleware) ClearPATAllScopeEntry(ctx context.Context, session authn.Session, 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.ClearPATAllScopeEntry(ctx, session, patID) -} - -func (lm *loggingMiddleware) IdentifyPAT(ctx context.Context, paToken string) (pa pat.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, platformEntityType pat.PlatformEntityType, optionalDomainID string, optionalDomainEntityType pat.DomainEntityType, operation pat.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("Authorize PAT failed complete successfully", args...) - return - } - lm.logger.Info("Authorize PAT completed successfully", args...) - }(time.Now()) - return lm.svc.AuthorizePAT(ctx, paToken, platformEntityType, optionalDomainID, optionalDomainEntityType, operation, entityIDs...) -} - -func (lm *loggingMiddleware) CheckPAT(ctx context.Context, userID, patID string, platformEntityType pat.PlatformEntityType, optionalDomainID string, optionalDomainEntityType pat.DomainEntityType, operation pat.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("Check PAT failed complete successfully", args...) - return - } - lm.logger.Info("Check PAT completed successfully", args...) - }(time.Now()) - return lm.svc.CheckPAT(ctx, userID, patID, platformEntityType, optionalDomainID, optionalDomainEntityType, operation, entityIDs...) -} diff --git a/pat/middleware/metrics.go b/pat/middleware/metrics.go deleted file mode 100644 index 4a44bb0aa1..0000000000 --- a/pat/middleware/metrics.go +++ /dev/null @@ -1,142 +0,0 @@ -// Copyright (c) Abstract Machines -// SPDX-License-Identifier: Apache-2.0 - -package middleware - -import ( - "context" - "time" - - "github.com/absmach/magistrala/pat" - "github.com/absmach/magistrala/pkg/authn" - "github.com/go-kit/kit/metrics" -) - -var _ pat.Service = (*metricsMiddleware)(nil) - -type metricsMiddleware struct { - counter metrics.Counter - latency metrics.Histogram - svc pat.Service -} - -// MetricsMiddleware instruments core service by tracking request count and latency. -func MetricsMiddleware(svc pat.Service, counter metrics.Counter, latency metrics.Histogram) pat.Service { - return &metricsMiddleware{ - counter: counter, - latency: latency, - svc: svc, - } -} - -func (ms *metricsMiddleware) CreatePAT(ctx context.Context, session authn.Session, name, description string, duration time.Duration, scope pat.Scope) (pat.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.CreatePAT(ctx, session, name, description, duration, scope) -} - -func (ms *metricsMiddleware) UpdatePATName(ctx context.Context, session authn.Session, patID, name string) (pat.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.UpdatePATName(ctx, session, patID, name) -} - -func (ms *metricsMiddleware) UpdatePATDescription(ctx context.Context, session authn.Session, patID, description string) (pat.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.UpdatePATDescription(ctx, session, patID, description) -} - -func (ms *metricsMiddleware) RetrievePAT(ctx context.Context, session authn.Session, patID string) (pat.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.RetrievePAT(ctx, session, patID) -} - -func (ms *metricsMiddleware) ListPATS(ctx context.Context, session authn.Session, pm pat.PATSPageMeta) (pat.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.ListPATS(ctx, session, pm) -} - -func (ms *metricsMiddleware) DeletePAT(ctx context.Context, session authn.Session, 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.DeletePAT(ctx, session, patID) -} - -func (ms *metricsMiddleware) ResetPATSecret(ctx context.Context, session authn.Session, patID string, duration time.Duration) (pat.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.ResetPATSecret(ctx, session, patID, duration) -} - -func (ms *metricsMiddleware) RevokePATSecret(ctx context.Context, session authn.Session, 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.RevokePATSecret(ctx, session, patID) -} - -func (ms *metricsMiddleware) AddPATScopeEntry(ctx context.Context, session authn.Session, patID string, platformEntityType pat.PlatformEntityType, optionalDomainID string, optionalDomainEntityType pat.DomainEntityType, operation pat.OperationType, entityIDs ...string) (pat.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.AddPATScopeEntry(ctx, session, patID, platformEntityType, optionalDomainID, optionalDomainEntityType, operation, entityIDs...) -} - -func (ms *metricsMiddleware) RemovePATScopeEntry(ctx context.Context, session authn.Session, patID string, platformEntityType pat.PlatformEntityType, optionalDomainID string, optionalDomainEntityType pat.DomainEntityType, operation pat.OperationType, entityIDs ...string) (pat.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.RemovePATScopeEntry(ctx, session, patID, platformEntityType, optionalDomainID, optionalDomainEntityType, operation, entityIDs...) -} - -func (ms *metricsMiddleware) ClearPATAllScopeEntry(ctx context.Context, session authn.Session, 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.ClearPATAllScopeEntry(ctx, session, patID) -} - -func (ms *metricsMiddleware) IdentifyPAT(ctx context.Context, paToken string) (pat.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, platformEntityType pat.PlatformEntityType, optionalDomainID string, optionalDomainEntityType pat.DomainEntityType, operation pat.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, platformEntityType, optionalDomainID, optionalDomainEntityType, operation, entityIDs...) -} - -func (ms *metricsMiddleware) CheckPAT(ctx context.Context, userID, patID string, platformEntityType pat.PlatformEntityType, optionalDomainID string, optionalDomainEntityType pat.DomainEntityType, operation pat.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/pat/pat.go b/pat/pat.go deleted file mode 100644 index 5b9cb08045..0000000000 --- a/pat/pat.go +++ /dev/null @@ -1,753 +0,0 @@ -// Copyright (c) Abstract Machines -// SPDX-License-Identifier: Apache-2.0 - -package pat - -import ( - "context" - "encoding/json" - "fmt" - "time" - - "github.com/absmach/magistrala/pkg/authn" - "github.com/absmach/magistrala/pkg/errors" -) - -var errAddEntityToAnyIDs = errors.New("could not add entity id to any ID scope value") - -// Define OperationType. -type OperationType uint32 - -const ( - CreateOp OperationType = iota - ReadOp - ListOp - UpdateOp - DeleteOp -) - -const ( - createOpStr = "create" - readOpStr = "read" - listOpStr = "list" - updateOpStr = "update" - deleteOpStr = "delete" -) - -func (ot OperationType) String() string { - switch ot { - case CreateOp: - return createOpStr - case ReadOp: - return readOpStr - case ListOp: - return listOpStr - case UpdateOp: - return updateOpStr - case DeleteOp: - return deleteOpStr - default: - return fmt.Sprintf("unknown operation type %d", ot) - } -} - -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: - return CreateOp, nil - case readOpStr: - return ReadOp, nil - case listOpStr: - return ListOp, nil - case updateOpStr: - return UpdateOp, nil - case deleteOpStr: - return DeleteOp, nil - default: - return 0, fmt.Errorf("unknown operation type %s", 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 -} - -func (ot *OperationType) UnmarshalText(data []byte) (err error) { - *ot, err = ParseOperationType(string(data)) - return err -} - -// Define DomainEntityType. -type DomainEntityType uint32 - -const ( - DomainManagementScope DomainEntityType = iota - DomainGroupsScope - DomainChannelsScope - DomainThingsScope - DomainNullScope -) - -const ( - domainManagementScopeStr = "domain_management" - domainGroupsScopeStr = "groups" - domainChannelsScopeStr = "channels" - domainThingsScopeStr = "things" -) - -func (det DomainEntityType) String() string { - switch det { - case DomainManagementScope: - return domainManagementScopeStr - case DomainGroupsScope: - return domainGroupsScopeStr - case DomainChannelsScope: - return domainChannelsScopeStr - case DomainThingsScope: - return domainThingsScopeStr - default: - return fmt.Sprintf("unknown domain entity type %d", det) - } -} - -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: - return DomainManagementScope, nil - case domainGroupsScopeStr: - return DomainGroupsScope, nil - case domainChannelsScopeStr: - return DomainChannelsScope, nil - case domainThingsScopeStr: - 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() ([]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 - -const ( - PlatformUsersScope PlatformEntityType = iota - PlatformDomainsScope -) - -const ( - platformUsersScopeStr = "users" - platformDomainsScopeStr = "domains" -) - -func (pet PlatformEntityType) String() string { - switch pet { - case PlatformUsersScope: - return platformUsersScopeStr - case PlatformDomainsScope: - return platformDomainsScopeStr - default: - return fmt.Sprintf("unknown platform entity type %d", pet) - } -} - -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: - return PlatformUsersScope, nil - case platformDomainsScopeStr: - 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 -} - -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 - 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) 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{} - -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 -} - -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 map[OperationType]ScopeValue - -func (os *OperationScope) UnmarshalJSON(data []byte) error { - type tempOperationScope map[OperationType]json.RawMessage - - var tempScope tempOperationScope - if err := json.Unmarshal(data, &tempScope); err != nil { - return err - } - // Initialize the Operations map - *os = OperationScope{} - - for opType, rawMessage := range tempScope { - var stringValue string - var stringArrayValue []string - - // Try to unmarshal as string - if err := json.Unmarshal(rawMessage, &stringValue); err == nil { - if err := os.Add(opType, stringValue); err != nil { - return err - } - continue - } - - // Try to unmarshal as []string - if err := json.Unmarshal(rawMessage, &stringArrayValue); err == nil { - if err := os.Add(opType, stringArrayValue...); err != nil { - return err - } - continue - } - - // If neither unmarshalling succeeded, return an error - return fmt.Errorf("invalid ScopeValue for OperationType %v", opType) - } - - 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 = &OperationScope{} - } - - if len(entityIDs) == 0 { - return fmt.Errorf("entity ID is missing") - } - switch { - 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) - } - sids[entityID] = struct{}{} - } - value = &sids - } - (*os)[operation] = value - return nil -} - -func (os *OperationScope) Delete(operation OperationType, entityIDs ...string) error { - if os == nil { - return nil - } - - opEntityIDs, exists := (*os)[operation] - if !exists { - return nil - } - - if len(entityIDs) == 0 { - return fmt.Errorf("failed to delete operation %s: entity ID is missing", operation.String()) - } - - switch eIDs := opEntityIDs.(type) { - case *AnyIDs: - if !(len(entityIDs) == 1 && entityIDs[0] == "*") { - return fmt.Errorf("failed to delete operation %s: invalid list", operation.String()) - } - delete((*os), operation) - return nil - 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((*os), operation) - } - } - return nil - default: - return fmt.Errorf("failed to delete operation: invalid entity id type %d", operation) - } -} - -func (os *OperationScope) Check(operation OperationType, entityIDs ...string) bool { - if os == nil { - return false - } - - if scopeValue, ok := (*os)[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) == 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 - } - if domainEntityType == DomainManagementScope { - return ds.DomainManagement.Check(operation, ids...) - } - os, exists := ds.Entities[domainEntityType] - if !exists { - return false - } - - return os.Check(operation, ids...) -} - -// Example Scope as JSON -// -// { -// "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.Marshal(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"` - Name string `json:"name,omitempty"` - Description string `json:"description,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"` - 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 PATSPageMeta struct { - Offset uint64 `json:"offset"` - Limit uint64 `json:"limit"` -} -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()) - } - return string(str) -} - -// Expired verifies if the key is expired. -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 Service --output=./mocks --filename pats.go --quiet --note "Copyright (c) Abstract Machines" - -type Service interface { - // Create function creates new PAT for given valid inputs. - CreatePAT(ctx context.Context, session authn.Session, name, description string, duration time.Duration, scope Scope) (PAT, error) - - // UpdateName function updates the name for the given PAT ID. - UpdatePATName(ctx context.Context, session authn.Session, patID, name string) (PAT, error) - - // UpdateDescription function updates the description for the given PAT ID. - UpdatePATDescription(ctx context.Context, session authn.Session, patID, description string) (PAT, error) - - // Retrieve function retrieves the PAT for given ID. - RetrievePAT(ctx context.Context, session authn.Session, patID string) (PAT, error) - - // List function lists all the PATs for the user. - ListPATS(ctx context.Context, session authn.Session, pm PATSPageMeta) (PATSPage, error) - - // Delete function deletes the PAT for given ID. - DeletePAT(ctx context.Context, session authn.Session, patID string) error - - // ResetSecret function reset the secret and creates new secret for the given ID. - ResetPATSecret(ctx context.Context, session authn.Session, patID string, duration time.Duration) (PAT, error) - - // RevokeSecret function revokes the secret for the given ID. - RevokePATSecret(ctx context.Context, session authn.Session, patID string) error - - // AddScope function adds a new scope entry. - AddPATScopeEntry(ctx context.Context, session authn.Session, patID string, platformEntityType PlatformEntityType, optionalDomainID string, optionalDomainEntityType DomainEntityType, operation OperationType, entityIDs ...string) (Scope, error) - - // RemoveScope function removes a scope entry. - RemovePATScopeEntry(ctx context.Context, session authn.Session, patID string, platformEntityType PlatformEntityType, optionalDomainID string, optionalDomainEntityType DomainEntityType, operation OperationType, entityIDs ...string) (Scope, error) - - // ClearAllScope function removes all scope entry. - ClearPATAllScopeEntry(ctx context.Context, session authn.Session, patID 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, 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. -// -//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) (err error) - - // 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) - - // 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) - - // RetrieveAll retrieves all PATs belongs to userID. - 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 - - // 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 - - 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) - - 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/pat/service.go b/pat/service.go deleted file mode 100644 index 9c995914e4..0000000000 --- a/pat/service.go +++ /dev/null @@ -1,259 +0,0 @@ -// Copyright (c) Abstract Machines -// SPDX-License-Identifier: Apache-2.0 - -package pat - -import ( - "context" - "encoding/base64" - "math/rand" - "strings" - "time" - - "github.com/absmach/magistrala" - "github.com/absmach/magistrala/pkg/authn" - "github.com/absmach/magistrala/pkg/errors" - svcerr "github.com/absmach/magistrala/pkg/errors/service" - "github.com/google/uuid" -) - -const ( - randStr = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890!@#$%^&&*|+-=" - patPrefix = "pat" - patSecretSeparator = "_" -) - -var ( - 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") - 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") - errClearAllScope = errors.New("failed to clear all entry in scope") -) - -type service struct { - pats PATSRepository - hasher Hasher - idProvider magistrala.IDProvider -} - -var _ Service = (*service)(nil) - -// New instantiates the auth service implementation. -func New(pats PATSRepository, hasher Hasher, idp magistrala.IDProvider) Service { - return &service{ - pats: pats, - hasher: hasher, - idProvider: idp, - } -} - -func (svc service) CreatePAT(ctx context.Context, session authn.Session, name, description string, duration time.Duration, scope Scope) (PAT, error) { - id, err := svc.idProvider.ID() - if err != nil { - return PAT{}, errors.Wrap(svcerr.ErrCreateEntity, err) - } - secret, hash, err := svc.generateSecretAndHash(session.UserID, id) - if err != nil { - return PAT{}, errors.Wrap(svcerr.ErrCreateEntity, err) - } - - now := time.Now() - pat := PAT{ - ID: id, - User: session.UserID, - 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 -} - -func (svc service) UpdatePATName(ctx context.Context, session authn.Session, patID, name string) (PAT, error) { - pat, err := svc.pats.UpdateName(ctx, session.UserID, patID, name) - if err != nil { - return PAT{}, errors.Wrap(errUpdatePAT, err) - } - return pat, nil -} - -func (svc service) UpdatePATDescription(ctx context.Context, session authn.Session, patID, description string) (PAT, error) { - pat, err := svc.pats.UpdateDescription(ctx, session.UserID, patID, description) - if err != nil { - return PAT{}, errors.Wrap(errUpdatePAT, err) - } - return pat, nil -} - -func (svc service) RetrievePAT(ctx context.Context, session authn.Session, patID string) (PAT, error) { - pat, err := svc.pats.Retrieve(ctx, session.UserID, patID) - if err != nil { - return PAT{}, errors.Wrap(errRetrievePAT, err) - } - return pat, nil -} - -func (svc service) ListPATS(ctx context.Context, session authn.Session, pm PATSPageMeta) (PATSPage, error) { - patsPage, err := svc.pats.RetrieveAll(ctx, session.UserID, pm) - if err != nil { - return PATSPage{}, errors.Wrap(errRetrievePAT, err) - } - return patsPage, nil -} - -func (svc service) DeletePAT(ctx context.Context, session authn.Session, patID string) error { - if err := svc.pats.Remove(ctx, session.UserID, patID); err != nil { - return errors.Wrap(errDeletePAT, err) - } - return nil -} - -func (svc service) ResetPATSecret(ctx context.Context, session authn.Session, patID string, duration time.Duration) (PAT, error) { - // Generate new HashToken take place here - secret, hash, err := svc.generateSecretAndHash(session.UserID, patID) - if err != nil { - return PAT{}, errors.Wrap(svcerr.ErrUpdateEntity, err) - } - - pat, err := svc.pats.UpdateTokenHash(ctx, session.UserID, patID, hash, time.Now().Add(duration)) - if err != nil { - return PAT{}, errors.Wrap(svcerr.ErrUpdateEntity, err) - } - - if err := svc.pats.Reactivate(ctx, session.UserID, patID); err != nil { - return PAT{}, errors.Wrap(svcerr.ErrUpdateEntity, err) - } - pat.Secret = secret - pat.Revoked = false - pat.RevokedAt = time.Time{} - return pat, nil -} - -func (svc service) RevokePATSecret(ctx context.Context, session authn.Session, patID string) error { - if err := svc.pats.Revoke(ctx, session.UserID, patID); err != nil { - return errors.Wrap(errRevokePAT, err) - } - return nil -} - -func (svc service) AddPATScopeEntry(ctx context.Context, session authn.Session, patID string, platformEntityType PlatformEntityType, optionalDomainID string, optionalDomainEntityType DomainEntityType, operation OperationType, entityIDs ...string) (Scope, error) { - scope, err := svc.pats.AddScopeEntry(ctx, session.UserID, 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, session authn.Session, patID string, platformEntityType PlatformEntityType, optionalDomainID string, optionalDomainEntityType DomainEntityType, operation OperationType, entityIDs ...string) (Scope, error) { - scope, err := svc.pats.RemoveScopeEntry(ctx, session.UserID, patID, platformEntityType, optionalDomainID, optionalDomainEntityType, operation, entityIDs...) - if err != nil { - return Scope{}, err - } - return scope, nil -} - -func (svc service) ClearPATAllScopeEntry(ctx context.Context, session authn.Session, patID string) error { - if err := svc.pats.RemoveAllScopeEntry(ctx, session.UserID, patID); err != nil { - return errors.Wrap(errClearAllScope, err) - } - return nil -} - -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, res.ID, platformEntityType, optionalDomainID, optionalDomainEntityType, operation, entityIDs...); err != nil { - return errors.Wrap(svcerr.ErrAuthorization, err) - } - return 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 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/pat/tracing/tracing.go b/pat/tracing/tracing.go deleted file mode 100644 index 4fab5fae49..0000000000 --- a/pat/tracing/tracing.go +++ /dev/null @@ -1,164 +0,0 @@ -// Copyright (c) Abstract Machines -// SPDX-License-Identifier: Apache-2.0 - -package tracing - -import ( - "context" - "time" - - "github.com/absmach/magistrala/pat" - "github.com/absmach/magistrala/pkg/authn" - "go.opentelemetry.io/otel/attribute" - "go.opentelemetry.io/otel/trace" -) - -var _ pat.Service = (*tracingMiddleware)(nil) - -type tracingMiddleware struct { - tracer trace.Tracer - svc pat.Service -} - -// New returns a new group service with tracing capabilities. -func New(svc pat.Service, tracer trace.Tracer) pat.Service { - return &tracingMiddleware{tracer, svc} -} - -func (tm *tracingMiddleware) CreatePAT(ctx context.Context, session authn.Session, name, description string, duration time.Duration, scope pat.Scope) (pat.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.CreatePAT(ctx, session, name, description, duration, scope) -} - -func (tm *tracingMiddleware) UpdatePATName(ctx context.Context, session authn.Session, patID, name string) (pat.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.UpdatePATName(ctx, session, patID, name) -} - -func (tm *tracingMiddleware) UpdatePATDescription(ctx context.Context, session authn.Session, patID, description string) (pat.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.UpdatePATDescription(ctx, session, patID, description) -} - -func (tm *tracingMiddleware) RetrievePAT(ctx context.Context, session authn.Session, patID string) (pat.PAT, error) { - ctx, span := tm.tracer.Start(ctx, "retrieve_pat", trace.WithAttributes( - attribute.String("pat_id", patID), - )) - defer span.End() - return tm.svc.RetrievePAT(ctx, session, patID) -} - -func (tm *tracingMiddleware) ListPATS(ctx context.Context, session authn.Session, pm pat.PATSPageMeta) (pat.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.ListPATS(ctx, session, pm) -} - -func (tm *tracingMiddleware) DeletePAT(ctx context.Context, session authn.Session, patID string) error { - ctx, span := tm.tracer.Start(ctx, "delete_pat", trace.WithAttributes( - attribute.String("pat_id", patID), - )) - defer span.End() - return tm.svc.DeletePAT(ctx, session, patID) -} - -func (tm *tracingMiddleware) ResetPATSecret(ctx context.Context, session authn.Session, patID string, duration time.Duration) (pat.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.ResetPATSecret(ctx, session, patID, duration) -} - -func (tm *tracingMiddleware) RevokePATSecret(ctx context.Context, session authn.Session, 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.RevokePATSecret(ctx, session, patID) -} - -func (tm *tracingMiddleware) AddPATScopeEntry(ctx context.Context, session authn.Session, patID string, platformEntityType pat.PlatformEntityType, optionalDomainID string, optionalDomainEntityType pat.DomainEntityType, operation pat.OperationType, entityIDs ...string) (pat.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), - attribute.String("optional_domain_entity", optionalDomainEntityType.String()), - attribute.String("operation", operation.String()), - attribute.StringSlice("entities", entityIDs), - )) - defer span.End() - return tm.svc.AddPATScopeEntry(ctx, session, patID, platformEntityType, optionalDomainID, optionalDomainEntityType, operation, entityIDs...) -} - -func (tm *tracingMiddleware) RemovePATScopeEntry(ctx context.Context, session authn.Session, patID string, platformEntityType pat.PlatformEntityType, optionalDomainID string, optionalDomainEntityType pat.DomainEntityType, operation pat.OperationType, entityIDs ...string) (pat.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), - attribute.String("optional_domain_entity", optionalDomainEntityType.String()), - attribute.String("operation", operation.String()), - attribute.StringSlice("entities", entityIDs), - )) - defer span.End() - return tm.svc.RemovePATScopeEntry(ctx, session, patID, platformEntityType, optionalDomainID, optionalDomainEntityType, operation, entityIDs...) -} - -func (tm *tracingMiddleware) ClearPATAllScopeEntry(ctx context.Context, session authn.Session, 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.ClearPATAllScopeEntry(ctx, session, patID) -} - -func (tm *tracingMiddleware) IdentifyPAT(ctx context.Context, paToken string) (pat.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 pat.PlatformEntityType, optionalDomainID string, optionalDomainEntityType pat.DomainEntityType, operation pat.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), - 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, platformEntityType, optionalDomainID, optionalDomainEntityType, operation, entityIDs...) -} - -func (tm *tracingMiddleware) CheckPAT(ctx context.Context, userID, patID string, platformEntityType pat.PlatformEntityType, optionalDomainID string, optionalDomainEntityType pat.DomainEntityType, operation pat.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.CheckPAT(ctx, userID, patID, platformEntityType, optionalDomainID, optionalDomainEntityType, operation, entityIDs...) -} From 9fdeeea9f4bcea8a2df96866d638f4ba16ce4bbd Mon Sep 17 00:00:00 2001 From: nyagamunene Date: Thu, 31 Oct 2024 12:02:35 +0300 Subject: [PATCH 05/72] Update grpc api Signed-off-by: nyagamunene --- api/grpc/auth/v1/auth.pb.go | 18 +++--------------- api/grpc/auth/v1/auth_grpc.pb.go | 4 ---- 2 files changed, 3 insertions(+), 19 deletions(-) diff --git a/api/grpc/auth/v1/auth.pb.go b/api/grpc/auth/v1/auth.pb.go index bd6deda5e9..d5b433ef86 100644 --- a/api/grpc/auth/v1/auth.pb.go +++ b/api/grpc/auth/v1/auth.pb.go @@ -386,7 +386,7 @@ func (x *AuthZRes) ProtoReflect() protoreflect.Message { // Deprecated: Use AuthZRes.ProtoReflect.Descriptor instead. func (*AuthZRes) Descriptor() ([]byte, []int) { - return file_auth_v1_auth_proto_rawDescGZIP(), []int{4} + return file_auth_v1_auth_proto_rawDescGZIP(), []int{3} } func (x *AuthZRes) GetAuthorized() bool { @@ -488,7 +488,7 @@ func file_auth_v1_auth_proto_rawDescGZIP() []byte { return file_auth_v1_auth_proto_rawDescData } -var file_auth_v1_auth_proto_msgTypes = make([]protoimpl.MessageInfo, 5) +var file_auth_v1_auth_proto_msgTypes = make([]protoimpl.MessageInfo, 4) var file_auth_v1_auth_proto_goTypes = []any{ (*AuthNReq)(nil), // 0: auth.v1.AuthNReq (*AuthNRes)(nil), // 1: auth.v1.AuthNRes @@ -555,18 +555,6 @@ func file_auth_v1_auth_proto_init() { } } file_auth_v1_auth_proto_msgTypes[3].Exporter = func(v any, i int) any { - switch v := v.(*AuthZpatReq); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_auth_v1_auth_proto_msgTypes[4].Exporter = func(v any, i int) any { switch v := v.(*AuthZRes); i { case 0: return &v.state @@ -585,7 +573,7 @@ func file_auth_v1_auth_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_auth_v1_auth_proto_rawDesc, NumEnums: 0, - NumMessages: 5, + NumMessages: 4, NumExtensions: 0, NumServices: 1, }, diff --git a/api/grpc/auth/v1/auth_grpc.pb.go b/api/grpc/auth/v1/auth_grpc.pb.go index d85fabae7c..e0494d597f 100644 --- a/api/grpc/auth/v1/auth_grpc.pb.go +++ b/api/grpc/auth/v1/auth_grpc.pb.go @@ -226,10 +226,6 @@ var AuthService_ServiceDesc = grpc.ServiceDesc{ MethodName: "Authorize", Handler: _AuthService_Authorize_Handler, }, - { - MethodName: "AuthorizePAT", - Handler: _AuthService_AuthorizePAT_Handler, - }, { MethodName: "Authenticate", Handler: _AuthService_Authenticate_Handler, From c416f55a3a15afb809f69b2d61daf6dd0f0ac56d Mon Sep 17 00:00:00 2001 From: nyagamunene Date: Thu, 31 Oct 2024 12:56:33 +0300 Subject: [PATCH 06/72] Update cmd file Signed-off-by: nyagamunene --- cmd/auth/main.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cmd/auth/main.go b/cmd/auth/main.go index 88696086df..17e0d879b2 100644 --- a/cmd/auth/main.go +++ b/cmd/auth/main.go @@ -28,8 +28,8 @@ import ( "github.com/absmach/supermq/auth/tracing" boltclient "github.com/absmach/supermq/internal/clients/bolt" smqlog "github.com/absmach/supermq/logger" - "github.com/absmach/supermq/pat/bolt" - "github.com/absmach/supermq/pat/hasher" + "github.com/absmach/supermq/auth/bolt" + "github.com/absmach/supermq/auth/hasher" "github.com/absmach/supermq/pkg/jaeger" "github.com/absmach/supermq/pkg/policies/spicedb" pgclient "github.com/absmach/supermq/pkg/postgres" From 4b671f16dc28de7d5154f6f47f35f086adc16432 Mon Sep 17 00:00:00 2001 From: nyagamunene Date: Thu, 31 Oct 2024 13:19:56 +0300 Subject: [PATCH 07/72] Update pat service Signed-off-by: nyagamunene --- auth/api/http/transport.go | 2 +- auth/api/logging.go | 1 - auth/api/metrics.go | 1 - auth/mocks/service.go | 3 --- auth/tracing/tracing.go | 1 - 5 files changed, 1 insertion(+), 7 deletions(-) diff --git a/auth/api/http/transport.go b/auth/api/http/transport.go index 6f125b1622..c3a8d723c0 100644 --- a/auth/api/http/transport.go +++ b/auth/api/http/transport.go @@ -15,7 +15,7 @@ import ( ) // MakeHandler returns a HTTP handler for API endpoints. -func MakeHandler(svc auth.Service, authn mgauthn.Authentication, logger *slog.Logger, instanceID string) http.Handler { +func MakeHandler(svc auth.Service, logger *slog.Logger, instanceID string) http.Handler { mux := chi.NewRouter() mux = keys.MakeHandler(svc, mux, logger) diff --git a/auth/api/logging.go b/auth/api/logging.go index b8a6d9c71a..94bacfd3d7 100644 --- a/auth/api/logging.go +++ b/auth/api/logging.go @@ -11,7 +11,6 @@ import ( "time" "github.com/absmach/supermq/auth" - "github.com/absmach/supermq/pkg/authn" "github.com/absmach/supermq/pkg/policies" ) diff --git a/auth/api/metrics.go b/auth/api/metrics.go index ee3ff19b3b..081165b3ff 100644 --- a/auth/api/metrics.go +++ b/auth/api/metrics.go @@ -10,7 +10,6 @@ import ( "time" "github.com/absmach/supermq/auth" - "github.com/absmach/supermq/pkg/authn" "github.com/absmach/supermq/pkg/policies" "github.com/go-kit/kit/metrics" ) diff --git a/auth/mocks/service.go b/auth/mocks/service.go index 9ff90b6f1e..0a01591776 100644 --- a/auth/mocks/service.go +++ b/auth/mocks/service.go @@ -5,9 +5,6 @@ package mocks import ( - auth "github.com/absmach/magistrala/auth" - authn "github.com/absmach/magistrala/pkg/authn" - context "context" auth "github.com/absmach/supermq/auth" diff --git a/auth/tracing/tracing.go b/auth/tracing/tracing.go index 47735db6e7..0321eaf3bd 100644 --- a/auth/tracing/tracing.go +++ b/auth/tracing/tracing.go @@ -9,7 +9,6 @@ import ( "time" "github.com/absmach/supermq/auth" - "github.com/absmach/supermq/pkg/authn" "github.com/absmach/supermq/pkg/policies" "go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/trace" From 34c971ddd5e834f9902fc07739208e523d512d82 Mon Sep 17 00:00:00 2001 From: nyagamunene Date: Thu, 31 Oct 2024 13:36:28 +0300 Subject: [PATCH 08/72] Update nginx variables Signed-off-by: nyagamunene --- docker/nginx/entrypoint.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/docker/nginx/entrypoint.sh b/docker/nginx/entrypoint.sh index c1a3e0a6bf..4952421d0f 100755 --- a/docker/nginx/entrypoint.sh +++ b/docker/nginx/entrypoint.sh @@ -14,6 +14,7 @@ fi envsubst ' ${SMQ_NGINX_SERVER_NAME} ${SMQ_DOMAINS_HTTP_PORT} + ${SMQ_AUTH_HTTP_PORT} ${SMQ_GROUPS_HTTP_PORT} ${SMQ_USERS_HTTP_PORT} ${SMQ_CLIENTS_HTTP_PORT} From 833f795e698e06c753cb4bf438a83f892b2d9db1 Mon Sep 17 00:00:00 2001 From: nyagamunene Date: Thu, 7 Nov 2024 15:20:22 +0300 Subject: [PATCH 09/72] Update protoc version Signed-off-by: nyagamunene --- api/grpc/auth/v1/auth.pb.go | 98 +++++-------------------------------- 1 file changed, 12 insertions(+), 86 deletions(-) diff --git a/api/grpc/auth/v1/auth.pb.go b/api/grpc/auth/v1/auth.pb.go index d5b433ef86..2e62f2ce86 100644 --- a/api/grpc/auth/v1/auth.pb.go +++ b/api/grpc/auth/v1/auth.pb.go @@ -32,16 +32,9 @@ type AuthNReq struct { func (x *AuthNReq) Reset() { *x = AuthNReq{} - if protoimpl.UnsafeEnabled { - mi := &file_auth_v1_auth_proto_msgTypes[0] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } - if protoimpl.UnsafeEnabled { - mi := &file_auth_v1_auth_proto_msgTypes[0] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } + mi := &file_auth_v1_auth_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } func (x *AuthNReq) String() string { @@ -52,8 +45,7 @@ func (*AuthNReq) ProtoMessage() {} func (x *AuthNReq) ProtoReflect() protoreflect.Message { mi := &file_auth_v1_auth_proto_msgTypes[0] - if protoimpl.UnsafeEnabled && x != nil { - if protoimpl.UnsafeEnabled && x != nil { + if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -86,16 +78,9 @@ type AuthNRes struct { func (x *AuthNRes) Reset() { *x = AuthNRes{} - if protoimpl.UnsafeEnabled { - mi := &file_auth_v1_auth_proto_msgTypes[1] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } - if protoimpl.UnsafeEnabled { - mi := &file_auth_v1_auth_proto_msgTypes[1] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } + mi := &file_auth_v1_auth_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } func (x *AuthNRes) String() string { @@ -106,8 +91,7 @@ func (*AuthNRes) ProtoMessage() {} func (x *AuthNRes) ProtoReflect() protoreflect.Message { mi := &file_auth_v1_auth_proto_msgTypes[1] - if protoimpl.UnsafeEnabled && x != nil { - if protoimpl.UnsafeEnabled && x != nil { + if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -160,16 +144,9 @@ type AuthZReq struct { func (x *AuthZReq) Reset() { *x = AuthZReq{} - if protoimpl.UnsafeEnabled { - mi := &file_auth_v1_auth_proto_msgTypes[2] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } - if protoimpl.UnsafeEnabled { - mi := &file_auth_v1_auth_proto_msgTypes[2] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } + mi := &file_auth_v1_auth_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } func (x *AuthZReq) String() string { @@ -180,8 +157,7 @@ func (*AuthZReq) ProtoMessage() {} func (x *AuthZReq) ProtoReflect() protoreflect.Message { mi := &file_auth_v1_auth_proto_msgTypes[2] - if protoimpl.UnsafeEnabled && x != nil { - if protoimpl.UnsafeEnabled && x != nil { + if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -517,56 +493,6 @@ func file_auth_v1_auth_proto_init() { if File_auth_v1_auth_proto != nil { return } - if !protoimpl.UnsafeEnabled { - file_auth_v1_auth_proto_msgTypes[0].Exporter = func(v any, i int) any { - switch v := v.(*AuthNReq); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_auth_v1_auth_proto_msgTypes[1].Exporter = func(v any, i int) any { - switch v := v.(*AuthNRes); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_auth_v1_auth_proto_msgTypes[2].Exporter = func(v any, i int) any { - switch v := v.(*AuthZReq); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_auth_v1_auth_proto_msgTypes[3].Exporter = func(v any, i int) any { - switch v := v.(*AuthZRes); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - } type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ From 3cae2a2be535c59e18070648adcf62f6e77bb07c Mon Sep 17 00:00:00 2001 From: nyagamunene Date: Wed, 13 Nov 2024 13:46:19 +0300 Subject: [PATCH 10/72] Add authenticate PAT grpc endpoint Signed-off-by: nyagamunene --- api/grpc/auth/v1/auth.pb.go | 65 ++++++++++++++++++++++++++++--- auth/api/grpc/auth/responses.go | 6 +-- auth/service.go | 15 ------- internal/proto/auth/v1/auth.proto | 5 +++ pkg/authn/authn.go | 1 + 5 files changed, 68 insertions(+), 24 deletions(-) diff --git a/api/grpc/auth/v1/auth.pb.go b/api/grpc/auth/v1/auth.pb.go index 2e62f2ce86..704219246b 100644 --- a/api/grpc/auth/v1/auth.pb.go +++ b/api/grpc/auth/v1/auth.pb.go @@ -127,6 +127,59 @@ func (x *AuthNRes) GetDomainId() string { return "" } +type AuthNPATRes struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` // pat id + UserId string `protobuf:"bytes,2,opt,name=user_id,json=userId,proto3" json:"user_id,omitempty"` // user id +} + +func (x *AuthNPATRes) Reset() { + *x = AuthNPATRes{} + mi := &file_auth_v1_auth_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *AuthNPATRes) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*AuthNPATRes) ProtoMessage() {} + +func (x *AuthNPATRes) ProtoReflect() protoreflect.Message { + mi := &file_auth_v1_auth_proto_msgTypes[2] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use AuthNPATRes.ProtoReflect.Descriptor instead. +func (*AuthNPATRes) Descriptor() ([]byte, []int) { + return file_auth_v1_auth_proto_rawDescGZIP(), []int{2} +} + +func (x *AuthNPATRes) GetId() string { + if x != nil { + return x.Id + } + return "" +} + +func (x *AuthNPATRes) GetUserId() string { + if x != nil { + return x.UserId + } + return "" +} + type AuthZReq struct { state protoimpl.MessageState `protogen:"open.v1"` Domain string `protobuf:"bytes,1,opt,name=domain,proto3" json:"domain,omitempty"` // Domain @@ -144,7 +197,7 @@ type AuthZReq struct { func (x *AuthZReq) Reset() { *x = AuthZReq{} - mi := &file_auth_v1_auth_proto_msgTypes[2] + mi := &file_auth_v1_auth_proto_msgTypes[3] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -156,7 +209,7 @@ func (x *AuthZReq) String() string { func (*AuthZReq) ProtoMessage() {} func (x *AuthZReq) ProtoReflect() protoreflect.Message { - mi := &file_auth_v1_auth_proto_msgTypes[2] + mi := &file_auth_v1_auth_proto_msgTypes[3] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -169,7 +222,7 @@ func (x *AuthZReq) ProtoReflect() protoreflect.Message { // Deprecated: Use AuthZReq.ProtoReflect.Descriptor instead. func (*AuthZReq) Descriptor() ([]byte, []int) { - return file_auth_v1_auth_proto_rawDescGZIP(), []int{2} + return file_auth_v1_auth_proto_rawDescGZIP(), []int{3} } func (x *AuthZReq) GetDomain() string { @@ -362,7 +415,7 @@ func (x *AuthZRes) ProtoReflect() protoreflect.Message { // Deprecated: Use AuthZRes.ProtoReflect.Descriptor instead. func (*AuthZRes) Descriptor() ([]byte, []int) { - return file_auth_v1_auth_proto_rawDescGZIP(), []int{3} + return file_auth_v1_auth_proto_rawDescGZIP(), []int{4} } func (x *AuthZRes) GetAuthorized() bool { @@ -464,7 +517,7 @@ func file_auth_v1_auth_proto_rawDescGZIP() []byte { return file_auth_v1_auth_proto_rawDescData } -var file_auth_v1_auth_proto_msgTypes = make([]protoimpl.MessageInfo, 4) +var file_auth_v1_auth_proto_msgTypes = make([]protoimpl.MessageInfo, 5) var file_auth_v1_auth_proto_goTypes = []any{ (*AuthNReq)(nil), // 0: auth.v1.AuthNReq (*AuthNRes)(nil), // 1: auth.v1.AuthNRes @@ -499,7 +552,7 @@ func file_auth_v1_auth_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_auth_v1_auth_proto_rawDesc, NumEnums: 0, - NumMessages: 4, + NumMessages: 5, NumExtensions: 0, NumServices: 1, }, diff --git a/auth/api/grpc/auth/responses.go b/auth/api/grpc/auth/responses.go index dc9ad1cde5..f7a39deb46 100644 --- a/auth/api/grpc/auth/responses.go +++ b/auth/api/grpc/auth/responses.go @@ -4,9 +4,9 @@ package auth type authenticateRes struct { - id string - userID string - domainID string + id string + userID string + domainID string } type authorizeRes struct { diff --git a/auth/service.go b/auth/service.go index 583a784462..579d8466fd 100644 --- a/auth/service.go +++ b/auth/service.go @@ -167,21 +167,6 @@ func (svc service) RetrieveKey(ctx context.Context, token, id string) (Key, erro } func (svc service) Identify(ctx context.Context, token string) (Key, error) { - if strings.HasPrefix(token, patPrefix+patSecretSeparator) { - pat, err := svc.IdentifyPAT(ctx, token) - if err != nil { - return Key{}, err - } - return Key{ - ID: pat.ID, - Type: PersonalAccessToken, - Subject: pat.User, - User: pat.User, - IssuedAt: pat.IssuedAt, - ExpiresAt: pat.ExpiresAt, - }, nil - } - key, err := svc.tokenizer.Parse(token) if errors.Contains(err, ErrExpiry) { err = svc.keys.Remove(ctx, key.Issuer, key.ID) diff --git a/internal/proto/auth/v1/auth.proto b/internal/proto/auth/v1/auth.proto index 7d28932d57..e5a310aba3 100644 --- a/internal/proto/auth/v1/auth.proto +++ b/internal/proto/auth/v1/auth.proto @@ -26,6 +26,11 @@ message AuthNRes { string domain_id = 3; // domain id } +message AuthNPATRes { + string id = 1; // pat id + string user_id = 2; // user id +} + message AuthZReq { string domain = 1; // Domain string subject_type = 2; // Client or User diff --git a/pkg/authn/authn.go b/pkg/authn/authn.go index 5bfe92cd0b..30435f9926 100644 --- a/pkg/authn/authn.go +++ b/pkg/authn/authn.go @@ -41,4 +41,5 @@ type Session struct { //go:generate mockery --name Authentication --output=./mocks --filename authn.go --quiet --note "Copyright (c) Abstract Machines" type Authentication interface { Authenticate(ctx context.Context, token string) (Session, error) + AuthenticatePAT(ctx context.Context, token string) (Session, error) } From 77277de1041c852e9820def8e7b1122177ad1ed0 Mon Sep 17 00:00:00 2001 From: nyagamunene Date: Mon, 28 Oct 2024 20:29:11 +0300 Subject: [PATCH 11/72] Update middleware, service and api Signed-off-by: nyagamunene --- auth/hasher/hasher.go | 4 +- auth/pat.go | 24 +-- pat/api/endpoint.go | 259 +++++++++++++++++++++++++++++ pat/api/requests.go | 361 +++++++++++++++++++++++++++++++++++++++++ pat/api/responses.go | 208 ++++++++++++++++++++++++ pat/api/transport.go | 271 +++++++++++++++++++++++++++++++ pat/tracing/tracing.go | 164 +++++++++++++++++++ 7 files changed, 1277 insertions(+), 14 deletions(-) create mode 100644 pat/api/endpoint.go create mode 100644 pat/api/requests.go create mode 100644 pat/api/responses.go create mode 100644 pat/api/transport.go create mode 100644 pat/tracing/tracing.go diff --git a/auth/hasher/hasher.go b/auth/hasher/hasher.go index a9e4b2df08..ac2f3e55b6 100644 --- a/auth/hasher/hasher.go +++ b/auth/hasher/hasher.go @@ -24,12 +24,12 @@ var ( errDecode = errors.New("failed to decode") ) -var _ auth.Hasher = (*bcryptHasher)(nil) +var _ pat.Hasher = (*bcryptHasher)(nil) type bcryptHasher struct{} // New instantiates a bcrypt-based hasher implementation. -func New() auth.Hasher { +func New() pat.Hasher { return &bcryptHasher{} } diff --git a/auth/pat.go b/auth/pat.go index 4a168fa0ec..41878614aa 100644 --- a/auth/pat.go +++ b/auth/pat.go @@ -714,41 +714,41 @@ func (pat PAT) Expired() bool { } // 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" +//go:generate mockery --name Service --output=./mocks --filename pats.go --quiet --note "Copyright (c) Abstract Machines" -type PATS interface { +type Service interface { // Create function creates new PAT for given valid inputs. - CreatePAT(ctx context.Context, token, name, description string, duration time.Duration, scope Scope) (PAT, error) + CreatePAT(ctx context.Context, session authn.Session, name, description string, duration time.Duration, scope Scope) (PAT, error) // UpdateName function updates the name for the given PAT ID. - UpdatePATName(ctx context.Context, token, patID, name string) (PAT, error) + UpdatePATName(ctx context.Context, session authn.Session, patID, name string) (PAT, error) // UpdateDescription function updates the description for the given PAT ID. - UpdatePATDescription(ctx context.Context, token, patID, description string) (PAT, error) + UpdatePATDescription(ctx context.Context, session authn.Session, patID, description string) (PAT, error) // Retrieve function retrieves the PAT for given ID. RetrievePAT(ctx context.Context, userID string, patID string) (PAT, error) // List function lists all the PATs for the user. - ListPATS(ctx context.Context, token string, pm PATSPageMeta) (PATSPage, error) + ListPATS(ctx context.Context, session authn.Session, pm PATSPageMeta) (PATSPage, error) // Delete function deletes the PAT for given ID. - DeletePAT(ctx context.Context, token, patID string) error + DeletePAT(ctx context.Context, session authn.Session, patID string) error // ResetSecret function reset the secret and creates new secret for the given ID. - ResetPATSecret(ctx context.Context, token, patID string, duration time.Duration) (PAT, error) + ResetPATSecret(ctx context.Context, session authn.Session, patID string, duration time.Duration) (PAT, error) // RevokeSecret function revokes the secret for the given ID. - RevokePATSecret(ctx context.Context, token, patID string) error + RevokePATSecret(ctx context.Context, session authn.Session, patID string) error // AddScope function adds a new scope entry. - AddPATScopeEntry(ctx context.Context, token, patID string, platformEntityType PlatformEntityType, optionalDomainID string, optionalDomainEntityType DomainEntityType, operation OperationType, entityIDs ...string) (Scope, error) + AddPATScopeEntry(ctx context.Context, session authn.Session, patID string, platformEntityType PlatformEntityType, optionalDomainID string, optionalDomainEntityType DomainEntityType, operation OperationType, entityIDs ...string) (Scope, error) // RemoveScope function removes a scope entry. - RemovePATScopeEntry(ctx context.Context, token, patID string, platformEntityType PlatformEntityType, optionalDomainID string, optionalDomainEntityType DomainEntityType, operation OperationType, entityIDs ...string) (Scope, error) + RemovePATScopeEntry(ctx context.Context, session authn.Session, patID string, platformEntityType PlatformEntityType, optionalDomainID string, optionalDomainEntityType DomainEntityType, operation OperationType, entityIDs ...string) (Scope, error) // ClearAllScope function removes all scope entry. - ClearPATAllScopeEntry(ctx context.Context, token, patID string) error + ClearPATAllScopeEntry(ctx context.Context, session authn.Session, patID string) error // IdentifyPAT function will valid the secret. IdentifyPAT(ctx context.Context, paToken string) (PAT, error) diff --git a/pat/api/endpoint.go b/pat/api/endpoint.go new file mode 100644 index 0000000000..94cbdf5e80 --- /dev/null +++ b/pat/api/endpoint.go @@ -0,0 +1,259 @@ +// Copyright (c) Abstract Machines +// SPDX-License-Identifier: Apache-2.0 + +package pats + +import ( + "context" + + "github.com/absmach/magistrala/internal/api" + "github.com/absmach/magistrala/pat" + "github.com/absmach/magistrala/pkg/authn" + svcerr "github.com/absmach/magistrala/pkg/errors/service" + "github.com/go-kit/kit/endpoint" +) + +func createPATEndpoint(svc pat.Service) endpoint.Endpoint { + return func(ctx context.Context, request interface{}) (interface{}, error) { + req := request.(createPatReq) + if err := req.validate(); err != nil { + return nil, err + } + + session, ok := ctx.Value(api.SessionKey).(authn.Session) + if !ok { + return nil, svcerr.ErrAuthentication + } + + pat, err := svc.CreatePAT(ctx, session, req.Name, req.Description, req.Duration, req.Scope) + if err != nil { + return nil, err + } + + return createPatRes{pat}, nil + } +} + +func retrievePATEndpoint(svc pat.Service) endpoint.Endpoint { + return func(ctx context.Context, request interface{}) (interface{}, error) { + req := request.(retrievePatReq) + if err := req.validate(); err != nil { + return nil, err + } + + session, ok := ctx.Value(api.SessionKey).(authn.Session) + if !ok { + return nil, svcerr.ErrAuthentication + } + + pat, err := svc.RetrievePAT(ctx, session, req.id) + if err != nil { + return nil, err + } + + return retrievePatRes{pat}, nil + } +} + +func updatePATNameEndpoint(svc pat.Service) endpoint.Endpoint { + return func(ctx context.Context, request interface{}) (interface{}, error) { + req := request.(updatePatNameReq) + if err := req.validate(); err != nil { + return nil, err + } + + session, ok := ctx.Value(api.SessionKey).(authn.Session) + if !ok { + return nil, svcerr.ErrAuthentication + } + pat, err := svc.UpdatePATName(ctx, session, req.id, req.Name) + if err != nil { + return nil, err + } + + return updatePatNameRes{pat}, nil + } +} + +func updatePATDescriptionEndpoint(svc pat.Service) endpoint.Endpoint { + return func(ctx context.Context, request interface{}) (interface{}, error) { + req := request.(updatePatDescriptionReq) + if err := req.validate(); err != nil { + return nil, err + } + + session, ok := ctx.Value(api.SessionKey).(authn.Session) + if !ok { + return nil, svcerr.ErrAuthentication + } + + pat, err := svc.UpdatePATDescription(ctx, session, req.id, req.Description) + if err != nil { + return nil, err + } + + return updatePatDescriptionRes{pat}, nil + } +} + +func listPATSEndpoint(svc pat.Service) endpoint.Endpoint { + return func(ctx context.Context, request interface{}) (interface{}, error) { + req := request.(listPatsReq) + if err := req.validate(); err != nil { + return nil, err + } + + session, ok := ctx.Value(api.SessionKey).(authn.Session) + if !ok { + return nil, svcerr.ErrAuthentication + } + + pm := pat.PATSPageMeta{ + Limit: req.limit, + Offset: req.offset, + } + patsPage, err := svc.ListPATS(ctx, session, pm) + if err != nil { + return nil, err + } + + return listPatsRes{patsPage}, nil + } +} + +func deletePATEndpoint(svc pat.Service) endpoint.Endpoint { + return func(ctx context.Context, request interface{}) (interface{}, error) { + req := request.(deletePatReq) + if err := req.validate(); err != nil { + return nil, err + } + + session, ok := ctx.Value(api.SessionKey).(authn.Session) + if !ok { + return nil, svcerr.ErrAuthentication + } + + if err := svc.DeletePAT(ctx, session, req.id); err != nil { + return nil, err + } + + return deletePatRes{}, nil + } +} + +func resetPATSecretEndpoint(svc pat.Service) endpoint.Endpoint { + return func(ctx context.Context, request interface{}) (interface{}, error) { + req := request.(resetPatSecretReq) + if err := req.validate(); err != nil { + return nil, err + } + + session, ok := ctx.Value(api.SessionKey).(authn.Session) + if !ok { + return nil, svcerr.ErrAuthentication + } + + pat, err := svc.ResetPATSecret(ctx, session, req.id, req.Duration) + if err != nil { + return nil, err + } + + return resetPatSecretRes{pat}, nil + } +} + +func revokePATSecretEndpoint(svc pat.Service) endpoint.Endpoint { + return func(ctx context.Context, request interface{}) (interface{}, error) { + req := request.(revokePatSecretReq) + if err := req.validate(); err != nil { + return nil, err + } + + session, ok := ctx.Value(api.SessionKey).(authn.Session) + if !ok { + return nil, svcerr.ErrAuthentication + } + + if err := svc.RevokePATSecret(ctx, session, req.id); err != nil { + return nil, err + } + + return revokePatSecretRes{}, nil + } +} + +func addPATScopeEntryEndpoint(svc pat.Service) endpoint.Endpoint { + return func(ctx context.Context, request interface{}) (interface{}, error) { + req := request.(addPatScopeEntryReq) + if err := req.validate(); err != nil { + return nil, err + } + + session, ok := ctx.Value(api.SessionKey).(authn.Session) + if !ok { + return nil, svcerr.ErrAuthentication + } + + scope, err := svc.AddPATScopeEntry(ctx, session, req.id, req.PlatformEntityType, req.OptionalDomainID, req.OptionalDomainEntityType, req.Operation, req.EntityIDs...) + if err != nil { + return nil, err + } + + return addPatScopeEntryRes{scope}, nil + } +} + +func removePATScopeEntryEndpoint(svc pat.Service) endpoint.Endpoint { + return func(ctx context.Context, request interface{}) (interface{}, error) { + req := request.(removePatScopeEntryReq) + if err := req.validate(); err != nil { + return nil, err + } + + session, ok := ctx.Value(api.SessionKey).(authn.Session) + if !ok { + return nil, svcerr.ErrAuthentication + } + + scope, err := svc.RemovePATScopeEntry(ctx, session, req.id, req.PlatformEntityType, req.OptionalDomainID, req.OptionalDomainEntityType, req.Operation, req.EntityIDs...) + if err != nil { + return nil, err + } + return removePatScopeEntryRes{scope}, nil + } +} + +func clearPATAllScopeEntryEndpoint(svc pat.Service) endpoint.Endpoint { + return func(ctx context.Context, request interface{}) (interface{}, error) { + req := request.(clearAllScopeEntryReq) + if err := req.validate(); err != nil { + return nil, err + } + + session, ok := ctx.Value(api.SessionKey).(authn.Session) + if !ok { + return nil, svcerr.ErrAuthentication + } + + if err := svc.ClearPATAllScopeEntry(ctx, session, req.id); err != nil { + return nil, err + } + + return clearAllScopeEntryRes{}, nil + } +} + +func authorizePATEndpoint(svc pat.Service) endpoint.Endpoint { + return func(ctx context.Context, request interface{}) (interface{}, error) { + req := request.(authorizePATReq) + if err := req.validate(); err != nil { + return nil, err + } + + if err := svc.AuthorizePAT(ctx, req.token, req.PlatformEntityType, req.OptionalDomainID, req.OptionalDomainEntityType, req.Operation, req.EntityIDs...); err != nil { + return nil, err + } + + return authorizePATRes{}, nil + } +} diff --git a/pat/api/requests.go b/pat/api/requests.go new file mode 100644 index 0000000000..1822ec457e --- /dev/null +++ b/pat/api/requests.go @@ -0,0 +1,361 @@ +// Copyright (c) Abstract Machines +// SPDX-License-Identifier: Apache-2.0 + +package pats + +import ( + "encoding/json" + "strings" + "time" + + "github.com/absmach/magistrala/pat" + "github.com/absmach/magistrala/pkg/apiutil" +) + +type createPatReq struct { + token string + Name string `json:"name,omitempty"` + Description string `json:"description,omitempty"` + Duration time.Duration `json:"duration,omitempty"` + Scope pat.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 pat.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 + } + + 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 pat.PlatformEntityType `json:"platform_entity_type,omitempty"` + OptionalDomainID string `json:"optional_domain_id,omitempty"` + OptionalDomainEntityType pat.DomainEntityType `json:"optional_domain_entity_type,omitempty"` + Operation pat.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 := pat.ParsePlatformEntityType(temp.PlatformEntityType) + if err != nil { + return err + } + odt, err := pat.ParseDomainEntityType(temp.OptionalDomainEntityType) + if err != nil { + return err + } + op, err := pat.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 pat.PlatformEntityType `json:"platform_entity_type,omitempty"` + OptionalDomainID string `json:"optional_domain_id,omitempty"` + OptionalDomainEntityType pat.DomainEntityType `json:"optional_domain_entity_type,omitempty"` + Operation pat.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 := pat.ParsePlatformEntityType(temp.PlatformEntityType) + if err != nil { + return err + } + odt, err := pat.ParseDomainEntityType(temp.OptionalDomainEntityType) + if err != nil { + return err + } + op, err := pat.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 authorizePATReq struct { + token string + PlatformEntityType pat.PlatformEntityType `json:"platform_entity_type,omitempty"` + OptionalDomainID string `json:"optional_domain_id,omitempty"` + OptionalDomainEntityType pat.DomainEntityType `json:"optional_domain_entity_type,omitempty"` + Operation pat.OperationType `json:"operation,omitempty"` + EntityIDs []string `json:"entity_ids,omitempty"` +} + +func (tcpsr *authorizePATReq) 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 + } + + tcpsr.OptionalDomainID = temp.OptionalDomainID + tcpsr.EntityIDs = temp.EntityIDs + + pet, err := pat.ParsePlatformEntityType(temp.PlatformEntityType) + if err != nil { + return err + } + tcpsr.PlatformEntityType = pet + + if temp.OptionalDomainEntityType != "" { + odt, err := pat.ParseDomainEntityType(temp.OptionalDomainEntityType) + if err != nil { + return err + } + tcpsr.OptionalDomainEntityType = odt + } + + if temp.OptionalDomainID != "" { + op, err := pat.ParseOperationType(temp.Operation) + if err != nil { + return err + } + tcpsr.Operation = op + } + + return nil +} + +func (req authorizePATReq) validate() (err error) { + if req.token == "" { + return apiutil.ErrBearerToken + } + + return nil +} diff --git a/pat/api/responses.go b/pat/api/responses.go new file mode 100644 index 0000000000..b2d0fe57de --- /dev/null +++ b/pat/api/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/pat" +) + +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 { + pat.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 { + pat.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 { + pat.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 { + pat.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 { + pat.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 { + pat.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 { + pat.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 { + pat.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 authorizePATRes struct{} + +func (res authorizePATRes) Code() int { + return http.StatusNoContent +} + +func (res authorizePATRes) Headers() map[string]string { + return map[string]string{} +} + +func (res authorizePATRes) Empty() bool { + return true +} diff --git a/pat/api/transport.go b/pat/api/transport.go new file mode 100644 index 0000000000..95d0f45efa --- /dev/null +++ b/pat/api/transport.go @@ -0,0 +1,271 @@ +// 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/internal/api" + "github.com/absmach/magistrala/pat" + "github.com/absmach/magistrala/pkg/apiutil" + mgauthn "github.com/absmach/magistrala/pkg/authn" + "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 pat.Service, mux *chi.Mux, authn mgauthn.Authentication, logger *slog.Logger) *chi.Mux { + opts := []kithttp.ServerOption{ + kithttp.ServerErrorEncoder(apiutil.LoggingErrorEncoder(logger, api.EncodeError)), + } + mux.Group(func(r chi.Router) { + mux.Use(api.AuthenticateMiddleware(authn, true)) + + mux.Route("/pats", func(r chi.Router) { + r.Post("/", kithttp.NewServer( + createPATEndpoint(svc), + decodeCreatePATRequest, + api.EncodeResponse, + opts..., + ).ServeHTTP) + + r.Get("/{id}", kithttp.NewServer( + (retrievePATEndpoint(svc)), + decodeRetrievePATRequest, + api.EncodeResponse, + opts..., + ).ServeHTTP) + + r.Put("/{id}/name", kithttp.NewServer( + (updatePATNameEndpoint(svc)), + decodeUpdatePATNameRequest, + api.EncodeResponse, + opts..., + ).ServeHTTP) + + r.Put("/{id}/description", kithttp.NewServer( + (updatePATDescriptionEndpoint(svc)), + decodeUpdatePATDescriptionRequest, + api.EncodeResponse, + opts..., + ).ServeHTTP) + + r.Get("/", kithttp.NewServer( + (listPATSEndpoint(svc)), + decodeListPATSRequest, + api.EncodeResponse, + opts..., + ).ServeHTTP) + + r.Delete("/{id}", kithttp.NewServer( + (deletePATEndpoint(svc)), + decodeDeletePATRequest, + api.EncodeResponse, + opts..., + ).ServeHTTP) + + r.Put("/{id}/secret/reset", kithttp.NewServer( + (resetPATSecretEndpoint(svc)), + decodeResetPATSecretRequest, + api.EncodeResponse, + opts..., + ).ServeHTTP) + + r.Put("/{id}/secret/revoke", kithttp.NewServer( + (revokePATSecretEndpoint(svc)), + decodeRevokePATSecretRequest, + api.EncodeResponse, + opts..., + ).ServeHTTP) + + r.Put("/{id}/scope/add", kithttp.NewServer( + (addPATScopeEntryEndpoint(svc)), + decodeAddPATScopeEntryRequest, + api.EncodeResponse, + opts..., + ).ServeHTTP) + + r.Put("/{id}/scope/remove", kithttp.NewServer( + (removePATScopeEntryEndpoint(svc)), + decodeRemovePATScopeEntryRequest, + api.EncodeResponse, + opts..., + ).ServeHTTP) + + r.Delete("/{id}/scope", kithttp.NewServer( + (clearPATAllScopeEntryEndpoint(svc)), + decodeClearPATAllScopeEntryRequest, + api.EncodeResponse, + opts..., + ).ServeHTTP) + + r.Post("/authorize", kithttp.NewServer( + (authorizePATEndpoint(svc)), + decodeAuthorizePATRequest, + 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 decodeUpdatePATNameRequest(_ 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 decodeUpdatePATDescriptionRequest(_ 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 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) + } + 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, + } + return req, nil +} + +func decodeDeletePATRequest(_ context.Context, r *http.Request) (interface{}, error) { + return deletePatReq{ + token: apiutil.ExtractBearerToken(r), + id: chi.URLParam(r, "id"), + }, nil +} + +func decodeResetPATSecretRequest(_ 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 decodeRevokePATSecretRequest(_ context.Context, r *http.Request) (interface{}, error) { + return revokePatSecretReq{ + token: apiutil.ExtractBearerToken(r), + id: chi.URLParam(r, "id"), + }, nil +} + +func decodeAddPATScopeEntryRequest(_ 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 decodeRemovePATScopeEntryRequest(_ 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 decodeClearPATAllScopeEntryRequest(_ 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 decodeAuthorizePATRequest(_ context.Context, r *http.Request) (interface{}, error) { + if !strings.Contains(r.Header.Get("Content-Type"), contentType) { + return nil, apiutil.ErrUnsupportedContentType + } + + req := authorizePATReq{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/pat/tracing/tracing.go b/pat/tracing/tracing.go new file mode 100644 index 0000000000..4fab5fae49 --- /dev/null +++ b/pat/tracing/tracing.go @@ -0,0 +1,164 @@ +// Copyright (c) Abstract Machines +// SPDX-License-Identifier: Apache-2.0 + +package tracing + +import ( + "context" + "time" + + "github.com/absmach/magistrala/pat" + "github.com/absmach/magistrala/pkg/authn" + "go.opentelemetry.io/otel/attribute" + "go.opentelemetry.io/otel/trace" +) + +var _ pat.Service = (*tracingMiddleware)(nil) + +type tracingMiddleware struct { + tracer trace.Tracer + svc pat.Service +} + +// New returns a new group service with tracing capabilities. +func New(svc pat.Service, tracer trace.Tracer) pat.Service { + return &tracingMiddleware{tracer, svc} +} + +func (tm *tracingMiddleware) CreatePAT(ctx context.Context, session authn.Session, name, description string, duration time.Duration, scope pat.Scope) (pat.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.CreatePAT(ctx, session, name, description, duration, scope) +} + +func (tm *tracingMiddleware) UpdatePATName(ctx context.Context, session authn.Session, patID, name string) (pat.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.UpdatePATName(ctx, session, patID, name) +} + +func (tm *tracingMiddleware) UpdatePATDescription(ctx context.Context, session authn.Session, patID, description string) (pat.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.UpdatePATDescription(ctx, session, patID, description) +} + +func (tm *tracingMiddleware) RetrievePAT(ctx context.Context, session authn.Session, patID string) (pat.PAT, error) { + ctx, span := tm.tracer.Start(ctx, "retrieve_pat", trace.WithAttributes( + attribute.String("pat_id", patID), + )) + defer span.End() + return tm.svc.RetrievePAT(ctx, session, patID) +} + +func (tm *tracingMiddleware) ListPATS(ctx context.Context, session authn.Session, pm pat.PATSPageMeta) (pat.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.ListPATS(ctx, session, pm) +} + +func (tm *tracingMiddleware) DeletePAT(ctx context.Context, session authn.Session, patID string) error { + ctx, span := tm.tracer.Start(ctx, "delete_pat", trace.WithAttributes( + attribute.String("pat_id", patID), + )) + defer span.End() + return tm.svc.DeletePAT(ctx, session, patID) +} + +func (tm *tracingMiddleware) ResetPATSecret(ctx context.Context, session authn.Session, patID string, duration time.Duration) (pat.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.ResetPATSecret(ctx, session, patID, duration) +} + +func (tm *tracingMiddleware) RevokePATSecret(ctx context.Context, session authn.Session, 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.RevokePATSecret(ctx, session, patID) +} + +func (tm *tracingMiddleware) AddPATScopeEntry(ctx context.Context, session authn.Session, patID string, platformEntityType pat.PlatformEntityType, optionalDomainID string, optionalDomainEntityType pat.DomainEntityType, operation pat.OperationType, entityIDs ...string) (pat.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), + attribute.String("optional_domain_entity", optionalDomainEntityType.String()), + attribute.String("operation", operation.String()), + attribute.StringSlice("entities", entityIDs), + )) + defer span.End() + return tm.svc.AddPATScopeEntry(ctx, session, patID, platformEntityType, optionalDomainID, optionalDomainEntityType, operation, entityIDs...) +} + +func (tm *tracingMiddleware) RemovePATScopeEntry(ctx context.Context, session authn.Session, patID string, platformEntityType pat.PlatformEntityType, optionalDomainID string, optionalDomainEntityType pat.DomainEntityType, operation pat.OperationType, entityIDs ...string) (pat.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), + attribute.String("optional_domain_entity", optionalDomainEntityType.String()), + attribute.String("operation", operation.String()), + attribute.StringSlice("entities", entityIDs), + )) + defer span.End() + return tm.svc.RemovePATScopeEntry(ctx, session, patID, platformEntityType, optionalDomainID, optionalDomainEntityType, operation, entityIDs...) +} + +func (tm *tracingMiddleware) ClearPATAllScopeEntry(ctx context.Context, session authn.Session, 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.ClearPATAllScopeEntry(ctx, session, patID) +} + +func (tm *tracingMiddleware) IdentifyPAT(ctx context.Context, paToken string) (pat.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 pat.PlatformEntityType, optionalDomainID string, optionalDomainEntityType pat.DomainEntityType, operation pat.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), + 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, platformEntityType, optionalDomainID, optionalDomainEntityType, operation, entityIDs...) +} + +func (tm *tracingMiddleware) CheckPAT(ctx context.Context, userID, patID string, platformEntityType pat.PlatformEntityType, optionalDomainID string, optionalDomainEntityType pat.DomainEntityType, operation pat.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.CheckPAT(ctx, userID, patID, platformEntityType, optionalDomainID, optionalDomainEntityType, operation, entityIDs...) +} From 6087cd77ed63b1243dc1d90f53d2e742d554dd9b Mon Sep 17 00:00:00 2001 From: nyagamunene Date: Wed, 30 Oct 2024 13:21:26 +0300 Subject: [PATCH 12/72] Add grpc authorizePAT Signed-off-by: nyagamunene --- api/grpc/auth/v1/auth_grpc.pb.go | 4 ++ auth/service.go | 15 +++++ pat/api/{ => http}/endpoint.go | 0 pat/api/{ => http}/requests.go | 0 pat/api/{ => http}/responses.go | 0 pat/api/{ => http}/transport.go | 0 pat/events/streams.go | 94 ++++++++++++++++++++++++++++++++ 7 files changed, 113 insertions(+) rename pat/api/{ => http}/endpoint.go (100%) rename pat/api/{ => http}/requests.go (100%) rename pat/api/{ => http}/responses.go (100%) rename pat/api/{ => http}/transport.go (100%) create mode 100644 pat/events/streams.go diff --git a/api/grpc/auth/v1/auth_grpc.pb.go b/api/grpc/auth/v1/auth_grpc.pb.go index e0494d597f..d85fabae7c 100644 --- a/api/grpc/auth/v1/auth_grpc.pb.go +++ b/api/grpc/auth/v1/auth_grpc.pb.go @@ -226,6 +226,10 @@ var AuthService_ServiceDesc = grpc.ServiceDesc{ MethodName: "Authorize", Handler: _AuthService_Authorize_Handler, }, + { + MethodName: "AuthorizePAT", + Handler: _AuthService_AuthorizePAT_Handler, + }, { MethodName: "Authenticate", Handler: _AuthService_Authenticate_Handler, diff --git a/auth/service.go b/auth/service.go index 579d8466fd..c266ed2fa7 100644 --- a/auth/service.go +++ b/auth/service.go @@ -167,6 +167,21 @@ func (svc service) RetrieveKey(ctx context.Context, token, id string) (Key, erro } func (svc service) Identify(ctx context.Context, token string) (Key, error) { + if strings.HasPrefix(token, "pat"+"_") { + pat, err := svc.IdentifyPAT(ctx, token) + if err != nil { + return Key{}, err + } + return Key{ + ID: pat.ID, + Type: PersonalAccessToken, + Subject: pat.User, + User: pat.User, + IssuedAt: pat.IssuedAt, + ExpiresAt: pat.ExpiresAt, + }, nil + } + key, err := svc.tokenizer.Parse(token) if errors.Contains(err, ErrExpiry) { err = svc.keys.Remove(ctx, key.Issuer, key.ID) diff --git a/pat/api/endpoint.go b/pat/api/http/endpoint.go similarity index 100% rename from pat/api/endpoint.go rename to pat/api/http/endpoint.go diff --git a/pat/api/requests.go b/pat/api/http/requests.go similarity index 100% rename from pat/api/requests.go rename to pat/api/http/requests.go diff --git a/pat/api/responses.go b/pat/api/http/responses.go similarity index 100% rename from pat/api/responses.go rename to pat/api/http/responses.go diff --git a/pat/api/transport.go b/pat/api/http/transport.go similarity index 100% rename from pat/api/transport.go rename to pat/api/http/transport.go diff --git a/pat/events/streams.go b/pat/events/streams.go new file mode 100644 index 0000000000..713ddea5a6 --- /dev/null +++ b/pat/events/streams.go @@ -0,0 +1,94 @@ +// Copyright (c) Abstract Machines +// SPDX-License-Identifier: Apache-2.0 + +package events + +import ( + "context" + "time" + + "github.com/absmach/magistrala/pat" + "github.com/absmach/magistrala/pkg/authn" + "github.com/absmach/magistrala/pkg/events" + "github.com/absmach/magistrala/pkg/events/store" +) + +const streamID = "magistrala.pat" + +var _ pat.Service = (*eventStore)(nil) + + +type eventStore struct { + events.Publisher + svc pat.Service +} + +// NewEventStoreMiddleware returns wrapper around pat service that sends +// events to event store. +func NewEventStoreMiddleware(ctx context.Context, svc pat.Service, url string) (pat.Service, error) { + publisher, err := store.NewPublisher(ctx, url, streamID) + if err != nil { + return nil, err + } + + return &eventStore{ + svc: svc, + Publisher: publisher, + }, nil +} + +func (es *eventStore) CreatePAT(ctx context.Context, session authn.Session, name, description string, duration time.Duration, scope pat.Scope) (pat.PAT, error) { + return es.svc.CreatePAT(ctx, session, name, description, duration, scope) +} + +func (es *eventStore) UpdatePATName(ctx context.Context, session authn.Session, patID, name string) (pat.PAT, error) { + return es.svc.UpdatePATName(ctx, session, patID, name) +} + +func (es *eventStore) UpdatePATDescription(ctx context.Context, session authn.Session, patID, description string) (pat.PAT, error) { + return es.svc.UpdatePATDescription(ctx, session, patID, description) +} + +func (es *eventStore) RetrievePAT(ctx context.Context, session authn.Session, patID string) (pat.PAT, error) { + return es.svc.RetrievePAT(ctx, session, patID) +} + +func (es *eventStore) ListPATS(ctx context.Context, session authn.Session, pm pat.PATSPageMeta) (pat.PATSPage, error) { + return es.svc.ListPATS(ctx, session, pm) +} + +func (es *eventStore) DeletePAT(ctx context.Context, session authn.Session, patID string) error { + return es.svc.DeletePAT(ctx, session, patID) +} + +func (es *eventStore) ResetPATSecret(ctx context.Context, session authn.Session, patID string, duration time.Duration) (pat.PAT, error) { + return es.svc.ResetPATSecret(ctx, session, patID, duration) +} + +func (es *eventStore) RevokePATSecret(ctx context.Context, session authn.Session, patID string) error { + return es.svc.RevokePATSecret(ctx, session, patID) +} + +func (es *eventStore) AddPATScopeEntry(ctx context.Context, session authn.Session, patID string, platformEntityType pat.PlatformEntityType, optionalDomainID string, optionalDomainEntityType pat.DomainEntityType, operation pat.OperationType, entityIDs ...string) (pat.Scope, error) { + return es.svc.AddPATScopeEntry(ctx, session, patID, platformEntityType, optionalDomainID, optionalDomainEntityType, operation, entityIDs...) +} + +func (es *eventStore) RemovePATScopeEntry(ctx context.Context, session authn.Session, patID string, platformEntityType pat.PlatformEntityType, optionalDomainID string, optionalDomainEntityType pat.DomainEntityType, operation pat.OperationType, entityIDs ...string) (pat.Scope, error) { + return es.svc.RemovePATScopeEntry(ctx, session, patID, platformEntityType, optionalDomainID, optionalDomainEntityType, operation, entityIDs...) +} + +func (es *eventStore) ClearPATAllScopeEntry(ctx context.Context, session authn.Session, patID string) error { + return es.svc.ClearPATAllScopeEntry(ctx, session, patID) +} + +func (es *eventStore) IdentifyPAT(ctx context.Context, paToken string) (pat.PAT, error) { + return es.svc.IdentifyPAT(ctx, paToken) +} + +func (es *eventStore) AuthorizePAT(ctx context.Context, paToken string, platformEntityType pat.PlatformEntityType, optionalDomainID string, optionalDomainEntityType pat.DomainEntityType, operation pat.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 pat.PlatformEntityType, optionalDomainID string, optionalDomainEntityType pat.DomainEntityType, operation pat.OperationType, entityIDs ...string) error { + return es.svc.CheckPAT(ctx, userID, patID, platformEntityType, optionalDomainID, optionalDomainEntityType, operation, entityIDs...) +} \ No newline at end of file From 5f5b671d3b9b3072c0409cea23e9b4768cc56d7c Mon Sep 17 00:00:00 2001 From: nyagamunene Date: Tue, 3 Dec 2024 13:43:12 +0300 Subject: [PATCH 13/72] fix failing linter Signed-off-by: nyagamunene --- api/grpc/auth/v1/auth.pb.go | 10 +- auth/api/grpc/auth/endpoint.go | 4 +- auth/hasher/hasher.go | 4 +- auth/pat.go | 24 +- internal/proto/auth/v1/auth.proto | 2 +- pat/api/http/endpoint.go | 259 --------------------- pat/api/http/requests.go | 361 ------------------------------ pat/api/http/responses.go | 208 ----------------- pat/api/http/transport.go | 271 ---------------------- pat/events/streams.go | 94 -------- pat/tracing/tracing.go | 164 -------------- pkg/authn/mocks/authn.go | 28 +++ 12 files changed, 50 insertions(+), 1379 deletions(-) delete mode 100644 pat/api/http/endpoint.go delete mode 100644 pat/api/http/requests.go delete mode 100644 pat/api/http/responses.go delete mode 100644 pat/api/http/transport.go delete mode 100644 pat/events/streams.go delete mode 100644 pat/tracing/tracing.go diff --git a/api/grpc/auth/v1/auth.pb.go b/api/grpc/auth/v1/auth.pb.go index 704219246b..a3cc52f11d 100644 --- a/api/grpc/auth/v1/auth.pb.go +++ b/api/grpc/auth/v1/auth.pb.go @@ -390,7 +390,7 @@ type AuthZRes struct { func (x *AuthZRes) Reset() { *x = AuthZRes{} - mi := &file_auth_v1_auth_proto_msgTypes[4] + mi := &file_auth_v1_auth_proto_msgTypes[5] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -402,7 +402,7 @@ func (x *AuthZRes) String() string { func (*AuthZRes) ProtoMessage() {} func (x *AuthZRes) ProtoReflect() protoreflect.Message { - mi := &file_auth_v1_auth_proto_msgTypes[4] + mi := &file_auth_v1_auth_proto_msgTypes[5] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -415,7 +415,7 @@ func (x *AuthZRes) ProtoReflect() protoreflect.Message { // Deprecated: Use AuthZRes.ProtoReflect.Descriptor instead. func (*AuthZRes) Descriptor() ([]byte, []int) { - return file_auth_v1_auth_proto_rawDescGZIP(), []int{4} + return file_auth_v1_auth_proto_rawDescGZIP(), []int{5} } func (x *AuthZRes) GetAuthorized() bool { @@ -517,7 +517,7 @@ func file_auth_v1_auth_proto_rawDescGZIP() []byte { return file_auth_v1_auth_proto_rawDescData } -var file_auth_v1_auth_proto_msgTypes = make([]protoimpl.MessageInfo, 5) +var file_auth_v1_auth_proto_msgTypes = make([]protoimpl.MessageInfo, 6) var file_auth_v1_auth_proto_goTypes = []any{ (*AuthNReq)(nil), // 0: auth.v1.AuthNReq (*AuthNRes)(nil), // 1: auth.v1.AuthNRes @@ -552,7 +552,7 @@ func file_auth_v1_auth_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_auth_v1_auth_proto_rawDesc, NumEnums: 0, - NumMessages: 5, + NumMessages: 6, NumExtensions: 0, NumServices: 1, }, diff --git a/auth/api/grpc/auth/endpoint.go b/auth/api/grpc/auth/endpoint.go index 05516b64e6..9f529e2773 100644 --- a/auth/api/grpc/auth/endpoint.go +++ b/auth/api/grpc/auth/endpoint.go @@ -23,7 +23,7 @@ func authenticateEndpoint(svc auth.Service) endpoint.Endpoint { return authenticateRes{}, err } - return authenticateRes{id: key.Subject, userID: key.User, domainID: key.Domain}, nil + return authenticateRes{id: key.ID, userID: key.User, domainID: key.Domain}, nil } } @@ -80,4 +80,4 @@ func authorizePATEndpoint(svc auth.Service) endpoint.Endpoint { } return authorizeRes{authorized: true}, nil } -} +} \ No newline at end of file diff --git a/auth/hasher/hasher.go b/auth/hasher/hasher.go index ac2f3e55b6..a9e4b2df08 100644 --- a/auth/hasher/hasher.go +++ b/auth/hasher/hasher.go @@ -24,12 +24,12 @@ var ( errDecode = errors.New("failed to decode") ) -var _ pat.Hasher = (*bcryptHasher)(nil) +var _ auth.Hasher = (*bcryptHasher)(nil) type bcryptHasher struct{} // New instantiates a bcrypt-based hasher implementation. -func New() pat.Hasher { +func New() auth.Hasher { return &bcryptHasher{} } diff --git a/auth/pat.go b/auth/pat.go index 41878614aa..4a168fa0ec 100644 --- a/auth/pat.go +++ b/auth/pat.go @@ -714,41 +714,41 @@ func (pat PAT) Expired() bool { } // PATS specifies function which are required for Personal access Token implementation. -//go:generate mockery --name Service --output=./mocks --filename pats.go --quiet --note "Copyright (c) Abstract Machines" +//go:generate mockery --name PATS --output=./mocks --filename pats.go --quiet --note "Copyright (c) Abstract Machines" -type Service interface { +type PATS interface { // Create function creates new PAT for given valid inputs. - CreatePAT(ctx context.Context, session authn.Session, 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. - UpdatePATName(ctx context.Context, session authn.Session, 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. - UpdatePATDescription(ctx context.Context, session authn.Session, patID, description string) (PAT, error) + UpdatePATDescription(ctx context.Context, token, patID, description string) (PAT, error) // Retrieve function retrieves the PAT for given ID. RetrievePAT(ctx context.Context, userID string, patID string) (PAT, error) // List function lists all the PATs for the user. - ListPATS(ctx context.Context, session authn.Session, pm PATSPageMeta) (PATSPage, error) + ListPATS(ctx context.Context, token string, pm PATSPageMeta) (PATSPage, error) // Delete function deletes the PAT for given ID. - DeletePAT(ctx context.Context, session authn.Session, patID string) error + DeletePAT(ctx context.Context, token, patID string) error // ResetSecret function reset the secret and creates new secret for the given ID. - ResetPATSecret(ctx context.Context, session authn.Session, 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. - RevokePATSecret(ctx context.Context, session authn.Session, patID string) error + RevokePATSecret(ctx context.Context, token, patID string) error // AddScope function adds a new scope entry. - AddPATScopeEntry(ctx context.Context, session authn.Session, 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. - RemovePATScopeEntry(ctx context.Context, session authn.Session, 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. - ClearPATAllScopeEntry(ctx context.Context, session authn.Session, patID string) error + ClearPATAllScopeEntry(ctx context.Context, token, patID string) error // IdentifyPAT function will valid the secret. IdentifyPAT(ctx context.Context, paToken string) (PAT, error) diff --git a/internal/proto/auth/v1/auth.proto b/internal/proto/auth/v1/auth.proto index e5a310aba3..4c1804c7c8 100644 --- a/internal/proto/auth/v1/auth.proto +++ b/internal/proto/auth/v1/auth.proto @@ -17,7 +17,7 @@ service AuthService { message AuthNReq { - string token = 1; + string token = 1; } message AuthNRes { diff --git a/pat/api/http/endpoint.go b/pat/api/http/endpoint.go deleted file mode 100644 index 94cbdf5e80..0000000000 --- a/pat/api/http/endpoint.go +++ /dev/null @@ -1,259 +0,0 @@ -// Copyright (c) Abstract Machines -// SPDX-License-Identifier: Apache-2.0 - -package pats - -import ( - "context" - - "github.com/absmach/magistrala/internal/api" - "github.com/absmach/magistrala/pat" - "github.com/absmach/magistrala/pkg/authn" - svcerr "github.com/absmach/magistrala/pkg/errors/service" - "github.com/go-kit/kit/endpoint" -) - -func createPATEndpoint(svc pat.Service) endpoint.Endpoint { - return func(ctx context.Context, request interface{}) (interface{}, error) { - req := request.(createPatReq) - if err := req.validate(); err != nil { - return nil, err - } - - session, ok := ctx.Value(api.SessionKey).(authn.Session) - if !ok { - return nil, svcerr.ErrAuthentication - } - - pat, err := svc.CreatePAT(ctx, session, req.Name, req.Description, req.Duration, req.Scope) - if err != nil { - return nil, err - } - - return createPatRes{pat}, nil - } -} - -func retrievePATEndpoint(svc pat.Service) endpoint.Endpoint { - return func(ctx context.Context, request interface{}) (interface{}, error) { - req := request.(retrievePatReq) - if err := req.validate(); err != nil { - return nil, err - } - - session, ok := ctx.Value(api.SessionKey).(authn.Session) - if !ok { - return nil, svcerr.ErrAuthentication - } - - pat, err := svc.RetrievePAT(ctx, session, req.id) - if err != nil { - return nil, err - } - - return retrievePatRes{pat}, nil - } -} - -func updatePATNameEndpoint(svc pat.Service) endpoint.Endpoint { - return func(ctx context.Context, request interface{}) (interface{}, error) { - req := request.(updatePatNameReq) - if err := req.validate(); err != nil { - return nil, err - } - - session, ok := ctx.Value(api.SessionKey).(authn.Session) - if !ok { - return nil, svcerr.ErrAuthentication - } - pat, err := svc.UpdatePATName(ctx, session, req.id, req.Name) - if err != nil { - return nil, err - } - - return updatePatNameRes{pat}, nil - } -} - -func updatePATDescriptionEndpoint(svc pat.Service) endpoint.Endpoint { - return func(ctx context.Context, request interface{}) (interface{}, error) { - req := request.(updatePatDescriptionReq) - if err := req.validate(); err != nil { - return nil, err - } - - session, ok := ctx.Value(api.SessionKey).(authn.Session) - if !ok { - return nil, svcerr.ErrAuthentication - } - - pat, err := svc.UpdatePATDescription(ctx, session, req.id, req.Description) - if err != nil { - return nil, err - } - - return updatePatDescriptionRes{pat}, nil - } -} - -func listPATSEndpoint(svc pat.Service) endpoint.Endpoint { - return func(ctx context.Context, request interface{}) (interface{}, error) { - req := request.(listPatsReq) - if err := req.validate(); err != nil { - return nil, err - } - - session, ok := ctx.Value(api.SessionKey).(authn.Session) - if !ok { - return nil, svcerr.ErrAuthentication - } - - pm := pat.PATSPageMeta{ - Limit: req.limit, - Offset: req.offset, - } - patsPage, err := svc.ListPATS(ctx, session, pm) - if err != nil { - return nil, err - } - - return listPatsRes{patsPage}, nil - } -} - -func deletePATEndpoint(svc pat.Service) endpoint.Endpoint { - return func(ctx context.Context, request interface{}) (interface{}, error) { - req := request.(deletePatReq) - if err := req.validate(); err != nil { - return nil, err - } - - session, ok := ctx.Value(api.SessionKey).(authn.Session) - if !ok { - return nil, svcerr.ErrAuthentication - } - - if err := svc.DeletePAT(ctx, session, req.id); err != nil { - return nil, err - } - - return deletePatRes{}, nil - } -} - -func resetPATSecretEndpoint(svc pat.Service) endpoint.Endpoint { - return func(ctx context.Context, request interface{}) (interface{}, error) { - req := request.(resetPatSecretReq) - if err := req.validate(); err != nil { - return nil, err - } - - session, ok := ctx.Value(api.SessionKey).(authn.Session) - if !ok { - return nil, svcerr.ErrAuthentication - } - - pat, err := svc.ResetPATSecret(ctx, session, req.id, req.Duration) - if err != nil { - return nil, err - } - - return resetPatSecretRes{pat}, nil - } -} - -func revokePATSecretEndpoint(svc pat.Service) endpoint.Endpoint { - return func(ctx context.Context, request interface{}) (interface{}, error) { - req := request.(revokePatSecretReq) - if err := req.validate(); err != nil { - return nil, err - } - - session, ok := ctx.Value(api.SessionKey).(authn.Session) - if !ok { - return nil, svcerr.ErrAuthentication - } - - if err := svc.RevokePATSecret(ctx, session, req.id); err != nil { - return nil, err - } - - return revokePatSecretRes{}, nil - } -} - -func addPATScopeEntryEndpoint(svc pat.Service) endpoint.Endpoint { - return func(ctx context.Context, request interface{}) (interface{}, error) { - req := request.(addPatScopeEntryReq) - if err := req.validate(); err != nil { - return nil, err - } - - session, ok := ctx.Value(api.SessionKey).(authn.Session) - if !ok { - return nil, svcerr.ErrAuthentication - } - - scope, err := svc.AddPATScopeEntry(ctx, session, req.id, req.PlatformEntityType, req.OptionalDomainID, req.OptionalDomainEntityType, req.Operation, req.EntityIDs...) - if err != nil { - return nil, err - } - - return addPatScopeEntryRes{scope}, nil - } -} - -func removePATScopeEntryEndpoint(svc pat.Service) endpoint.Endpoint { - return func(ctx context.Context, request interface{}) (interface{}, error) { - req := request.(removePatScopeEntryReq) - if err := req.validate(); err != nil { - return nil, err - } - - session, ok := ctx.Value(api.SessionKey).(authn.Session) - if !ok { - return nil, svcerr.ErrAuthentication - } - - scope, err := svc.RemovePATScopeEntry(ctx, session, req.id, req.PlatformEntityType, req.OptionalDomainID, req.OptionalDomainEntityType, req.Operation, req.EntityIDs...) - if err != nil { - return nil, err - } - return removePatScopeEntryRes{scope}, nil - } -} - -func clearPATAllScopeEntryEndpoint(svc pat.Service) endpoint.Endpoint { - return func(ctx context.Context, request interface{}) (interface{}, error) { - req := request.(clearAllScopeEntryReq) - if err := req.validate(); err != nil { - return nil, err - } - - session, ok := ctx.Value(api.SessionKey).(authn.Session) - if !ok { - return nil, svcerr.ErrAuthentication - } - - if err := svc.ClearPATAllScopeEntry(ctx, session, req.id); err != nil { - return nil, err - } - - return clearAllScopeEntryRes{}, nil - } -} - -func authorizePATEndpoint(svc pat.Service) endpoint.Endpoint { - return func(ctx context.Context, request interface{}) (interface{}, error) { - req := request.(authorizePATReq) - if err := req.validate(); err != nil { - return nil, err - } - - if err := svc.AuthorizePAT(ctx, req.token, req.PlatformEntityType, req.OptionalDomainID, req.OptionalDomainEntityType, req.Operation, req.EntityIDs...); err != nil { - return nil, err - } - - return authorizePATRes{}, nil - } -} diff --git a/pat/api/http/requests.go b/pat/api/http/requests.go deleted file mode 100644 index 1822ec457e..0000000000 --- a/pat/api/http/requests.go +++ /dev/null @@ -1,361 +0,0 @@ -// Copyright (c) Abstract Machines -// SPDX-License-Identifier: Apache-2.0 - -package pats - -import ( - "encoding/json" - "strings" - "time" - - "github.com/absmach/magistrala/pat" - "github.com/absmach/magistrala/pkg/apiutil" -) - -type createPatReq struct { - token string - Name string `json:"name,omitempty"` - Description string `json:"description,omitempty"` - Duration time.Duration `json:"duration,omitempty"` - Scope pat.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 pat.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 - } - - 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 pat.PlatformEntityType `json:"platform_entity_type,omitempty"` - OptionalDomainID string `json:"optional_domain_id,omitempty"` - OptionalDomainEntityType pat.DomainEntityType `json:"optional_domain_entity_type,omitempty"` - Operation pat.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 := pat.ParsePlatformEntityType(temp.PlatformEntityType) - if err != nil { - return err - } - odt, err := pat.ParseDomainEntityType(temp.OptionalDomainEntityType) - if err != nil { - return err - } - op, err := pat.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 pat.PlatformEntityType `json:"platform_entity_type,omitempty"` - OptionalDomainID string `json:"optional_domain_id,omitempty"` - OptionalDomainEntityType pat.DomainEntityType `json:"optional_domain_entity_type,omitempty"` - Operation pat.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 := pat.ParsePlatformEntityType(temp.PlatformEntityType) - if err != nil { - return err - } - odt, err := pat.ParseDomainEntityType(temp.OptionalDomainEntityType) - if err != nil { - return err - } - op, err := pat.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 authorizePATReq struct { - token string - PlatformEntityType pat.PlatformEntityType `json:"platform_entity_type,omitempty"` - OptionalDomainID string `json:"optional_domain_id,omitempty"` - OptionalDomainEntityType pat.DomainEntityType `json:"optional_domain_entity_type,omitempty"` - Operation pat.OperationType `json:"operation,omitempty"` - EntityIDs []string `json:"entity_ids,omitempty"` -} - -func (tcpsr *authorizePATReq) 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 - } - - tcpsr.OptionalDomainID = temp.OptionalDomainID - tcpsr.EntityIDs = temp.EntityIDs - - pet, err := pat.ParsePlatformEntityType(temp.PlatformEntityType) - if err != nil { - return err - } - tcpsr.PlatformEntityType = pet - - if temp.OptionalDomainEntityType != "" { - odt, err := pat.ParseDomainEntityType(temp.OptionalDomainEntityType) - if err != nil { - return err - } - tcpsr.OptionalDomainEntityType = odt - } - - if temp.OptionalDomainID != "" { - op, err := pat.ParseOperationType(temp.Operation) - if err != nil { - return err - } - tcpsr.Operation = op - } - - return nil -} - -func (req authorizePATReq) validate() (err error) { - if req.token == "" { - return apiutil.ErrBearerToken - } - - return nil -} diff --git a/pat/api/http/responses.go b/pat/api/http/responses.go deleted file mode 100644 index b2d0fe57de..0000000000 --- a/pat/api/http/responses.go +++ /dev/null @@ -1,208 +0,0 @@ -// Copyright (c) Abstract Machines -// SPDX-License-Identifier: Apache-2.0 - -package pats - -import ( - "net/http" - - "github.com/absmach/magistrala" - "github.com/absmach/magistrala/pat" -) - -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 { - pat.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 { - pat.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 { - pat.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 { - pat.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 { - pat.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 { - pat.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 { - pat.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 { - pat.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 authorizePATRes struct{} - -func (res authorizePATRes) Code() int { - return http.StatusNoContent -} - -func (res authorizePATRes) Headers() map[string]string { - return map[string]string{} -} - -func (res authorizePATRes) Empty() bool { - return true -} diff --git a/pat/api/http/transport.go b/pat/api/http/transport.go deleted file mode 100644 index 95d0f45efa..0000000000 --- a/pat/api/http/transport.go +++ /dev/null @@ -1,271 +0,0 @@ -// 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/internal/api" - "github.com/absmach/magistrala/pat" - "github.com/absmach/magistrala/pkg/apiutil" - mgauthn "github.com/absmach/magistrala/pkg/authn" - "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 pat.Service, mux *chi.Mux, authn mgauthn.Authentication, logger *slog.Logger) *chi.Mux { - opts := []kithttp.ServerOption{ - kithttp.ServerErrorEncoder(apiutil.LoggingErrorEncoder(logger, api.EncodeError)), - } - mux.Group(func(r chi.Router) { - mux.Use(api.AuthenticateMiddleware(authn, true)) - - mux.Route("/pats", func(r chi.Router) { - r.Post("/", kithttp.NewServer( - createPATEndpoint(svc), - decodeCreatePATRequest, - api.EncodeResponse, - opts..., - ).ServeHTTP) - - r.Get("/{id}", kithttp.NewServer( - (retrievePATEndpoint(svc)), - decodeRetrievePATRequest, - api.EncodeResponse, - opts..., - ).ServeHTTP) - - r.Put("/{id}/name", kithttp.NewServer( - (updatePATNameEndpoint(svc)), - decodeUpdatePATNameRequest, - api.EncodeResponse, - opts..., - ).ServeHTTP) - - r.Put("/{id}/description", kithttp.NewServer( - (updatePATDescriptionEndpoint(svc)), - decodeUpdatePATDescriptionRequest, - api.EncodeResponse, - opts..., - ).ServeHTTP) - - r.Get("/", kithttp.NewServer( - (listPATSEndpoint(svc)), - decodeListPATSRequest, - api.EncodeResponse, - opts..., - ).ServeHTTP) - - r.Delete("/{id}", kithttp.NewServer( - (deletePATEndpoint(svc)), - decodeDeletePATRequest, - api.EncodeResponse, - opts..., - ).ServeHTTP) - - r.Put("/{id}/secret/reset", kithttp.NewServer( - (resetPATSecretEndpoint(svc)), - decodeResetPATSecretRequest, - api.EncodeResponse, - opts..., - ).ServeHTTP) - - r.Put("/{id}/secret/revoke", kithttp.NewServer( - (revokePATSecretEndpoint(svc)), - decodeRevokePATSecretRequest, - api.EncodeResponse, - opts..., - ).ServeHTTP) - - r.Put("/{id}/scope/add", kithttp.NewServer( - (addPATScopeEntryEndpoint(svc)), - decodeAddPATScopeEntryRequest, - api.EncodeResponse, - opts..., - ).ServeHTTP) - - r.Put("/{id}/scope/remove", kithttp.NewServer( - (removePATScopeEntryEndpoint(svc)), - decodeRemovePATScopeEntryRequest, - api.EncodeResponse, - opts..., - ).ServeHTTP) - - r.Delete("/{id}/scope", kithttp.NewServer( - (clearPATAllScopeEntryEndpoint(svc)), - decodeClearPATAllScopeEntryRequest, - api.EncodeResponse, - opts..., - ).ServeHTTP) - - r.Post("/authorize", kithttp.NewServer( - (authorizePATEndpoint(svc)), - decodeAuthorizePATRequest, - 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 decodeUpdatePATNameRequest(_ 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 decodeUpdatePATDescriptionRequest(_ 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 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) - } - 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, - } - return req, nil -} - -func decodeDeletePATRequest(_ context.Context, r *http.Request) (interface{}, error) { - return deletePatReq{ - token: apiutil.ExtractBearerToken(r), - id: chi.URLParam(r, "id"), - }, nil -} - -func decodeResetPATSecretRequest(_ 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 decodeRevokePATSecretRequest(_ context.Context, r *http.Request) (interface{}, error) { - return revokePatSecretReq{ - token: apiutil.ExtractBearerToken(r), - id: chi.URLParam(r, "id"), - }, nil -} - -func decodeAddPATScopeEntryRequest(_ 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 decodeRemovePATScopeEntryRequest(_ 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 decodeClearPATAllScopeEntryRequest(_ 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 decodeAuthorizePATRequest(_ context.Context, r *http.Request) (interface{}, error) { - if !strings.Contains(r.Header.Get("Content-Type"), contentType) { - return nil, apiutil.ErrUnsupportedContentType - } - - req := authorizePATReq{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/pat/events/streams.go b/pat/events/streams.go deleted file mode 100644 index 713ddea5a6..0000000000 --- a/pat/events/streams.go +++ /dev/null @@ -1,94 +0,0 @@ -// Copyright (c) Abstract Machines -// SPDX-License-Identifier: Apache-2.0 - -package events - -import ( - "context" - "time" - - "github.com/absmach/magistrala/pat" - "github.com/absmach/magistrala/pkg/authn" - "github.com/absmach/magistrala/pkg/events" - "github.com/absmach/magistrala/pkg/events/store" -) - -const streamID = "magistrala.pat" - -var _ pat.Service = (*eventStore)(nil) - - -type eventStore struct { - events.Publisher - svc pat.Service -} - -// NewEventStoreMiddleware returns wrapper around pat service that sends -// events to event store. -func NewEventStoreMiddleware(ctx context.Context, svc pat.Service, url string) (pat.Service, error) { - publisher, err := store.NewPublisher(ctx, url, streamID) - if err != nil { - return nil, err - } - - return &eventStore{ - svc: svc, - Publisher: publisher, - }, nil -} - -func (es *eventStore) CreatePAT(ctx context.Context, session authn.Session, name, description string, duration time.Duration, scope pat.Scope) (pat.PAT, error) { - return es.svc.CreatePAT(ctx, session, name, description, duration, scope) -} - -func (es *eventStore) UpdatePATName(ctx context.Context, session authn.Session, patID, name string) (pat.PAT, error) { - return es.svc.UpdatePATName(ctx, session, patID, name) -} - -func (es *eventStore) UpdatePATDescription(ctx context.Context, session authn.Session, patID, description string) (pat.PAT, error) { - return es.svc.UpdatePATDescription(ctx, session, patID, description) -} - -func (es *eventStore) RetrievePAT(ctx context.Context, session authn.Session, patID string) (pat.PAT, error) { - return es.svc.RetrievePAT(ctx, session, patID) -} - -func (es *eventStore) ListPATS(ctx context.Context, session authn.Session, pm pat.PATSPageMeta) (pat.PATSPage, error) { - return es.svc.ListPATS(ctx, session, pm) -} - -func (es *eventStore) DeletePAT(ctx context.Context, session authn.Session, patID string) error { - return es.svc.DeletePAT(ctx, session, patID) -} - -func (es *eventStore) ResetPATSecret(ctx context.Context, session authn.Session, patID string, duration time.Duration) (pat.PAT, error) { - return es.svc.ResetPATSecret(ctx, session, patID, duration) -} - -func (es *eventStore) RevokePATSecret(ctx context.Context, session authn.Session, patID string) error { - return es.svc.RevokePATSecret(ctx, session, patID) -} - -func (es *eventStore) AddPATScopeEntry(ctx context.Context, session authn.Session, patID string, platformEntityType pat.PlatformEntityType, optionalDomainID string, optionalDomainEntityType pat.DomainEntityType, operation pat.OperationType, entityIDs ...string) (pat.Scope, error) { - return es.svc.AddPATScopeEntry(ctx, session, patID, platformEntityType, optionalDomainID, optionalDomainEntityType, operation, entityIDs...) -} - -func (es *eventStore) RemovePATScopeEntry(ctx context.Context, session authn.Session, patID string, platformEntityType pat.PlatformEntityType, optionalDomainID string, optionalDomainEntityType pat.DomainEntityType, operation pat.OperationType, entityIDs ...string) (pat.Scope, error) { - return es.svc.RemovePATScopeEntry(ctx, session, patID, platformEntityType, optionalDomainID, optionalDomainEntityType, operation, entityIDs...) -} - -func (es *eventStore) ClearPATAllScopeEntry(ctx context.Context, session authn.Session, patID string) error { - return es.svc.ClearPATAllScopeEntry(ctx, session, patID) -} - -func (es *eventStore) IdentifyPAT(ctx context.Context, paToken string) (pat.PAT, error) { - return es.svc.IdentifyPAT(ctx, paToken) -} - -func (es *eventStore) AuthorizePAT(ctx context.Context, paToken string, platformEntityType pat.PlatformEntityType, optionalDomainID string, optionalDomainEntityType pat.DomainEntityType, operation pat.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 pat.PlatformEntityType, optionalDomainID string, optionalDomainEntityType pat.DomainEntityType, operation pat.OperationType, entityIDs ...string) error { - return es.svc.CheckPAT(ctx, userID, patID, platformEntityType, optionalDomainID, optionalDomainEntityType, operation, entityIDs...) -} \ No newline at end of file diff --git a/pat/tracing/tracing.go b/pat/tracing/tracing.go deleted file mode 100644 index 4fab5fae49..0000000000 --- a/pat/tracing/tracing.go +++ /dev/null @@ -1,164 +0,0 @@ -// Copyright (c) Abstract Machines -// SPDX-License-Identifier: Apache-2.0 - -package tracing - -import ( - "context" - "time" - - "github.com/absmach/magistrala/pat" - "github.com/absmach/magistrala/pkg/authn" - "go.opentelemetry.io/otel/attribute" - "go.opentelemetry.io/otel/trace" -) - -var _ pat.Service = (*tracingMiddleware)(nil) - -type tracingMiddleware struct { - tracer trace.Tracer - svc pat.Service -} - -// New returns a new group service with tracing capabilities. -func New(svc pat.Service, tracer trace.Tracer) pat.Service { - return &tracingMiddleware{tracer, svc} -} - -func (tm *tracingMiddleware) CreatePAT(ctx context.Context, session authn.Session, name, description string, duration time.Duration, scope pat.Scope) (pat.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.CreatePAT(ctx, session, name, description, duration, scope) -} - -func (tm *tracingMiddleware) UpdatePATName(ctx context.Context, session authn.Session, patID, name string) (pat.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.UpdatePATName(ctx, session, patID, name) -} - -func (tm *tracingMiddleware) UpdatePATDescription(ctx context.Context, session authn.Session, patID, description string) (pat.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.UpdatePATDescription(ctx, session, patID, description) -} - -func (tm *tracingMiddleware) RetrievePAT(ctx context.Context, session authn.Session, patID string) (pat.PAT, error) { - ctx, span := tm.tracer.Start(ctx, "retrieve_pat", trace.WithAttributes( - attribute.String("pat_id", patID), - )) - defer span.End() - return tm.svc.RetrievePAT(ctx, session, patID) -} - -func (tm *tracingMiddleware) ListPATS(ctx context.Context, session authn.Session, pm pat.PATSPageMeta) (pat.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.ListPATS(ctx, session, pm) -} - -func (tm *tracingMiddleware) DeletePAT(ctx context.Context, session authn.Session, patID string) error { - ctx, span := tm.tracer.Start(ctx, "delete_pat", trace.WithAttributes( - attribute.String("pat_id", patID), - )) - defer span.End() - return tm.svc.DeletePAT(ctx, session, patID) -} - -func (tm *tracingMiddleware) ResetPATSecret(ctx context.Context, session authn.Session, patID string, duration time.Duration) (pat.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.ResetPATSecret(ctx, session, patID, duration) -} - -func (tm *tracingMiddleware) RevokePATSecret(ctx context.Context, session authn.Session, 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.RevokePATSecret(ctx, session, patID) -} - -func (tm *tracingMiddleware) AddPATScopeEntry(ctx context.Context, session authn.Session, patID string, platformEntityType pat.PlatformEntityType, optionalDomainID string, optionalDomainEntityType pat.DomainEntityType, operation pat.OperationType, entityIDs ...string) (pat.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), - attribute.String("optional_domain_entity", optionalDomainEntityType.String()), - attribute.String("operation", operation.String()), - attribute.StringSlice("entities", entityIDs), - )) - defer span.End() - return tm.svc.AddPATScopeEntry(ctx, session, patID, platformEntityType, optionalDomainID, optionalDomainEntityType, operation, entityIDs...) -} - -func (tm *tracingMiddleware) RemovePATScopeEntry(ctx context.Context, session authn.Session, patID string, platformEntityType pat.PlatformEntityType, optionalDomainID string, optionalDomainEntityType pat.DomainEntityType, operation pat.OperationType, entityIDs ...string) (pat.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), - attribute.String("optional_domain_entity", optionalDomainEntityType.String()), - attribute.String("operation", operation.String()), - attribute.StringSlice("entities", entityIDs), - )) - defer span.End() - return tm.svc.RemovePATScopeEntry(ctx, session, patID, platformEntityType, optionalDomainID, optionalDomainEntityType, operation, entityIDs...) -} - -func (tm *tracingMiddleware) ClearPATAllScopeEntry(ctx context.Context, session authn.Session, 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.ClearPATAllScopeEntry(ctx, session, patID) -} - -func (tm *tracingMiddleware) IdentifyPAT(ctx context.Context, paToken string) (pat.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 pat.PlatformEntityType, optionalDomainID string, optionalDomainEntityType pat.DomainEntityType, operation pat.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), - 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, platformEntityType, optionalDomainID, optionalDomainEntityType, operation, entityIDs...) -} - -func (tm *tracingMiddleware) CheckPAT(ctx context.Context, userID, patID string, platformEntityType pat.PlatformEntityType, optionalDomainID string, optionalDomainEntityType pat.DomainEntityType, operation pat.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.CheckPAT(ctx, userID, patID, platformEntityType, optionalDomainID, optionalDomainEntityType, operation, entityIDs...) -} diff --git a/pkg/authn/mocks/authn.go b/pkg/authn/mocks/authn.go index 8512858efe..87c8a60a51 100644 --- a/pkg/authn/mocks/authn.go +++ b/pkg/authn/mocks/authn.go @@ -45,6 +45,34 @@ func (_m *Authentication) Authenticate(ctx context.Context, token string) (authn return r0, r1 } +// AuthenticatePAT provides a mock function with given fields: ctx, token +func (_m *Authentication) AuthenticatePAT(ctx context.Context, token string) (authn.Session, error) { + ret := _m.Called(ctx, token) + + if len(ret) == 0 { + panic("no return value specified for AuthenticatePAT") + } + + var r0 authn.Session + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, string) (authn.Session, error)); ok { + return rf(ctx, token) + } + if rf, ok := ret.Get(0).(func(context.Context, string) authn.Session); ok { + r0 = rf(ctx, token) + } else { + r0 = ret.Get(0).(authn.Session) + } + + if rf, ok := ret.Get(1).(func(context.Context, string) error); ok { + r1 = rf(ctx, token) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + // NewAuthentication creates a new instance of Authentication. 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 NewAuthentication(t interface { From 58b8ae535466743f6e949fb39f5354fe3746362b Mon Sep 17 00:00:00 2001 From: nyagamunene Date: Tue, 3 Dec 2024 13:53:13 +0300 Subject: [PATCH 14/72] fix protoc linter Signed-off-by: nyagamunene --- go.mod | 1 - 1 file changed, 1 deletion(-) diff --git a/go.mod b/go.mod index 072a6485ac..f79cb10647 100644 --- a/go.mod +++ b/go.mod @@ -169,7 +169,6 @@ require ( github.com/xeipuuv/gojsonschema v1.2.0 // indirect github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect go.opentelemetry.io/auto/sdk v1.1.0 // indirect - go.etcd.io/bbolt v1.3.11 // indirect go.opentelemetry.io/otel/metric v1.33.0 // indirect go.opentelemetry.io/proto/otlp v1.4.0 // indirect go.uber.org/atomic v1.11.0 // indirect From 392e752c6e72b1901bb453a059ebfb552c0afe4d Mon Sep 17 00:00:00 2001 From: nyagamunene Date: Tue, 3 Dec 2024 14:14:27 +0300 Subject: [PATCH 15/72] fix indentation Signed-off-by: nyagamunene --- internal/proto/auth/v1/auth.proto | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/proto/auth/v1/auth.proto b/internal/proto/auth/v1/auth.proto index 4c1804c7c8..e5a310aba3 100644 --- a/internal/proto/auth/v1/auth.proto +++ b/internal/proto/auth/v1/auth.proto @@ -17,7 +17,7 @@ service AuthService { message AuthNReq { - string token = 1; + string token = 1; } message AuthNRes { From 08d46f508222c34fb2b812d7bb3abe7c747bd93a Mon Sep 17 00:00:00 2001 From: nyagamunene Date: Tue, 3 Dec 2024 14:33:42 +0300 Subject: [PATCH 16/72] remove unused code Signed-off-by: nyagamunene --- auth/api/grpc/auth/endpoint.go | 2 +- auth/api/grpc/auth/responses.go | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/auth/api/grpc/auth/endpoint.go b/auth/api/grpc/auth/endpoint.go index 9f529e2773..d665f64153 100644 --- a/auth/api/grpc/auth/endpoint.go +++ b/auth/api/grpc/auth/endpoint.go @@ -80,4 +80,4 @@ func authorizePATEndpoint(svc auth.Service) endpoint.Endpoint { } return authorizeRes{authorized: true}, nil } -} \ No newline at end of file +} diff --git a/auth/api/grpc/auth/responses.go b/auth/api/grpc/auth/responses.go index f7a39deb46..dc9ad1cde5 100644 --- a/auth/api/grpc/auth/responses.go +++ b/auth/api/grpc/auth/responses.go @@ -4,9 +4,9 @@ package auth type authenticateRes struct { - id string - userID string - domainID string + id string + userID string + domainID string } type authorizeRes struct { From a6c5b8c0eddc8564241fc6e1df28f408e64ebf63 Mon Sep 17 00:00:00 2001 From: nyagamunene Date: Wed, 4 Dec 2024 15:07:43 +0300 Subject: [PATCH 17/72] fix proto buffer files Signed-off-by: nyagamunene --- api/grpc/auth/v1/auth_grpc.pb.go | 2 +- api/grpc/channels/v1/channels_grpc.pb.go | 2 +- api/grpc/clients/v1/clients_grpc.pb.go | 2 +- api/grpc/domains/v1/domains_grpc.pb.go | 2 +- api/grpc/groups/v1/groups_grpc.pb.go | 2 +- api/grpc/token/v1/token_grpc.pb.go | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/api/grpc/auth/v1/auth_grpc.pb.go b/api/grpc/auth/v1/auth_grpc.pb.go index d85fabae7c..5a45cba4fa 100644 --- a/api/grpc/auth/v1/auth_grpc.pb.go +++ b/api/grpc/auth/v1/auth_grpc.pb.go @@ -4,7 +4,7 @@ // Code generated by protoc-gen-go-grpc. DO NOT EDIT. // versions: // - protoc-gen-go-grpc v1.5.1 -// - protoc v5.29.0 +// - protoc v5.28.3 // source: auth/v1/auth.proto package v1 diff --git a/api/grpc/channels/v1/channels_grpc.pb.go b/api/grpc/channels/v1/channels_grpc.pb.go index 612280b166..70c292e16f 100644 --- a/api/grpc/channels/v1/channels_grpc.pb.go +++ b/api/grpc/channels/v1/channels_grpc.pb.go @@ -4,7 +4,7 @@ // Code generated by protoc-gen-go-grpc. DO NOT EDIT. // versions: // - protoc-gen-go-grpc v1.5.1 -// - protoc v5.29.0 +// - protoc v5.28.3 // source: channels/v1/channels.proto package v1 diff --git a/api/grpc/clients/v1/clients_grpc.pb.go b/api/grpc/clients/v1/clients_grpc.pb.go index bd04a8c47f..814dacd4ec 100644 --- a/api/grpc/clients/v1/clients_grpc.pb.go +++ b/api/grpc/clients/v1/clients_grpc.pb.go @@ -4,7 +4,7 @@ // Code generated by protoc-gen-go-grpc. DO NOT EDIT. // versions: // - protoc-gen-go-grpc v1.5.1 -// - protoc v5.29.0 +// - protoc v5.28.3 // source: clients/v1/clients.proto package v1 diff --git a/api/grpc/domains/v1/domains_grpc.pb.go b/api/grpc/domains/v1/domains_grpc.pb.go index 90b85349f8..ea8b5574e4 100644 --- a/api/grpc/domains/v1/domains_grpc.pb.go +++ b/api/grpc/domains/v1/domains_grpc.pb.go @@ -4,7 +4,7 @@ // Code generated by protoc-gen-go-grpc. DO NOT EDIT. // versions: // - protoc-gen-go-grpc v1.5.1 -// - protoc v5.29.0 +// - protoc v5.28.3 // source: domains/v1/domains.proto package v1 diff --git a/api/grpc/groups/v1/groups_grpc.pb.go b/api/grpc/groups/v1/groups_grpc.pb.go index d362f88c2b..d246f10634 100644 --- a/api/grpc/groups/v1/groups_grpc.pb.go +++ b/api/grpc/groups/v1/groups_grpc.pb.go @@ -4,7 +4,7 @@ // Code generated by protoc-gen-go-grpc. DO NOT EDIT. // versions: // - protoc-gen-go-grpc v1.5.1 -// - protoc v5.29.0 +// - protoc v5.28.3 // source: groups/v1/groups.proto package v1 diff --git a/api/grpc/token/v1/token_grpc.pb.go b/api/grpc/token/v1/token_grpc.pb.go index f3adacfb70..b999fada9b 100644 --- a/api/grpc/token/v1/token_grpc.pb.go +++ b/api/grpc/token/v1/token_grpc.pb.go @@ -4,7 +4,7 @@ // Code generated by protoc-gen-go-grpc. DO NOT EDIT. // versions: // - protoc-gen-go-grpc v1.5.1 -// - protoc v5.29.0 +// - protoc v5.28.3 // source: token/v1/token.proto package v1 From e527c515d066bf8fcced2a4a4c4e83b894f8040f Mon Sep 17 00:00:00 2001 From: nyagamunene Date: Thu, 5 Dec 2024 15:39:21 +0300 Subject: [PATCH 18/72] fix imports Signed-off-by: nyagamunene --- cmd/auth/main.go | 2 -- 1 file changed, 2 deletions(-) diff --git a/cmd/auth/main.go b/cmd/auth/main.go index 17e0d879b2..c5ef6d2b5e 100644 --- a/cmd/auth/main.go +++ b/cmd/auth/main.go @@ -28,8 +28,6 @@ import ( "github.com/absmach/supermq/auth/tracing" boltclient "github.com/absmach/supermq/internal/clients/bolt" smqlog "github.com/absmach/supermq/logger" - "github.com/absmach/supermq/auth/bolt" - "github.com/absmach/supermq/auth/hasher" "github.com/absmach/supermq/pkg/jaeger" "github.com/absmach/supermq/pkg/policies/spicedb" pgclient "github.com/absmach/supermq/pkg/postgres" From c52275c040c91dad194a675f2e5b9b2056e5af35 Mon Sep 17 00:00:00 2001 From: nyagamunene Date: Thu, 5 Dec 2024 15:42:18 +0300 Subject: [PATCH 19/72] update protoc files Signed-off-by: nyagamunene --- api/grpc/auth/v1/auth_grpc.pb.go | 2 +- api/grpc/channels/v1/channels_grpc.pb.go | 2 +- api/grpc/clients/v1/clients_grpc.pb.go | 2 +- api/grpc/domains/v1/domains_grpc.pb.go | 2 +- api/grpc/groups/v1/groups_grpc.pb.go | 2 +- api/grpc/token/v1/token_grpc.pb.go | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/api/grpc/auth/v1/auth_grpc.pb.go b/api/grpc/auth/v1/auth_grpc.pb.go index 5a45cba4fa..d85fabae7c 100644 --- a/api/grpc/auth/v1/auth_grpc.pb.go +++ b/api/grpc/auth/v1/auth_grpc.pb.go @@ -4,7 +4,7 @@ // Code generated by protoc-gen-go-grpc. DO NOT EDIT. // versions: // - protoc-gen-go-grpc v1.5.1 -// - protoc v5.28.3 +// - protoc v5.29.0 // source: auth/v1/auth.proto package v1 diff --git a/api/grpc/channels/v1/channels_grpc.pb.go b/api/grpc/channels/v1/channels_grpc.pb.go index 70c292e16f..612280b166 100644 --- a/api/grpc/channels/v1/channels_grpc.pb.go +++ b/api/grpc/channels/v1/channels_grpc.pb.go @@ -4,7 +4,7 @@ // Code generated by protoc-gen-go-grpc. DO NOT EDIT. // versions: // - protoc-gen-go-grpc v1.5.1 -// - protoc v5.28.3 +// - protoc v5.29.0 // source: channels/v1/channels.proto package v1 diff --git a/api/grpc/clients/v1/clients_grpc.pb.go b/api/grpc/clients/v1/clients_grpc.pb.go index 814dacd4ec..bd04a8c47f 100644 --- a/api/grpc/clients/v1/clients_grpc.pb.go +++ b/api/grpc/clients/v1/clients_grpc.pb.go @@ -4,7 +4,7 @@ // Code generated by protoc-gen-go-grpc. DO NOT EDIT. // versions: // - protoc-gen-go-grpc v1.5.1 -// - protoc v5.28.3 +// - protoc v5.29.0 // source: clients/v1/clients.proto package v1 diff --git a/api/grpc/domains/v1/domains_grpc.pb.go b/api/grpc/domains/v1/domains_grpc.pb.go index ea8b5574e4..90b85349f8 100644 --- a/api/grpc/domains/v1/domains_grpc.pb.go +++ b/api/grpc/domains/v1/domains_grpc.pb.go @@ -4,7 +4,7 @@ // Code generated by protoc-gen-go-grpc. DO NOT EDIT. // versions: // - protoc-gen-go-grpc v1.5.1 -// - protoc v5.28.3 +// - protoc v5.29.0 // source: domains/v1/domains.proto package v1 diff --git a/api/grpc/groups/v1/groups_grpc.pb.go b/api/grpc/groups/v1/groups_grpc.pb.go index d246f10634..d362f88c2b 100644 --- a/api/grpc/groups/v1/groups_grpc.pb.go +++ b/api/grpc/groups/v1/groups_grpc.pb.go @@ -4,7 +4,7 @@ // Code generated by protoc-gen-go-grpc. DO NOT EDIT. // versions: // - protoc-gen-go-grpc v1.5.1 -// - protoc v5.28.3 +// - protoc v5.29.0 // source: groups/v1/groups.proto package v1 diff --git a/api/grpc/token/v1/token_grpc.pb.go b/api/grpc/token/v1/token_grpc.pb.go index b999fada9b..f3adacfb70 100644 --- a/api/grpc/token/v1/token_grpc.pb.go +++ b/api/grpc/token/v1/token_grpc.pb.go @@ -4,7 +4,7 @@ // Code generated by protoc-gen-go-grpc. DO NOT EDIT. // versions: // - protoc-gen-go-grpc v1.5.1 -// - protoc v5.28.3 +// - protoc v5.29.0 // source: token/v1/token.proto package v1 From d08ff0511da4687943f177574dd9e178ee00403e Mon Sep 17 00:00:00 2001 From: nyagamunene Date: Thu, 5 Dec 2024 15:59:52 +0300 Subject: [PATCH 20/72] fix auth test Signed-off-by: nyagamunene --- auth/api/grpc/auth/endpoint.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/auth/api/grpc/auth/endpoint.go b/auth/api/grpc/auth/endpoint.go index d665f64153..05516b64e6 100644 --- a/auth/api/grpc/auth/endpoint.go +++ b/auth/api/grpc/auth/endpoint.go @@ -23,7 +23,7 @@ func authenticateEndpoint(svc auth.Service) endpoint.Endpoint { return authenticateRes{}, err } - return authenticateRes{id: key.ID, userID: key.User, domainID: key.Domain}, nil + return authenticateRes{id: key.Subject, userID: key.User, domainID: key.Domain}, nil } } From bf9f1135291585ebffea01c199222dc1e111f82d Mon Sep 17 00:00:00 2001 From: nyagamunene Date: Thu, 5 Dec 2024 16:15:01 +0300 Subject: [PATCH 21/72] fix nginx Signed-off-by: nyagamunene --- docker/nginx/entrypoint.sh | 1 - docker/nginx/nginx-key.conf | 7 ------- 2 files changed, 8 deletions(-) diff --git a/docker/nginx/entrypoint.sh b/docker/nginx/entrypoint.sh index 4952421d0f..c1a3e0a6bf 100755 --- a/docker/nginx/entrypoint.sh +++ b/docker/nginx/entrypoint.sh @@ -14,7 +14,6 @@ fi envsubst ' ${SMQ_NGINX_SERVER_NAME} ${SMQ_DOMAINS_HTTP_PORT} - ${SMQ_AUTH_HTTP_PORT} ${SMQ_GROUPS_HTTP_PORT} ${SMQ_USERS_HTTP_PORT} ${SMQ_CLIENTS_HTTP_PORT} diff --git a/docker/nginx/nginx-key.conf b/docker/nginx/nginx-key.conf index db8a5aaad0..9779466dc2 100644 --- a/docker/nginx/nginx-key.conf +++ b/docker/nginx/nginx-key.conf @@ -64,13 +64,6 @@ http { proxy_pass http://domains:${SMQ_DOMAINS_HTTP_PORT}; } - # Proxy pass to auth service - location ~ ^/(domains|keys|pats) { - include snippets/proxy-headers.conf; - add_header Access-Control-Expose-Headers Location; - proxy_pass http://auth:${MG_AUTH_HTTP_PORT}; - } - # Proxy pass to users service location ~ ^/(users|password|authorize|oauth/callback/[^/]+) { include snippets/proxy-headers.conf; From 7aa73c2064088224e7de1b478aafbe5e1422fd29 Mon Sep 17 00:00:00 2001 From: nyagamunene Date: Fri, 6 Dec 2024 16:36:12 +0300 Subject: [PATCH 22/72] add check for token type Signed-off-by: nyagamunene --- api/http/common.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/api/http/common.go b/api/http/common.go index 36210d4b5e..b0e04aa635 100644 --- a/api/http/common.go +++ b/api/http/common.go @@ -128,7 +128,8 @@ func EncodeError(_ context.Context, err error, w http.ResponseWriter) { case errors.Contains(err, svcerr.ErrAuthorization), errors.Contains(err, svcerr.ErrDomainAuthorization), errors.Contains(err, bootstrap.ErrExternalKey), - errors.Contains(err, bootstrap.ErrExternalKeySecure): + errors.Contains(err, bootstrap.ErrExternalKeySecure), + errors.Contains(err, apiutil.ErrUnsupportedTokenType): err = unwrap(err) w.WriteHeader(http.StatusForbidden) From 7aeab477e4095de55d1da3ef03cee1df6f6226e8 Mon Sep 17 00:00:00 2001 From: nyagamunene Date: Fri, 6 Dec 2024 17:25:57 +0300 Subject: [PATCH 23/72] refactor keys endpoints Signed-off-by: nyagamunene --- auth/api/http/keys/transport.go | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/auth/api/http/keys/transport.go b/auth/api/http/keys/transport.go index 30e3e11dee..9a17849e44 100644 --- a/auth/api/http/keys/transport.go +++ b/auth/api/http/keys/transport.go @@ -18,7 +18,10 @@ import ( kithttp "github.com/go-kit/kit/transport/http" ) -const contentType = "application/json" +const ( + contentType = "application/json" + patPrefix = "pat_" +) // MakeHandler returns a HTTP handler for API endpoints. func MakeHandler(svc auth.Service, mux *chi.Mux, logger *slog.Logger) *chi.Mux { @@ -55,7 +58,12 @@ func decodeIssue(_ context.Context, r *http.Request) (interface{}, error) { return nil, apiutil.ErrUnsupportedContentType } - req := issueKeyReq{token: apiutil.ExtractBearerToken(r)} + token := apiutil.ExtractBearerToken(r) + if strings.HasPrefix(token, patPrefix) { + return nil, apiutil.ErrUnsupportedTokenType + } + + req := issueKeyReq{token: token} if err := json.NewDecoder(r.Body).Decode(&req); err != nil { return nil, errors.Wrap(errors.ErrMalformedEntity, err) } @@ -64,8 +72,13 @@ func decodeIssue(_ context.Context, r *http.Request) (interface{}, error) { } func decodeKeyReq(_ context.Context, r *http.Request) (interface{}, error) { + token := apiutil.ExtractBearerToken(r) + if strings.HasPrefix(token, patPrefix) { + return nil, apiutil.ErrUnsupportedTokenType + } + req := keyReq{ - token: apiutil.ExtractBearerToken(r), + token: token, id: chi.URLParam(r, "id"), } return req, nil From 860687c59927609cc9051c8620e47e86bc8f14a8 Mon Sep 17 00:00:00 2001 From: nyagamunene Date: Wed, 11 Dec 2024 20:18:44 +0300 Subject: [PATCH 24/72] address comments Signed-off-by: nyagamunene --- api/http/common.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/api/http/common.go b/api/http/common.go index b0e04aa635..36210d4b5e 100644 --- a/api/http/common.go +++ b/api/http/common.go @@ -128,8 +128,7 @@ func EncodeError(_ context.Context, err error, w http.ResponseWriter) { case errors.Contains(err, svcerr.ErrAuthorization), errors.Contains(err, svcerr.ErrDomainAuthorization), errors.Contains(err, bootstrap.ErrExternalKey), - errors.Contains(err, bootstrap.ErrExternalKeySecure), - errors.Contains(err, apiutil.ErrUnsupportedTokenType): + errors.Contains(err, bootstrap.ErrExternalKeySecure): err = unwrap(err) w.WriteHeader(http.StatusForbidden) From cecc2bf215fc4d2393a82c53270c66b63c4c0005 Mon Sep 17 00:00:00 2001 From: nyagamunene Date: Tue, 17 Dec 2024 13:26:09 +0300 Subject: [PATCH 25/72] remove pat check in keys api Signed-off-by: nyagamunene --- auth/api/http/keys/transport.go | 14 ++------------ 1 file changed, 2 insertions(+), 12 deletions(-) diff --git a/auth/api/http/keys/transport.go b/auth/api/http/keys/transport.go index 9a17849e44..7518f15c52 100644 --- a/auth/api/http/keys/transport.go +++ b/auth/api/http/keys/transport.go @@ -58,12 +58,7 @@ func decodeIssue(_ context.Context, r *http.Request) (interface{}, error) { return nil, apiutil.ErrUnsupportedContentType } - token := apiutil.ExtractBearerToken(r) - if strings.HasPrefix(token, patPrefix) { - return nil, apiutil.ErrUnsupportedTokenType - } - - req := issueKeyReq{token: token} + req := issueKeyReq{token: apiutil.ExtractBearerToken(r)} if err := json.NewDecoder(r.Body).Decode(&req); err != nil { return nil, errors.Wrap(errors.ErrMalformedEntity, err) } @@ -72,13 +67,8 @@ func decodeIssue(_ context.Context, r *http.Request) (interface{}, error) { } func decodeKeyReq(_ context.Context, r *http.Request) (interface{}, error) { - token := apiutil.ExtractBearerToken(r) - if strings.HasPrefix(token, patPrefix) { - return nil, apiutil.ErrUnsupportedTokenType - } - req := keyReq{ - token: token, + token: apiutil.ExtractBearerToken(r), id: chi.URLParam(r, "id"), } return req, nil From 8c519508e76bd45cbc50684b3446a94e165293a9 Mon Sep 17 00:00:00 2001 From: nyagamunene Date: Tue, 17 Dec 2024 14:59:20 +0300 Subject: [PATCH 26/72] revert pat changes and update mocks Signed-off-by: nyagamunene --- auth/api/http/keys/transport.go | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/auth/api/http/keys/transport.go b/auth/api/http/keys/transport.go index 7518f15c52..30e3e11dee 100644 --- a/auth/api/http/keys/transport.go +++ b/auth/api/http/keys/transport.go @@ -18,10 +18,7 @@ import ( kithttp "github.com/go-kit/kit/transport/http" ) -const ( - contentType = "application/json" - patPrefix = "pat_" -) +const contentType = "application/json" // MakeHandler returns a HTTP handler for API endpoints. func MakeHandler(svc auth.Service, mux *chi.Mux, logger *slog.Logger) *chi.Mux { From 693d8ef2b44f54403d725cc76c139b2eb53e636b Mon Sep 17 00:00:00 2001 From: nyagamunene Date: Tue, 17 Dec 2024 15:43:25 +0300 Subject: [PATCH 27/72] address comments Signed-off-by: nyagamunene --- api/grpc/auth/v1/auth.pb.go | 69 ++++--------------------------- auth/service.go | 15 ------- internal/proto/auth/v1/auth.proto | 5 --- pkg/authn/authn.go | 1 - pkg/authn/mocks/authn.go | 28 ------------- 5 files changed, 8 insertions(+), 110 deletions(-) diff --git a/api/grpc/auth/v1/auth.pb.go b/api/grpc/auth/v1/auth.pb.go index a3cc52f11d..1ee360a95f 100644 --- a/api/grpc/auth/v1/auth.pb.go +++ b/api/grpc/auth/v1/auth.pb.go @@ -127,59 +127,6 @@ func (x *AuthNRes) GetDomainId() string { return "" } -type AuthNPATRes struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` // pat id - UserId string `protobuf:"bytes,2,opt,name=user_id,json=userId,proto3" json:"user_id,omitempty"` // user id -} - -func (x *AuthNPATRes) Reset() { - *x = AuthNPATRes{} - mi := &file_auth_v1_auth_proto_msgTypes[2] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *AuthNPATRes) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*AuthNPATRes) ProtoMessage() {} - -func (x *AuthNPATRes) ProtoReflect() protoreflect.Message { - mi := &file_auth_v1_auth_proto_msgTypes[2] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use AuthNPATRes.ProtoReflect.Descriptor instead. -func (*AuthNPATRes) Descriptor() ([]byte, []int) { - return file_auth_v1_auth_proto_rawDescGZIP(), []int{2} -} - -func (x *AuthNPATRes) GetId() string { - if x != nil { - return x.Id - } - return "" -} - -func (x *AuthNPATRes) GetUserId() string { - if x != nil { - return x.UserId - } - return "" -} - type AuthZReq struct { state protoimpl.MessageState `protogen:"open.v1"` Domain string `protobuf:"bytes,1,opt,name=domain,proto3" json:"domain,omitempty"` // Domain @@ -197,7 +144,7 @@ type AuthZReq struct { func (x *AuthZReq) Reset() { *x = AuthZReq{} - mi := &file_auth_v1_auth_proto_msgTypes[3] + mi := &file_auth_v1_auth_proto_msgTypes[2] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -209,7 +156,7 @@ func (x *AuthZReq) String() string { func (*AuthZReq) ProtoMessage() {} func (x *AuthZReq) ProtoReflect() protoreflect.Message { - mi := &file_auth_v1_auth_proto_msgTypes[3] + mi := &file_auth_v1_auth_proto_msgTypes[2] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -222,7 +169,7 @@ func (x *AuthZReq) ProtoReflect() protoreflect.Message { // Deprecated: Use AuthZReq.ProtoReflect.Descriptor instead. func (*AuthZReq) Descriptor() ([]byte, []int) { - return file_auth_v1_auth_proto_rawDescGZIP(), []int{3} + return file_auth_v1_auth_proto_rawDescGZIP(), []int{2} } func (x *AuthZReq) GetDomain() string { @@ -390,7 +337,7 @@ type AuthZRes struct { func (x *AuthZRes) Reset() { *x = AuthZRes{} - mi := &file_auth_v1_auth_proto_msgTypes[5] + mi := &file_auth_v1_auth_proto_msgTypes[4] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -402,7 +349,7 @@ func (x *AuthZRes) String() string { func (*AuthZRes) ProtoMessage() {} func (x *AuthZRes) ProtoReflect() protoreflect.Message { - mi := &file_auth_v1_auth_proto_msgTypes[5] + mi := &file_auth_v1_auth_proto_msgTypes[4] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -415,7 +362,7 @@ func (x *AuthZRes) ProtoReflect() protoreflect.Message { // Deprecated: Use AuthZRes.ProtoReflect.Descriptor instead. func (*AuthZRes) Descriptor() ([]byte, []int) { - return file_auth_v1_auth_proto_rawDescGZIP(), []int{5} + return file_auth_v1_auth_proto_rawDescGZIP(), []int{4} } func (x *AuthZRes) GetAuthorized() bool { @@ -517,7 +464,7 @@ func file_auth_v1_auth_proto_rawDescGZIP() []byte { return file_auth_v1_auth_proto_rawDescData } -var file_auth_v1_auth_proto_msgTypes = make([]protoimpl.MessageInfo, 6) +var file_auth_v1_auth_proto_msgTypes = make([]protoimpl.MessageInfo, 5) var file_auth_v1_auth_proto_goTypes = []any{ (*AuthNReq)(nil), // 0: auth.v1.AuthNReq (*AuthNRes)(nil), // 1: auth.v1.AuthNRes @@ -552,7 +499,7 @@ func file_auth_v1_auth_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_auth_v1_auth_proto_rawDesc, NumEnums: 0, - NumMessages: 6, + NumMessages: 5, NumExtensions: 0, NumServices: 1, }, diff --git a/auth/service.go b/auth/service.go index c266ed2fa7..579d8466fd 100644 --- a/auth/service.go +++ b/auth/service.go @@ -167,21 +167,6 @@ func (svc service) RetrieveKey(ctx context.Context, token, id string) (Key, erro } func (svc service) Identify(ctx context.Context, token string) (Key, error) { - if strings.HasPrefix(token, "pat"+"_") { - pat, err := svc.IdentifyPAT(ctx, token) - if err != nil { - return Key{}, err - } - return Key{ - ID: pat.ID, - Type: PersonalAccessToken, - Subject: pat.User, - User: pat.User, - IssuedAt: pat.IssuedAt, - ExpiresAt: pat.ExpiresAt, - }, nil - } - key, err := svc.tokenizer.Parse(token) if errors.Contains(err, ErrExpiry) { err = svc.keys.Remove(ctx, key.Issuer, key.ID) diff --git a/internal/proto/auth/v1/auth.proto b/internal/proto/auth/v1/auth.proto index e5a310aba3..7d28932d57 100644 --- a/internal/proto/auth/v1/auth.proto +++ b/internal/proto/auth/v1/auth.proto @@ -26,11 +26,6 @@ message AuthNRes { string domain_id = 3; // domain id } -message AuthNPATRes { - string id = 1; // pat id - string user_id = 2; // user id -} - message AuthZReq { string domain = 1; // Domain string subject_type = 2; // Client or User diff --git a/pkg/authn/authn.go b/pkg/authn/authn.go index 30435f9926..5bfe92cd0b 100644 --- a/pkg/authn/authn.go +++ b/pkg/authn/authn.go @@ -41,5 +41,4 @@ type Session struct { //go:generate mockery --name Authentication --output=./mocks --filename authn.go --quiet --note "Copyright (c) Abstract Machines" type Authentication interface { Authenticate(ctx context.Context, token string) (Session, error) - AuthenticatePAT(ctx context.Context, token string) (Session, error) } diff --git a/pkg/authn/mocks/authn.go b/pkg/authn/mocks/authn.go index 87c8a60a51..8512858efe 100644 --- a/pkg/authn/mocks/authn.go +++ b/pkg/authn/mocks/authn.go @@ -45,34 +45,6 @@ func (_m *Authentication) Authenticate(ctx context.Context, token string) (authn return r0, r1 } -// AuthenticatePAT provides a mock function with given fields: ctx, token -func (_m *Authentication) AuthenticatePAT(ctx context.Context, token string) (authn.Session, error) { - ret := _m.Called(ctx, token) - - if len(ret) == 0 { - panic("no return value specified for AuthenticatePAT") - } - - var r0 authn.Session - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, string) (authn.Session, error)); ok { - return rf(ctx, token) - } - if rf, ok := ret.Get(0).(func(context.Context, string) authn.Session); ok { - r0 = rf(ctx, token) - } else { - r0 = ret.Get(0).(authn.Session) - } - - if rf, ok := ret.Get(1).(func(context.Context, string) error); ok { - r1 = rf(ctx, token) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - // NewAuthentication creates a new instance of Authentication. 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 NewAuthentication(t interface { From ffbcd3346eda630b39ed2bfbdfa5f22a94a987d1 Mon Sep 17 00:00:00 2001 From: nyagamunene Date: Mon, 28 Oct 2024 15:09:07 +0300 Subject: [PATCH 28/72] First implementation Signed-off-by: nyagamunene --- go.mod | 1 + pat/bolt/doc.go | 6 + pat/bolt/init.go | 21 + pat/bolt/pat.go | 773 ++++++++++++++++++++++++++++++++ pat/hasher.go | 17 + pat/hasher/doc.go | 6 + pat/hasher/hasher.go | 86 ++++ pat/middleware/authorization.go | 4 + pat/middleware/logging.go | 275 ++++++++++++ pat/middleware/metrics.go | 4 + pat/pat.go | 752 +++++++++++++++++++++++++++++++ pat/service.go | 302 +++++++++++++ 12 files changed, 2247 insertions(+) create mode 100644 pat/bolt/doc.go create mode 100644 pat/bolt/init.go create mode 100644 pat/bolt/pat.go create mode 100644 pat/hasher.go create mode 100644 pat/hasher/doc.go create mode 100644 pat/hasher/hasher.go create mode 100644 pat/middleware/authorization.go create mode 100644 pat/middleware/logging.go create mode 100644 pat/middleware/metrics.go create mode 100644 pat/pat.go create mode 100644 pat/service.go diff --git a/go.mod b/go.mod index f79cb10647..072a6485ac 100644 --- a/go.mod +++ b/go.mod @@ -169,6 +169,7 @@ require ( github.com/xeipuuv/gojsonschema v1.2.0 // indirect github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect go.opentelemetry.io/auto/sdk v1.1.0 // indirect + go.etcd.io/bbolt v1.3.11 // indirect go.opentelemetry.io/otel/metric v1.33.0 // indirect go.opentelemetry.io/proto/otlp v1.4.0 // indirect go.uber.org/atomic v1.11.0 // indirect diff --git a/pat/bolt/doc.go b/pat/bolt/doc.go new file mode 100644 index 0000000000..dcd06ac566 --- /dev/null +++ b/pat/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/pat/bolt/init.go b/pat/bolt/init.go new file mode 100644 index 0000000000..9d496e65ca --- /dev/null +++ b/pat/bolt/init.go @@ -0,0 +1,21 @@ +// Copyright (c) Abstract Machines +// SPDX-License-Identifier: Apache-2.0 + +// 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/pat/bolt/pat.go b/pat/bolt/pat.go new file mode 100644 index 0000000000..4534dc4e85 --- /dev/null +++ b/pat/bolt/pat.go @@ -0,0 +1,773 @@ +// Copyright (c) Abstract Machines +// SPDX-License-Identifier: Apache-2.0 + +package bolt + +import ( + "bytes" + "context" + "encoding/binary" + "fmt" + "strings" + "time" + + "github.com/absmach/magistrala/auth" + "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" + secretKey = "secret_key" + scopeKey = "scope" + issuedAtKey = "issued_at" + expiresAtKey = "expires_at" + updatedAtKey = "updated_at" + lastUsedAtKey = "last_used_at" + revokedKey = "revoked" + revokedAtKey = "revoked_at" + platformEntitiesKey = "platform_entities" + patKey = "pat" + + keySeparator = ":" + anyID = "*" +) + +var ( + activateValue = []byte{0x00} + revokedValue = []byte{0x01} + entityValue = []byte{0x02} + anyIDValue = []byte{0x03} + selectedIDsValue = []byte{0x04} +) + +type patRepo struct { + db *bolt.DB + bucketName string +} + +// NewPATSRepository instantiates a bolt +// implementation of PAT repository. +func NewPATSRepository(db *bolt.DB, bucketName string) auth.PATSRepository { + return &patRepo{ + db: db, + bucketName: bucketName, + } +} + +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 + } + return pr.db.Update(func(tx *bolt.Tx) error { + 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) + } + 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) + } + } + if err := rootBucket.Put(idxKey, []byte(pat.ID)); err != nil { + return errors.Wrap(repoerr.ErrCreateEntity, err) + } + return nil + }) +} + +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.View(func(tx *bolt.Tx) error { + b, err := pr.retrieveUserBucket(tx, userID, patID, repoerr.ErrViewEntity) + 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 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)) +} + +func (pr *patRepo) UpdateDescription(ctx context.Context, userID, patID, description string) (auth.PAT, error) { + 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) { + 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+secretKey), []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) + } + 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) RetrieveAll(ctx context.Context, userID string, pm auth.PATSPageMeta) (auth.PATSPage, error) { + prefix := []byte(userID + keySeparator + patKey + keySeparator) + + patIDs := []string{} + if err := pr.db.View(func(tx *bolt.Tx) error { + b, err := pr.retrieveRootBucket(tx) + if err != nil { + 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() { + if v != nil { + patIDs = append(patIDs, string(v)) + } + } + return nil + }); err != nil { + return auth.PATSPage{}, err + } + + total := len(patIDs) + + var pats []auth.PAT + + 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 < pm.Offset+aLimit; i++ { + if int(i) < total { + 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 { + 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), 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) 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, patID, repoerr.ErrRemoveEntity) + if err != nil { + return 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) + } + } + 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 + } + + 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) { + 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, patID, repoerr.ErrCreateEntity) + if err != nil { + return 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) + } + } + 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 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, patID, repoerr.ErrRemoveEntity) + if err != nil { + return 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) + } + } + 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 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 { + return pr.db.Update(func(tx *bolt.Tx) error { + b, err := pr.retrieveUserBucket(tx, userID, patID, repoerr.ErrViewEntity) + 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) 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 { + 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, patID string, wrap error) (*bolt.Bucket, error) { + rootBucket, err := pr.retrieveRootBucket(tx) + if err != nil { + 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, errors.Wrap(wrap, fmt.Errorf("user %s not found", userID)) + } + 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 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), + secretKey: []byte(pat.Secret), + 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 { + 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 { + 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 { + 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 keyValueToBasicPAT(kv map[string][]byte) auth.PAT { + 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 +} + +func keyValueToPAT(kv map[string][]byte) (auth.PAT, error) { + pat := keyValueToBasicPAT(kv) + 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) { + 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: + scope.Users, err = parseOperation(platformEntityType, scope.Users, key, keyParts, value) + if err != nil { + return auth.Scope{}, errors.Wrap(repoerr.ErrViewEntity, err) + } + + 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(): + 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] = domainScope + default: + 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 == nil { + opScope = make(map[auth.OperationType]auth.ScopeValue) + } + + 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[opType]; !oValueExists { + opScope[opType] = &auth.SelectedIDs{} + } + 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[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[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[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[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[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 timeToBytes(t time.Time) []byte { + timeBytes := make([]byte, 8) + binary.BigEndian.PutUint64(timeBytes, uint64(t.Unix())) + return timeBytes +} + +func bytesToTime(b []byte) time.Time { + timeAtSeconds := binary.BigEndian.Uint64(b) + return time.Unix(int64(timeAtSeconds), 0) +} + +func booleanToBytes(b bool) []byte { + if b { + return []byte{1} + } + return []byte{0} +} + +func bytesToBoolean(b []byte) bool { + if len(b) > 1 || b[0] != activateValue[0] { + return true + } + return false +} diff --git a/pat/hasher.go b/pat/hasher.go new file mode 100644 index 0000000000..b0452c1e1d --- /dev/null +++ b/pat/hasher.go @@ -0,0 +1,17 @@ +// Copyright (c) Abstract Machines +// SPDX-License-Identifier: Apache-2.0 + +package pat + +// 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/pat/hasher/doc.go b/pat/hasher/doc.go new file mode 100644 index 0000000000..98be992262 --- /dev/null +++ b/pat/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/pat/hasher/hasher.go b/pat/hasher/hasher.go new file mode 100644 index 0000000000..c417bf7b80 --- /dev/null +++ b/pat/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/pat/middleware/authorization.go b/pat/middleware/authorization.go new file mode 100644 index 0000000000..71995a25ab --- /dev/null +++ b/pat/middleware/authorization.go @@ -0,0 +1,4 @@ +// Copyright (c) Abstract Machines +// SPDX-License-Identifier: Apache-2.0 + +package middleware \ No newline at end of file diff --git a/pat/middleware/logging.go b/pat/middleware/logging.go new file mode 100644 index 0000000000..650eab5115 --- /dev/null +++ b/pat/middleware/logging.go @@ -0,0 +1,275 @@ +// Copyright (c) Abstract Machines +// SPDX-License-Identifier: Apache-2.0 + +package middleware + +import ( + "context" + "fmt" + "log/slog" + "time" + + "github.com/absmach/magistrala/pat" +) + +var _ pat.Service = (*loggingMiddleware)(nil) + +type loggingMiddleware struct { + logger *slog.Logger + svc pat.Service +} + +// LoggingMiddleware adds logging facilities to the core service. +func LoggingMiddleware(svc pat.Service, logger *slog.Logger) pat.Service { + return &loggingMiddleware{logger, svc} +} + +func (lm *loggingMiddleware) CreatePAT(ctx context.Context, token, name, description string, duration time.Duration, scope pat.Scope) (pa pat.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.CreatePAT(ctx, token, name, description, duration, scope) +} + +func (lm *loggingMiddleware) UpdatePATName(ctx context.Context, token, patID, name string) (pa pat.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.UpdatePATName(ctx, token, patID, name) +} + +func (lm *loggingMiddleware) UpdatePATDescription(ctx context.Context, token, patID, description string) (pa pat.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.UpdatePATDescription(ctx, token, patID, description) +} + +func (lm *loggingMiddleware) RetrievePAT(ctx context.Context, token, patID string) (pa pat.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.RetrievePAT(ctx, token, patID) +} + +func (lm *loggingMiddleware) ListPATS(ctx context.Context, token string, pm pat.PATSPageMeta) (pp pat.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.ListPATS(ctx, token, pm) +} + +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()), + 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.DeletePAT(ctx, token, patID) +} + +func (lm *loggingMiddleware) ResetPATSecret(ctx context.Context, token, patID string, duration time.Duration) (pa pat.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.ResetPATSecret(ctx, token, patID, duration) +} + +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()), + 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.RevokePATSecret(ctx, token, patID) +} + +func (lm *loggingMiddleware) AddPATScopeEntry(ctx context.Context, token, patID string, platformEntityType pat.PlatformEntityType, optionalDomainID string, optionalDomainEntityType pat.DomainEntityType, operation pat.OperationType, entityIDs ...string) (sc pat.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.AddPATScopeEntry(ctx, token, patID, platformEntityType, optionalDomainID, optionalDomainEntityType, operation, entityIDs...) +} + +func (lm *loggingMiddleware) RemovePATScopeEntry(ctx context.Context, token, patID string, platformEntityType pat.PlatformEntityType, optionalDomainID string, optionalDomainEntityType pat.DomainEntityType, operation pat.OperationType, entityIDs ...string) (sc pat.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.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{ + 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.ClearPATAllScopeEntry(ctx, token, patID) +} + +func (lm *loggingMiddleware) IdentifyPAT(ctx context.Context, paToken string) (pa pat.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, platformEntityType pat.PlatformEntityType, optionalDomainID string, optionalDomainEntityType pat.DomainEntityType, operation pat.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("Authorize PAT failed complete successfully", args...) + return + } + lm.logger.Info("Authorize PAT completed successfully", args...) + }(time.Now()) + return lm.svc.AuthorizePAT(ctx, paToken, platformEntityType, optionalDomainID, optionalDomainEntityType, operation, entityIDs...) +} + +func (lm *loggingMiddleware) CheckPAT(ctx context.Context, userID, patID string, platformEntityType pat.PlatformEntityType, optionalDomainID string, optionalDomainEntityType pat.DomainEntityType, operation pat.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("Check PAT failed complete successfully", args...) + return + } + lm.logger.Info("Check PAT completed successfully", args...) + }(time.Now()) + return lm.svc.CheckPAT(ctx, userID, patID, platformEntityType, optionalDomainID, optionalDomainEntityType, operation, entityIDs...) +} \ No newline at end of file diff --git a/pat/middleware/metrics.go b/pat/middleware/metrics.go new file mode 100644 index 0000000000..b41a88a838 --- /dev/null +++ b/pat/middleware/metrics.go @@ -0,0 +1,4 @@ +// Copyright (c) Abstract Machines +// SPDX-License-Identifier: Apache-2.0 + +package middleware diff --git a/pat/pat.go b/pat/pat.go new file mode 100644 index 0000000000..8e427b4332 --- /dev/null +++ b/pat/pat.go @@ -0,0 +1,752 @@ +// Copyright (c) Abstract Machines +// SPDX-License-Identifier: Apache-2.0 + +package pat + +import ( + "context" + "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. +type OperationType uint32 + +const ( + CreateOp OperationType = iota + ReadOp + ListOp + UpdateOp + DeleteOp +) + +const ( + createOpStr = "create" + readOpStr = "read" + listOpStr = "list" + updateOpStr = "update" + deleteOpStr = "delete" +) + +func (ot OperationType) String() string { + switch ot { + case CreateOp: + return createOpStr + case ReadOp: + return readOpStr + case ListOp: + return listOpStr + case UpdateOp: + return updateOpStr + case DeleteOp: + return deleteOpStr + default: + return fmt.Sprintf("unknown operation type %d", ot) + } +} + +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: + return CreateOp, nil + case readOpStr: + return ReadOp, nil + case listOpStr: + return ListOp, nil + case updateOpStr: + return UpdateOp, nil + case deleteOpStr: + return DeleteOp, nil + default: + return 0, fmt.Errorf("unknown operation type %s", 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 +} + +func (ot *OperationType) UnmarshalText(data []byte) (err error) { + *ot, err = ParseOperationType(string(data)) + return err +} + +// Define DomainEntityType. +type DomainEntityType uint32 + +const ( + DomainManagementScope DomainEntityType = iota + DomainGroupsScope + DomainChannelsScope + DomainThingsScope + DomainNullScope +) + +const ( + domainManagementScopeStr = "domain_management" + domainGroupsScopeStr = "groups" + domainChannelsScopeStr = "channels" + domainThingsScopeStr = "things" +) + +func (det DomainEntityType) String() string { + switch det { + case DomainManagementScope: + return domainManagementScopeStr + case DomainGroupsScope: + return domainGroupsScopeStr + case DomainChannelsScope: + return domainChannelsScopeStr + case DomainThingsScope: + return domainThingsScopeStr + default: + return fmt.Sprintf("unknown domain entity type %d", det) + } +} + +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: + return DomainManagementScope, nil + case domainGroupsScopeStr: + return DomainGroupsScope, nil + case domainChannelsScopeStr: + return DomainChannelsScope, nil + case domainThingsScopeStr: + 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() ([]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 + +const ( + PlatformUsersScope PlatformEntityType = iota + PlatformDomainsScope +) + +const ( + platformUsersScopeStr = "users" + platformDomainsScopeStr = "domains" +) + +func (pet PlatformEntityType) String() string { + switch pet { + case PlatformUsersScope: + return platformUsersScopeStr + case PlatformDomainsScope: + return platformDomainsScopeStr + default: + return fmt.Sprintf("unknown platform entity type %d", pet) + } +} + +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: + return PlatformUsersScope, nil + case platformDomainsScopeStr: + 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 +} + +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 + 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) 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{} + +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 +} + +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 map[OperationType]ScopeValue + +func (os *OperationScope) UnmarshalJSON(data []byte) error { + type tempOperationScope map[OperationType]json.RawMessage + + var tempScope tempOperationScope + if err := json.Unmarshal(data, &tempScope); err != nil { + return err + } + // Initialize the Operations map + *os = OperationScope{} + + for opType, rawMessage := range tempScope { + var stringValue string + var stringArrayValue []string + + // Try to unmarshal as string + if err := json.Unmarshal(rawMessage, &stringValue); err == nil { + if err := os.Add(opType, stringValue); err != nil { + return err + } + continue + } + + // Try to unmarshal as []string + if err := json.Unmarshal(rawMessage, &stringArrayValue); err == nil { + if err := os.Add(opType, stringArrayValue...); err != nil { + return err + } + continue + } + + // If neither unmarshalling succeeded, return an error + return fmt.Errorf("invalid ScopeValue for OperationType %v", opType) + } + + 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 = &OperationScope{} + } + + if len(entityIDs) == 0 { + return fmt.Errorf("entity ID is missing") + } + switch { + 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) + } + sids[entityID] = struct{}{} + } + value = &sids + } + (*os)[operation] = value + return nil +} + +func (os *OperationScope) Delete(operation OperationType, entityIDs ...string) error { + if os == nil { + return nil + } + + opEntityIDs, exists := (*os)[operation] + if !exists { + return nil + } + + if len(entityIDs) == 0 { + return fmt.Errorf("failed to delete operation %s: entity ID is missing", operation.String()) + } + + switch eIDs := opEntityIDs.(type) { + case *AnyIDs: + if !(len(entityIDs) == 1 && entityIDs[0] == "*") { + return fmt.Errorf("failed to delete operation %s: invalid list", operation.String()) + } + delete((*os), operation) + return nil + 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((*os), operation) + } + } + return nil + default: + return fmt.Errorf("failed to delete operation: invalid entity id type %d", operation) + } +} + +func (os *OperationScope) Check(operation OperationType, entityIDs ...string) bool { + if os == nil { + return false + } + + if scopeValue, ok := (*os)[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) == 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 + } + if domainEntityType == DomainManagementScope { + return ds.DomainManagement.Check(operation, ids...) + } + os, exists := ds.Entities[domainEntityType] + if !exists { + return false + } + + return os.Check(operation, ids...) +} + +// Example Scope as JSON +// +// { +// "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.Marshal(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"` + Name string `json:"name,omitempty"` + Description string `json:"description,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"` + 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 PATSPageMeta struct { + Offset uint64 `json:"offset"` + Limit uint64 `json:"limit"` +} +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()) + } + return string(str) +} + +// Expired verifies if the key is expired. +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. + 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. + UpdatePATName(ctx context.Context, token, patID, name string) (PAT, error) + + // UpdateDescription function updates the description for the given PAT ID. + UpdatePATDescription(ctx context.Context, token, patID, description string) (PAT, error) + + // Retrieve function retrieves the PAT for given ID. + RetrievePAT(ctx context.Context, token, patID string) (PAT, error) + + // List function lists all the PATs for the user. + ListPATS(ctx context.Context, token string, pm PATSPageMeta) (PATSPage, error) + + // Delete function deletes the PAT for given ID. + DeletePAT(ctx context.Context, token, patID string) error + + // ResetSecret function reset the secret and creates new secret for the given ID. + ResetPATSecret(ctx context.Context, token, patID string, duration time.Duration) (PAT, error) + + // RevokeSecret function revokes the secret for the given ID. + RevokePATSecret(ctx context.Context, token, patID string) error + + // AddScope function adds a new scope entry. + 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. + 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. + ClearPATAllScopeEntry(ctx context.Context, token, patID 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, 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. +// +//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) (err error) + + // 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) + + // 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) + + // RetrieveAll retrieves all PATs belongs to userID. + 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 + + // 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 + + 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) + + 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/pat/service.go b/pat/service.go new file mode 100644 index 0000000000..5161e3cb57 --- /dev/null +++ b/pat/service.go @@ -0,0 +1,302 @@ +// Copyright (c) Abstract Machines +// SPDX-License-Identifier: Apache-2.0 + +package pat + +import ( + "context" + "encoding/base64" + "math/rand" + "strings" + "time" + + "github.com/absmach/magistrala/pkg/errors" + svcerr "github.com/absmach/magistrala/pkg/errors/service" +) + +const ( + randStr = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890!@#$%^&&*|+-=" + patPrefix = "pat" + patSecretSeparator = "_" +) + +var ( + 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") + 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") + errClearAllScope = errors.New("failed to clear all entry in scope") +) + +type Service struct { + pats PATSRepository + hasher Hasher +} + +var _ Service = (*Service)(nil) + +// New instantiates the auth service implementation. +func New(pats PATSRepository, hasher Hasher) Service { + return &Service{ + pats: pats, + hasher: hasher, + } +} + +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 + } + + id, err := svc.idProvider.ID() + 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, + 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 +} + +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 + } + 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) UpdatePATDescription(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) RetrievePAT(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) ListPATS(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, pm) + if err != nil { + return PATSPage{}, errors.Wrap(errRetrievePAT, err) + } + return patsPage, nil +} + +func (svc Service) DeletePAT(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) ResetPATSecret(ctx context.Context, token, patID string, duration time.Duration) (PAT, error) { + key, err := svc.Identify(ctx, token) + if err != nil { + return PAT{}, err + } + + // 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, hash, time.Now().Add(duration)) + if err != nil { + 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 +} + +func (svc Service) RevokePATSecret(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) 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 + } + 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 { + return Scope{}, err + } + 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) ClearPATAllScopeEntry(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) 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, res.ID, platformEntityType, optionalDomainID, optionalDomainEntityType, operation, entityIDs...); err != nil { + return errors.Wrap(svcerr.ErrAuthorization, err) + } + return 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 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) +} From f5757fb00731f071c14cbb2f4622de04ecb581bf Mon Sep 17 00:00:00 2001 From: nyagamunene Date: Tue, 3 Dec 2024 13:53:13 +0300 Subject: [PATCH 29/72] fix protoc linter Signed-off-by: nyagamunene --- go.mod | 1 - 1 file changed, 1 deletion(-) diff --git a/go.mod b/go.mod index 072a6485ac..f79cb10647 100644 --- a/go.mod +++ b/go.mod @@ -169,7 +169,6 @@ require ( github.com/xeipuuv/gojsonschema v1.2.0 // indirect github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect go.opentelemetry.io/auto/sdk v1.1.0 // indirect - go.etcd.io/bbolt v1.3.11 // indirect go.opentelemetry.io/otel/metric v1.33.0 // indirect go.opentelemetry.io/proto/otlp v1.4.0 // indirect go.uber.org/atomic v1.11.0 // indirect From 56266e4bfa86b0acf6ffd809e9de5ccdf58fe324 Mon Sep 17 00:00:00 2001 From: Felix Gateru Date: Mon, 11 Nov 2024 14:31:38 +0300 Subject: [PATCH 30/72] NOISSUE - Rename Things to Clients Signed-off-by: Felix Gateru Signed-off-by: Arvindh --- api/grpc/auth/v1/auth.pb.go | 70 ++++++- api/grpc/auth/v1/auth_grpc.pb.go | 23 +-- api/grpc/channels/v1/channels.pb.go | 118 ++++++++++-- api/grpc/channels/v1/channels_grpc.pb.go | 23 +-- api/grpc/clients/v1/clients.pb.go | 134 +++++++++++--- api/grpc/clients/v1/clients_grpc.pb.go | 23 +-- api/grpc/common/v1/common.pb.go | 222 +++++++++++++++++++---- api/grpc/domains/v1/domains.pb.go | 46 ++++- api/grpc/domains/v1/domains_grpc.pb.go | 23 +-- api/grpc/groups/v1/groups_grpc.pb.go | 23 +-- api/grpc/token/v1/token.pb.go | 68 +++++-- api/grpc/token/v1/token_grpc.pb.go | 23 +-- go.mod | 1 + pkg/messaging/message.pb.go | 24 ++- 14 files changed, 605 insertions(+), 216 deletions(-) diff --git a/api/grpc/auth/v1/auth.pb.go b/api/grpc/auth/v1/auth.pb.go index 1ee360a95f..16b2b49395 100644 --- a/api/grpc/auth/v1/auth.pb.go +++ b/api/grpc/auth/v1/auth.pb.go @@ -32,9 +32,11 @@ type AuthNReq struct { func (x *AuthNReq) Reset() { *x = AuthNReq{} - mi := &file_auth_v1_auth_proto_msgTypes[0] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) + if protoimpl.UnsafeEnabled { + mi := &file_auth_v1_auth_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } } func (x *AuthNReq) String() string { @@ -45,7 +47,7 @@ func (*AuthNReq) ProtoMessage() {} func (x *AuthNReq) ProtoReflect() protoreflect.Message { mi := &file_auth_v1_auth_proto_msgTypes[0] - if x != nil { + if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -78,9 +80,11 @@ type AuthNRes struct { func (x *AuthNRes) Reset() { *x = AuthNRes{} - mi := &file_auth_v1_auth_proto_msgTypes[1] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) + if protoimpl.UnsafeEnabled { + mi := &file_auth_v1_auth_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } } func (x *AuthNRes) String() string { @@ -91,7 +95,7 @@ func (*AuthNRes) ProtoMessage() {} func (x *AuthNRes) ProtoReflect() protoreflect.Message { mi := &file_auth_v1_auth_proto_msgTypes[1] - if x != nil { + if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -493,6 +497,56 @@ func file_auth_v1_auth_proto_init() { if File_auth_v1_auth_proto != nil { return } + if !protoimpl.UnsafeEnabled { + file_auth_v1_auth_proto_msgTypes[0].Exporter = func(v any, i int) any { + switch v := v.(*AuthNReq); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_auth_v1_auth_proto_msgTypes[1].Exporter = func(v any, i int) any { + switch v := v.(*AuthNRes); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_auth_v1_auth_proto_msgTypes[2].Exporter = func(v any, i int) any { + switch v := v.(*AuthZReq); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_auth_v1_auth_proto_msgTypes[3].Exporter = func(v any, i int) any { + switch v := v.(*AuthZRes); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ diff --git a/api/grpc/auth/v1/auth_grpc.pb.go b/api/grpc/auth/v1/auth_grpc.pb.go index d85fabae7c..eae65dac16 100644 --- a/api/grpc/auth/v1/auth_grpc.pb.go +++ b/api/grpc/auth/v1/auth_grpc.pb.go @@ -18,8 +18,8 @@ import ( // This is a compile-time assertion to ensure that this generated file // is compatible with the grpc package it is being compiled against. -// Requires gRPC-Go v1.64.0 or later. -const _ = grpc.SupportPackageIsVersion9 +// Requires gRPC-Go v1.62.0 or later. +const _ = grpc.SupportPackageIsVersion8 const ( AuthService_Authorize_FullMethodName = "/auth.v1.AuthService/Authorize" @@ -91,7 +91,7 @@ func (c *authServiceClient) AuthenticatePAT(ctx context.Context, in *AuthNReq, o // AuthServiceServer is the server API for AuthService service. // All implementations must embed UnimplementedAuthServiceServer -// for forward compatibility. +// for forward compatibility // // AuthService is a service that provides authentication // and authorization functionalities for SuperMQ services. @@ -103,12 +103,9 @@ type AuthServiceServer interface { mustEmbedUnimplementedAuthServiceServer() } -// UnimplementedAuthServiceServer must be embedded to have -// forward compatible implementations. -// -// NOTE: this should be embedded by value instead of pointer to avoid a nil -// pointer dereference when methods are called. -type UnimplementedAuthServiceServer struct{} +// UnimplementedAuthServiceServer must be embedded to have forward compatible implementations. +type UnimplementedAuthServiceServer struct { +} func (UnimplementedAuthServiceServer) Authorize(context.Context, *AuthZReq) (*AuthZRes, error) { return nil, status.Errorf(codes.Unimplemented, "method Authorize not implemented") @@ -123,7 +120,6 @@ func (UnimplementedAuthServiceServer) AuthenticatePAT(context.Context, *AuthNReq return nil, status.Errorf(codes.Unimplemented, "method AuthenticatePAT not implemented") } func (UnimplementedAuthServiceServer) mustEmbedUnimplementedAuthServiceServer() {} -func (UnimplementedAuthServiceServer) testEmbeddedByValue() {} // UnsafeAuthServiceServer may be embedded to opt out of forward compatibility for this service. // Use of this interface is not recommended, as added methods to AuthServiceServer will @@ -133,13 +129,6 @@ type UnsafeAuthServiceServer interface { } func RegisterAuthServiceServer(s grpc.ServiceRegistrar, srv AuthServiceServer) { - // If the following call pancis, it indicates UnimplementedAuthServiceServer was - // embedded by pointer and is nil. This will cause panics if an - // unimplemented method is ever invoked, so we test this at initialization - // time to prevent it from happening at runtime later due to I/O. - if t, ok := srv.(interface{ testEmbeddedByValue() }); ok { - t.testEmbeddedByValue() - } s.RegisterService(&AuthService_ServiceDesc, srv) } diff --git a/api/grpc/channels/v1/channels.pb.go b/api/grpc/channels/v1/channels.pb.go index d9750f5f7d..a5208e78ef 100644 --- a/api/grpc/channels/v1/channels.pb.go +++ b/api/grpc/channels/v1/channels.pb.go @@ -46,7 +46,7 @@ func (*RemoveClientConnectionsReq) ProtoMessage() {} func (x *RemoveClientConnectionsReq) ProtoReflect() protoreflect.Message { mi := &file_channels_v1_channels_proto_msgTypes[0] - if x != nil { + if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -89,7 +89,7 @@ func (*RemoveClientConnectionsRes) ProtoMessage() {} func (x *RemoveClientConnectionsRes) ProtoReflect() protoreflect.Message { mi := &file_channels_v1_channels_proto_msgTypes[1] - if x != nil { + if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -113,9 +113,11 @@ type UnsetParentGroupFromChannelsReq struct { func (x *UnsetParentGroupFromChannelsReq) Reset() { *x = UnsetParentGroupFromChannelsReq{} - mi := &file_channels_v1_channels_proto_msgTypes[2] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) + if protoimpl.UnsafeEnabled { + mi := &file_channels_v1_channels_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } } func (x *UnsetParentGroupFromChannelsReq) String() string { @@ -126,7 +128,7 @@ func (*UnsetParentGroupFromChannelsReq) ProtoMessage() {} func (x *UnsetParentGroupFromChannelsReq) ProtoReflect() protoreflect.Message { mi := &file_channels_v1_channels_proto_msgTypes[2] - if x != nil { + if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -156,9 +158,11 @@ type UnsetParentGroupFromChannelsRes struct { func (x *UnsetParentGroupFromChannelsRes) Reset() { *x = UnsetParentGroupFromChannelsRes{} - mi := &file_channels_v1_channels_proto_msgTypes[3] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) + if protoimpl.UnsafeEnabled { + mi := &file_channels_v1_channels_proto_msgTypes[3] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } } func (x *UnsetParentGroupFromChannelsRes) String() string { @@ -169,7 +173,7 @@ func (*UnsetParentGroupFromChannelsRes) ProtoMessage() {} func (x *UnsetParentGroupFromChannelsRes) ProtoReflect() protoreflect.Message { mi := &file_channels_v1_channels_proto_msgTypes[3] - if x != nil { + if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -197,9 +201,11 @@ type AuthzReq struct { func (x *AuthzReq) Reset() { *x = AuthzReq{} - mi := &file_channels_v1_channels_proto_msgTypes[4] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) + if protoimpl.UnsafeEnabled { + mi := &file_channels_v1_channels_proto_msgTypes[4] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } } func (x *AuthzReq) String() string { @@ -210,7 +216,7 @@ func (*AuthzReq) ProtoMessage() {} func (x *AuthzReq) ProtoReflect() protoreflect.Message { mi := &file_channels_v1_channels_proto_msgTypes[4] - if x != nil { + if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -269,9 +275,11 @@ type AuthzRes struct { func (x *AuthzRes) Reset() { *x = AuthzRes{} - mi := &file_channels_v1_channels_proto_msgTypes[5] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) + if protoimpl.UnsafeEnabled { + mi := &file_channels_v1_channels_proto_msgTypes[5] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } } func (x *AuthzRes) String() string { @@ -282,7 +290,7 @@ func (*AuthzRes) ProtoMessage() {} func (x *AuthzRes) ProtoReflect() protoreflect.Message { mi := &file_channels_v1_channels_proto_msgTypes[5] - if x != nil { + if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -411,6 +419,80 @@ func file_channels_v1_channels_proto_init() { if File_channels_v1_channels_proto != nil { return } + if !protoimpl.UnsafeEnabled { + file_channels_v1_channels_proto_msgTypes[0].Exporter = func(v any, i int) any { + switch v := v.(*RemoveClientConnectionsReq); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_channels_v1_channels_proto_msgTypes[1].Exporter = func(v any, i int) any { + switch v := v.(*RemoveClientConnectionsRes); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_channels_v1_channels_proto_msgTypes[2].Exporter = func(v any, i int) any { + switch v := v.(*UnsetParentGroupFromChannelsReq); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_channels_v1_channels_proto_msgTypes[3].Exporter = func(v any, i int) any { + switch v := v.(*UnsetParentGroupFromChannelsRes); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_channels_v1_channels_proto_msgTypes[4].Exporter = func(v any, i int) any { + switch v := v.(*AuthzReq); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_channels_v1_channels_proto_msgTypes[5].Exporter = func(v any, i int) any { + switch v := v.(*AuthzRes); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ diff --git a/api/grpc/channels/v1/channels_grpc.pb.go b/api/grpc/channels/v1/channels_grpc.pb.go index 612280b166..6c4ebeda28 100644 --- a/api/grpc/channels/v1/channels_grpc.pb.go +++ b/api/grpc/channels/v1/channels_grpc.pb.go @@ -19,8 +19,8 @@ import ( // This is a compile-time assertion to ensure that this generated file // is compatible with the grpc package it is being compiled against. -// Requires gRPC-Go v1.64.0 or later. -const _ = grpc.SupportPackageIsVersion9 +// Requires gRPC-Go v1.62.0 or later. +const _ = grpc.SupportPackageIsVersion8 const ( ChannelsService_Authorize_FullMethodName = "/channels.v1.ChannelsService/Authorize" @@ -89,7 +89,7 @@ func (c *channelsServiceClient) RetrieveEntity(ctx context.Context, in *v1.Retri // ChannelsServiceServer is the server API for ChannelsService service. // All implementations must embed UnimplementedChannelsServiceServer -// for forward compatibility. +// for forward compatibility type ChannelsServiceServer interface { Authorize(context.Context, *AuthzReq) (*AuthzRes, error) RemoveClientConnections(context.Context, *RemoveClientConnectionsReq) (*RemoveClientConnectionsRes, error) @@ -98,12 +98,9 @@ type ChannelsServiceServer interface { mustEmbedUnimplementedChannelsServiceServer() } -// UnimplementedChannelsServiceServer must be embedded to have -// forward compatible implementations. -// -// NOTE: this should be embedded by value instead of pointer to avoid a nil -// pointer dereference when methods are called. -type UnimplementedChannelsServiceServer struct{} +// UnimplementedChannelsServiceServer must be embedded to have forward compatible implementations. +type UnimplementedChannelsServiceServer struct { +} func (UnimplementedChannelsServiceServer) Authorize(context.Context, *AuthzReq) (*AuthzRes, error) { return nil, status.Errorf(codes.Unimplemented, "method Authorize not implemented") @@ -118,7 +115,6 @@ func (UnimplementedChannelsServiceServer) RetrieveEntity(context.Context, *v1.Re return nil, status.Errorf(codes.Unimplemented, "method RetrieveEntity not implemented") } func (UnimplementedChannelsServiceServer) mustEmbedUnimplementedChannelsServiceServer() {} -func (UnimplementedChannelsServiceServer) testEmbeddedByValue() {} // UnsafeChannelsServiceServer may be embedded to opt out of forward compatibility for this service. // Use of this interface is not recommended, as added methods to ChannelsServiceServer will @@ -128,13 +124,6 @@ type UnsafeChannelsServiceServer interface { } func RegisterChannelsServiceServer(s grpc.ServiceRegistrar, srv ChannelsServiceServer) { - // If the following call pancis, it indicates UnimplementedChannelsServiceServer was - // embedded by pointer and is nil. This will cause panics if an - // unimplemented method is ever invoked, so we test this at initialization - // time to prevent it from happening at runtime later due to I/O. - if t, ok := srv.(interface{ testEmbeddedByValue() }); ok { - t.testEmbeddedByValue() - } s.RegisterService(&ChannelsService_ServiceDesc, srv) } diff --git a/api/grpc/clients/v1/clients.pb.go b/api/grpc/clients/v1/clients.pb.go index 5bf1fc93b4..b525505537 100644 --- a/api/grpc/clients/v1/clients.pb.go +++ b/api/grpc/clients/v1/clients.pb.go @@ -34,9 +34,11 @@ type AuthnReq struct { func (x *AuthnReq) Reset() { *x = AuthnReq{} - mi := &file_clients_v1_clients_proto_msgTypes[0] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) + if protoimpl.UnsafeEnabled { + mi := &file_clients_v1_clients_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } } func (x *AuthnReq) String() string { @@ -47,7 +49,7 @@ func (*AuthnReq) ProtoMessage() {} func (x *AuthnReq) ProtoReflect() protoreflect.Message { mi := &file_clients_v1_clients_proto_msgTypes[0] - if x != nil { + if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -86,9 +88,11 @@ type AuthnRes struct { func (x *AuthnRes) Reset() { *x = AuthnRes{} - mi := &file_clients_v1_clients_proto_msgTypes[1] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) + if protoimpl.UnsafeEnabled { + mi := &file_clients_v1_clients_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } } func (x *AuthnRes) String() string { @@ -99,7 +103,7 @@ func (*AuthnRes) ProtoMessage() {} func (x *AuthnRes) ProtoReflect() protoreflect.Message { mi := &file_clients_v1_clients_proto_msgTypes[1] - if x != nil { + if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -137,9 +141,11 @@ type RemoveChannelConnectionsReq struct { func (x *RemoveChannelConnectionsReq) Reset() { *x = RemoveChannelConnectionsReq{} - mi := &file_clients_v1_clients_proto_msgTypes[2] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) + if protoimpl.UnsafeEnabled { + mi := &file_clients_v1_clients_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } } func (x *RemoveChannelConnectionsReq) String() string { @@ -150,7 +156,7 @@ func (*RemoveChannelConnectionsReq) ProtoMessage() {} func (x *RemoveChannelConnectionsReq) ProtoReflect() protoreflect.Message { mi := &file_clients_v1_clients_proto_msgTypes[2] - if x != nil { + if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -180,9 +186,11 @@ type RemoveChannelConnectionsRes struct { func (x *RemoveChannelConnectionsRes) Reset() { *x = RemoveChannelConnectionsRes{} - mi := &file_clients_v1_clients_proto_msgTypes[3] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) + if protoimpl.UnsafeEnabled { + mi := &file_clients_v1_clients_proto_msgTypes[3] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } } func (x *RemoveChannelConnectionsRes) String() string { @@ -193,7 +201,7 @@ func (*RemoveChannelConnectionsRes) ProtoMessage() {} func (x *RemoveChannelConnectionsRes) ProtoReflect() protoreflect.Message { mi := &file_clients_v1_clients_proto_msgTypes[3] - if x != nil { + if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -217,9 +225,11 @@ type UnsetParentGroupFromClientReq struct { func (x *UnsetParentGroupFromClientReq) Reset() { *x = UnsetParentGroupFromClientReq{} - mi := &file_clients_v1_clients_proto_msgTypes[4] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) + if protoimpl.UnsafeEnabled { + mi := &file_clients_v1_clients_proto_msgTypes[4] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } } func (x *UnsetParentGroupFromClientReq) String() string { @@ -230,7 +240,7 @@ func (*UnsetParentGroupFromClientReq) ProtoMessage() {} func (x *UnsetParentGroupFromClientReq) ProtoReflect() protoreflect.Message { mi := &file_clients_v1_clients_proto_msgTypes[4] - if x != nil { + if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -260,9 +270,11 @@ type UnsetParentGroupFromClientRes struct { func (x *UnsetParentGroupFromClientRes) Reset() { *x = UnsetParentGroupFromClientRes{} - mi := &file_clients_v1_clients_proto_msgTypes[5] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) + if protoimpl.UnsafeEnabled { + mi := &file_clients_v1_clients_proto_msgTypes[5] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } } func (x *UnsetParentGroupFromClientRes) String() string { @@ -273,7 +285,7 @@ func (*UnsetParentGroupFromClientRes) ProtoMessage() {} func (x *UnsetParentGroupFromClientRes) ProtoReflect() protoreflect.Message { mi := &file_clients_v1_clients_proto_msgTypes[5] - if x != nil { + if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -419,6 +431,80 @@ func file_clients_v1_clients_proto_init() { if File_clients_v1_clients_proto != nil { return } + if !protoimpl.UnsafeEnabled { + file_clients_v1_clients_proto_msgTypes[0].Exporter = func(v any, i int) any { + switch v := v.(*AuthnReq); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_clients_v1_clients_proto_msgTypes[1].Exporter = func(v any, i int) any { + switch v := v.(*AuthnRes); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_clients_v1_clients_proto_msgTypes[2].Exporter = func(v any, i int) any { + switch v := v.(*RemoveChannelConnectionsReq); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_clients_v1_clients_proto_msgTypes[3].Exporter = func(v any, i int) any { + switch v := v.(*RemoveChannelConnectionsRes); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_clients_v1_clients_proto_msgTypes[4].Exporter = func(v any, i int) any { + switch v := v.(*UnsetParentGroupFromClientReq); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_clients_v1_clients_proto_msgTypes[5].Exporter = func(v any, i int) any { + switch v := v.(*UnsetParentGroupFromClientRes); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ diff --git a/api/grpc/clients/v1/clients_grpc.pb.go b/api/grpc/clients/v1/clients_grpc.pb.go index bd04a8c47f..a2b627b843 100644 --- a/api/grpc/clients/v1/clients_grpc.pb.go +++ b/api/grpc/clients/v1/clients_grpc.pb.go @@ -19,8 +19,8 @@ import ( // This is a compile-time assertion to ensure that this generated file // is compatible with the grpc package it is being compiled against. -// Requires gRPC-Go v1.64.0 or later. -const _ = grpc.SupportPackageIsVersion9 +// Requires gRPC-Go v1.62.0 or later. +const _ = grpc.SupportPackageIsVersion8 const ( ClientsService_Authenticate_FullMethodName = "/clients.v1.ClientsService/Authenticate" @@ -129,7 +129,7 @@ func (c *clientsServiceClient) UnsetParentGroupFromClient(ctx context.Context, i // ClientsServiceServer is the server API for ClientsService service. // All implementations must embed UnimplementedClientsServiceServer -// for forward compatibility. +// for forward compatibility // // ClientsService is a service that provides clients // authorization functionalities for SuperMQ services. @@ -145,12 +145,9 @@ type ClientsServiceServer interface { mustEmbedUnimplementedClientsServiceServer() } -// UnimplementedClientsServiceServer must be embedded to have -// forward compatible implementations. -// -// NOTE: this should be embedded by value instead of pointer to avoid a nil -// pointer dereference when methods are called. -type UnimplementedClientsServiceServer struct{} +// UnimplementedClientsServiceServer must be embedded to have forward compatible implementations. +type UnimplementedClientsServiceServer struct { +} func (UnimplementedClientsServiceServer) Authenticate(context.Context, *AuthnReq) (*AuthnRes, error) { return nil, status.Errorf(codes.Unimplemented, "method Authenticate not implemented") @@ -174,7 +171,6 @@ func (UnimplementedClientsServiceServer) UnsetParentGroupFromClient(context.Cont return nil, status.Errorf(codes.Unimplemented, "method UnsetParentGroupFromClient not implemented") } func (UnimplementedClientsServiceServer) mustEmbedUnimplementedClientsServiceServer() {} -func (UnimplementedClientsServiceServer) testEmbeddedByValue() {} // UnsafeClientsServiceServer may be embedded to opt out of forward compatibility for this service. // Use of this interface is not recommended, as added methods to ClientsServiceServer will @@ -184,13 +180,6 @@ type UnsafeClientsServiceServer interface { } func RegisterClientsServiceServer(s grpc.ServiceRegistrar, srv ClientsServiceServer) { - // If the following call pancis, it indicates UnimplementedClientsServiceServer was - // embedded by pointer and is nil. This will cause panics if an - // unimplemented method is ever invoked, so we test this at initialization - // time to prevent it from happening at runtime later due to I/O. - if t, ok := srv.(interface{ testEmbeddedByValue() }); ok { - t.testEmbeddedByValue() - } s.RegisterService(&ClientsService_ServiceDesc, srv) } diff --git a/api/grpc/common/v1/common.pb.go b/api/grpc/common/v1/common.pb.go index 281366d3e6..faa8c157e2 100644 --- a/api/grpc/common/v1/common.pb.go +++ b/api/grpc/common/v1/common.pb.go @@ -32,9 +32,11 @@ type RetrieveEntitiesReq struct { func (x *RetrieveEntitiesReq) Reset() { *x = RetrieveEntitiesReq{} - mi := &file_common_v1_common_proto_msgTypes[0] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) + if protoimpl.UnsafeEnabled { + mi := &file_common_v1_common_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } } func (x *RetrieveEntitiesReq) String() string { @@ -45,7 +47,7 @@ func (*RetrieveEntitiesReq) ProtoMessage() {} func (x *RetrieveEntitiesReq) ProtoReflect() protoreflect.Message { mi := &file_common_v1_common_proto_msgTypes[0] - if x != nil { + if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -79,9 +81,11 @@ type RetrieveEntitiesRes struct { func (x *RetrieveEntitiesRes) Reset() { *x = RetrieveEntitiesRes{} - mi := &file_common_v1_common_proto_msgTypes[1] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) + if protoimpl.UnsafeEnabled { + mi := &file_common_v1_common_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } } func (x *RetrieveEntitiesRes) String() string { @@ -92,7 +96,7 @@ func (*RetrieveEntitiesRes) ProtoMessage() {} func (x *RetrieveEntitiesRes) ProtoReflect() protoreflect.Message { mi := &file_common_v1_common_proto_msgTypes[1] - if x != nil { + if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -144,9 +148,11 @@ type RetrieveEntityReq struct { func (x *RetrieveEntityReq) Reset() { *x = RetrieveEntityReq{} - mi := &file_common_v1_common_proto_msgTypes[2] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) + if protoimpl.UnsafeEnabled { + mi := &file_common_v1_common_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } } func (x *RetrieveEntityReq) String() string { @@ -157,7 +163,7 @@ func (*RetrieveEntityReq) ProtoMessage() {} func (x *RetrieveEntityReq) ProtoReflect() protoreflect.Message { mi := &file_common_v1_common_proto_msgTypes[2] - if x != nil { + if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -188,9 +194,11 @@ type RetrieveEntityRes struct { func (x *RetrieveEntityRes) Reset() { *x = RetrieveEntityRes{} - mi := &file_common_v1_common_proto_msgTypes[3] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) + if protoimpl.UnsafeEnabled { + mi := &file_common_v1_common_proto_msgTypes[3] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } } func (x *RetrieveEntityRes) String() string { @@ -201,7 +209,7 @@ func (*RetrieveEntityRes) ProtoMessage() {} func (x *RetrieveEntityRes) ProtoReflect() protoreflect.Message { mi := &file_common_v1_common_proto_msgTypes[3] - if x != nil { + if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -235,9 +243,11 @@ type EntityBasic struct { func (x *EntityBasic) Reset() { *x = EntityBasic{} - mi := &file_common_v1_common_proto_msgTypes[4] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) + if protoimpl.UnsafeEnabled { + mi := &file_common_v1_common_proto_msgTypes[4] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } } func (x *EntityBasic) String() string { @@ -248,7 +258,7 @@ func (*EntityBasic) ProtoMessage() {} func (x *EntityBasic) ProtoReflect() protoreflect.Message { mi := &file_common_v1_common_proto_msgTypes[4] - if x != nil { + if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -300,9 +310,11 @@ type AddConnectionsReq struct { func (x *AddConnectionsReq) Reset() { *x = AddConnectionsReq{} - mi := &file_common_v1_common_proto_msgTypes[5] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) + if protoimpl.UnsafeEnabled { + mi := &file_common_v1_common_proto_msgTypes[5] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } } func (x *AddConnectionsReq) String() string { @@ -313,7 +325,7 @@ func (*AddConnectionsReq) ProtoMessage() {} func (x *AddConnectionsReq) ProtoReflect() protoreflect.Message { mi := &file_common_v1_common_proto_msgTypes[5] - if x != nil { + if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -344,9 +356,11 @@ type AddConnectionsRes struct { func (x *AddConnectionsRes) Reset() { *x = AddConnectionsRes{} - mi := &file_common_v1_common_proto_msgTypes[6] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) + if protoimpl.UnsafeEnabled { + mi := &file_common_v1_common_proto_msgTypes[6] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } } func (x *AddConnectionsRes) String() string { @@ -357,7 +371,7 @@ func (*AddConnectionsRes) ProtoMessage() {} func (x *AddConnectionsRes) ProtoReflect() protoreflect.Message { mi := &file_common_v1_common_proto_msgTypes[6] - if x != nil { + if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -388,9 +402,11 @@ type RemoveConnectionsReq struct { func (x *RemoveConnectionsReq) Reset() { *x = RemoveConnectionsReq{} - mi := &file_common_v1_common_proto_msgTypes[7] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) + if protoimpl.UnsafeEnabled { + mi := &file_common_v1_common_proto_msgTypes[7] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } } func (x *RemoveConnectionsReq) String() string { @@ -401,7 +417,7 @@ func (*RemoveConnectionsReq) ProtoMessage() {} func (x *RemoveConnectionsReq) ProtoReflect() protoreflect.Message { mi := &file_common_v1_common_proto_msgTypes[7] - if x != nil { + if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -432,9 +448,11 @@ type RemoveConnectionsRes struct { func (x *RemoveConnectionsRes) Reset() { *x = RemoveConnectionsRes{} - mi := &file_common_v1_common_proto_msgTypes[8] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) + if protoimpl.UnsafeEnabled { + mi := &file_common_v1_common_proto_msgTypes[8] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } } func (x *RemoveConnectionsRes) String() string { @@ -445,7 +463,7 @@ func (*RemoveConnectionsRes) ProtoMessage() {} func (x *RemoveConnectionsRes) ProtoReflect() protoreflect.Message { mi := &file_common_v1_common_proto_msgTypes[8] - if x != nil { + if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -479,9 +497,11 @@ type Connection struct { func (x *Connection) Reset() { *x = Connection{} - mi := &file_common_v1_common_proto_msgTypes[9] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) + if protoimpl.UnsafeEnabled { + mi := &file_common_v1_common_proto_msgTypes[9] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } } func (x *Connection) String() string { @@ -492,7 +512,7 @@ func (*Connection) ProtoMessage() {} func (x *Connection) ProtoReflect() protoreflect.Message { mi := &file_common_v1_common_proto_msgTypes[9] - if x != nil { + if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -637,6 +657,128 @@ func file_common_v1_common_proto_init() { if File_common_v1_common_proto != nil { return } + if !protoimpl.UnsafeEnabled { + file_common_v1_common_proto_msgTypes[0].Exporter = func(v any, i int) any { + switch v := v.(*RetrieveEntitiesReq); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_common_v1_common_proto_msgTypes[1].Exporter = func(v any, i int) any { + switch v := v.(*RetrieveEntitiesRes); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_common_v1_common_proto_msgTypes[2].Exporter = func(v any, i int) any { + switch v := v.(*RetrieveEntityReq); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_common_v1_common_proto_msgTypes[3].Exporter = func(v any, i int) any { + switch v := v.(*RetrieveEntityRes); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_common_v1_common_proto_msgTypes[4].Exporter = func(v any, i int) any { + switch v := v.(*EntityBasic); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_common_v1_common_proto_msgTypes[5].Exporter = func(v any, i int) any { + switch v := v.(*AddConnectionsReq); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_common_v1_common_proto_msgTypes[6].Exporter = func(v any, i int) any { + switch v := v.(*AddConnectionsRes); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_common_v1_common_proto_msgTypes[7].Exporter = func(v any, i int) any { + switch v := v.(*RemoveConnectionsReq); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_common_v1_common_proto_msgTypes[8].Exporter = func(v any, i int) any { + switch v := v.(*RemoveConnectionsRes); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_common_v1_common_proto_msgTypes[9].Exporter = func(v any, i int) any { + switch v := v.(*Connection); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ diff --git a/api/grpc/domains/v1/domains.pb.go b/api/grpc/domains/v1/domains.pb.go index 897979d4aa..8a2dcc211d 100644 --- a/api/grpc/domains/v1/domains.pb.go +++ b/api/grpc/domains/v1/domains.pb.go @@ -33,9 +33,11 @@ type DeleteUserRes struct { func (x *DeleteUserRes) Reset() { *x = DeleteUserRes{} - mi := &file_domains_v1_domains_proto_msgTypes[0] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) + if protoimpl.UnsafeEnabled { + mi := &file_domains_v1_domains_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } } func (x *DeleteUserRes) String() string { @@ -46,7 +48,7 @@ func (*DeleteUserRes) ProtoMessage() {} func (x *DeleteUserRes) ProtoReflect() protoreflect.Message { mi := &file_domains_v1_domains_proto_msgTypes[0] - if x != nil { + if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -77,9 +79,11 @@ type DeleteUserReq struct { func (x *DeleteUserReq) Reset() { *x = DeleteUserReq{} - mi := &file_domains_v1_domains_proto_msgTypes[1] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) + if protoimpl.UnsafeEnabled { + mi := &file_domains_v1_domains_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } } func (x *DeleteUserReq) String() string { @@ -90,7 +94,7 @@ func (*DeleteUserReq) ProtoMessage() {} func (x *DeleteUserReq) ProtoReflect() protoreflect.Message { mi := &file_domains_v1_domains_proto_msgTypes[1] - if x != nil { + if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -177,6 +181,32 @@ func file_domains_v1_domains_proto_init() { if File_domains_v1_domains_proto != nil { return } + if !protoimpl.UnsafeEnabled { + file_domains_v1_domains_proto_msgTypes[0].Exporter = func(v any, i int) any { + switch v := v.(*DeleteUserRes); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_domains_v1_domains_proto_msgTypes[1].Exporter = func(v any, i int) any { + switch v := v.(*DeleteUserReq); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ diff --git a/api/grpc/domains/v1/domains_grpc.pb.go b/api/grpc/domains/v1/domains_grpc.pb.go index 90b85349f8..1e348de890 100644 --- a/api/grpc/domains/v1/domains_grpc.pb.go +++ b/api/grpc/domains/v1/domains_grpc.pb.go @@ -19,8 +19,8 @@ import ( // This is a compile-time assertion to ensure that this generated file // is compatible with the grpc package it is being compiled against. -// Requires gRPC-Go v1.64.0 or later. -const _ = grpc.SupportPackageIsVersion9 +// Requires gRPC-Go v1.62.0 or later. +const _ = grpc.SupportPackageIsVersion8 const ( DomainsService_DeleteUserFromDomains_FullMethodName = "/domains.v1.DomainsService/DeleteUserFromDomains" @@ -68,7 +68,7 @@ func (c *domainsServiceClient) RetrieveEntity(ctx context.Context, in *v1.Retrie // DomainsServiceServer is the server API for DomainsService service. // All implementations must embed UnimplementedDomainsServiceServer -// for forward compatibility. +// for forward compatibility // // DomainsService is a service that provides access to // domains functionalities for SuperMQ services. @@ -78,12 +78,9 @@ type DomainsServiceServer interface { mustEmbedUnimplementedDomainsServiceServer() } -// UnimplementedDomainsServiceServer must be embedded to have -// forward compatible implementations. -// -// NOTE: this should be embedded by value instead of pointer to avoid a nil -// pointer dereference when methods are called. -type UnimplementedDomainsServiceServer struct{} +// UnimplementedDomainsServiceServer must be embedded to have forward compatible implementations. +type UnimplementedDomainsServiceServer struct { +} func (UnimplementedDomainsServiceServer) DeleteUserFromDomains(context.Context, *DeleteUserReq) (*DeleteUserRes, error) { return nil, status.Errorf(codes.Unimplemented, "method DeleteUserFromDomains not implemented") @@ -92,7 +89,6 @@ func (UnimplementedDomainsServiceServer) RetrieveEntity(context.Context, *v1.Ret return nil, status.Errorf(codes.Unimplemented, "method RetrieveEntity not implemented") } func (UnimplementedDomainsServiceServer) mustEmbedUnimplementedDomainsServiceServer() {} -func (UnimplementedDomainsServiceServer) testEmbeddedByValue() {} // UnsafeDomainsServiceServer may be embedded to opt out of forward compatibility for this service. // Use of this interface is not recommended, as added methods to DomainsServiceServer will @@ -102,13 +98,6 @@ type UnsafeDomainsServiceServer interface { } func RegisterDomainsServiceServer(s grpc.ServiceRegistrar, srv DomainsServiceServer) { - // If the following call pancis, it indicates UnimplementedDomainsServiceServer was - // embedded by pointer and is nil. This will cause panics if an - // unimplemented method is ever invoked, so we test this at initialization - // time to prevent it from happening at runtime later due to I/O. - if t, ok := srv.(interface{ testEmbeddedByValue() }); ok { - t.testEmbeddedByValue() - } s.RegisterService(&DomainsService_ServiceDesc, srv) } diff --git a/api/grpc/groups/v1/groups_grpc.pb.go b/api/grpc/groups/v1/groups_grpc.pb.go index d362f88c2b..86132eea9e 100644 --- a/api/grpc/groups/v1/groups_grpc.pb.go +++ b/api/grpc/groups/v1/groups_grpc.pb.go @@ -19,8 +19,8 @@ import ( // This is a compile-time assertion to ensure that this generated file // is compatible with the grpc package it is being compiled against. -// Requires gRPC-Go v1.64.0 or later. -const _ = grpc.SupportPackageIsVersion9 +// Requires gRPC-Go v1.62.0 or later. +const _ = grpc.SupportPackageIsVersion8 const ( GroupsService_RetrieveEntity_FullMethodName = "/groups.v1.GroupsService/RetrieveEntity" @@ -56,7 +56,7 @@ func (c *groupsServiceClient) RetrieveEntity(ctx context.Context, in *v1.Retriev // GroupsServiceServer is the server API for GroupsService service. // All implementations must embed UnimplementedGroupsServiceServer -// for forward compatibility. +// for forward compatibility // // GroupssService is a service that provides groups // functionalities for SuperMQ services. @@ -65,18 +65,14 @@ type GroupsServiceServer interface { mustEmbedUnimplementedGroupsServiceServer() } -// UnimplementedGroupsServiceServer must be embedded to have -// forward compatible implementations. -// -// NOTE: this should be embedded by value instead of pointer to avoid a nil -// pointer dereference when methods are called. -type UnimplementedGroupsServiceServer struct{} +// UnimplementedGroupsServiceServer must be embedded to have forward compatible implementations. +type UnimplementedGroupsServiceServer struct { +} func (UnimplementedGroupsServiceServer) RetrieveEntity(context.Context, *v1.RetrieveEntityReq) (*v1.RetrieveEntityRes, error) { return nil, status.Errorf(codes.Unimplemented, "method RetrieveEntity not implemented") } func (UnimplementedGroupsServiceServer) mustEmbedUnimplementedGroupsServiceServer() {} -func (UnimplementedGroupsServiceServer) testEmbeddedByValue() {} // UnsafeGroupsServiceServer may be embedded to opt out of forward compatibility for this service. // Use of this interface is not recommended, as added methods to GroupsServiceServer will @@ -86,13 +82,6 @@ type UnsafeGroupsServiceServer interface { } func RegisterGroupsServiceServer(s grpc.ServiceRegistrar, srv GroupsServiceServer) { - // If the following call pancis, it indicates UnimplementedGroupsServiceServer was - // embedded by pointer and is nil. This will cause panics if an - // unimplemented method is ever invoked, so we test this at initialization - // time to prevent it from happening at runtime later due to I/O. - if t, ok := srv.(interface{ testEmbeddedByValue() }); ok { - t.testEmbeddedByValue() - } s.RegisterService(&GroupsService_ServiceDesc, srv) } diff --git a/api/grpc/token/v1/token.pb.go b/api/grpc/token/v1/token.pb.go index 54e897e51f..9a7722514f 100644 --- a/api/grpc/token/v1/token.pb.go +++ b/api/grpc/token/v1/token.pb.go @@ -33,9 +33,11 @@ type IssueReq struct { func (x *IssueReq) Reset() { *x = IssueReq{} - mi := &file_token_v1_token_proto_msgTypes[0] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) + if protoimpl.UnsafeEnabled { + mi := &file_token_v1_token_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } } func (x *IssueReq) String() string { @@ -46,7 +48,7 @@ func (*IssueReq) ProtoMessage() {} func (x *IssueReq) ProtoReflect() protoreflect.Message { mi := &file_token_v1_token_proto_msgTypes[0] - if x != nil { + if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -84,9 +86,11 @@ type RefreshReq struct { func (x *RefreshReq) Reset() { *x = RefreshReq{} - mi := &file_token_v1_token_proto_msgTypes[1] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) + if protoimpl.UnsafeEnabled { + mi := &file_token_v1_token_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } } func (x *RefreshReq) String() string { @@ -97,7 +101,7 @@ func (*RefreshReq) ProtoMessage() {} func (x *RefreshReq) ProtoReflect() protoreflect.Message { mi := &file_token_v1_token_proto_msgTypes[1] - if x != nil { + if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -133,9 +137,11 @@ type Token struct { func (x *Token) Reset() { *x = Token{} - mi := &file_token_v1_token_proto_msgTypes[2] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) + if protoimpl.UnsafeEnabled { + mi := &file_token_v1_token_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } } func (x *Token) String() string { @@ -146,7 +152,7 @@ func (*Token) ProtoMessage() {} func (x *Token) ProtoReflect() protoreflect.Message { mi := &file_token_v1_token_proto_msgTypes[2] - if x != nil { + if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -251,6 +257,44 @@ func file_token_v1_token_proto_init() { if File_token_v1_token_proto != nil { return } + if !protoimpl.UnsafeEnabled { + file_token_v1_token_proto_msgTypes[0].Exporter = func(v any, i int) any { + switch v := v.(*IssueReq); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_token_v1_token_proto_msgTypes[1].Exporter = func(v any, i int) any { + switch v := v.(*RefreshReq); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_token_v1_token_proto_msgTypes[2].Exporter = func(v any, i int) any { + switch v := v.(*Token); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } file_token_v1_token_proto_msgTypes[2].OneofWrappers = []any{} type x struct{} out := protoimpl.TypeBuilder{ diff --git a/api/grpc/token/v1/token_grpc.pb.go b/api/grpc/token/v1/token_grpc.pb.go index f3adacfb70..8b8cf03931 100644 --- a/api/grpc/token/v1/token_grpc.pb.go +++ b/api/grpc/token/v1/token_grpc.pb.go @@ -18,8 +18,8 @@ import ( // This is a compile-time assertion to ensure that this generated file // is compatible with the grpc package it is being compiled against. -// Requires gRPC-Go v1.64.0 or later. -const _ = grpc.SupportPackageIsVersion9 +// Requires gRPC-Go v1.62.0 or later. +const _ = grpc.SupportPackageIsVersion8 const ( TokenService_Issue_FullMethodName = "/token.v1.TokenService/Issue" @@ -64,19 +64,16 @@ func (c *tokenServiceClient) Refresh(ctx context.Context, in *RefreshReq, opts . // TokenServiceServer is the server API for TokenService service. // All implementations must embed UnimplementedTokenServiceServer -// for forward compatibility. +// for forward compatibility type TokenServiceServer interface { Issue(context.Context, *IssueReq) (*Token, error) Refresh(context.Context, *RefreshReq) (*Token, error) mustEmbedUnimplementedTokenServiceServer() } -// UnimplementedTokenServiceServer must be embedded to have -// forward compatible implementations. -// -// NOTE: this should be embedded by value instead of pointer to avoid a nil -// pointer dereference when methods are called. -type UnimplementedTokenServiceServer struct{} +// UnimplementedTokenServiceServer must be embedded to have forward compatible implementations. +type UnimplementedTokenServiceServer struct { +} func (UnimplementedTokenServiceServer) Issue(context.Context, *IssueReq) (*Token, error) { return nil, status.Errorf(codes.Unimplemented, "method Issue not implemented") @@ -85,7 +82,6 @@ func (UnimplementedTokenServiceServer) Refresh(context.Context, *RefreshReq) (*T return nil, status.Errorf(codes.Unimplemented, "method Refresh not implemented") } func (UnimplementedTokenServiceServer) mustEmbedUnimplementedTokenServiceServer() {} -func (UnimplementedTokenServiceServer) testEmbeddedByValue() {} // UnsafeTokenServiceServer may be embedded to opt out of forward compatibility for this service. // Use of this interface is not recommended, as added methods to TokenServiceServer will @@ -95,13 +91,6 @@ type UnsafeTokenServiceServer interface { } func RegisterTokenServiceServer(s grpc.ServiceRegistrar, srv TokenServiceServer) { - // If the following call pancis, it indicates UnimplementedTokenServiceServer was - // embedded by pointer and is nil. This will cause panics if an - // unimplemented method is ever invoked, so we test this at initialization - // time to prevent it from happening at runtime later due to I/O. - if t, ok := srv.(interface{ testEmbeddedByValue() }); ok { - t.testEmbeddedByValue() - } s.RegisterService(&TokenService_ServiceDesc, srv) } diff --git a/go.mod b/go.mod index f79cb10647..1134d10b25 100644 --- a/go.mod +++ b/go.mod @@ -44,6 +44,7 @@ require ( github.com/spf13/cobra v1.8.1 github.com/spf13/viper v1.19.0 github.com/sqids/sqids-go v0.4.1 + github.com/sqids/sqids-go v0.4.1 github.com/stretchr/testify v1.10.0 go.etcd.io/bbolt v1.3.11 go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.58.0 diff --git a/pkg/messaging/message.pb.go b/pkg/messaging/message.pb.go index bdf2bb1231..c2833f72b6 100644 --- a/pkg/messaging/message.pb.go +++ b/pkg/messaging/message.pb.go @@ -38,9 +38,11 @@ type Message struct { func (x *Message) Reset() { *x = Message{} - mi := &file_pkg_messaging_message_proto_msgTypes[0] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) + if protoimpl.UnsafeEnabled { + mi := &file_pkg_messaging_message_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } } func (x *Message) String() string { @@ -51,7 +53,7 @@ func (*Message) ProtoMessage() {} func (x *Message) ProtoReflect() protoreflect.Message { mi := &file_pkg_messaging_message_proto_msgTypes[0] - if x != nil { + if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -157,6 +159,20 @@ func file_pkg_messaging_message_proto_init() { if File_pkg_messaging_message_proto != nil { return } + if !protoimpl.UnsafeEnabled { + file_pkg_messaging_message_proto_msgTypes[0].Exporter = func(v any, i int) any { + switch v := v.(*Message); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ From 4af3b94a58e285955fb078043e980ba060fc5ca0 Mon Sep 17 00:00:00 2001 From: nyagamunene Date: Mon, 28 Oct 2024 15:09:07 +0300 Subject: [PATCH 31/72] First implementation Signed-off-by: nyagamunene --- go.mod | 1 + 1 file changed, 1 insertion(+) diff --git a/go.mod b/go.mod index 1134d10b25..52b54b3997 100644 --- a/go.mod +++ b/go.mod @@ -170,6 +170,7 @@ require ( github.com/xeipuuv/gojsonschema v1.2.0 // indirect github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect go.opentelemetry.io/auto/sdk v1.1.0 // indirect + go.etcd.io/bbolt v1.3.11 // indirect go.opentelemetry.io/otel/metric v1.33.0 // indirect go.opentelemetry.io/proto/otlp v1.4.0 // indirect go.uber.org/atomic v1.11.0 // indirect From ac14bceaa16450cf993d372536b9f573899b0f9e Mon Sep 17 00:00:00 2001 From: nyagamunene Date: Mon, 28 Oct 2024 20:29:11 +0300 Subject: [PATCH 32/72] Update middleware, service and api Signed-off-by: nyagamunene --- pat/api/endpoint.go | 259 +++++++++++++++++++++++ pat/api/requests.go | 361 ++++++++++++++++++++++++++++++++ pat/api/responses.go | 208 ++++++++++++++++++ pat/api/transport.go | 271 ++++++++++++++++++++++++ pat/bolt/pat.go | 158 +++++++------- pat/hasher/hasher.go | 6 +- pat/middleware/authorization.go | 82 +++++++- pat/middleware/logging.go | 48 ++--- pat/middleware/metrics.go | 138 ++++++++++++ pat/pat.go | 27 +-- pat/service.go | 127 ++++------- pat/tracing/tracing.go | 164 +++++++++++++++ 12 files changed, 1644 insertions(+), 205 deletions(-) create mode 100644 pat/api/endpoint.go create mode 100644 pat/api/requests.go create mode 100644 pat/api/responses.go create mode 100644 pat/api/transport.go create mode 100644 pat/tracing/tracing.go diff --git a/pat/api/endpoint.go b/pat/api/endpoint.go new file mode 100644 index 0000000000..94cbdf5e80 --- /dev/null +++ b/pat/api/endpoint.go @@ -0,0 +1,259 @@ +// Copyright (c) Abstract Machines +// SPDX-License-Identifier: Apache-2.0 + +package pats + +import ( + "context" + + "github.com/absmach/magistrala/internal/api" + "github.com/absmach/magistrala/pat" + "github.com/absmach/magistrala/pkg/authn" + svcerr "github.com/absmach/magistrala/pkg/errors/service" + "github.com/go-kit/kit/endpoint" +) + +func createPATEndpoint(svc pat.Service) endpoint.Endpoint { + return func(ctx context.Context, request interface{}) (interface{}, error) { + req := request.(createPatReq) + if err := req.validate(); err != nil { + return nil, err + } + + session, ok := ctx.Value(api.SessionKey).(authn.Session) + if !ok { + return nil, svcerr.ErrAuthentication + } + + pat, err := svc.CreatePAT(ctx, session, req.Name, req.Description, req.Duration, req.Scope) + if err != nil { + return nil, err + } + + return createPatRes{pat}, nil + } +} + +func retrievePATEndpoint(svc pat.Service) endpoint.Endpoint { + return func(ctx context.Context, request interface{}) (interface{}, error) { + req := request.(retrievePatReq) + if err := req.validate(); err != nil { + return nil, err + } + + session, ok := ctx.Value(api.SessionKey).(authn.Session) + if !ok { + return nil, svcerr.ErrAuthentication + } + + pat, err := svc.RetrievePAT(ctx, session, req.id) + if err != nil { + return nil, err + } + + return retrievePatRes{pat}, nil + } +} + +func updatePATNameEndpoint(svc pat.Service) endpoint.Endpoint { + return func(ctx context.Context, request interface{}) (interface{}, error) { + req := request.(updatePatNameReq) + if err := req.validate(); err != nil { + return nil, err + } + + session, ok := ctx.Value(api.SessionKey).(authn.Session) + if !ok { + return nil, svcerr.ErrAuthentication + } + pat, err := svc.UpdatePATName(ctx, session, req.id, req.Name) + if err != nil { + return nil, err + } + + return updatePatNameRes{pat}, nil + } +} + +func updatePATDescriptionEndpoint(svc pat.Service) endpoint.Endpoint { + return func(ctx context.Context, request interface{}) (interface{}, error) { + req := request.(updatePatDescriptionReq) + if err := req.validate(); err != nil { + return nil, err + } + + session, ok := ctx.Value(api.SessionKey).(authn.Session) + if !ok { + return nil, svcerr.ErrAuthentication + } + + pat, err := svc.UpdatePATDescription(ctx, session, req.id, req.Description) + if err != nil { + return nil, err + } + + return updatePatDescriptionRes{pat}, nil + } +} + +func listPATSEndpoint(svc pat.Service) endpoint.Endpoint { + return func(ctx context.Context, request interface{}) (interface{}, error) { + req := request.(listPatsReq) + if err := req.validate(); err != nil { + return nil, err + } + + session, ok := ctx.Value(api.SessionKey).(authn.Session) + if !ok { + return nil, svcerr.ErrAuthentication + } + + pm := pat.PATSPageMeta{ + Limit: req.limit, + Offset: req.offset, + } + patsPage, err := svc.ListPATS(ctx, session, pm) + if err != nil { + return nil, err + } + + return listPatsRes{patsPage}, nil + } +} + +func deletePATEndpoint(svc pat.Service) endpoint.Endpoint { + return func(ctx context.Context, request interface{}) (interface{}, error) { + req := request.(deletePatReq) + if err := req.validate(); err != nil { + return nil, err + } + + session, ok := ctx.Value(api.SessionKey).(authn.Session) + if !ok { + return nil, svcerr.ErrAuthentication + } + + if err := svc.DeletePAT(ctx, session, req.id); err != nil { + return nil, err + } + + return deletePatRes{}, nil + } +} + +func resetPATSecretEndpoint(svc pat.Service) endpoint.Endpoint { + return func(ctx context.Context, request interface{}) (interface{}, error) { + req := request.(resetPatSecretReq) + if err := req.validate(); err != nil { + return nil, err + } + + session, ok := ctx.Value(api.SessionKey).(authn.Session) + if !ok { + return nil, svcerr.ErrAuthentication + } + + pat, err := svc.ResetPATSecret(ctx, session, req.id, req.Duration) + if err != nil { + return nil, err + } + + return resetPatSecretRes{pat}, nil + } +} + +func revokePATSecretEndpoint(svc pat.Service) endpoint.Endpoint { + return func(ctx context.Context, request interface{}) (interface{}, error) { + req := request.(revokePatSecretReq) + if err := req.validate(); err != nil { + return nil, err + } + + session, ok := ctx.Value(api.SessionKey).(authn.Session) + if !ok { + return nil, svcerr.ErrAuthentication + } + + if err := svc.RevokePATSecret(ctx, session, req.id); err != nil { + return nil, err + } + + return revokePatSecretRes{}, nil + } +} + +func addPATScopeEntryEndpoint(svc pat.Service) endpoint.Endpoint { + return func(ctx context.Context, request interface{}) (interface{}, error) { + req := request.(addPatScopeEntryReq) + if err := req.validate(); err != nil { + return nil, err + } + + session, ok := ctx.Value(api.SessionKey).(authn.Session) + if !ok { + return nil, svcerr.ErrAuthentication + } + + scope, err := svc.AddPATScopeEntry(ctx, session, req.id, req.PlatformEntityType, req.OptionalDomainID, req.OptionalDomainEntityType, req.Operation, req.EntityIDs...) + if err != nil { + return nil, err + } + + return addPatScopeEntryRes{scope}, nil + } +} + +func removePATScopeEntryEndpoint(svc pat.Service) endpoint.Endpoint { + return func(ctx context.Context, request interface{}) (interface{}, error) { + req := request.(removePatScopeEntryReq) + if err := req.validate(); err != nil { + return nil, err + } + + session, ok := ctx.Value(api.SessionKey).(authn.Session) + if !ok { + return nil, svcerr.ErrAuthentication + } + + scope, err := svc.RemovePATScopeEntry(ctx, session, req.id, req.PlatformEntityType, req.OptionalDomainID, req.OptionalDomainEntityType, req.Operation, req.EntityIDs...) + if err != nil { + return nil, err + } + return removePatScopeEntryRes{scope}, nil + } +} + +func clearPATAllScopeEntryEndpoint(svc pat.Service) endpoint.Endpoint { + return func(ctx context.Context, request interface{}) (interface{}, error) { + req := request.(clearAllScopeEntryReq) + if err := req.validate(); err != nil { + return nil, err + } + + session, ok := ctx.Value(api.SessionKey).(authn.Session) + if !ok { + return nil, svcerr.ErrAuthentication + } + + if err := svc.ClearPATAllScopeEntry(ctx, session, req.id); err != nil { + return nil, err + } + + return clearAllScopeEntryRes{}, nil + } +} + +func authorizePATEndpoint(svc pat.Service) endpoint.Endpoint { + return func(ctx context.Context, request interface{}) (interface{}, error) { + req := request.(authorizePATReq) + if err := req.validate(); err != nil { + return nil, err + } + + if err := svc.AuthorizePAT(ctx, req.token, req.PlatformEntityType, req.OptionalDomainID, req.OptionalDomainEntityType, req.Operation, req.EntityIDs...); err != nil { + return nil, err + } + + return authorizePATRes{}, nil + } +} diff --git a/pat/api/requests.go b/pat/api/requests.go new file mode 100644 index 0000000000..1822ec457e --- /dev/null +++ b/pat/api/requests.go @@ -0,0 +1,361 @@ +// Copyright (c) Abstract Machines +// SPDX-License-Identifier: Apache-2.0 + +package pats + +import ( + "encoding/json" + "strings" + "time" + + "github.com/absmach/magistrala/pat" + "github.com/absmach/magistrala/pkg/apiutil" +) + +type createPatReq struct { + token string + Name string `json:"name,omitempty"` + Description string `json:"description,omitempty"` + Duration time.Duration `json:"duration,omitempty"` + Scope pat.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 pat.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 + } + + 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 pat.PlatformEntityType `json:"platform_entity_type,omitempty"` + OptionalDomainID string `json:"optional_domain_id,omitempty"` + OptionalDomainEntityType pat.DomainEntityType `json:"optional_domain_entity_type,omitempty"` + Operation pat.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 := pat.ParsePlatformEntityType(temp.PlatformEntityType) + if err != nil { + return err + } + odt, err := pat.ParseDomainEntityType(temp.OptionalDomainEntityType) + if err != nil { + return err + } + op, err := pat.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 pat.PlatformEntityType `json:"platform_entity_type,omitempty"` + OptionalDomainID string `json:"optional_domain_id,omitempty"` + OptionalDomainEntityType pat.DomainEntityType `json:"optional_domain_entity_type,omitempty"` + Operation pat.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 := pat.ParsePlatformEntityType(temp.PlatformEntityType) + if err != nil { + return err + } + odt, err := pat.ParseDomainEntityType(temp.OptionalDomainEntityType) + if err != nil { + return err + } + op, err := pat.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 authorizePATReq struct { + token string + PlatformEntityType pat.PlatformEntityType `json:"platform_entity_type,omitempty"` + OptionalDomainID string `json:"optional_domain_id,omitempty"` + OptionalDomainEntityType pat.DomainEntityType `json:"optional_domain_entity_type,omitempty"` + Operation pat.OperationType `json:"operation,omitempty"` + EntityIDs []string `json:"entity_ids,omitempty"` +} + +func (tcpsr *authorizePATReq) 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 + } + + tcpsr.OptionalDomainID = temp.OptionalDomainID + tcpsr.EntityIDs = temp.EntityIDs + + pet, err := pat.ParsePlatformEntityType(temp.PlatformEntityType) + if err != nil { + return err + } + tcpsr.PlatformEntityType = pet + + if temp.OptionalDomainEntityType != "" { + odt, err := pat.ParseDomainEntityType(temp.OptionalDomainEntityType) + if err != nil { + return err + } + tcpsr.OptionalDomainEntityType = odt + } + + if temp.OptionalDomainID != "" { + op, err := pat.ParseOperationType(temp.Operation) + if err != nil { + return err + } + tcpsr.Operation = op + } + + return nil +} + +func (req authorizePATReq) validate() (err error) { + if req.token == "" { + return apiutil.ErrBearerToken + } + + return nil +} diff --git a/pat/api/responses.go b/pat/api/responses.go new file mode 100644 index 0000000000..b2d0fe57de --- /dev/null +++ b/pat/api/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/pat" +) + +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 { + pat.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 { + pat.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 { + pat.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 { + pat.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 { + pat.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 { + pat.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 { + pat.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 { + pat.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 authorizePATRes struct{} + +func (res authorizePATRes) Code() int { + return http.StatusNoContent +} + +func (res authorizePATRes) Headers() map[string]string { + return map[string]string{} +} + +func (res authorizePATRes) Empty() bool { + return true +} diff --git a/pat/api/transport.go b/pat/api/transport.go new file mode 100644 index 0000000000..95d0f45efa --- /dev/null +++ b/pat/api/transport.go @@ -0,0 +1,271 @@ +// 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/internal/api" + "github.com/absmach/magistrala/pat" + "github.com/absmach/magistrala/pkg/apiutil" + mgauthn "github.com/absmach/magistrala/pkg/authn" + "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 pat.Service, mux *chi.Mux, authn mgauthn.Authentication, logger *slog.Logger) *chi.Mux { + opts := []kithttp.ServerOption{ + kithttp.ServerErrorEncoder(apiutil.LoggingErrorEncoder(logger, api.EncodeError)), + } + mux.Group(func(r chi.Router) { + mux.Use(api.AuthenticateMiddleware(authn, true)) + + mux.Route("/pats", func(r chi.Router) { + r.Post("/", kithttp.NewServer( + createPATEndpoint(svc), + decodeCreatePATRequest, + api.EncodeResponse, + opts..., + ).ServeHTTP) + + r.Get("/{id}", kithttp.NewServer( + (retrievePATEndpoint(svc)), + decodeRetrievePATRequest, + api.EncodeResponse, + opts..., + ).ServeHTTP) + + r.Put("/{id}/name", kithttp.NewServer( + (updatePATNameEndpoint(svc)), + decodeUpdatePATNameRequest, + api.EncodeResponse, + opts..., + ).ServeHTTP) + + r.Put("/{id}/description", kithttp.NewServer( + (updatePATDescriptionEndpoint(svc)), + decodeUpdatePATDescriptionRequest, + api.EncodeResponse, + opts..., + ).ServeHTTP) + + r.Get("/", kithttp.NewServer( + (listPATSEndpoint(svc)), + decodeListPATSRequest, + api.EncodeResponse, + opts..., + ).ServeHTTP) + + r.Delete("/{id}", kithttp.NewServer( + (deletePATEndpoint(svc)), + decodeDeletePATRequest, + api.EncodeResponse, + opts..., + ).ServeHTTP) + + r.Put("/{id}/secret/reset", kithttp.NewServer( + (resetPATSecretEndpoint(svc)), + decodeResetPATSecretRequest, + api.EncodeResponse, + opts..., + ).ServeHTTP) + + r.Put("/{id}/secret/revoke", kithttp.NewServer( + (revokePATSecretEndpoint(svc)), + decodeRevokePATSecretRequest, + api.EncodeResponse, + opts..., + ).ServeHTTP) + + r.Put("/{id}/scope/add", kithttp.NewServer( + (addPATScopeEntryEndpoint(svc)), + decodeAddPATScopeEntryRequest, + api.EncodeResponse, + opts..., + ).ServeHTTP) + + r.Put("/{id}/scope/remove", kithttp.NewServer( + (removePATScopeEntryEndpoint(svc)), + decodeRemovePATScopeEntryRequest, + api.EncodeResponse, + opts..., + ).ServeHTTP) + + r.Delete("/{id}/scope", kithttp.NewServer( + (clearPATAllScopeEntryEndpoint(svc)), + decodeClearPATAllScopeEntryRequest, + api.EncodeResponse, + opts..., + ).ServeHTTP) + + r.Post("/authorize", kithttp.NewServer( + (authorizePATEndpoint(svc)), + decodeAuthorizePATRequest, + 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 decodeUpdatePATNameRequest(_ 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 decodeUpdatePATDescriptionRequest(_ 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 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) + } + 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, + } + return req, nil +} + +func decodeDeletePATRequest(_ context.Context, r *http.Request) (interface{}, error) { + return deletePatReq{ + token: apiutil.ExtractBearerToken(r), + id: chi.URLParam(r, "id"), + }, nil +} + +func decodeResetPATSecretRequest(_ 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 decodeRevokePATSecretRequest(_ context.Context, r *http.Request) (interface{}, error) { + return revokePatSecretReq{ + token: apiutil.ExtractBearerToken(r), + id: chi.URLParam(r, "id"), + }, nil +} + +func decodeAddPATScopeEntryRequest(_ 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 decodeRemovePATScopeEntryRequest(_ 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 decodeClearPATAllScopeEntryRequest(_ 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 decodeAuthorizePATRequest(_ context.Context, r *http.Request) (interface{}, error) { + if !strings.Contains(r.Header.Get("Content-Type"), contentType) { + return nil, apiutil.ErrUnsupportedContentType + } + + req := authorizePATReq{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/pat/bolt/pat.go b/pat/bolt/pat.go index 4534dc4e85..b3af87890b 100644 --- a/pat/bolt/pat.go +++ b/pat/bolt/pat.go @@ -11,7 +11,7 @@ import ( "strings" "time" - "github.com/absmach/magistrala/auth" + "github.com/absmach/magistrala/pat" "github.com/absmach/magistrala/pkg/errors" repoerr "github.com/absmach/magistrala/pkg/errors/repository" bolt "go.etcd.io/bbolt" @@ -52,14 +52,14 @@ type patRepo struct { // NewPATSRepository instantiates a bolt // implementation of PAT repository. -func NewPATSRepository(db *bolt.DB, bucketName string) auth.PATSRepository { +func NewPATSRepository(db *bolt.DB, bucketName string) pat.PATSRepository { return &patRepo{ db: db, bucketName: bucketName, } } -func (pr *patRepo) Save(ctx context.Context, pat auth.PAT) error { +func (pr *patRepo) Save(ctx context.Context, pat pat.PAT) error { idxKey := []byte(pat.User + keySeparator + patKey + keySeparator + pat.ID) kv, err := patToKeyValue(pat) if err != nil { @@ -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) { +func (pr *patRepo) Retrieve(ctx context.Context, userID, patID string) (pat.PAT, error) { prefix := []byte(patID + keySeparator) kv := map[string][]byte{} if err := pr.db.View(func(tx *bolt.Tx) error { @@ -101,7 +101,7 @@ func (pr *patRepo) Retrieve(ctx context.Context, userID, patID string) (auth.PAT } return nil }); err != nil { - return auth.PAT{}, err + return pat.PAT{}, err } return keyValueToPAT(kv) @@ -126,15 +126,15 @@ func (pr *patRepo) RetrieveSecretAndRevokeStatus(ctx context.Context, userID, pa return secretHash, revoked, nil } -func (pr *patRepo) UpdateName(ctx context.Context, userID, patID, name string) (auth.PAT, error) { +func (pr *patRepo) UpdateName(ctx context.Context, userID, patID, name string) (pat.PAT, error) { return pr.updatePATField(ctx, userID, patID, nameKey, []byte(name)) } -func (pr *patRepo) UpdateDescription(ctx context.Context, userID, patID, description string) (auth.PAT, error) { +func (pr *patRepo) UpdateDescription(ctx context.Context, userID, patID, description string) (pat.PAT, error) { 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) { +func (pr *patRepo) UpdateTokenHash(ctx context.Context, userID, patID, tokenHash string, expiryAt time.Time) (pat.PAT, error) { prefix := []byte(patID + keySeparator) kv := map[string][]byte{} if err := pr.db.Update(func(tx *bolt.Tx) error { @@ -157,12 +157,12 @@ func (pr *patRepo) UpdateTokenHash(ctx context.Context, userID, patID, tokenHash } return nil }); err != nil { - return auth.PAT{}, err + return pat.PAT{}, err } return keyValueToPAT(kv) } -func (pr *patRepo) RetrieveAll(ctx context.Context, userID string, pm auth.PATSPageMeta) (auth.PATSPage, error) { +func (pr *patRepo) RetrieveAll(ctx context.Context, userID string, pm pat.PATSPageMeta) (pat.PATSPage, error) { prefix := []byte(userID + keySeparator + patKey + keySeparator) patIDs := []string{} @@ -179,14 +179,14 @@ func (pr *patRepo) RetrieveAll(ctx context.Context, userID string, pm auth.PATSP } return nil }); err != nil { - return auth.PATSPage{}, err + return pat.PATSPage{}, err } total := len(patIDs) - var pats []auth.PAT + var pats []pat.PAT - patsPage := auth.PATSPage{ + patsPage := pat.PATSPage{ Total: uint64(total), Limit: pm.Limit, Offset: pm.Offset, @@ -282,7 +282,7 @@ 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) { +func (pr *patRepo) AddScopeEntry(ctx context.Context, userID, patID string, platformEntityType pat.PlatformEntityType, optionalDomainID string, optionalDomainEntityType pat.DomainEntityType, operation pat.OperationType, entityIDs ...string) (pat.Scope, error) { prefix := []byte(patID + keySeparator + scopeKey) var rKV map[string][]byte if err := pr.db.Update(func(tx *bolt.Tx) error { @@ -306,15 +306,15 @@ func (pr *patRepo) AddScopeEntry(ctx context.Context, userID, patID string, plat } return nil }); err != nil { - return auth.Scope{}, err + return pat.Scope{}, err } 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) { +func (pr *patRepo) RemoveScopeEntry(ctx context.Context, userID, patID string, platformEntityType pat.PlatformEntityType, optionalDomainID string, optionalDomainEntityType pat.DomainEntityType, operation pat.OperationType, entityIDs ...string) (pat.Scope, error) { if len(entityIDs) == 0 { - return auth.Scope{}, repoerr.ErrMalformedEntity + return pat.Scope{}, repoerr.ErrMalformedEntity } prefix := []byte(patID + keySeparator + scopeKey) var rKV map[string][]byte @@ -339,12 +339,12 @@ func (pr *patRepo) RemoveScopeEntry(ctx context.Context, userID, patID string, p } return nil }); err != nil { - return auth.Scope{}, err + return pat.Scope{}, err } 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 { +func (pr *patRepo) CheckScopeEntry(ctx context.Context, userID, patID string, platformEntityType pat.PlatformEntityType, optionalDomainID string, optionalDomainEntityType pat.DomainEntityType, operation pat.OperationType, entityIDs ...string) error { return pr.db.Update(func(tx *bolt.Tx) error { b, err := pr.retrieveUserBucket(tx, userID, patID, repoerr.ErrViewEntity) if err != nil { @@ -373,7 +373,7 @@ 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) { +func (pr *patRepo) updatePATField(_ context.Context, userID, patID, key string, value []byte) (pat.PAT, error) { prefix := []byte(patID + keySeparator) kv := map[string][]byte{} if err := pr.db.Update(func(tx *bolt.Tx) error { @@ -393,7 +393,7 @@ func (pr *patRepo) updatePATField(_ context.Context, userID, patID, key string, } return nil }); err != nil { - return auth.PAT{}, err + return pat.PAT{}, err } return keyValueToPAT(kv) } @@ -433,7 +433,7 @@ func (pr *patRepo) retrieveRootBucket(tx *bolt.Tx) (*bolt.Bucket, error) { return rootBucket, nil } -func patToKeyValue(pat auth.PAT) (map[string][]byte, error) { +func patToKeyValue(pat pat.PAT) (map[string][]byte, error) { kv := map[string][]byte{ idKey: []byte(pat.ID), userKey: []byte(pat.User), @@ -457,10 +457,10 @@ func patToKeyValue(pat auth.PAT) (map[string][]byte, error) { return kv, nil } -func scopeToKeyValue(scope auth.Scope) (map[string][]byte, error) { +func scopeToKeyValue(scope pat.Scope) (map[string][]byte, error) { kv := map[string][]byte{} for opType, scopeValue := range scope.Users { - tempKV, err := scopeEntryToKeyValue(auth.PlatformUsersScope, "", auth.DomainNullScope, opType, scopeValue.Values()...) + tempKV, err := scopeEntryToKeyValue(pat.PlatformUsersScope, "", pat.DomainNullScope, opType, scopeValue.Values()...) if err != nil { return nil, err } @@ -470,7 +470,7 @@ func scopeToKeyValue(scope auth.Scope) (map[string][]byte, error) { } for domainID, domainScope := range scope.Domains { for opType, scopeValue := range domainScope.DomainManagement { - tempKV, err := scopeEntryToKeyValue(auth.PlatformDomainsScope, domainID, auth.DomainManagementScope, opType, scopeValue.Values()...) + tempKV, err := scopeEntryToKeyValue(pat.PlatformDomainsScope, domainID, pat.DomainManagementScope, opType, scopeValue.Values()...) if err != nil { return nil, errors.Wrap(repoerr.ErrCreateEntity, err) } @@ -480,7 +480,7 @@ func scopeToKeyValue(scope auth.Scope) (map[string][]byte, error) { } for entityType, scope := range domainScope.Entities { for opType, scopeValue := range scope { - tempKV, err := scopeEntryToKeyValue(auth.PlatformDomainsScope, domainID, entityType, opType, scopeValue.Values()...) + tempKV, err := scopeEntryToKeyValue(pat.PlatformDomainsScope, domainID, entityType, opType, scopeValue.Values()...) if err != nil { return nil, errors.Wrap(repoerr.ErrCreateEntity, err) } @@ -493,7 +493,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) { +func scopeEntryToKeyValue(platformEntityType pat.PlatformEntityType, optionalDomainID string, optionalDomainEntityType pat.DomainEntityType, operation pat.OperationType, entityIDs ...string) (map[string][]byte, error) { if len(entityIDs) == 0 { return nil, repoerr.ErrMalformedEntity } @@ -518,7 +518,7 @@ func scopeEntryToKeyValue(platformEntityType auth.PlatformEntityType, optionalDo return kv, nil } -func scopeRootKey(platformEntityType auth.PlatformEntityType, optionalDomainID string, optionalDomainEntityType auth.DomainEntityType, operation auth.OperationType) (string, error) { +func scopeRootKey(platformEntityType pat.PlatformEntityType, optionalDomainID string, optionalDomainEntityType pat.DomainEntityType, operation pat.OperationType) (string, error) { op, err := operation.ValidString() if err != nil { return "", errors.Wrap(repoerr.ErrMalformedEntity, err) @@ -532,9 +532,9 @@ func scopeRootKey(platformEntityType auth.PlatformEntityType, optionalDomainID s rootKey.WriteString(keySeparator) switch platformEntityType { - case auth.PlatformUsersScope: + case pat.PlatformUsersScope: rootKey.WriteString(op) - case auth.PlatformDomainsScope: + case pat.PlatformDomainsScope: if optionalDomainID == "" { return "", fmt.Errorf("failed to add platform %s scope: invalid domain id", platformEntityType.String()) } @@ -554,8 +554,8 @@ func scopeRootKey(platformEntityType auth.PlatformEntityType, optionalDomainID s return rootKey.String(), nil } -func keyValueToBasicPAT(kv map[string][]byte) auth.PAT { - var pat auth.PAT +func keyValueToBasicPAT(kv map[string][]byte) pat.PAT { + var pat pat.PAT for k, v := range kv { switch { case strings.HasSuffix(k, keySeparator+idKey): @@ -583,157 +583,157 @@ func keyValueToBasicPAT(kv map[string][]byte) auth.PAT { return pat } -func keyValueToPAT(kv map[string][]byte) (auth.PAT, error) { - pat := keyValueToBasicPAT(kv) +func keyValueToPAT(kv map[string][]byte) (pat.PAT, error) { + res := keyValueToBasicPAT(kv) scope, err := parseKeyValueToScope(kv) if err != nil { - return auth.PAT{}, err + return pat.PAT{}, err } - pat.Scope = scope - return pat, nil + res.Scope = scope + return res, nil } -func parseKeyValueToScope(kv map[string][]byte) (auth.Scope, error) { - scope := auth.Scope{ - Domains: make(map[string]auth.DomainScope), +func parseKeyValueToScope(kv map[string][]byte) (pat.Scope, error) { + scope := pat.Scope{ + Domains: make(map[string]pat.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]) + platformEntityType, err := pat.ParsePlatformEntityType(keyParts[2]) if err != nil { - return auth.Scope{}, errors.Wrap(repoerr.ErrViewEntity, err) + return pat.Scope{}, errors.Wrap(repoerr.ErrViewEntity, err) } switch platformEntityType { - case auth.PlatformUsersScope: + case pat.PlatformUsersScope: scope.Users, err = parseOperation(platformEntityType, scope.Users, key, keyParts, value) if err != nil { - return auth.Scope{}, errors.Wrap(repoerr.ErrViewEntity, err) + return pat.Scope{}, errors.Wrap(repoerr.ErrViewEntity, err) } - case auth.PlatformDomainsScope: + case pat.PlatformDomainsScope: if len(keyParts) < 6 { - return auth.Scope{}, fmt.Errorf("invalid scope key format: %s", key) + return pat.Scope{}, fmt.Errorf("invalid scope key format: %s", key) } domainID := keyParts[3] if scope.Domains == nil { - scope.Domains = make(map[string]auth.DomainScope) + scope.Domains = make(map[string]pat.DomainScope) } if _, ok := scope.Domains[domainID]; !ok { - scope.Domains[domainID] = auth.DomainScope{} + scope.Domains[domainID] = pat.DomainScope{} } domainScope := scope.Domains[domainID] entityType := keyParts[4] switch entityType { - case auth.DomainManagementScope.String(): + case pat.DomainManagementScope.String(): domainScope.DomainManagement, err = parseOperation(platformEntityType, domainScope.DomainManagement, key, keyParts, value) if err != nil { - return auth.Scope{}, errors.Wrap(repoerr.ErrViewEntity, err) + return pat.Scope{}, errors.Wrap(repoerr.ErrViewEntity, err) } default: - etype, err := auth.ParseDomainEntityType(entityType) + etype, err := pat.ParseDomainEntityType(entityType) if err != nil { - return auth.Scope{}, fmt.Errorf("key %s invalid entity type %s : %w", key, entityType, err) + return pat.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) + domainScope.Entities = make(map[pat.DomainEntityType]pat.OperationScope) } if _, ok := domainScope.Entities[etype]; !ok { - domainScope.Entities[etype] = auth.OperationScope{} + domainScope.Entities[etype] = pat.OperationScope{} } entityOperationScope := domainScope.Entities[etype] entityOperationScope, err = parseOperation(platformEntityType, entityOperationScope, key, keyParts, value) if err != nil { - return auth.Scope{}, errors.Wrap(repoerr.ErrViewEntity, err) + return pat.Scope{}, errors.Wrap(repoerr.ErrViewEntity, err) } 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 pat.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) { +func parseOperation(platformEntityType pat.PlatformEntityType, opScope pat.OperationScope, key string, keyParts []string, value []byte) (pat.OperationScope, error) { if opScope == nil { - opScope = make(map[auth.OperationType]auth.ScopeValue) + opScope = make(map[pat.OperationType]pat.ScopeValue) } if err := validateOperation(platformEntityType, opScope, key, keyParts, value); err != nil { - return auth.OperationScope{}, err + return pat.OperationScope{}, err } switch string(value) { case string(entityValue): - opType, err := auth.ParseOperationType(keyParts[len(keyParts)-2]) + opType, err := pat.ParseOperationType(keyParts[len(keyParts)-2]) if err != nil { - return auth.OperationScope{}, errors.Wrap(repoerr.ErrViewEntity, err) + return pat.OperationScope{}, errors.Wrap(repoerr.ErrViewEntity, err) } entityID := keyParts[len(keyParts)-1] if _, oValueExists := opScope[opType]; !oValueExists { - opScope[opType] = &auth.SelectedIDs{} + opScope[opType] = &pat.SelectedIDs{} } 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) + return pat.OperationScope{}, fmt.Errorf("failed to add scope key %s with entity value %v : %w", key, entityID, err) } opScope[opType] = oValue case string(anyIDValue): - opType, err := auth.ParseOperationType(keyParts[len(keyParts)-1]) + opType, err := pat.ParseOperationType(keyParts[len(keyParts)-1]) if err != nil { - return auth.OperationScope{}, errors.Wrap(repoerr.ErrViewEntity, err) + return pat.OperationScope{}, errors.Wrap(repoerr.ErrViewEntity, err) } 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) + if _, ok := oValue.(*pat.AnyIDs); !ok { + return pat.OperationScope{}, fmt.Errorf("failed to add scope key %s with entity anyIDs scope value : key already initialized with different type", key) } } - opScope[opType] = &auth.AnyIDs{} + opScope[opType] = &pat.AnyIDs{} case string(selectedIDsValue): - opType, err := auth.ParseOperationType(keyParts[len(keyParts)-1]) + opType, err := pat.ParseOperationType(keyParts[len(keyParts)-1]) if err != nil { - return auth.OperationScope{}, errors.Wrap(repoerr.ErrViewEntity, err) + return pat.OperationScope{}, errors.Wrap(repoerr.ErrViewEntity, err) } 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 _, ok := oValue.(*pat.SelectedIDs); !ok { + return pat.OperationScope{}, fmt.Errorf("failed to add scope key %s with entity selectedIDs scope value : key already initialized with different type", key) } } if !oValueExists { - opScope[opType] = &auth.SelectedIDs{} + opScope[opType] = &pat.SelectedIDs{} } default: - return auth.OperationScope{}, fmt.Errorf("key %s have invalid value %v", key, value) + return pat.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 { +func validateOperation(platformEntityType pat.PlatformEntityType, _ pat.OperationScope, key string, keyParts []string, value []byte) error { expectedKeyPartsLength := 0 switch string(value) { case string(entityValue): switch platformEntityType { - case auth.PlatformDomainsScope: + case pat.PlatformDomainsScope: expectedKeyPartsLength = 7 - case auth.PlatformUsersScope: + case pat.PlatformUsersScope: expectedKeyPartsLength = 5 default: return fmt.Errorf("invalid platform entity type : %s", platformEntityType.String()) } case string(selectedIDsValue), string(anyIDValue): switch platformEntityType { - case auth.PlatformDomainsScope: + case pat.PlatformDomainsScope: expectedKeyPartsLength = 6 - case auth.PlatformUsersScope: + case pat.PlatformUsersScope: expectedKeyPartsLength = 4 default: return fmt.Errorf("invalid platform entity type : %s", platformEntityType.String()) diff --git a/pat/hasher/hasher.go b/pat/hasher/hasher.go index c417bf7b80..4d74669f02 100644 --- a/pat/hasher/hasher.go +++ b/pat/hasher/hasher.go @@ -10,7 +10,7 @@ import ( "strings" "time" - "github.com/absmach/magistrala/auth" + "github.com/absmach/magistrala/pat" "github.com/absmach/magistrala/pkg/errors" "golang.org/x/crypto/scrypt" ) @@ -24,12 +24,12 @@ var ( errDecode = errors.New("failed to decode") ) -var _ auth.Hasher = (*bcryptHasher)(nil) +var _ pat.Hasher = (*bcryptHasher)(nil) type bcryptHasher struct{} // New instantiates a bcrypt-based hasher implementation. -func New() auth.Hasher { +func New() pat.Hasher { return &bcryptHasher{} } diff --git a/pat/middleware/authorization.go b/pat/middleware/authorization.go index 71995a25ab..1064590088 100644 --- a/pat/middleware/authorization.go +++ b/pat/middleware/authorization.go @@ -1,4 +1,84 @@ // Copyright (c) Abstract Machines // SPDX-License-Identifier: Apache-2.0 -package middleware \ No newline at end of file +package middleware + +import ( + "context" + "time" + + "github.com/absmach/magistrala/pat" + "github.com/absmach/magistrala/pkg/authn" + mgauthz "github.com/absmach/magistrala/pkg/authz" +) + +var _ pat.Service = (*authorizationMiddleware)(nil) + +type authorizationMiddleware struct { + svc pat.Service + authz mgauthz.Authorization +} + +// AuthorizationMiddleware adds authorization to the clients service. +func AuthorizationMiddleware(entityType string, svc pat.Service, authz mgauthz.Authorization) (pat.Service, error) { + return &authorizationMiddleware{ + svc: svc, + authz: authz, + }, nil +} + +func (am *authorizationMiddleware) CreatePAT(ctx context.Context, session authn.Session, name, description string, duration time.Duration, scope pat.Scope) (pat.PAT, error) { + return am.svc.CreatePAT(ctx, session, name, description, duration, scope) +} + +func (am *authorizationMiddleware) UpdatePATName(ctx context.Context, session authn.Session, patID, name string) (pat.PAT, error) { + return am.svc.UpdatePATName(ctx, session, patID, name) +} + +func (am *authorizationMiddleware) UpdatePATDescription(ctx context.Context, session authn.Session, patID, description string) (pat.PAT, error) { + return am.svc.UpdatePATDescription(ctx, session, patID, description) +} + +func (am *authorizationMiddleware) RetrievePAT(ctx context.Context, session authn.Session, patID string) (pat.PAT, error) { + return am.svc.RetrievePAT(ctx, session, patID) +} + +func (am *authorizationMiddleware) ListPATS(ctx context.Context, session authn.Session, pm pat.PATSPageMeta) (pat.PATSPage, error) { + return am.svc.ListPATS(ctx, session, pm) +} + +func (am *authorizationMiddleware) DeletePAT(ctx context.Context, session authn.Session, patID string) error { + return am.svc.DeletePAT(ctx, session, patID) +} + +func (am *authorizationMiddleware) ResetPATSecret(ctx context.Context, session authn.Session, patID string, duration time.Duration) (pat.PAT, error) { + return am.svc.ResetPATSecret(ctx, session, patID, duration) +} + +func (am *authorizationMiddleware) RevokePATSecret(ctx context.Context, session authn.Session, patID string) error { + return am.svc.RevokePATSecret(ctx, session, patID) +} + +func (am *authorizationMiddleware) AddPATScopeEntry(ctx context.Context, session authn.Session, patID string, platformEntityType pat.PlatformEntityType, optionalDomainID string, optionalDomainEntityType pat.DomainEntityType, operation pat.OperationType, entityIDs ...string) (pat.Scope, error) { + return am.svc.AddPATScopeEntry(ctx, session, patID, platformEntityType, optionalDomainID, optionalDomainEntityType, operation, entityIDs...) +} + +func (am *authorizationMiddleware) RemovePATScopeEntry(ctx context.Context, session authn.Session, patID string, platformEntityType pat.PlatformEntityType, optionalDomainID string, optionalDomainEntityType pat.DomainEntityType, operation pat.OperationType, entityIDs ...string) (pat.Scope, error) { + return am.svc.RemovePATScopeEntry(ctx, session, patID, platformEntityType, optionalDomainID, optionalDomainEntityType, operation, entityIDs...) +} + +func (am *authorizationMiddleware) ClearPATAllScopeEntry(ctx context.Context, session authn.Session, patID string) error { + return am.svc.ClearPATAllScopeEntry(ctx, session, patID) +} + +func (am *authorizationMiddleware) IdentifyPAT(ctx context.Context, secret string) (pat.PAT, error) { + return am.svc.IdentifyPAT(ctx, secret) +} + +func (am *authorizationMiddleware) AuthorizePAT(ctx context.Context, paToken string, platformEntityType pat.PlatformEntityType, optionalDomainID string, optionalDomainEntityType pat.DomainEntityType, operation pat.OperationType, entityIDs ...string) error { + return am.svc.AuthorizePAT(ctx, paToken, platformEntityType, optionalDomainID, optionalDomainEntityType, operation, entityIDs...) +} + +func (am *authorizationMiddleware) CheckPAT(ctx context.Context, userID, patID string, platformEntityType pat.PlatformEntityType, optionalDomainID string, optionalDomainEntityType pat.DomainEntityType, operation pat.OperationType, entityIDs ...string) error { + return am.svc.CheckPAT(ctx, userID, patID, platformEntityType, optionalDomainID, optionalDomainEntityType, operation, entityIDs...) +} diff --git a/pat/middleware/logging.go b/pat/middleware/logging.go index 650eab5115..51767d39c1 100644 --- a/pat/middleware/logging.go +++ b/pat/middleware/logging.go @@ -5,11 +5,11 @@ package middleware import ( "context" - "fmt" "log/slog" "time" "github.com/absmach/magistrala/pat" + "github.com/absmach/magistrala/pkg/authn" ) var _ pat.Service = (*loggingMiddleware)(nil) @@ -24,7 +24,7 @@ func LoggingMiddleware(svc pat.Service, logger *slog.Logger) pat.Service { return &loggingMiddleware{logger, svc} } -func (lm *loggingMiddleware) CreatePAT(ctx context.Context, token, name, description string, duration time.Duration, scope pat.Scope) (pa pat.PAT, err error) { +func (lm *loggingMiddleware) CreatePAT(ctx context.Context, session authn.Session, name, description string, duration time.Duration, scope pat.Scope) (pa pat.PAT, err error) { defer func(begin time.Time) { args := []any{ slog.String("duration", time.Since(begin).String()), @@ -40,10 +40,10 @@ func (lm *loggingMiddleware) CreatePAT(ctx context.Context, token, name, descrip } lm.logger.Info("Create PAT completed successfully", args...) }(time.Now()) - return lm.svc.CreatePAT(ctx, token, name, description, duration, scope) + return lm.svc.CreatePAT(ctx, session, name, description, duration, scope) } -func (lm *loggingMiddleware) UpdatePATName(ctx context.Context, token, patID, name string) (pa pat.PAT, err error) { +func (lm *loggingMiddleware) UpdatePATName(ctx context.Context, session authn.Session, patID, name string) (pa pat.PAT, err error) { defer func(begin time.Time) { args := []any{ slog.String("duration", time.Since(begin).String()), @@ -57,10 +57,10 @@ func (lm *loggingMiddleware) UpdatePATName(ctx context.Context, token, patID, na } lm.logger.Info("Update PAT name completed successfully", args...) }(time.Now()) - return lm.svc.UpdatePATName(ctx, token, patID, name) + return lm.svc.UpdatePATName(ctx, session, patID, name) } -func (lm *loggingMiddleware) UpdatePATDescription(ctx context.Context, token, patID, description string) (pa pat.PAT, err error) { +func (lm *loggingMiddleware) UpdatePATDescription(ctx context.Context, session authn.Session, patID, description string) (pa pat.PAT, err error) { defer func(begin time.Time) { args := []any{ slog.String("duration", time.Since(begin).String()), @@ -74,10 +74,10 @@ func (lm *loggingMiddleware) UpdatePATDescription(ctx context.Context, token, pa } lm.logger.Info("Update PAT description completed successfully", args...) }(time.Now()) - return lm.svc.UpdatePATDescription(ctx, token, patID, description) + return lm.svc.UpdatePATDescription(ctx, session, patID, description) } -func (lm *loggingMiddleware) RetrievePAT(ctx context.Context, token, patID string) (pa pat.PAT, err error) { +func (lm *loggingMiddleware) RetrievePAT(ctx context.Context, session authn.Session, patID string) (pa pat.PAT, err error) { defer func(begin time.Time) { args := []any{ slog.String("duration", time.Since(begin).String()), @@ -90,10 +90,10 @@ func (lm *loggingMiddleware) RetrievePAT(ctx context.Context, token, patID strin } lm.logger.Info("Retrieve PAT completed successfully", args...) }(time.Now()) - return lm.svc.RetrievePAT(ctx, token, patID) + return lm.svc.RetrievePAT(ctx, session, patID) } -func (lm *loggingMiddleware) ListPATS(ctx context.Context, token string, pm pat.PATSPageMeta) (pp pat.PATSPage, err error) { +func (lm *loggingMiddleware) ListPATS(ctx context.Context, session authn.Session, pm pat.PATSPageMeta) (pp pat.PATSPage, err error) { defer func(begin time.Time) { args := []any{ slog.String("duration", time.Since(begin).String()), @@ -107,10 +107,10 @@ func (lm *loggingMiddleware) ListPATS(ctx context.Context, token string, pm pat. } lm.logger.Info("List PATS completed successfully", args...) }(time.Now()) - return lm.svc.ListPATS(ctx, token, pm) + return lm.svc.ListPATS(ctx, session, pm) } -func (lm *loggingMiddleware) DeletePAT(ctx context.Context, token, patID string) (err error) { +func (lm *loggingMiddleware) DeletePAT(ctx context.Context, session authn.Session, patID string) (err error) { defer func(begin time.Time) { args := []any{ slog.String("duration", time.Since(begin).String()), @@ -123,10 +123,10 @@ func (lm *loggingMiddleware) DeletePAT(ctx context.Context, token, patID string) } lm.logger.Info("Delete PAT completed successfully", args...) }(time.Now()) - return lm.svc.DeletePAT(ctx, token, patID) + return lm.svc.DeletePAT(ctx, session, patID) } -func (lm *loggingMiddleware) ResetPATSecret(ctx context.Context, token, patID string, duration time.Duration) (pa pat.PAT, err error) { +func (lm *loggingMiddleware) ResetPATSecret(ctx context.Context, session authn.Session, patID string, duration time.Duration) (pa pat.PAT, err error) { defer func(begin time.Time) { args := []any{ slog.String("duration", time.Since(begin).String()), @@ -140,10 +140,10 @@ func (lm *loggingMiddleware) ResetPATSecret(ctx context.Context, token, patID st } lm.logger.Info("Reset PAT secret completed successfully", args...) }(time.Now()) - return lm.svc.ResetPATSecret(ctx, token, patID, duration) + return lm.svc.ResetPATSecret(ctx, session, patID, duration) } -func (lm *loggingMiddleware) RevokePATSecret(ctx context.Context, token, patID string) (err error) { +func (lm *loggingMiddleware) RevokePATSecret(ctx context.Context, session authn.Session, patID string) (err error) { defer func(begin time.Time) { args := []any{ slog.String("duration", time.Since(begin).String()), @@ -156,10 +156,10 @@ func (lm *loggingMiddleware) RevokePATSecret(ctx context.Context, token, patID s } lm.logger.Info("Revoke PAT secret completed successfully", args...) }(time.Now()) - return lm.svc.RevokePATSecret(ctx, token, patID) + return lm.svc.RevokePATSecret(ctx, session, patID) } -func (lm *loggingMiddleware) AddPATScopeEntry(ctx context.Context, token, patID string, platformEntityType pat.PlatformEntityType, optionalDomainID string, optionalDomainEntityType pat.DomainEntityType, operation pat.OperationType, entityIDs ...string) (sc pat.Scope, err error) { +func (lm *loggingMiddleware) AddPATScopeEntry(ctx context.Context, session authn.Session, patID string, platformEntityType pat.PlatformEntityType, optionalDomainID string, optionalDomainEntityType pat.DomainEntityType, operation pat.OperationType, entityIDs ...string) (sc pat.Scope, err error) { defer func(begin time.Time) { args := []any{ slog.String("duration", time.Since(begin).String()), @@ -177,10 +177,10 @@ func (lm *loggingMiddleware) AddPATScopeEntry(ctx context.Context, token, patID } lm.logger.Info("Add entry to PAT scope completed successfully", args...) }(time.Now()) - return lm.svc.AddPATScopeEntry(ctx, token, patID, platformEntityType, optionalDomainID, optionalDomainEntityType, operation, entityIDs...) + return lm.svc.AddPATScopeEntry(ctx, session, patID, platformEntityType, optionalDomainID, optionalDomainEntityType, operation, entityIDs...) } -func (lm *loggingMiddleware) RemovePATScopeEntry(ctx context.Context, token, patID string, platformEntityType pat.PlatformEntityType, optionalDomainID string, optionalDomainEntityType pat.DomainEntityType, operation pat.OperationType, entityIDs ...string) (sc pat.Scope, err error) { +func (lm *loggingMiddleware) RemovePATScopeEntry(ctx context.Context, session authn.Session, patID string, platformEntityType pat.PlatformEntityType, optionalDomainID string, optionalDomainEntityType pat.DomainEntityType, operation pat.OperationType, entityIDs ...string) (sc pat.Scope, err error) { defer func(begin time.Time) { args := []any{ slog.String("duration", time.Since(begin).String()), @@ -198,10 +198,10 @@ func (lm *loggingMiddleware) RemovePATScopeEntry(ctx context.Context, token, pat } lm.logger.Info("Remove entry from PAT scope completed successfully", args...) }(time.Now()) - return lm.svc.RemovePATScopeEntry(ctx, token, patID, platformEntityType, optionalDomainID, optionalDomainEntityType, operation, entityIDs...) + return lm.svc.RemovePATScopeEntry(ctx, session, patID, platformEntityType, optionalDomainID, optionalDomainEntityType, operation, entityIDs...) } -func (lm *loggingMiddleware) ClearPATAllScopeEntry(ctx context.Context, token, patID string) (err error) { +func (lm *loggingMiddleware) ClearPATAllScopeEntry(ctx context.Context, session authn.Session, patID string) (err error) { defer func(begin time.Time) { args := []any{ slog.String("duration", time.Since(begin).String()), @@ -214,7 +214,7 @@ func (lm *loggingMiddleware) ClearPATAllScopeEntry(ctx context.Context, token, p } lm.logger.Info("Clear all entry from PAT scope completed successfully", args...) }(time.Now()) - return lm.svc.ClearPATAllScopeEntry(ctx, token, patID) + return lm.svc.ClearPATAllScopeEntry(ctx, session, patID) } func (lm *loggingMiddleware) IdentifyPAT(ctx context.Context, paToken string) (pa pat.PAT, err error) { @@ -272,4 +272,4 @@ func (lm *loggingMiddleware) CheckPAT(ctx context.Context, userID, patID string, lm.logger.Info("Check PAT completed successfully", args...) }(time.Now()) return lm.svc.CheckPAT(ctx, userID, patID, platformEntityType, optionalDomainID, optionalDomainEntityType, operation, entityIDs...) -} \ No newline at end of file +} diff --git a/pat/middleware/metrics.go b/pat/middleware/metrics.go index b41a88a838..4a44bb0aa1 100644 --- a/pat/middleware/metrics.go +++ b/pat/middleware/metrics.go @@ -2,3 +2,141 @@ // SPDX-License-Identifier: Apache-2.0 package middleware + +import ( + "context" + "time" + + "github.com/absmach/magistrala/pat" + "github.com/absmach/magistrala/pkg/authn" + "github.com/go-kit/kit/metrics" +) + +var _ pat.Service = (*metricsMiddleware)(nil) + +type metricsMiddleware struct { + counter metrics.Counter + latency metrics.Histogram + svc pat.Service +} + +// MetricsMiddleware instruments core service by tracking request count and latency. +func MetricsMiddleware(svc pat.Service, counter metrics.Counter, latency metrics.Histogram) pat.Service { + return &metricsMiddleware{ + counter: counter, + latency: latency, + svc: svc, + } +} + +func (ms *metricsMiddleware) CreatePAT(ctx context.Context, session authn.Session, name, description string, duration time.Duration, scope pat.Scope) (pat.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.CreatePAT(ctx, session, name, description, duration, scope) +} + +func (ms *metricsMiddleware) UpdatePATName(ctx context.Context, session authn.Session, patID, name string) (pat.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.UpdatePATName(ctx, session, patID, name) +} + +func (ms *metricsMiddleware) UpdatePATDescription(ctx context.Context, session authn.Session, patID, description string) (pat.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.UpdatePATDescription(ctx, session, patID, description) +} + +func (ms *metricsMiddleware) RetrievePAT(ctx context.Context, session authn.Session, patID string) (pat.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.RetrievePAT(ctx, session, patID) +} + +func (ms *metricsMiddleware) ListPATS(ctx context.Context, session authn.Session, pm pat.PATSPageMeta) (pat.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.ListPATS(ctx, session, pm) +} + +func (ms *metricsMiddleware) DeletePAT(ctx context.Context, session authn.Session, 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.DeletePAT(ctx, session, patID) +} + +func (ms *metricsMiddleware) ResetPATSecret(ctx context.Context, session authn.Session, patID string, duration time.Duration) (pat.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.ResetPATSecret(ctx, session, patID, duration) +} + +func (ms *metricsMiddleware) RevokePATSecret(ctx context.Context, session authn.Session, 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.RevokePATSecret(ctx, session, patID) +} + +func (ms *metricsMiddleware) AddPATScopeEntry(ctx context.Context, session authn.Session, patID string, platformEntityType pat.PlatformEntityType, optionalDomainID string, optionalDomainEntityType pat.DomainEntityType, operation pat.OperationType, entityIDs ...string) (pat.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.AddPATScopeEntry(ctx, session, patID, platformEntityType, optionalDomainID, optionalDomainEntityType, operation, entityIDs...) +} + +func (ms *metricsMiddleware) RemovePATScopeEntry(ctx context.Context, session authn.Session, patID string, platformEntityType pat.PlatformEntityType, optionalDomainID string, optionalDomainEntityType pat.DomainEntityType, operation pat.OperationType, entityIDs ...string) (pat.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.RemovePATScopeEntry(ctx, session, patID, platformEntityType, optionalDomainID, optionalDomainEntityType, operation, entityIDs...) +} + +func (ms *metricsMiddleware) ClearPATAllScopeEntry(ctx context.Context, session authn.Session, 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.ClearPATAllScopeEntry(ctx, session, patID) +} + +func (ms *metricsMiddleware) IdentifyPAT(ctx context.Context, paToken string) (pat.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, platformEntityType pat.PlatformEntityType, optionalDomainID string, optionalDomainEntityType pat.DomainEntityType, operation pat.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, platformEntityType, optionalDomainID, optionalDomainEntityType, operation, entityIDs...) +} + +func (ms *metricsMiddleware) CheckPAT(ctx context.Context, userID, patID string, platformEntityType pat.PlatformEntityType, optionalDomainID string, optionalDomainEntityType pat.DomainEntityType, operation pat.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/pat/pat.go b/pat/pat.go index 8e427b4332..5b9cb08045 100644 --- a/pat/pat.go +++ b/pat/pat.go @@ -9,6 +9,7 @@ import ( "fmt" "time" + "github.com/absmach/magistrala/pkg/authn" "github.com/absmach/magistrala/pkg/errors" ) @@ -662,41 +663,41 @@ func (pat PAT) Expired() bool { } // 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" +//go:generate mockery --name Service --output=./mocks --filename pats.go --quiet --note "Copyright (c) Abstract Machines" -type PATS interface { +type Service interface { // Create function creates new PAT for given valid inputs. - CreatePAT(ctx context.Context, token, name, description string, duration time.Duration, scope Scope) (PAT, error) + CreatePAT(ctx context.Context, session authn.Session, name, description string, duration time.Duration, scope Scope) (PAT, error) // UpdateName function updates the name for the given PAT ID. - UpdatePATName(ctx context.Context, token, patID, name string) (PAT, error) + UpdatePATName(ctx context.Context, session authn.Session, patID, name string) (PAT, error) // UpdateDescription function updates the description for the given PAT ID. - UpdatePATDescription(ctx context.Context, token, patID, description string) (PAT, error) + UpdatePATDescription(ctx context.Context, session authn.Session, patID, description string) (PAT, error) // Retrieve function retrieves the PAT for given ID. - RetrievePAT(ctx context.Context, token, patID string) (PAT, error) + RetrievePAT(ctx context.Context, session authn.Session, patID string) (PAT, error) // List function lists all the PATs for the user. - ListPATS(ctx context.Context, token string, pm PATSPageMeta) (PATSPage, error) + ListPATS(ctx context.Context, session authn.Session, pm PATSPageMeta) (PATSPage, error) // Delete function deletes the PAT for given ID. - DeletePAT(ctx context.Context, token, patID string) error + DeletePAT(ctx context.Context, session authn.Session, patID string) error // ResetSecret function reset the secret and creates new secret for the given ID. - ResetPATSecret(ctx context.Context, token, patID string, duration time.Duration) (PAT, error) + ResetPATSecret(ctx context.Context, session authn.Session, patID string, duration time.Duration) (PAT, error) // RevokeSecret function revokes the secret for the given ID. - RevokePATSecret(ctx context.Context, token, patID string) error + RevokePATSecret(ctx context.Context, session authn.Session, patID string) error // AddScope function adds a new scope entry. - AddPATScopeEntry(ctx context.Context, token, patID string, platformEntityType PlatformEntityType, optionalDomainID string, optionalDomainEntityType DomainEntityType, operation OperationType, entityIDs ...string) (Scope, error) + AddPATScopeEntry(ctx context.Context, session authn.Session, patID string, platformEntityType PlatformEntityType, optionalDomainID string, optionalDomainEntityType DomainEntityType, operation OperationType, entityIDs ...string) (Scope, error) // RemoveScope function removes a scope entry. - RemovePATScopeEntry(ctx context.Context, token, patID string, platformEntityType PlatformEntityType, optionalDomainID string, optionalDomainEntityType DomainEntityType, operation OperationType, entityIDs ...string) (Scope, error) + RemovePATScopeEntry(ctx context.Context, session authn.Session, patID string, platformEntityType PlatformEntityType, optionalDomainID string, optionalDomainEntityType DomainEntityType, operation OperationType, entityIDs ...string) (Scope, error) // ClearAllScope function removes all scope entry. - ClearPATAllScopeEntry(ctx context.Context, token, patID string) error + ClearPATAllScopeEntry(ctx context.Context, session authn.Session, patID string) error // IdentifyPAT function will valid the secret. IdentifyPAT(ctx context.Context, paToken string) (PAT, error) diff --git a/pat/service.go b/pat/service.go index 5161e3cb57..9c995914e4 100644 --- a/pat/service.go +++ b/pat/service.go @@ -10,8 +10,11 @@ import ( "strings" "time" + "github.com/absmach/magistrala" + "github.com/absmach/magistrala/pkg/authn" "github.com/absmach/magistrala/pkg/errors" svcerr "github.com/absmach/magistrala/pkg/errors/service" + "github.com/google/uuid" ) const ( @@ -33,32 +36,29 @@ var ( errClearAllScope = errors.New("failed to clear all entry in scope") ) -type Service struct { - pats PATSRepository - hasher Hasher +type service struct { + pats PATSRepository + hasher Hasher + idProvider magistrala.IDProvider } -var _ Service = (*Service)(nil) +var _ Service = (*service)(nil) // New instantiates the auth service implementation. -func New(pats PATSRepository, hasher Hasher) Service { - return &Service{ - pats: pats, - hasher: hasher, +func New(pats PATSRepository, hasher Hasher, idp magistrala.IDProvider) Service { + return &service{ + pats: pats, + hasher: hasher, + idProvider: idp, } } -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 - } - +func (svc service) CreatePAT(ctx context.Context, session authn.Session, name, description string, duration time.Duration, scope Scope) (PAT, error) { id, err := svc.idProvider.ID() if err != nil { return PAT{}, errors.Wrap(svcerr.ErrCreateEntity, err) } - secret, hash, err := svc.generateSecretAndHash(key.User, id) + secret, hash, err := svc.generateSecretAndHash(session.UserID, id) if err != nil { return PAT{}, errors.Wrap(svcerr.ErrCreateEntity, err) } @@ -66,7 +66,7 @@ func (svc Service) CreatePAT(ctx context.Context, token, name, description strin now := time.Now() pat := PAT{ ID: id, - User: key.User, + User: session.UserID, Name: name, Description: description, Secret: hash, @@ -81,84 +81,58 @@ func (svc Service) CreatePAT(ctx context.Context, token, name, description strin return pat, nil } -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 - } - pat, err := svc.pats.UpdateName(ctx, key.User, patID, name) +func (svc service) UpdatePATName(ctx context.Context, session authn.Session, patID, name string) (PAT, error) { + pat, err := svc.pats.UpdateName(ctx, session.UserID, patID, name) if err != nil { return PAT{}, errors.Wrap(errUpdatePAT, err) } 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 { - return PAT{}, err - } - pat, err := svc.pats.UpdateDescription(ctx, key.User, patID, description) +func (svc service) UpdatePATDescription(ctx context.Context, session authn.Session, patID, description string) (PAT, error) { + pat, err := svc.pats.UpdateDescription(ctx, session.UserID, patID, description) if err != nil { return PAT{}, errors.Wrap(errUpdatePAT, err) } return pat, nil } -func (svc Service) RetrievePAT(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) +func (svc service) RetrievePAT(ctx context.Context, session authn.Session, patID string) (PAT, error) { + pat, err := svc.pats.Retrieve(ctx, session.UserID, patID) if err != nil { return PAT{}, errors.Wrap(errRetrievePAT, err) } 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 { - return PATSPage{}, err - } - patsPage, err := svc.pats.RetrieveAll(ctx, key.User, pm) +func (svc service) ListPATS(ctx context.Context, session authn.Session, pm PATSPageMeta) (PATSPage, error) { + patsPage, err := svc.pats.RetrieveAll(ctx, session.UserID, pm) if err != nil { return PATSPage{}, errors.Wrap(errRetrievePAT, err) } return patsPage, nil } -func (svc Service) DeletePAT(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 { +func (svc service) DeletePAT(ctx context.Context, session authn.Session, patID string) error { + if err := svc.pats.Remove(ctx, session.UserID, patID); err != nil { return errors.Wrap(errDeletePAT, err) } 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 { - return PAT{}, err - } - +func (svc service) ResetPATSecret(ctx context.Context, session authn.Session, patID string, duration time.Duration) (PAT, error) { // Generate new HashToken take place here - secret, hash, err := svc.generateSecretAndHash(key.User, patID) + secret, hash, err := svc.generateSecretAndHash(session.UserID, patID) if err != nil { return PAT{}, errors.Wrap(svcerr.ErrUpdateEntity, err) } - pat, err := svc.pats.UpdateTokenHash(ctx, key.User, patID, hash, time.Now().Add(duration)) + pat, err := svc.pats.UpdateTokenHash(ctx, session.UserID, patID, hash, time.Now().Add(duration)) if err != nil { return PAT{}, errors.Wrap(svcerr.ErrUpdateEntity, err) } - if err := svc.pats.Reactivate(ctx, key.User, patID); err != nil { + if err := svc.pats.Reactivate(ctx, session.UserID, patID); err != nil { return PAT{}, errors.Wrap(svcerr.ErrUpdateEntity, err) } pat.Secret = secret @@ -167,54 +141,37 @@ 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 { - return err - } - - if err := svc.pats.Revoke(ctx, key.User, patID); err != nil { +func (svc service) RevokePATSecret(ctx context.Context, session authn.Session, patID string) error { + if err := svc.pats.Revoke(ctx, session.UserID, patID); err != nil { return errors.Wrap(errRevokePAT, err) } return nil } -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 - } - scope, err := svc.pats.AddScopeEntry(ctx, key.User, patID, platformEntityType, optionalDomainID, optionalDomainEntityType, operation, entityIDs...) +func (svc service) AddPATScopeEntry(ctx context.Context, session authn.Session, patID string, platformEntityType PlatformEntityType, optionalDomainID string, optionalDomainEntityType DomainEntityType, operation OperationType, entityIDs ...string) (Scope, error) { + scope, err := svc.pats.AddScopeEntry(ctx, session.UserID, 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 { - return Scope{}, err - } - scope, err := svc.pats.RemoveScopeEntry(ctx, key.User, patID, platformEntityType, optionalDomainID, optionalDomainEntityType, operation, entityIDs...) +func (svc service) RemovePATScopeEntry(ctx context.Context, session authn.Session, patID string, platformEntityType PlatformEntityType, optionalDomainID string, optionalDomainEntityType DomainEntityType, operation OperationType, entityIDs ...string) (Scope, error) { + scope, err := svc.pats.RemoveScopeEntry(ctx, session.UserID, patID, platformEntityType, optionalDomainID, optionalDomainEntityType, operation, entityIDs...) if err != nil { return Scope{}, err } return scope, nil } -func (svc Service) ClearPATAllScopeEntry(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 { +func (svc service) ClearPATAllScopeEntry(ctx context.Context, session authn.Session, patID string) error { + if err := svc.pats.RemoveAllScopeEntry(ctx, session.UserID, patID); err != nil { return errors.Wrap(errClearAllScope, err) } return nil } -func (svc Service) IdentifyPAT(ctx context.Context, secret string) (PAT, 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) @@ -236,7 +193,7 @@ func (svc Service) IdentifyPAT(ctx context.Context, secret string) (PAT, error) 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 { +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 @@ -247,14 +204,14 @@ func (svc Service) AuthorizePAT(ctx context.Context, paToken string, platformEnt return nil } -func (svc Service) CheckPAT(ctx context.Context, userID, patID string, platformEntityType PlatformEntityType, optionalDomainID string, optionalDomainEntityType DomainEntityType, operation OperationType, entityIDs ...string) error { +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) { +func (svc service) generateSecretAndHash(userID, patID string) (string, string, error) { uID, err := uuid.Parse(userID) if err != nil { return "", "", errors.Wrap(errFailedToParseUUID, err) diff --git a/pat/tracing/tracing.go b/pat/tracing/tracing.go new file mode 100644 index 0000000000..4fab5fae49 --- /dev/null +++ b/pat/tracing/tracing.go @@ -0,0 +1,164 @@ +// Copyright (c) Abstract Machines +// SPDX-License-Identifier: Apache-2.0 + +package tracing + +import ( + "context" + "time" + + "github.com/absmach/magistrala/pat" + "github.com/absmach/magistrala/pkg/authn" + "go.opentelemetry.io/otel/attribute" + "go.opentelemetry.io/otel/trace" +) + +var _ pat.Service = (*tracingMiddleware)(nil) + +type tracingMiddleware struct { + tracer trace.Tracer + svc pat.Service +} + +// New returns a new group service with tracing capabilities. +func New(svc pat.Service, tracer trace.Tracer) pat.Service { + return &tracingMiddleware{tracer, svc} +} + +func (tm *tracingMiddleware) CreatePAT(ctx context.Context, session authn.Session, name, description string, duration time.Duration, scope pat.Scope) (pat.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.CreatePAT(ctx, session, name, description, duration, scope) +} + +func (tm *tracingMiddleware) UpdatePATName(ctx context.Context, session authn.Session, patID, name string) (pat.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.UpdatePATName(ctx, session, patID, name) +} + +func (tm *tracingMiddleware) UpdatePATDescription(ctx context.Context, session authn.Session, patID, description string) (pat.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.UpdatePATDescription(ctx, session, patID, description) +} + +func (tm *tracingMiddleware) RetrievePAT(ctx context.Context, session authn.Session, patID string) (pat.PAT, error) { + ctx, span := tm.tracer.Start(ctx, "retrieve_pat", trace.WithAttributes( + attribute.String("pat_id", patID), + )) + defer span.End() + return tm.svc.RetrievePAT(ctx, session, patID) +} + +func (tm *tracingMiddleware) ListPATS(ctx context.Context, session authn.Session, pm pat.PATSPageMeta) (pat.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.ListPATS(ctx, session, pm) +} + +func (tm *tracingMiddleware) DeletePAT(ctx context.Context, session authn.Session, patID string) error { + ctx, span := tm.tracer.Start(ctx, "delete_pat", trace.WithAttributes( + attribute.String("pat_id", patID), + )) + defer span.End() + return tm.svc.DeletePAT(ctx, session, patID) +} + +func (tm *tracingMiddleware) ResetPATSecret(ctx context.Context, session authn.Session, patID string, duration time.Duration) (pat.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.ResetPATSecret(ctx, session, patID, duration) +} + +func (tm *tracingMiddleware) RevokePATSecret(ctx context.Context, session authn.Session, 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.RevokePATSecret(ctx, session, patID) +} + +func (tm *tracingMiddleware) AddPATScopeEntry(ctx context.Context, session authn.Session, patID string, platformEntityType pat.PlatformEntityType, optionalDomainID string, optionalDomainEntityType pat.DomainEntityType, operation pat.OperationType, entityIDs ...string) (pat.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), + attribute.String("optional_domain_entity", optionalDomainEntityType.String()), + attribute.String("operation", operation.String()), + attribute.StringSlice("entities", entityIDs), + )) + defer span.End() + return tm.svc.AddPATScopeEntry(ctx, session, patID, platformEntityType, optionalDomainID, optionalDomainEntityType, operation, entityIDs...) +} + +func (tm *tracingMiddleware) RemovePATScopeEntry(ctx context.Context, session authn.Session, patID string, platformEntityType pat.PlatformEntityType, optionalDomainID string, optionalDomainEntityType pat.DomainEntityType, operation pat.OperationType, entityIDs ...string) (pat.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), + attribute.String("optional_domain_entity", optionalDomainEntityType.String()), + attribute.String("operation", operation.String()), + attribute.StringSlice("entities", entityIDs), + )) + defer span.End() + return tm.svc.RemovePATScopeEntry(ctx, session, patID, platformEntityType, optionalDomainID, optionalDomainEntityType, operation, entityIDs...) +} + +func (tm *tracingMiddleware) ClearPATAllScopeEntry(ctx context.Context, session authn.Session, 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.ClearPATAllScopeEntry(ctx, session, patID) +} + +func (tm *tracingMiddleware) IdentifyPAT(ctx context.Context, paToken string) (pat.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 pat.PlatformEntityType, optionalDomainID string, optionalDomainEntityType pat.DomainEntityType, operation pat.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), + 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, platformEntityType, optionalDomainID, optionalDomainEntityType, operation, entityIDs...) +} + +func (tm *tracingMiddleware) CheckPAT(ctx context.Context, userID, patID string, platformEntityType pat.PlatformEntityType, optionalDomainID string, optionalDomainEntityType pat.DomainEntityType, operation pat.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.CheckPAT(ctx, userID, patID, platformEntityType, optionalDomainID, optionalDomainEntityType, operation, entityIDs...) +} From 10a393e94113c69e698a635bc98aa06de74de76e Mon Sep 17 00:00:00 2001 From: nyagamunene Date: Wed, 30 Oct 2024 13:21:26 +0300 Subject: [PATCH 33/72] Add grpc authorizePAT Signed-off-by: nyagamunene --- api/grpc/auth/v1/auth.pb.go | 24 +++++++++ docker/nginx/nginx-key.conf | 7 +++ pat/api/{ => http}/endpoint.go | 0 pat/api/{ => http}/requests.go | 0 pat/api/{ => http}/responses.go | 0 pat/api/{ => http}/transport.go | 0 pat/events/streams.go | 94 +++++++++++++++++++++++++++++++++ 7 files changed, 125 insertions(+) rename pat/api/{ => http}/endpoint.go (100%) rename pat/api/{ => http}/requests.go (100%) rename pat/api/{ => http}/responses.go (100%) rename pat/api/{ => http}/transport.go (100%) create mode 100644 pat/events/streams.go diff --git a/api/grpc/auth/v1/auth.pb.go b/api/grpc/auth/v1/auth.pb.go index 16b2b49395..e1bc883184 100644 --- a/api/grpc/auth/v1/auth.pb.go +++ b/api/grpc/auth/v1/auth.pb.go @@ -37,6 +37,11 @@ func (x *AuthNReq) Reset() { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } + if protoimpl.UnsafeEnabled { + mi := &file_auth_v1_auth_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } } func (x *AuthNReq) String() string { @@ -47,6 +52,7 @@ func (*AuthNReq) ProtoMessage() {} func (x *AuthNReq) ProtoReflect() protoreflect.Message { mi := &file_auth_v1_auth_proto_msgTypes[0] + if protoimpl.UnsafeEnabled && x != nil { if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -85,6 +91,11 @@ func (x *AuthNRes) Reset() { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } + if protoimpl.UnsafeEnabled { + mi := &file_auth_v1_auth_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } } func (x *AuthNRes) String() string { @@ -95,6 +106,7 @@ func (*AuthNRes) ProtoMessage() {} func (x *AuthNRes) ProtoReflect() protoreflect.Message { mi := &file_auth_v1_auth_proto_msgTypes[1] + if protoimpl.UnsafeEnabled && x != nil { if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -535,6 +547,18 @@ func file_auth_v1_auth_proto_init() { } } file_auth_v1_auth_proto_msgTypes[3].Exporter = func(v any, i int) any { + switch v := v.(*AuthZpatReq); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_auth_v1_auth_proto_msgTypes[4].Exporter = func(v any, i int) any { switch v := v.(*AuthZRes); i { case 0: return &v.state diff --git a/docker/nginx/nginx-key.conf b/docker/nginx/nginx-key.conf index 9779466dc2..db8a5aaad0 100644 --- a/docker/nginx/nginx-key.conf +++ b/docker/nginx/nginx-key.conf @@ -64,6 +64,13 @@ http { proxy_pass http://domains:${SMQ_DOMAINS_HTTP_PORT}; } + # Proxy pass to auth service + location ~ ^/(domains|keys|pats) { + include snippets/proxy-headers.conf; + add_header Access-Control-Expose-Headers Location; + proxy_pass http://auth:${MG_AUTH_HTTP_PORT}; + } + # Proxy pass to users service location ~ ^/(users|password|authorize|oauth/callback/[^/]+) { include snippets/proxy-headers.conf; diff --git a/pat/api/endpoint.go b/pat/api/http/endpoint.go similarity index 100% rename from pat/api/endpoint.go rename to pat/api/http/endpoint.go diff --git a/pat/api/requests.go b/pat/api/http/requests.go similarity index 100% rename from pat/api/requests.go rename to pat/api/http/requests.go diff --git a/pat/api/responses.go b/pat/api/http/responses.go similarity index 100% rename from pat/api/responses.go rename to pat/api/http/responses.go diff --git a/pat/api/transport.go b/pat/api/http/transport.go similarity index 100% rename from pat/api/transport.go rename to pat/api/http/transport.go diff --git a/pat/events/streams.go b/pat/events/streams.go new file mode 100644 index 0000000000..713ddea5a6 --- /dev/null +++ b/pat/events/streams.go @@ -0,0 +1,94 @@ +// Copyright (c) Abstract Machines +// SPDX-License-Identifier: Apache-2.0 + +package events + +import ( + "context" + "time" + + "github.com/absmach/magistrala/pat" + "github.com/absmach/magistrala/pkg/authn" + "github.com/absmach/magistrala/pkg/events" + "github.com/absmach/magistrala/pkg/events/store" +) + +const streamID = "magistrala.pat" + +var _ pat.Service = (*eventStore)(nil) + + +type eventStore struct { + events.Publisher + svc pat.Service +} + +// NewEventStoreMiddleware returns wrapper around pat service that sends +// events to event store. +func NewEventStoreMiddleware(ctx context.Context, svc pat.Service, url string) (pat.Service, error) { + publisher, err := store.NewPublisher(ctx, url, streamID) + if err != nil { + return nil, err + } + + return &eventStore{ + svc: svc, + Publisher: publisher, + }, nil +} + +func (es *eventStore) CreatePAT(ctx context.Context, session authn.Session, name, description string, duration time.Duration, scope pat.Scope) (pat.PAT, error) { + return es.svc.CreatePAT(ctx, session, name, description, duration, scope) +} + +func (es *eventStore) UpdatePATName(ctx context.Context, session authn.Session, patID, name string) (pat.PAT, error) { + return es.svc.UpdatePATName(ctx, session, patID, name) +} + +func (es *eventStore) UpdatePATDescription(ctx context.Context, session authn.Session, patID, description string) (pat.PAT, error) { + return es.svc.UpdatePATDescription(ctx, session, patID, description) +} + +func (es *eventStore) RetrievePAT(ctx context.Context, session authn.Session, patID string) (pat.PAT, error) { + return es.svc.RetrievePAT(ctx, session, patID) +} + +func (es *eventStore) ListPATS(ctx context.Context, session authn.Session, pm pat.PATSPageMeta) (pat.PATSPage, error) { + return es.svc.ListPATS(ctx, session, pm) +} + +func (es *eventStore) DeletePAT(ctx context.Context, session authn.Session, patID string) error { + return es.svc.DeletePAT(ctx, session, patID) +} + +func (es *eventStore) ResetPATSecret(ctx context.Context, session authn.Session, patID string, duration time.Duration) (pat.PAT, error) { + return es.svc.ResetPATSecret(ctx, session, patID, duration) +} + +func (es *eventStore) RevokePATSecret(ctx context.Context, session authn.Session, patID string) error { + return es.svc.RevokePATSecret(ctx, session, patID) +} + +func (es *eventStore) AddPATScopeEntry(ctx context.Context, session authn.Session, patID string, platformEntityType pat.PlatformEntityType, optionalDomainID string, optionalDomainEntityType pat.DomainEntityType, operation pat.OperationType, entityIDs ...string) (pat.Scope, error) { + return es.svc.AddPATScopeEntry(ctx, session, patID, platformEntityType, optionalDomainID, optionalDomainEntityType, operation, entityIDs...) +} + +func (es *eventStore) RemovePATScopeEntry(ctx context.Context, session authn.Session, patID string, platformEntityType pat.PlatformEntityType, optionalDomainID string, optionalDomainEntityType pat.DomainEntityType, operation pat.OperationType, entityIDs ...string) (pat.Scope, error) { + return es.svc.RemovePATScopeEntry(ctx, session, patID, platformEntityType, optionalDomainID, optionalDomainEntityType, operation, entityIDs...) +} + +func (es *eventStore) ClearPATAllScopeEntry(ctx context.Context, session authn.Session, patID string) error { + return es.svc.ClearPATAllScopeEntry(ctx, session, patID) +} + +func (es *eventStore) IdentifyPAT(ctx context.Context, paToken string) (pat.PAT, error) { + return es.svc.IdentifyPAT(ctx, paToken) +} + +func (es *eventStore) AuthorizePAT(ctx context.Context, paToken string, platformEntityType pat.PlatformEntityType, optionalDomainID string, optionalDomainEntityType pat.DomainEntityType, operation pat.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 pat.PlatformEntityType, optionalDomainID string, optionalDomainEntityType pat.DomainEntityType, operation pat.OperationType, entityIDs ...string) error { + return es.svc.CheckPAT(ctx, userID, patID, platformEntityType, optionalDomainID, optionalDomainEntityType, operation, entityIDs...) +} \ No newline at end of file From ef78debb20e1bc2018e42a37fcf38dbe88d8ea23 Mon Sep 17 00:00:00 2001 From: nyagamunene Date: Thu, 31 Oct 2024 11:55:27 +0300 Subject: [PATCH 34/72] Move pat to auth Signed-off-by: nyagamunene --- auth/api/http/transport.go | 3 +- auth/mocks/service.go | 3 + pat/api/http/endpoint.go | 259 ----------- pat/api/http/requests.go | 361 --------------- pat/api/http/responses.go | 208 --------- pat/api/http/transport.go | 271 ----------- pat/bolt/doc.go | 6 - pat/bolt/init.go | 21 - pat/bolt/pat.go | 773 -------------------------------- pat/events/streams.go | 94 ---- pat/hasher.go | 17 - pat/hasher/doc.go | 6 - pat/hasher/hasher.go | 86 ---- pat/middleware/authorization.go | 84 ---- pat/middleware/logging.go | 275 ------------ pat/middleware/metrics.go | 142 ------ pat/pat.go | 753 ------------------------------- pat/service.go | 259 ----------- pat/tracing/tracing.go | 164 ------- 19 files changed, 5 insertions(+), 3780 deletions(-) delete mode 100644 pat/api/http/endpoint.go delete mode 100644 pat/api/http/requests.go delete mode 100644 pat/api/http/responses.go delete mode 100644 pat/api/http/transport.go delete mode 100644 pat/bolt/doc.go delete mode 100644 pat/bolt/init.go delete mode 100644 pat/bolt/pat.go delete mode 100644 pat/events/streams.go delete mode 100644 pat/hasher.go delete mode 100644 pat/hasher/doc.go delete mode 100644 pat/hasher/hasher.go delete mode 100644 pat/middleware/authorization.go delete mode 100644 pat/middleware/logging.go delete mode 100644 pat/middleware/metrics.go delete mode 100644 pat/pat.go delete mode 100644 pat/service.go delete mode 100644 pat/tracing/tracing.go diff --git a/auth/api/http/transport.go b/auth/api/http/transport.go index c3a8d723c0..c84e4a2dcd 100644 --- a/auth/api/http/transport.go +++ b/auth/api/http/transport.go @@ -11,11 +11,12 @@ import ( "github.com/absmach/supermq/auth/api/http/keys" "github.com/absmach/supermq/auth/api/http/pats" "github.com/go-chi/chi/v5" + "github.com/absmach/magistrala/auth/api/http/pats" "github.com/prometheus/client_golang/prometheus/promhttp" ) // MakeHandler returns a HTTP handler for API endpoints. -func MakeHandler(svc auth.Service, logger *slog.Logger, instanceID string) http.Handler { +func MakeHandler(svc auth.Service, authn mgauthn.Authentication, logger *slog.Logger, instanceID string) http.Handler { mux := chi.NewRouter() mux = keys.MakeHandler(svc, mux, logger) diff --git a/auth/mocks/service.go b/auth/mocks/service.go index 0a01591776..9ff90b6f1e 100644 --- a/auth/mocks/service.go +++ b/auth/mocks/service.go @@ -5,6 +5,9 @@ package mocks import ( + auth "github.com/absmach/magistrala/auth" + authn "github.com/absmach/magistrala/pkg/authn" + context "context" auth "github.com/absmach/supermq/auth" diff --git a/pat/api/http/endpoint.go b/pat/api/http/endpoint.go deleted file mode 100644 index 94cbdf5e80..0000000000 --- a/pat/api/http/endpoint.go +++ /dev/null @@ -1,259 +0,0 @@ -// Copyright (c) Abstract Machines -// SPDX-License-Identifier: Apache-2.0 - -package pats - -import ( - "context" - - "github.com/absmach/magistrala/internal/api" - "github.com/absmach/magistrala/pat" - "github.com/absmach/magistrala/pkg/authn" - svcerr "github.com/absmach/magistrala/pkg/errors/service" - "github.com/go-kit/kit/endpoint" -) - -func createPATEndpoint(svc pat.Service) endpoint.Endpoint { - return func(ctx context.Context, request interface{}) (interface{}, error) { - req := request.(createPatReq) - if err := req.validate(); err != nil { - return nil, err - } - - session, ok := ctx.Value(api.SessionKey).(authn.Session) - if !ok { - return nil, svcerr.ErrAuthentication - } - - pat, err := svc.CreatePAT(ctx, session, req.Name, req.Description, req.Duration, req.Scope) - if err != nil { - return nil, err - } - - return createPatRes{pat}, nil - } -} - -func retrievePATEndpoint(svc pat.Service) endpoint.Endpoint { - return func(ctx context.Context, request interface{}) (interface{}, error) { - req := request.(retrievePatReq) - if err := req.validate(); err != nil { - return nil, err - } - - session, ok := ctx.Value(api.SessionKey).(authn.Session) - if !ok { - return nil, svcerr.ErrAuthentication - } - - pat, err := svc.RetrievePAT(ctx, session, req.id) - if err != nil { - return nil, err - } - - return retrievePatRes{pat}, nil - } -} - -func updatePATNameEndpoint(svc pat.Service) endpoint.Endpoint { - return func(ctx context.Context, request interface{}) (interface{}, error) { - req := request.(updatePatNameReq) - if err := req.validate(); err != nil { - return nil, err - } - - session, ok := ctx.Value(api.SessionKey).(authn.Session) - if !ok { - return nil, svcerr.ErrAuthentication - } - pat, err := svc.UpdatePATName(ctx, session, req.id, req.Name) - if err != nil { - return nil, err - } - - return updatePatNameRes{pat}, nil - } -} - -func updatePATDescriptionEndpoint(svc pat.Service) endpoint.Endpoint { - return func(ctx context.Context, request interface{}) (interface{}, error) { - req := request.(updatePatDescriptionReq) - if err := req.validate(); err != nil { - return nil, err - } - - session, ok := ctx.Value(api.SessionKey).(authn.Session) - if !ok { - return nil, svcerr.ErrAuthentication - } - - pat, err := svc.UpdatePATDescription(ctx, session, req.id, req.Description) - if err != nil { - return nil, err - } - - return updatePatDescriptionRes{pat}, nil - } -} - -func listPATSEndpoint(svc pat.Service) endpoint.Endpoint { - return func(ctx context.Context, request interface{}) (interface{}, error) { - req := request.(listPatsReq) - if err := req.validate(); err != nil { - return nil, err - } - - session, ok := ctx.Value(api.SessionKey).(authn.Session) - if !ok { - return nil, svcerr.ErrAuthentication - } - - pm := pat.PATSPageMeta{ - Limit: req.limit, - Offset: req.offset, - } - patsPage, err := svc.ListPATS(ctx, session, pm) - if err != nil { - return nil, err - } - - return listPatsRes{patsPage}, nil - } -} - -func deletePATEndpoint(svc pat.Service) endpoint.Endpoint { - return func(ctx context.Context, request interface{}) (interface{}, error) { - req := request.(deletePatReq) - if err := req.validate(); err != nil { - return nil, err - } - - session, ok := ctx.Value(api.SessionKey).(authn.Session) - if !ok { - return nil, svcerr.ErrAuthentication - } - - if err := svc.DeletePAT(ctx, session, req.id); err != nil { - return nil, err - } - - return deletePatRes{}, nil - } -} - -func resetPATSecretEndpoint(svc pat.Service) endpoint.Endpoint { - return func(ctx context.Context, request interface{}) (interface{}, error) { - req := request.(resetPatSecretReq) - if err := req.validate(); err != nil { - return nil, err - } - - session, ok := ctx.Value(api.SessionKey).(authn.Session) - if !ok { - return nil, svcerr.ErrAuthentication - } - - pat, err := svc.ResetPATSecret(ctx, session, req.id, req.Duration) - if err != nil { - return nil, err - } - - return resetPatSecretRes{pat}, nil - } -} - -func revokePATSecretEndpoint(svc pat.Service) endpoint.Endpoint { - return func(ctx context.Context, request interface{}) (interface{}, error) { - req := request.(revokePatSecretReq) - if err := req.validate(); err != nil { - return nil, err - } - - session, ok := ctx.Value(api.SessionKey).(authn.Session) - if !ok { - return nil, svcerr.ErrAuthentication - } - - if err := svc.RevokePATSecret(ctx, session, req.id); err != nil { - return nil, err - } - - return revokePatSecretRes{}, nil - } -} - -func addPATScopeEntryEndpoint(svc pat.Service) endpoint.Endpoint { - return func(ctx context.Context, request interface{}) (interface{}, error) { - req := request.(addPatScopeEntryReq) - if err := req.validate(); err != nil { - return nil, err - } - - session, ok := ctx.Value(api.SessionKey).(authn.Session) - if !ok { - return nil, svcerr.ErrAuthentication - } - - scope, err := svc.AddPATScopeEntry(ctx, session, req.id, req.PlatformEntityType, req.OptionalDomainID, req.OptionalDomainEntityType, req.Operation, req.EntityIDs...) - if err != nil { - return nil, err - } - - return addPatScopeEntryRes{scope}, nil - } -} - -func removePATScopeEntryEndpoint(svc pat.Service) endpoint.Endpoint { - return func(ctx context.Context, request interface{}) (interface{}, error) { - req := request.(removePatScopeEntryReq) - if err := req.validate(); err != nil { - return nil, err - } - - session, ok := ctx.Value(api.SessionKey).(authn.Session) - if !ok { - return nil, svcerr.ErrAuthentication - } - - scope, err := svc.RemovePATScopeEntry(ctx, session, req.id, req.PlatformEntityType, req.OptionalDomainID, req.OptionalDomainEntityType, req.Operation, req.EntityIDs...) - if err != nil { - return nil, err - } - return removePatScopeEntryRes{scope}, nil - } -} - -func clearPATAllScopeEntryEndpoint(svc pat.Service) endpoint.Endpoint { - return func(ctx context.Context, request interface{}) (interface{}, error) { - req := request.(clearAllScopeEntryReq) - if err := req.validate(); err != nil { - return nil, err - } - - session, ok := ctx.Value(api.SessionKey).(authn.Session) - if !ok { - return nil, svcerr.ErrAuthentication - } - - if err := svc.ClearPATAllScopeEntry(ctx, session, req.id); err != nil { - return nil, err - } - - return clearAllScopeEntryRes{}, nil - } -} - -func authorizePATEndpoint(svc pat.Service) endpoint.Endpoint { - return func(ctx context.Context, request interface{}) (interface{}, error) { - req := request.(authorizePATReq) - if err := req.validate(); err != nil { - return nil, err - } - - if err := svc.AuthorizePAT(ctx, req.token, req.PlatformEntityType, req.OptionalDomainID, req.OptionalDomainEntityType, req.Operation, req.EntityIDs...); err != nil { - return nil, err - } - - return authorizePATRes{}, nil - } -} diff --git a/pat/api/http/requests.go b/pat/api/http/requests.go deleted file mode 100644 index 1822ec457e..0000000000 --- a/pat/api/http/requests.go +++ /dev/null @@ -1,361 +0,0 @@ -// Copyright (c) Abstract Machines -// SPDX-License-Identifier: Apache-2.0 - -package pats - -import ( - "encoding/json" - "strings" - "time" - - "github.com/absmach/magistrala/pat" - "github.com/absmach/magistrala/pkg/apiutil" -) - -type createPatReq struct { - token string - Name string `json:"name,omitempty"` - Description string `json:"description,omitempty"` - Duration time.Duration `json:"duration,omitempty"` - Scope pat.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 pat.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 - } - - 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 pat.PlatformEntityType `json:"platform_entity_type,omitempty"` - OptionalDomainID string `json:"optional_domain_id,omitempty"` - OptionalDomainEntityType pat.DomainEntityType `json:"optional_domain_entity_type,omitempty"` - Operation pat.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 := pat.ParsePlatformEntityType(temp.PlatformEntityType) - if err != nil { - return err - } - odt, err := pat.ParseDomainEntityType(temp.OptionalDomainEntityType) - if err != nil { - return err - } - op, err := pat.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 pat.PlatformEntityType `json:"platform_entity_type,omitempty"` - OptionalDomainID string `json:"optional_domain_id,omitempty"` - OptionalDomainEntityType pat.DomainEntityType `json:"optional_domain_entity_type,omitempty"` - Operation pat.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 := pat.ParsePlatformEntityType(temp.PlatformEntityType) - if err != nil { - return err - } - odt, err := pat.ParseDomainEntityType(temp.OptionalDomainEntityType) - if err != nil { - return err - } - op, err := pat.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 authorizePATReq struct { - token string - PlatformEntityType pat.PlatformEntityType `json:"platform_entity_type,omitempty"` - OptionalDomainID string `json:"optional_domain_id,omitempty"` - OptionalDomainEntityType pat.DomainEntityType `json:"optional_domain_entity_type,omitempty"` - Operation pat.OperationType `json:"operation,omitempty"` - EntityIDs []string `json:"entity_ids,omitempty"` -} - -func (tcpsr *authorizePATReq) 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 - } - - tcpsr.OptionalDomainID = temp.OptionalDomainID - tcpsr.EntityIDs = temp.EntityIDs - - pet, err := pat.ParsePlatformEntityType(temp.PlatformEntityType) - if err != nil { - return err - } - tcpsr.PlatformEntityType = pet - - if temp.OptionalDomainEntityType != "" { - odt, err := pat.ParseDomainEntityType(temp.OptionalDomainEntityType) - if err != nil { - return err - } - tcpsr.OptionalDomainEntityType = odt - } - - if temp.OptionalDomainID != "" { - op, err := pat.ParseOperationType(temp.Operation) - if err != nil { - return err - } - tcpsr.Operation = op - } - - return nil -} - -func (req authorizePATReq) validate() (err error) { - if req.token == "" { - return apiutil.ErrBearerToken - } - - return nil -} diff --git a/pat/api/http/responses.go b/pat/api/http/responses.go deleted file mode 100644 index b2d0fe57de..0000000000 --- a/pat/api/http/responses.go +++ /dev/null @@ -1,208 +0,0 @@ -// Copyright (c) Abstract Machines -// SPDX-License-Identifier: Apache-2.0 - -package pats - -import ( - "net/http" - - "github.com/absmach/magistrala" - "github.com/absmach/magistrala/pat" -) - -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 { - pat.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 { - pat.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 { - pat.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 { - pat.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 { - pat.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 { - pat.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 { - pat.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 { - pat.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 authorizePATRes struct{} - -func (res authorizePATRes) Code() int { - return http.StatusNoContent -} - -func (res authorizePATRes) Headers() map[string]string { - return map[string]string{} -} - -func (res authorizePATRes) Empty() bool { - return true -} diff --git a/pat/api/http/transport.go b/pat/api/http/transport.go deleted file mode 100644 index 95d0f45efa..0000000000 --- a/pat/api/http/transport.go +++ /dev/null @@ -1,271 +0,0 @@ -// 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/internal/api" - "github.com/absmach/magistrala/pat" - "github.com/absmach/magistrala/pkg/apiutil" - mgauthn "github.com/absmach/magistrala/pkg/authn" - "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 pat.Service, mux *chi.Mux, authn mgauthn.Authentication, logger *slog.Logger) *chi.Mux { - opts := []kithttp.ServerOption{ - kithttp.ServerErrorEncoder(apiutil.LoggingErrorEncoder(logger, api.EncodeError)), - } - mux.Group(func(r chi.Router) { - mux.Use(api.AuthenticateMiddleware(authn, true)) - - mux.Route("/pats", func(r chi.Router) { - r.Post("/", kithttp.NewServer( - createPATEndpoint(svc), - decodeCreatePATRequest, - api.EncodeResponse, - opts..., - ).ServeHTTP) - - r.Get("/{id}", kithttp.NewServer( - (retrievePATEndpoint(svc)), - decodeRetrievePATRequest, - api.EncodeResponse, - opts..., - ).ServeHTTP) - - r.Put("/{id}/name", kithttp.NewServer( - (updatePATNameEndpoint(svc)), - decodeUpdatePATNameRequest, - api.EncodeResponse, - opts..., - ).ServeHTTP) - - r.Put("/{id}/description", kithttp.NewServer( - (updatePATDescriptionEndpoint(svc)), - decodeUpdatePATDescriptionRequest, - api.EncodeResponse, - opts..., - ).ServeHTTP) - - r.Get("/", kithttp.NewServer( - (listPATSEndpoint(svc)), - decodeListPATSRequest, - api.EncodeResponse, - opts..., - ).ServeHTTP) - - r.Delete("/{id}", kithttp.NewServer( - (deletePATEndpoint(svc)), - decodeDeletePATRequest, - api.EncodeResponse, - opts..., - ).ServeHTTP) - - r.Put("/{id}/secret/reset", kithttp.NewServer( - (resetPATSecretEndpoint(svc)), - decodeResetPATSecretRequest, - api.EncodeResponse, - opts..., - ).ServeHTTP) - - r.Put("/{id}/secret/revoke", kithttp.NewServer( - (revokePATSecretEndpoint(svc)), - decodeRevokePATSecretRequest, - api.EncodeResponse, - opts..., - ).ServeHTTP) - - r.Put("/{id}/scope/add", kithttp.NewServer( - (addPATScopeEntryEndpoint(svc)), - decodeAddPATScopeEntryRequest, - api.EncodeResponse, - opts..., - ).ServeHTTP) - - r.Put("/{id}/scope/remove", kithttp.NewServer( - (removePATScopeEntryEndpoint(svc)), - decodeRemovePATScopeEntryRequest, - api.EncodeResponse, - opts..., - ).ServeHTTP) - - r.Delete("/{id}/scope", kithttp.NewServer( - (clearPATAllScopeEntryEndpoint(svc)), - decodeClearPATAllScopeEntryRequest, - api.EncodeResponse, - opts..., - ).ServeHTTP) - - r.Post("/authorize", kithttp.NewServer( - (authorizePATEndpoint(svc)), - decodeAuthorizePATRequest, - 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 decodeUpdatePATNameRequest(_ 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 decodeUpdatePATDescriptionRequest(_ 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 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) - } - 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, - } - return req, nil -} - -func decodeDeletePATRequest(_ context.Context, r *http.Request) (interface{}, error) { - return deletePatReq{ - token: apiutil.ExtractBearerToken(r), - id: chi.URLParam(r, "id"), - }, nil -} - -func decodeResetPATSecretRequest(_ 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 decodeRevokePATSecretRequest(_ context.Context, r *http.Request) (interface{}, error) { - return revokePatSecretReq{ - token: apiutil.ExtractBearerToken(r), - id: chi.URLParam(r, "id"), - }, nil -} - -func decodeAddPATScopeEntryRequest(_ 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 decodeRemovePATScopeEntryRequest(_ 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 decodeClearPATAllScopeEntryRequest(_ 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 decodeAuthorizePATRequest(_ context.Context, r *http.Request) (interface{}, error) { - if !strings.Contains(r.Header.Get("Content-Type"), contentType) { - return nil, apiutil.ErrUnsupportedContentType - } - - req := authorizePATReq{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/pat/bolt/doc.go b/pat/bolt/doc.go deleted file mode 100644 index dcd06ac566..0000000000 --- a/pat/bolt/doc.go +++ /dev/null @@ -1,6 +0,0 @@ -// 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/pat/bolt/init.go b/pat/bolt/init.go deleted file mode 100644 index 9d496e65ca..0000000000 --- a/pat/bolt/init.go +++ /dev/null @@ -1,21 +0,0 @@ -// Copyright (c) Abstract Machines -// SPDX-License-Identifier: Apache-2.0 - -// 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/pat/bolt/pat.go b/pat/bolt/pat.go deleted file mode 100644 index b3af87890b..0000000000 --- a/pat/bolt/pat.go +++ /dev/null @@ -1,773 +0,0 @@ -// Copyright (c) Abstract Machines -// SPDX-License-Identifier: Apache-2.0 - -package bolt - -import ( - "bytes" - "context" - "encoding/binary" - "fmt" - "strings" - "time" - - "github.com/absmach/magistrala/pat" - "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" - secretKey = "secret_key" - scopeKey = "scope" - issuedAtKey = "issued_at" - expiresAtKey = "expires_at" - updatedAtKey = "updated_at" - lastUsedAtKey = "last_used_at" - revokedKey = "revoked" - revokedAtKey = "revoked_at" - platformEntitiesKey = "platform_entities" - patKey = "pat" - - keySeparator = ":" - anyID = "*" -) - -var ( - activateValue = []byte{0x00} - revokedValue = []byte{0x01} - entityValue = []byte{0x02} - anyIDValue = []byte{0x03} - selectedIDsValue = []byte{0x04} -) - -type patRepo struct { - db *bolt.DB - bucketName string -} - -// NewPATSRepository instantiates a bolt -// implementation of PAT repository. -func NewPATSRepository(db *bolt.DB, bucketName string) pat.PATSRepository { - return &patRepo{ - db: db, - bucketName: bucketName, - } -} - -func (pr *patRepo) Save(ctx context.Context, pat pat.PAT) error { - idxKey := []byte(pat.User + keySeparator + patKey + keySeparator + pat.ID) - kv, err := patToKeyValue(pat) - if err != nil { - return err - } - return pr.db.Update(func(tx *bolt.Tx) error { - 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) - } - 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) - } - } - if err := rootBucket.Put(idxKey, []byte(pat.ID)); err != nil { - return errors.Wrap(repoerr.ErrCreateEntity, err) - } - return nil - }) -} - -func (pr *patRepo) Retrieve(ctx context.Context, userID, patID string) (pat.PAT, error) { - prefix := []byte(patID + keySeparator) - kv := map[string][]byte{} - if err := pr.db.View(func(tx *bolt.Tx) error { - b, err := pr.retrieveUserBucket(tx, userID, patID, repoerr.ErrViewEntity) - 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 pat.PAT{}, err - } - - 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) (pat.PAT, error) { - return pr.updatePATField(ctx, userID, patID, nameKey, []byte(name)) -} - -func (pr *patRepo) UpdateDescription(ctx context.Context, userID, patID, description string) (pat.PAT, error) { - return pr.updatePATField(ctx, userID, patID, descriptionKey, []byte(description)) -} - -func (pr *patRepo) UpdateTokenHash(ctx context.Context, userID, patID, tokenHash string, expiryAt time.Time) (pat.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+secretKey), []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) - } - 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 pat.PAT{}, err - } - return keyValueToPAT(kv) -} - -func (pr *patRepo) RetrieveAll(ctx context.Context, userID string, pm pat.PATSPageMeta) (pat.PATSPage, error) { - prefix := []byte(userID + keySeparator + patKey + keySeparator) - - patIDs := []string{} - if err := pr.db.View(func(tx *bolt.Tx) error { - b, err := pr.retrieveRootBucket(tx) - if err != nil { - 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() { - if v != nil { - patIDs = append(patIDs, string(v)) - } - } - return nil - }); err != nil { - return pat.PATSPage{}, err - } - - total := len(patIDs) - - var pats []pat.PAT - - patsPage := pat.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 < pm.Offset+aLimit; i++ { - if int(i) < total { - 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 { - 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), 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) 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, patID, repoerr.ErrRemoveEntity) - if err != nil { - return 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) - } - } - 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 - } - - return nil -} - -func (pr *patRepo) AddScopeEntry(ctx context.Context, userID, patID string, platformEntityType pat.PlatformEntityType, optionalDomainID string, optionalDomainEntityType pat.DomainEntityType, operation pat.OperationType, entityIDs ...string) (pat.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, patID, repoerr.ErrCreateEntity) - if err != nil { - return 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) - } - } - 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 pat.Scope{}, err - } - - return parseKeyValueToScope(rKV) -} - -func (pr *patRepo) RemoveScopeEntry(ctx context.Context, userID, patID string, platformEntityType pat.PlatformEntityType, optionalDomainID string, optionalDomainEntityType pat.DomainEntityType, operation pat.OperationType, entityIDs ...string) (pat.Scope, error) { - if len(entityIDs) == 0 { - return pat.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, patID, repoerr.ErrRemoveEntity) - if err != nil { - return 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) - } - } - 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 pat.Scope{}, err - } - return parseKeyValueToScope(rKV) -} - -func (pr *patRepo) CheckScopeEntry(ctx context.Context, userID, patID string, platformEntityType pat.PlatformEntityType, optionalDomainID string, optionalDomainEntityType pat.DomainEntityType, operation pat.OperationType, entityIDs ...string) error { - return pr.db.Update(func(tx *bolt.Tx) error { - b, err := pr.retrieveUserBucket(tx, userID, patID, repoerr.ErrViewEntity) - 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) updatePATField(_ context.Context, userID, patID, key string, value []byte) (pat.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 pat.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 { - 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, patID string, wrap error) (*bolt.Bucket, error) { - rootBucket, err := pr.retrieveRootBucket(tx) - if err != nil { - 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, errors.Wrap(wrap, fmt.Errorf("user %s not found", userID)) - } - 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 patToKeyValue(pat pat.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), - secretKey: []byte(pat.Secret), - 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 pat.Scope) (map[string][]byte, error) { - kv := map[string][]byte{} - for opType, scopeValue := range scope.Users { - tempKV, err := scopeEntryToKeyValue(pat.PlatformUsersScope, "", pat.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 { - tempKV, err := scopeEntryToKeyValue(pat.PlatformDomainsScope, domainID, pat.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 { - tempKV, err := scopeEntryToKeyValue(pat.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 pat.PlatformEntityType, optionalDomainID string, optionalDomainEntityType pat.DomainEntityType, operation pat.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 pat.PlatformEntityType, optionalDomainID string, optionalDomainEntityType pat.DomainEntityType, operation pat.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 pat.PlatformUsersScope: - rootKey.WriteString(op) - case pat.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 keyValueToBasicPAT(kv map[string][]byte) pat.PAT { - var pat pat.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 -} - -func keyValueToPAT(kv map[string][]byte) (pat.PAT, error) { - res := keyValueToBasicPAT(kv) - scope, err := parseKeyValueToScope(kv) - if err != nil { - return pat.PAT{}, err - } - res.Scope = scope - return res, nil -} - -func parseKeyValueToScope(kv map[string][]byte) (pat.Scope, error) { - scope := pat.Scope{ - Domains: make(map[string]pat.DomainScope), - } - for key, value := range kv { - if strings.Index(key, keySeparator+scopeKey+keySeparator) > 0 { - keyParts := strings.Split(key, keySeparator) - - platformEntityType, err := pat.ParsePlatformEntityType(keyParts[2]) - if err != nil { - return pat.Scope{}, errors.Wrap(repoerr.ErrViewEntity, err) - } - - switch platformEntityType { - case pat.PlatformUsersScope: - scope.Users, err = parseOperation(platformEntityType, scope.Users, key, keyParts, value) - if err != nil { - return pat.Scope{}, errors.Wrap(repoerr.ErrViewEntity, err) - } - - case pat.PlatformDomainsScope: - if len(keyParts) < 6 { - return pat.Scope{}, fmt.Errorf("invalid scope key format: %s", key) - } - domainID := keyParts[3] - if scope.Domains == nil { - scope.Domains = make(map[string]pat.DomainScope) - } - if _, ok := scope.Domains[domainID]; !ok { - scope.Domains[domainID] = pat.DomainScope{} - } - domainScope := scope.Domains[domainID] - - entityType := keyParts[4] - - switch entityType { - case pat.DomainManagementScope.String(): - domainScope.DomainManagement, err = parseOperation(platformEntityType, domainScope.DomainManagement, key, keyParts, value) - if err != nil { - return pat.Scope{}, errors.Wrap(repoerr.ErrViewEntity, err) - } - default: - etype, err := pat.ParseDomainEntityType(entityType) - if err != nil { - return pat.Scope{}, fmt.Errorf("key %s invalid entity type %s : %w", key, entityType, err) - } - if domainScope.Entities == nil { - domainScope.Entities = make(map[pat.DomainEntityType]pat.OperationScope) - } - if _, ok := domainScope.Entities[etype]; !ok { - domainScope.Entities[etype] = pat.OperationScope{} - } - entityOperationScope := domainScope.Entities[etype] - entityOperationScope, err = parseOperation(platformEntityType, entityOperationScope, key, keyParts, value) - if err != nil { - return pat.Scope{}, errors.Wrap(repoerr.ErrViewEntity, err) - } - domainScope.Entities[etype] = entityOperationScope - } - scope.Domains[domainID] = domainScope - default: - return pat.Scope{}, errors.Wrap(repoerr.ErrViewEntity, fmt.Errorf("invalid platform entity type : %s", platformEntityType.String())) - } - } - } - return scope, nil -} - -func parseOperation(platformEntityType pat.PlatformEntityType, opScope pat.OperationScope, key string, keyParts []string, value []byte) (pat.OperationScope, error) { - if opScope == nil { - opScope = make(map[pat.OperationType]pat.ScopeValue) - } - - if err := validateOperation(platformEntityType, opScope, key, keyParts, value); err != nil { - return pat.OperationScope{}, err - } - - switch string(value) { - case string(entityValue): - opType, err := pat.ParseOperationType(keyParts[len(keyParts)-2]) - if err != nil { - return pat.OperationScope{}, errors.Wrap(repoerr.ErrViewEntity, err) - } - entityID := keyParts[len(keyParts)-1] - - if _, oValueExists := opScope[opType]; !oValueExists { - opScope[opType] = &pat.SelectedIDs{} - } - oValue := opScope[opType] - if err := oValue.AddValues(entityID); err != nil { - return pat.OperationScope{}, fmt.Errorf("failed to add scope key %s with entity value %v : %w", key, entityID, err) - } - opScope[opType] = oValue - case string(anyIDValue): - opType, err := pat.ParseOperationType(keyParts[len(keyParts)-1]) - if err != nil { - return pat.OperationScope{}, errors.Wrap(repoerr.ErrViewEntity, err) - } - if oValue, oValueExists := opScope[opType]; oValueExists && oValue != nil { - if _, ok := oValue.(*pat.AnyIDs); !ok { - return pat.OperationScope{}, fmt.Errorf("failed to add scope key %s with entity anyIDs scope value : key already initialized with different type", key) - } - } - opScope[opType] = &pat.AnyIDs{} - case string(selectedIDsValue): - opType, err := pat.ParseOperationType(keyParts[len(keyParts)-1]) - if err != nil { - return pat.OperationScope{}, errors.Wrap(repoerr.ErrViewEntity, err) - } - oValue, oValueExists := opScope[opType] - if oValueExists && oValue != nil { - if _, ok := oValue.(*pat.SelectedIDs); !ok { - return pat.OperationScope{}, fmt.Errorf("failed to add scope key %s with entity selectedIDs scope value : key already initialized with different type", key) - } - } - if !oValueExists { - opScope[opType] = &pat.SelectedIDs{} - } - default: - return pat.OperationScope{}, fmt.Errorf("key %s have invalid value %v", key, value) - } - return opScope, nil -} - -func validateOperation(platformEntityType pat.PlatformEntityType, _ pat.OperationScope, key string, keyParts []string, value []byte) error { - expectedKeyPartsLength := 0 - switch string(value) { - case string(entityValue): - switch platformEntityType { - case pat.PlatformDomainsScope: - expectedKeyPartsLength = 7 - case pat.PlatformUsersScope: - expectedKeyPartsLength = 5 - default: - return fmt.Errorf("invalid platform entity type : %s", platformEntityType.String()) - } - case string(selectedIDsValue), string(anyIDValue): - switch platformEntityType { - case pat.PlatformDomainsScope: - expectedKeyPartsLength = 6 - case pat.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 timeToBytes(t time.Time) []byte { - timeBytes := make([]byte, 8) - binary.BigEndian.PutUint64(timeBytes, uint64(t.Unix())) - return timeBytes -} - -func bytesToTime(b []byte) time.Time { - timeAtSeconds := binary.BigEndian.Uint64(b) - return time.Unix(int64(timeAtSeconds), 0) -} - -func booleanToBytes(b bool) []byte { - if b { - return []byte{1} - } - return []byte{0} -} - -func bytesToBoolean(b []byte) bool { - if len(b) > 1 || b[0] != activateValue[0] { - return true - } - return false -} diff --git a/pat/events/streams.go b/pat/events/streams.go deleted file mode 100644 index 713ddea5a6..0000000000 --- a/pat/events/streams.go +++ /dev/null @@ -1,94 +0,0 @@ -// Copyright (c) Abstract Machines -// SPDX-License-Identifier: Apache-2.0 - -package events - -import ( - "context" - "time" - - "github.com/absmach/magistrala/pat" - "github.com/absmach/magistrala/pkg/authn" - "github.com/absmach/magistrala/pkg/events" - "github.com/absmach/magistrala/pkg/events/store" -) - -const streamID = "magistrala.pat" - -var _ pat.Service = (*eventStore)(nil) - - -type eventStore struct { - events.Publisher - svc pat.Service -} - -// NewEventStoreMiddleware returns wrapper around pat service that sends -// events to event store. -func NewEventStoreMiddleware(ctx context.Context, svc pat.Service, url string) (pat.Service, error) { - publisher, err := store.NewPublisher(ctx, url, streamID) - if err != nil { - return nil, err - } - - return &eventStore{ - svc: svc, - Publisher: publisher, - }, nil -} - -func (es *eventStore) CreatePAT(ctx context.Context, session authn.Session, name, description string, duration time.Duration, scope pat.Scope) (pat.PAT, error) { - return es.svc.CreatePAT(ctx, session, name, description, duration, scope) -} - -func (es *eventStore) UpdatePATName(ctx context.Context, session authn.Session, patID, name string) (pat.PAT, error) { - return es.svc.UpdatePATName(ctx, session, patID, name) -} - -func (es *eventStore) UpdatePATDescription(ctx context.Context, session authn.Session, patID, description string) (pat.PAT, error) { - return es.svc.UpdatePATDescription(ctx, session, patID, description) -} - -func (es *eventStore) RetrievePAT(ctx context.Context, session authn.Session, patID string) (pat.PAT, error) { - return es.svc.RetrievePAT(ctx, session, patID) -} - -func (es *eventStore) ListPATS(ctx context.Context, session authn.Session, pm pat.PATSPageMeta) (pat.PATSPage, error) { - return es.svc.ListPATS(ctx, session, pm) -} - -func (es *eventStore) DeletePAT(ctx context.Context, session authn.Session, patID string) error { - return es.svc.DeletePAT(ctx, session, patID) -} - -func (es *eventStore) ResetPATSecret(ctx context.Context, session authn.Session, patID string, duration time.Duration) (pat.PAT, error) { - return es.svc.ResetPATSecret(ctx, session, patID, duration) -} - -func (es *eventStore) RevokePATSecret(ctx context.Context, session authn.Session, patID string) error { - return es.svc.RevokePATSecret(ctx, session, patID) -} - -func (es *eventStore) AddPATScopeEntry(ctx context.Context, session authn.Session, patID string, platformEntityType pat.PlatformEntityType, optionalDomainID string, optionalDomainEntityType pat.DomainEntityType, operation pat.OperationType, entityIDs ...string) (pat.Scope, error) { - return es.svc.AddPATScopeEntry(ctx, session, patID, platformEntityType, optionalDomainID, optionalDomainEntityType, operation, entityIDs...) -} - -func (es *eventStore) RemovePATScopeEntry(ctx context.Context, session authn.Session, patID string, platformEntityType pat.PlatformEntityType, optionalDomainID string, optionalDomainEntityType pat.DomainEntityType, operation pat.OperationType, entityIDs ...string) (pat.Scope, error) { - return es.svc.RemovePATScopeEntry(ctx, session, patID, platformEntityType, optionalDomainID, optionalDomainEntityType, operation, entityIDs...) -} - -func (es *eventStore) ClearPATAllScopeEntry(ctx context.Context, session authn.Session, patID string) error { - return es.svc.ClearPATAllScopeEntry(ctx, session, patID) -} - -func (es *eventStore) IdentifyPAT(ctx context.Context, paToken string) (pat.PAT, error) { - return es.svc.IdentifyPAT(ctx, paToken) -} - -func (es *eventStore) AuthorizePAT(ctx context.Context, paToken string, platformEntityType pat.PlatformEntityType, optionalDomainID string, optionalDomainEntityType pat.DomainEntityType, operation pat.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 pat.PlatformEntityType, optionalDomainID string, optionalDomainEntityType pat.DomainEntityType, operation pat.OperationType, entityIDs ...string) error { - return es.svc.CheckPAT(ctx, userID, patID, platformEntityType, optionalDomainID, optionalDomainEntityType, operation, entityIDs...) -} \ No newline at end of file diff --git a/pat/hasher.go b/pat/hasher.go deleted file mode 100644 index b0452c1e1d..0000000000 --- a/pat/hasher.go +++ /dev/null @@ -1,17 +0,0 @@ -// Copyright (c) Abstract Machines -// SPDX-License-Identifier: Apache-2.0 - -package pat - -// 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/pat/hasher/doc.go b/pat/hasher/doc.go deleted file mode 100644 index 98be992262..0000000000 --- a/pat/hasher/doc.go +++ /dev/null @@ -1,6 +0,0 @@ -// 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/pat/hasher/hasher.go b/pat/hasher/hasher.go deleted file mode 100644 index 4d74669f02..0000000000 --- a/pat/hasher/hasher.go +++ /dev/null @@ -1,86 +0,0 @@ -// Copyright (c) Abstract Machines -// SPDX-License-Identifier: Apache-2.0 - -package hasher - -import ( - "encoding/base64" - "fmt" - "math/rand" - "strings" - "time" - - "github.com/absmach/magistrala/pat" - "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 _ pat.Hasher = (*bcryptHasher)(nil) - -type bcryptHasher struct{} - -// New instantiates a bcrypt-based hasher implementation. -func New() pat.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/pat/middleware/authorization.go b/pat/middleware/authorization.go deleted file mode 100644 index 1064590088..0000000000 --- a/pat/middleware/authorization.go +++ /dev/null @@ -1,84 +0,0 @@ -// Copyright (c) Abstract Machines -// SPDX-License-Identifier: Apache-2.0 - -package middleware - -import ( - "context" - "time" - - "github.com/absmach/magistrala/pat" - "github.com/absmach/magistrala/pkg/authn" - mgauthz "github.com/absmach/magistrala/pkg/authz" -) - -var _ pat.Service = (*authorizationMiddleware)(nil) - -type authorizationMiddleware struct { - svc pat.Service - authz mgauthz.Authorization -} - -// AuthorizationMiddleware adds authorization to the clients service. -func AuthorizationMiddleware(entityType string, svc pat.Service, authz mgauthz.Authorization) (pat.Service, error) { - return &authorizationMiddleware{ - svc: svc, - authz: authz, - }, nil -} - -func (am *authorizationMiddleware) CreatePAT(ctx context.Context, session authn.Session, name, description string, duration time.Duration, scope pat.Scope) (pat.PAT, error) { - return am.svc.CreatePAT(ctx, session, name, description, duration, scope) -} - -func (am *authorizationMiddleware) UpdatePATName(ctx context.Context, session authn.Session, patID, name string) (pat.PAT, error) { - return am.svc.UpdatePATName(ctx, session, patID, name) -} - -func (am *authorizationMiddleware) UpdatePATDescription(ctx context.Context, session authn.Session, patID, description string) (pat.PAT, error) { - return am.svc.UpdatePATDescription(ctx, session, patID, description) -} - -func (am *authorizationMiddleware) RetrievePAT(ctx context.Context, session authn.Session, patID string) (pat.PAT, error) { - return am.svc.RetrievePAT(ctx, session, patID) -} - -func (am *authorizationMiddleware) ListPATS(ctx context.Context, session authn.Session, pm pat.PATSPageMeta) (pat.PATSPage, error) { - return am.svc.ListPATS(ctx, session, pm) -} - -func (am *authorizationMiddleware) DeletePAT(ctx context.Context, session authn.Session, patID string) error { - return am.svc.DeletePAT(ctx, session, patID) -} - -func (am *authorizationMiddleware) ResetPATSecret(ctx context.Context, session authn.Session, patID string, duration time.Duration) (pat.PAT, error) { - return am.svc.ResetPATSecret(ctx, session, patID, duration) -} - -func (am *authorizationMiddleware) RevokePATSecret(ctx context.Context, session authn.Session, patID string) error { - return am.svc.RevokePATSecret(ctx, session, patID) -} - -func (am *authorizationMiddleware) AddPATScopeEntry(ctx context.Context, session authn.Session, patID string, platformEntityType pat.PlatformEntityType, optionalDomainID string, optionalDomainEntityType pat.DomainEntityType, operation pat.OperationType, entityIDs ...string) (pat.Scope, error) { - return am.svc.AddPATScopeEntry(ctx, session, patID, platformEntityType, optionalDomainID, optionalDomainEntityType, operation, entityIDs...) -} - -func (am *authorizationMiddleware) RemovePATScopeEntry(ctx context.Context, session authn.Session, patID string, platformEntityType pat.PlatformEntityType, optionalDomainID string, optionalDomainEntityType pat.DomainEntityType, operation pat.OperationType, entityIDs ...string) (pat.Scope, error) { - return am.svc.RemovePATScopeEntry(ctx, session, patID, platformEntityType, optionalDomainID, optionalDomainEntityType, operation, entityIDs...) -} - -func (am *authorizationMiddleware) ClearPATAllScopeEntry(ctx context.Context, session authn.Session, patID string) error { - return am.svc.ClearPATAllScopeEntry(ctx, session, patID) -} - -func (am *authorizationMiddleware) IdentifyPAT(ctx context.Context, secret string) (pat.PAT, error) { - return am.svc.IdentifyPAT(ctx, secret) -} - -func (am *authorizationMiddleware) AuthorizePAT(ctx context.Context, paToken string, platformEntityType pat.PlatformEntityType, optionalDomainID string, optionalDomainEntityType pat.DomainEntityType, operation pat.OperationType, entityIDs ...string) error { - return am.svc.AuthorizePAT(ctx, paToken, platformEntityType, optionalDomainID, optionalDomainEntityType, operation, entityIDs...) -} - -func (am *authorizationMiddleware) CheckPAT(ctx context.Context, userID, patID string, platformEntityType pat.PlatformEntityType, optionalDomainID string, optionalDomainEntityType pat.DomainEntityType, operation pat.OperationType, entityIDs ...string) error { - return am.svc.CheckPAT(ctx, userID, patID, platformEntityType, optionalDomainID, optionalDomainEntityType, operation, entityIDs...) -} diff --git a/pat/middleware/logging.go b/pat/middleware/logging.go deleted file mode 100644 index 51767d39c1..0000000000 --- a/pat/middleware/logging.go +++ /dev/null @@ -1,275 +0,0 @@ -// Copyright (c) Abstract Machines -// SPDX-License-Identifier: Apache-2.0 - -package middleware - -import ( - "context" - "log/slog" - "time" - - "github.com/absmach/magistrala/pat" - "github.com/absmach/magistrala/pkg/authn" -) - -var _ pat.Service = (*loggingMiddleware)(nil) - -type loggingMiddleware struct { - logger *slog.Logger - svc pat.Service -} - -// LoggingMiddleware adds logging facilities to the core service. -func LoggingMiddleware(svc pat.Service, logger *slog.Logger) pat.Service { - return &loggingMiddleware{logger, svc} -} - -func (lm *loggingMiddleware) CreatePAT(ctx context.Context, session authn.Session, name, description string, duration time.Duration, scope pat.Scope) (pa pat.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.CreatePAT(ctx, session, name, description, duration, scope) -} - -func (lm *loggingMiddleware) UpdatePATName(ctx context.Context, session authn.Session, patID, name string) (pa pat.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.UpdatePATName(ctx, session, patID, name) -} - -func (lm *loggingMiddleware) UpdatePATDescription(ctx context.Context, session authn.Session, patID, description string) (pa pat.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.UpdatePATDescription(ctx, session, patID, description) -} - -func (lm *loggingMiddleware) RetrievePAT(ctx context.Context, session authn.Session, patID string) (pa pat.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.RetrievePAT(ctx, session, patID) -} - -func (lm *loggingMiddleware) ListPATS(ctx context.Context, session authn.Session, pm pat.PATSPageMeta) (pp pat.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.ListPATS(ctx, session, pm) -} - -func (lm *loggingMiddleware) DeletePAT(ctx context.Context, session authn.Session, 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.DeletePAT(ctx, session, patID) -} - -func (lm *loggingMiddleware) ResetPATSecret(ctx context.Context, session authn.Session, patID string, duration time.Duration) (pa pat.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.ResetPATSecret(ctx, session, patID, duration) -} - -func (lm *loggingMiddleware) RevokePATSecret(ctx context.Context, session authn.Session, 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.RevokePATSecret(ctx, session, patID) -} - -func (lm *loggingMiddleware) AddPATScopeEntry(ctx context.Context, session authn.Session, patID string, platformEntityType pat.PlatformEntityType, optionalDomainID string, optionalDomainEntityType pat.DomainEntityType, operation pat.OperationType, entityIDs ...string) (sc pat.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.AddPATScopeEntry(ctx, session, patID, platformEntityType, optionalDomainID, optionalDomainEntityType, operation, entityIDs...) -} - -func (lm *loggingMiddleware) RemovePATScopeEntry(ctx context.Context, session authn.Session, patID string, platformEntityType pat.PlatformEntityType, optionalDomainID string, optionalDomainEntityType pat.DomainEntityType, operation pat.OperationType, entityIDs ...string) (sc pat.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.RemovePATScopeEntry(ctx, session, patID, platformEntityType, optionalDomainID, optionalDomainEntityType, operation, entityIDs...) -} - -func (lm *loggingMiddleware) ClearPATAllScopeEntry(ctx context.Context, session authn.Session, 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.ClearPATAllScopeEntry(ctx, session, patID) -} - -func (lm *loggingMiddleware) IdentifyPAT(ctx context.Context, paToken string) (pa pat.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, platformEntityType pat.PlatformEntityType, optionalDomainID string, optionalDomainEntityType pat.DomainEntityType, operation pat.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("Authorize PAT failed complete successfully", args...) - return - } - lm.logger.Info("Authorize PAT completed successfully", args...) - }(time.Now()) - return lm.svc.AuthorizePAT(ctx, paToken, platformEntityType, optionalDomainID, optionalDomainEntityType, operation, entityIDs...) -} - -func (lm *loggingMiddleware) CheckPAT(ctx context.Context, userID, patID string, platformEntityType pat.PlatformEntityType, optionalDomainID string, optionalDomainEntityType pat.DomainEntityType, operation pat.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("Check PAT failed complete successfully", args...) - return - } - lm.logger.Info("Check PAT completed successfully", args...) - }(time.Now()) - return lm.svc.CheckPAT(ctx, userID, patID, platformEntityType, optionalDomainID, optionalDomainEntityType, operation, entityIDs...) -} diff --git a/pat/middleware/metrics.go b/pat/middleware/metrics.go deleted file mode 100644 index 4a44bb0aa1..0000000000 --- a/pat/middleware/metrics.go +++ /dev/null @@ -1,142 +0,0 @@ -// Copyright (c) Abstract Machines -// SPDX-License-Identifier: Apache-2.0 - -package middleware - -import ( - "context" - "time" - - "github.com/absmach/magistrala/pat" - "github.com/absmach/magistrala/pkg/authn" - "github.com/go-kit/kit/metrics" -) - -var _ pat.Service = (*metricsMiddleware)(nil) - -type metricsMiddleware struct { - counter metrics.Counter - latency metrics.Histogram - svc pat.Service -} - -// MetricsMiddleware instruments core service by tracking request count and latency. -func MetricsMiddleware(svc pat.Service, counter metrics.Counter, latency metrics.Histogram) pat.Service { - return &metricsMiddleware{ - counter: counter, - latency: latency, - svc: svc, - } -} - -func (ms *metricsMiddleware) CreatePAT(ctx context.Context, session authn.Session, name, description string, duration time.Duration, scope pat.Scope) (pat.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.CreatePAT(ctx, session, name, description, duration, scope) -} - -func (ms *metricsMiddleware) UpdatePATName(ctx context.Context, session authn.Session, patID, name string) (pat.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.UpdatePATName(ctx, session, patID, name) -} - -func (ms *metricsMiddleware) UpdatePATDescription(ctx context.Context, session authn.Session, patID, description string) (pat.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.UpdatePATDescription(ctx, session, patID, description) -} - -func (ms *metricsMiddleware) RetrievePAT(ctx context.Context, session authn.Session, patID string) (pat.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.RetrievePAT(ctx, session, patID) -} - -func (ms *metricsMiddleware) ListPATS(ctx context.Context, session authn.Session, pm pat.PATSPageMeta) (pat.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.ListPATS(ctx, session, pm) -} - -func (ms *metricsMiddleware) DeletePAT(ctx context.Context, session authn.Session, 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.DeletePAT(ctx, session, patID) -} - -func (ms *metricsMiddleware) ResetPATSecret(ctx context.Context, session authn.Session, patID string, duration time.Duration) (pat.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.ResetPATSecret(ctx, session, patID, duration) -} - -func (ms *metricsMiddleware) RevokePATSecret(ctx context.Context, session authn.Session, 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.RevokePATSecret(ctx, session, patID) -} - -func (ms *metricsMiddleware) AddPATScopeEntry(ctx context.Context, session authn.Session, patID string, platformEntityType pat.PlatformEntityType, optionalDomainID string, optionalDomainEntityType pat.DomainEntityType, operation pat.OperationType, entityIDs ...string) (pat.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.AddPATScopeEntry(ctx, session, patID, platformEntityType, optionalDomainID, optionalDomainEntityType, operation, entityIDs...) -} - -func (ms *metricsMiddleware) RemovePATScopeEntry(ctx context.Context, session authn.Session, patID string, platformEntityType pat.PlatformEntityType, optionalDomainID string, optionalDomainEntityType pat.DomainEntityType, operation pat.OperationType, entityIDs ...string) (pat.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.RemovePATScopeEntry(ctx, session, patID, platformEntityType, optionalDomainID, optionalDomainEntityType, operation, entityIDs...) -} - -func (ms *metricsMiddleware) ClearPATAllScopeEntry(ctx context.Context, session authn.Session, 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.ClearPATAllScopeEntry(ctx, session, patID) -} - -func (ms *metricsMiddleware) IdentifyPAT(ctx context.Context, paToken string) (pat.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, platformEntityType pat.PlatformEntityType, optionalDomainID string, optionalDomainEntityType pat.DomainEntityType, operation pat.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, platformEntityType, optionalDomainID, optionalDomainEntityType, operation, entityIDs...) -} - -func (ms *metricsMiddleware) CheckPAT(ctx context.Context, userID, patID string, platformEntityType pat.PlatformEntityType, optionalDomainID string, optionalDomainEntityType pat.DomainEntityType, operation pat.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/pat/pat.go b/pat/pat.go deleted file mode 100644 index 5b9cb08045..0000000000 --- a/pat/pat.go +++ /dev/null @@ -1,753 +0,0 @@ -// Copyright (c) Abstract Machines -// SPDX-License-Identifier: Apache-2.0 - -package pat - -import ( - "context" - "encoding/json" - "fmt" - "time" - - "github.com/absmach/magistrala/pkg/authn" - "github.com/absmach/magistrala/pkg/errors" -) - -var errAddEntityToAnyIDs = errors.New("could not add entity id to any ID scope value") - -// Define OperationType. -type OperationType uint32 - -const ( - CreateOp OperationType = iota - ReadOp - ListOp - UpdateOp - DeleteOp -) - -const ( - createOpStr = "create" - readOpStr = "read" - listOpStr = "list" - updateOpStr = "update" - deleteOpStr = "delete" -) - -func (ot OperationType) String() string { - switch ot { - case CreateOp: - return createOpStr - case ReadOp: - return readOpStr - case ListOp: - return listOpStr - case UpdateOp: - return updateOpStr - case DeleteOp: - return deleteOpStr - default: - return fmt.Sprintf("unknown operation type %d", ot) - } -} - -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: - return CreateOp, nil - case readOpStr: - return ReadOp, nil - case listOpStr: - return ListOp, nil - case updateOpStr: - return UpdateOp, nil - case deleteOpStr: - return DeleteOp, nil - default: - return 0, fmt.Errorf("unknown operation type %s", 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 -} - -func (ot *OperationType) UnmarshalText(data []byte) (err error) { - *ot, err = ParseOperationType(string(data)) - return err -} - -// Define DomainEntityType. -type DomainEntityType uint32 - -const ( - DomainManagementScope DomainEntityType = iota - DomainGroupsScope - DomainChannelsScope - DomainThingsScope - DomainNullScope -) - -const ( - domainManagementScopeStr = "domain_management" - domainGroupsScopeStr = "groups" - domainChannelsScopeStr = "channels" - domainThingsScopeStr = "things" -) - -func (det DomainEntityType) String() string { - switch det { - case DomainManagementScope: - return domainManagementScopeStr - case DomainGroupsScope: - return domainGroupsScopeStr - case DomainChannelsScope: - return domainChannelsScopeStr - case DomainThingsScope: - return domainThingsScopeStr - default: - return fmt.Sprintf("unknown domain entity type %d", det) - } -} - -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: - return DomainManagementScope, nil - case domainGroupsScopeStr: - return DomainGroupsScope, nil - case domainChannelsScopeStr: - return DomainChannelsScope, nil - case domainThingsScopeStr: - 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() ([]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 - -const ( - PlatformUsersScope PlatformEntityType = iota - PlatformDomainsScope -) - -const ( - platformUsersScopeStr = "users" - platformDomainsScopeStr = "domains" -) - -func (pet PlatformEntityType) String() string { - switch pet { - case PlatformUsersScope: - return platformUsersScopeStr - case PlatformDomainsScope: - return platformDomainsScopeStr - default: - return fmt.Sprintf("unknown platform entity type %d", pet) - } -} - -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: - return PlatformUsersScope, nil - case platformDomainsScopeStr: - 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 -} - -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 - 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) 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{} - -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 -} - -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 map[OperationType]ScopeValue - -func (os *OperationScope) UnmarshalJSON(data []byte) error { - type tempOperationScope map[OperationType]json.RawMessage - - var tempScope tempOperationScope - if err := json.Unmarshal(data, &tempScope); err != nil { - return err - } - // Initialize the Operations map - *os = OperationScope{} - - for opType, rawMessage := range tempScope { - var stringValue string - var stringArrayValue []string - - // Try to unmarshal as string - if err := json.Unmarshal(rawMessage, &stringValue); err == nil { - if err := os.Add(opType, stringValue); err != nil { - return err - } - continue - } - - // Try to unmarshal as []string - if err := json.Unmarshal(rawMessage, &stringArrayValue); err == nil { - if err := os.Add(opType, stringArrayValue...); err != nil { - return err - } - continue - } - - // If neither unmarshalling succeeded, return an error - return fmt.Errorf("invalid ScopeValue for OperationType %v", opType) - } - - 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 = &OperationScope{} - } - - if len(entityIDs) == 0 { - return fmt.Errorf("entity ID is missing") - } - switch { - 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) - } - sids[entityID] = struct{}{} - } - value = &sids - } - (*os)[operation] = value - return nil -} - -func (os *OperationScope) Delete(operation OperationType, entityIDs ...string) error { - if os == nil { - return nil - } - - opEntityIDs, exists := (*os)[operation] - if !exists { - return nil - } - - if len(entityIDs) == 0 { - return fmt.Errorf("failed to delete operation %s: entity ID is missing", operation.String()) - } - - switch eIDs := opEntityIDs.(type) { - case *AnyIDs: - if !(len(entityIDs) == 1 && entityIDs[0] == "*") { - return fmt.Errorf("failed to delete operation %s: invalid list", operation.String()) - } - delete((*os), operation) - return nil - 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((*os), operation) - } - } - return nil - default: - return fmt.Errorf("failed to delete operation: invalid entity id type %d", operation) - } -} - -func (os *OperationScope) Check(operation OperationType, entityIDs ...string) bool { - if os == nil { - return false - } - - if scopeValue, ok := (*os)[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) == 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 - } - if domainEntityType == DomainManagementScope { - return ds.DomainManagement.Check(operation, ids...) - } - os, exists := ds.Entities[domainEntityType] - if !exists { - return false - } - - return os.Check(operation, ids...) -} - -// Example Scope as JSON -// -// { -// "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.Marshal(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"` - Name string `json:"name,omitempty"` - Description string `json:"description,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"` - 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 PATSPageMeta struct { - Offset uint64 `json:"offset"` - Limit uint64 `json:"limit"` -} -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()) - } - return string(str) -} - -// Expired verifies if the key is expired. -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 Service --output=./mocks --filename pats.go --quiet --note "Copyright (c) Abstract Machines" - -type Service interface { - // Create function creates new PAT for given valid inputs. - CreatePAT(ctx context.Context, session authn.Session, name, description string, duration time.Duration, scope Scope) (PAT, error) - - // UpdateName function updates the name for the given PAT ID. - UpdatePATName(ctx context.Context, session authn.Session, patID, name string) (PAT, error) - - // UpdateDescription function updates the description for the given PAT ID. - UpdatePATDescription(ctx context.Context, session authn.Session, patID, description string) (PAT, error) - - // Retrieve function retrieves the PAT for given ID. - RetrievePAT(ctx context.Context, session authn.Session, patID string) (PAT, error) - - // List function lists all the PATs for the user. - ListPATS(ctx context.Context, session authn.Session, pm PATSPageMeta) (PATSPage, error) - - // Delete function deletes the PAT for given ID. - DeletePAT(ctx context.Context, session authn.Session, patID string) error - - // ResetSecret function reset the secret and creates new secret for the given ID. - ResetPATSecret(ctx context.Context, session authn.Session, patID string, duration time.Duration) (PAT, error) - - // RevokeSecret function revokes the secret for the given ID. - RevokePATSecret(ctx context.Context, session authn.Session, patID string) error - - // AddScope function adds a new scope entry. - AddPATScopeEntry(ctx context.Context, session authn.Session, patID string, platformEntityType PlatformEntityType, optionalDomainID string, optionalDomainEntityType DomainEntityType, operation OperationType, entityIDs ...string) (Scope, error) - - // RemoveScope function removes a scope entry. - RemovePATScopeEntry(ctx context.Context, session authn.Session, patID string, platformEntityType PlatformEntityType, optionalDomainID string, optionalDomainEntityType DomainEntityType, operation OperationType, entityIDs ...string) (Scope, error) - - // ClearAllScope function removes all scope entry. - ClearPATAllScopeEntry(ctx context.Context, session authn.Session, patID 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, 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. -// -//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) (err error) - - // 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) - - // 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) - - // RetrieveAll retrieves all PATs belongs to userID. - 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 - - // 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 - - 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) - - 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/pat/service.go b/pat/service.go deleted file mode 100644 index 9c995914e4..0000000000 --- a/pat/service.go +++ /dev/null @@ -1,259 +0,0 @@ -// Copyright (c) Abstract Machines -// SPDX-License-Identifier: Apache-2.0 - -package pat - -import ( - "context" - "encoding/base64" - "math/rand" - "strings" - "time" - - "github.com/absmach/magistrala" - "github.com/absmach/magistrala/pkg/authn" - "github.com/absmach/magistrala/pkg/errors" - svcerr "github.com/absmach/magistrala/pkg/errors/service" - "github.com/google/uuid" -) - -const ( - randStr = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890!@#$%^&&*|+-=" - patPrefix = "pat" - patSecretSeparator = "_" -) - -var ( - 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") - 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") - errClearAllScope = errors.New("failed to clear all entry in scope") -) - -type service struct { - pats PATSRepository - hasher Hasher - idProvider magistrala.IDProvider -} - -var _ Service = (*service)(nil) - -// New instantiates the auth service implementation. -func New(pats PATSRepository, hasher Hasher, idp magistrala.IDProvider) Service { - return &service{ - pats: pats, - hasher: hasher, - idProvider: idp, - } -} - -func (svc service) CreatePAT(ctx context.Context, session authn.Session, name, description string, duration time.Duration, scope Scope) (PAT, error) { - id, err := svc.idProvider.ID() - if err != nil { - return PAT{}, errors.Wrap(svcerr.ErrCreateEntity, err) - } - secret, hash, err := svc.generateSecretAndHash(session.UserID, id) - if err != nil { - return PAT{}, errors.Wrap(svcerr.ErrCreateEntity, err) - } - - now := time.Now() - pat := PAT{ - ID: id, - User: session.UserID, - 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 -} - -func (svc service) UpdatePATName(ctx context.Context, session authn.Session, patID, name string) (PAT, error) { - pat, err := svc.pats.UpdateName(ctx, session.UserID, patID, name) - if err != nil { - return PAT{}, errors.Wrap(errUpdatePAT, err) - } - return pat, nil -} - -func (svc service) UpdatePATDescription(ctx context.Context, session authn.Session, patID, description string) (PAT, error) { - pat, err := svc.pats.UpdateDescription(ctx, session.UserID, patID, description) - if err != nil { - return PAT{}, errors.Wrap(errUpdatePAT, err) - } - return pat, nil -} - -func (svc service) RetrievePAT(ctx context.Context, session authn.Session, patID string) (PAT, error) { - pat, err := svc.pats.Retrieve(ctx, session.UserID, patID) - if err != nil { - return PAT{}, errors.Wrap(errRetrievePAT, err) - } - return pat, nil -} - -func (svc service) ListPATS(ctx context.Context, session authn.Session, pm PATSPageMeta) (PATSPage, error) { - patsPage, err := svc.pats.RetrieveAll(ctx, session.UserID, pm) - if err != nil { - return PATSPage{}, errors.Wrap(errRetrievePAT, err) - } - return patsPage, nil -} - -func (svc service) DeletePAT(ctx context.Context, session authn.Session, patID string) error { - if err := svc.pats.Remove(ctx, session.UserID, patID); err != nil { - return errors.Wrap(errDeletePAT, err) - } - return nil -} - -func (svc service) ResetPATSecret(ctx context.Context, session authn.Session, patID string, duration time.Duration) (PAT, error) { - // Generate new HashToken take place here - secret, hash, err := svc.generateSecretAndHash(session.UserID, patID) - if err != nil { - return PAT{}, errors.Wrap(svcerr.ErrUpdateEntity, err) - } - - pat, err := svc.pats.UpdateTokenHash(ctx, session.UserID, patID, hash, time.Now().Add(duration)) - if err != nil { - return PAT{}, errors.Wrap(svcerr.ErrUpdateEntity, err) - } - - if err := svc.pats.Reactivate(ctx, session.UserID, patID); err != nil { - return PAT{}, errors.Wrap(svcerr.ErrUpdateEntity, err) - } - pat.Secret = secret - pat.Revoked = false - pat.RevokedAt = time.Time{} - return pat, nil -} - -func (svc service) RevokePATSecret(ctx context.Context, session authn.Session, patID string) error { - if err := svc.pats.Revoke(ctx, session.UserID, patID); err != nil { - return errors.Wrap(errRevokePAT, err) - } - return nil -} - -func (svc service) AddPATScopeEntry(ctx context.Context, session authn.Session, patID string, platformEntityType PlatformEntityType, optionalDomainID string, optionalDomainEntityType DomainEntityType, operation OperationType, entityIDs ...string) (Scope, error) { - scope, err := svc.pats.AddScopeEntry(ctx, session.UserID, 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, session authn.Session, patID string, platformEntityType PlatformEntityType, optionalDomainID string, optionalDomainEntityType DomainEntityType, operation OperationType, entityIDs ...string) (Scope, error) { - scope, err := svc.pats.RemoveScopeEntry(ctx, session.UserID, patID, platformEntityType, optionalDomainID, optionalDomainEntityType, operation, entityIDs...) - if err != nil { - return Scope{}, err - } - return scope, nil -} - -func (svc service) ClearPATAllScopeEntry(ctx context.Context, session authn.Session, patID string) error { - if err := svc.pats.RemoveAllScopeEntry(ctx, session.UserID, patID); err != nil { - return errors.Wrap(errClearAllScope, err) - } - return nil -} - -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, res.ID, platformEntityType, optionalDomainID, optionalDomainEntityType, operation, entityIDs...); err != nil { - return errors.Wrap(svcerr.ErrAuthorization, err) - } - return 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 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/pat/tracing/tracing.go b/pat/tracing/tracing.go deleted file mode 100644 index 4fab5fae49..0000000000 --- a/pat/tracing/tracing.go +++ /dev/null @@ -1,164 +0,0 @@ -// Copyright (c) Abstract Machines -// SPDX-License-Identifier: Apache-2.0 - -package tracing - -import ( - "context" - "time" - - "github.com/absmach/magistrala/pat" - "github.com/absmach/magistrala/pkg/authn" - "go.opentelemetry.io/otel/attribute" - "go.opentelemetry.io/otel/trace" -) - -var _ pat.Service = (*tracingMiddleware)(nil) - -type tracingMiddleware struct { - tracer trace.Tracer - svc pat.Service -} - -// New returns a new group service with tracing capabilities. -func New(svc pat.Service, tracer trace.Tracer) pat.Service { - return &tracingMiddleware{tracer, svc} -} - -func (tm *tracingMiddleware) CreatePAT(ctx context.Context, session authn.Session, name, description string, duration time.Duration, scope pat.Scope) (pat.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.CreatePAT(ctx, session, name, description, duration, scope) -} - -func (tm *tracingMiddleware) UpdatePATName(ctx context.Context, session authn.Session, patID, name string) (pat.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.UpdatePATName(ctx, session, patID, name) -} - -func (tm *tracingMiddleware) UpdatePATDescription(ctx context.Context, session authn.Session, patID, description string) (pat.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.UpdatePATDescription(ctx, session, patID, description) -} - -func (tm *tracingMiddleware) RetrievePAT(ctx context.Context, session authn.Session, patID string) (pat.PAT, error) { - ctx, span := tm.tracer.Start(ctx, "retrieve_pat", trace.WithAttributes( - attribute.String("pat_id", patID), - )) - defer span.End() - return tm.svc.RetrievePAT(ctx, session, patID) -} - -func (tm *tracingMiddleware) ListPATS(ctx context.Context, session authn.Session, pm pat.PATSPageMeta) (pat.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.ListPATS(ctx, session, pm) -} - -func (tm *tracingMiddleware) DeletePAT(ctx context.Context, session authn.Session, patID string) error { - ctx, span := tm.tracer.Start(ctx, "delete_pat", trace.WithAttributes( - attribute.String("pat_id", patID), - )) - defer span.End() - return tm.svc.DeletePAT(ctx, session, patID) -} - -func (tm *tracingMiddleware) ResetPATSecret(ctx context.Context, session authn.Session, patID string, duration time.Duration) (pat.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.ResetPATSecret(ctx, session, patID, duration) -} - -func (tm *tracingMiddleware) RevokePATSecret(ctx context.Context, session authn.Session, 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.RevokePATSecret(ctx, session, patID) -} - -func (tm *tracingMiddleware) AddPATScopeEntry(ctx context.Context, session authn.Session, patID string, platformEntityType pat.PlatformEntityType, optionalDomainID string, optionalDomainEntityType pat.DomainEntityType, operation pat.OperationType, entityIDs ...string) (pat.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), - attribute.String("optional_domain_entity", optionalDomainEntityType.String()), - attribute.String("operation", operation.String()), - attribute.StringSlice("entities", entityIDs), - )) - defer span.End() - return tm.svc.AddPATScopeEntry(ctx, session, patID, platformEntityType, optionalDomainID, optionalDomainEntityType, operation, entityIDs...) -} - -func (tm *tracingMiddleware) RemovePATScopeEntry(ctx context.Context, session authn.Session, patID string, platformEntityType pat.PlatformEntityType, optionalDomainID string, optionalDomainEntityType pat.DomainEntityType, operation pat.OperationType, entityIDs ...string) (pat.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), - attribute.String("optional_domain_entity", optionalDomainEntityType.String()), - attribute.String("operation", operation.String()), - attribute.StringSlice("entities", entityIDs), - )) - defer span.End() - return tm.svc.RemovePATScopeEntry(ctx, session, patID, platformEntityType, optionalDomainID, optionalDomainEntityType, operation, entityIDs...) -} - -func (tm *tracingMiddleware) ClearPATAllScopeEntry(ctx context.Context, session authn.Session, 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.ClearPATAllScopeEntry(ctx, session, patID) -} - -func (tm *tracingMiddleware) IdentifyPAT(ctx context.Context, paToken string) (pat.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 pat.PlatformEntityType, optionalDomainID string, optionalDomainEntityType pat.DomainEntityType, operation pat.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), - 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, platformEntityType, optionalDomainID, optionalDomainEntityType, operation, entityIDs...) -} - -func (tm *tracingMiddleware) CheckPAT(ctx context.Context, userID, patID string, platformEntityType pat.PlatformEntityType, optionalDomainID string, optionalDomainEntityType pat.DomainEntityType, operation pat.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.CheckPAT(ctx, userID, patID, platformEntityType, optionalDomainID, optionalDomainEntityType, operation, entityIDs...) -} From 8edbae7347bbfc5378db5f04260b1d015a88c147 Mon Sep 17 00:00:00 2001 From: nyagamunene Date: Thu, 31 Oct 2024 12:02:35 +0300 Subject: [PATCH 35/72] Update grpc api Signed-off-by: nyagamunene --- api/grpc/auth/v1/auth.pb.go | 12 ------------ api/grpc/auth/v1/auth_grpc.pb.go | 4 ---- 2 files changed, 16 deletions(-) diff --git a/api/grpc/auth/v1/auth.pb.go b/api/grpc/auth/v1/auth.pb.go index e1bc883184..4093897985 100644 --- a/api/grpc/auth/v1/auth.pb.go +++ b/api/grpc/auth/v1/auth.pb.go @@ -547,18 +547,6 @@ func file_auth_v1_auth_proto_init() { } } file_auth_v1_auth_proto_msgTypes[3].Exporter = func(v any, i int) any { - switch v := v.(*AuthZpatReq); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_auth_v1_auth_proto_msgTypes[4].Exporter = func(v any, i int) any { switch v := v.(*AuthZRes); i { case 0: return &v.state diff --git a/api/grpc/auth/v1/auth_grpc.pb.go b/api/grpc/auth/v1/auth_grpc.pb.go index eae65dac16..73523424d7 100644 --- a/api/grpc/auth/v1/auth_grpc.pb.go +++ b/api/grpc/auth/v1/auth_grpc.pb.go @@ -215,10 +215,6 @@ var AuthService_ServiceDesc = grpc.ServiceDesc{ MethodName: "Authorize", Handler: _AuthService_Authorize_Handler, }, - { - MethodName: "AuthorizePAT", - Handler: _AuthService_AuthorizePAT_Handler, - }, { MethodName: "Authenticate", Handler: _AuthService_Authenticate_Handler, From aefea862ce24b39a3611c33198b44a889aded17d Mon Sep 17 00:00:00 2001 From: nyagamunene Date: Thu, 31 Oct 2024 13:19:56 +0300 Subject: [PATCH 36/72] Update pat service Signed-off-by: nyagamunene --- auth/api/http/transport.go | 3 ++- auth/mocks/service.go | 3 --- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/auth/api/http/transport.go b/auth/api/http/transport.go index c84e4a2dcd..75b920c253 100644 --- a/auth/api/http/transport.go +++ b/auth/api/http/transport.go @@ -12,11 +12,12 @@ import ( "github.com/absmach/supermq/auth/api/http/pats" "github.com/go-chi/chi/v5" "github.com/absmach/magistrala/auth/api/http/pats" + "github.com/go-chi/chi/v5" "github.com/prometheus/client_golang/prometheus/promhttp" ) // MakeHandler returns a HTTP handler for API endpoints. -func MakeHandler(svc auth.Service, authn mgauthn.Authentication, logger *slog.Logger, instanceID string) http.Handler { +func MakeHandler(svc auth.Service, logger *slog.Logger, instanceID string) http.Handler { mux := chi.NewRouter() mux = keys.MakeHandler(svc, mux, logger) diff --git a/auth/mocks/service.go b/auth/mocks/service.go index 9ff90b6f1e..0a01591776 100644 --- a/auth/mocks/service.go +++ b/auth/mocks/service.go @@ -5,9 +5,6 @@ package mocks import ( - auth "github.com/absmach/magistrala/auth" - authn "github.com/absmach/magistrala/pkg/authn" - context "context" auth "github.com/absmach/supermq/auth" From c963c3e3a065a8df48a07288f49c38c5cdf193e0 Mon Sep 17 00:00:00 2001 From: nyagamunene Date: Thu, 7 Nov 2024 15:20:22 +0300 Subject: [PATCH 37/72] Update protoc version Signed-off-by: nyagamunene --- api/grpc/auth/v1/auth.pb.go | 82 +-------- api/grpc/auth/v1/auth_grpc.pb.go | 23 ++- api/grpc/channels/v1/channels.pb.go | 44 ++--- api/grpc/channels/v1/channels_grpc.pb.go | 23 ++- api/grpc/common/v1/common.pb.go | 222 ++++------------------- api/grpc/domains/v1/domains.pb.go | 46 +---- api/grpc/domains/v1/domains_grpc.pb.go | 23 ++- api/grpc/groups/v1/groups_grpc.pb.go | 23 ++- api/grpc/token/v1/token.pb.go | 68 ++----- api/grpc/token/v1/token_grpc.pb.go | 23 ++- pkg/messaging/message.pb.go | 24 +-- 11 files changed, 175 insertions(+), 426 deletions(-) diff --git a/api/grpc/auth/v1/auth.pb.go b/api/grpc/auth/v1/auth.pb.go index 4093897985..1ee360a95f 100644 --- a/api/grpc/auth/v1/auth.pb.go +++ b/api/grpc/auth/v1/auth.pb.go @@ -32,16 +32,9 @@ type AuthNReq struct { func (x *AuthNReq) Reset() { *x = AuthNReq{} - if protoimpl.UnsafeEnabled { - mi := &file_auth_v1_auth_proto_msgTypes[0] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } - if protoimpl.UnsafeEnabled { - mi := &file_auth_v1_auth_proto_msgTypes[0] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } + mi := &file_auth_v1_auth_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } func (x *AuthNReq) String() string { @@ -52,8 +45,7 @@ func (*AuthNReq) ProtoMessage() {} func (x *AuthNReq) ProtoReflect() protoreflect.Message { mi := &file_auth_v1_auth_proto_msgTypes[0] - if protoimpl.UnsafeEnabled && x != nil { - if protoimpl.UnsafeEnabled && x != nil { + if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -86,16 +78,9 @@ type AuthNRes struct { func (x *AuthNRes) Reset() { *x = AuthNRes{} - if protoimpl.UnsafeEnabled { - mi := &file_auth_v1_auth_proto_msgTypes[1] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } - if protoimpl.UnsafeEnabled { - mi := &file_auth_v1_auth_proto_msgTypes[1] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } + mi := &file_auth_v1_auth_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } func (x *AuthNRes) String() string { @@ -106,8 +91,7 @@ func (*AuthNRes) ProtoMessage() {} func (x *AuthNRes) ProtoReflect() protoreflect.Message { mi := &file_auth_v1_auth_proto_msgTypes[1] - if protoimpl.UnsafeEnabled && x != nil { - if protoimpl.UnsafeEnabled && x != nil { + if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -509,56 +493,6 @@ func file_auth_v1_auth_proto_init() { if File_auth_v1_auth_proto != nil { return } - if !protoimpl.UnsafeEnabled { - file_auth_v1_auth_proto_msgTypes[0].Exporter = func(v any, i int) any { - switch v := v.(*AuthNReq); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_auth_v1_auth_proto_msgTypes[1].Exporter = func(v any, i int) any { - switch v := v.(*AuthNRes); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_auth_v1_auth_proto_msgTypes[2].Exporter = func(v any, i int) any { - switch v := v.(*AuthZReq); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_auth_v1_auth_proto_msgTypes[3].Exporter = func(v any, i int) any { - switch v := v.(*AuthZRes); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - } type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ diff --git a/api/grpc/auth/v1/auth_grpc.pb.go b/api/grpc/auth/v1/auth_grpc.pb.go index 73523424d7..e0494d597f 100644 --- a/api/grpc/auth/v1/auth_grpc.pb.go +++ b/api/grpc/auth/v1/auth_grpc.pb.go @@ -18,8 +18,8 @@ import ( // This is a compile-time assertion to ensure that this generated file // is compatible with the grpc package it is being compiled against. -// Requires gRPC-Go v1.62.0 or later. -const _ = grpc.SupportPackageIsVersion8 +// Requires gRPC-Go v1.64.0 or later. +const _ = grpc.SupportPackageIsVersion9 const ( AuthService_Authorize_FullMethodName = "/auth.v1.AuthService/Authorize" @@ -91,7 +91,7 @@ func (c *authServiceClient) AuthenticatePAT(ctx context.Context, in *AuthNReq, o // AuthServiceServer is the server API for AuthService service. // All implementations must embed UnimplementedAuthServiceServer -// for forward compatibility +// for forward compatibility. // // AuthService is a service that provides authentication // and authorization functionalities for SuperMQ services. @@ -103,9 +103,12 @@ type AuthServiceServer interface { mustEmbedUnimplementedAuthServiceServer() } -// UnimplementedAuthServiceServer must be embedded to have forward compatible implementations. -type UnimplementedAuthServiceServer struct { -} +// UnimplementedAuthServiceServer must be embedded to have +// forward compatible implementations. +// +// NOTE: this should be embedded by value instead of pointer to avoid a nil +// pointer dereference when methods are called. +type UnimplementedAuthServiceServer struct{} func (UnimplementedAuthServiceServer) Authorize(context.Context, *AuthZReq) (*AuthZRes, error) { return nil, status.Errorf(codes.Unimplemented, "method Authorize not implemented") @@ -120,6 +123,7 @@ func (UnimplementedAuthServiceServer) AuthenticatePAT(context.Context, *AuthNReq return nil, status.Errorf(codes.Unimplemented, "method AuthenticatePAT not implemented") } func (UnimplementedAuthServiceServer) mustEmbedUnimplementedAuthServiceServer() {} +func (UnimplementedAuthServiceServer) testEmbeddedByValue() {} // UnsafeAuthServiceServer may be embedded to opt out of forward compatibility for this service. // Use of this interface is not recommended, as added methods to AuthServiceServer will @@ -129,6 +133,13 @@ type UnsafeAuthServiceServer interface { } func RegisterAuthServiceServer(s grpc.ServiceRegistrar, srv AuthServiceServer) { + // If the following call pancis, it indicates UnimplementedAuthServiceServer was + // embedded by pointer and is nil. This will cause panics if an + // unimplemented method is ever invoked, so we test this at initialization + // time to prevent it from happening at runtime later due to I/O. + if t, ok := srv.(interface{ testEmbeddedByValue() }); ok { + t.testEmbeddedByValue() + } s.RegisterService(&AuthService_ServiceDesc, srv) } diff --git a/api/grpc/channels/v1/channels.pb.go b/api/grpc/channels/v1/channels.pb.go index a5208e78ef..5d607cc219 100644 --- a/api/grpc/channels/v1/channels.pb.go +++ b/api/grpc/channels/v1/channels.pb.go @@ -46,7 +46,7 @@ func (*RemoveClientConnectionsReq) ProtoMessage() {} func (x *RemoveClientConnectionsReq) ProtoReflect() protoreflect.Message { mi := &file_channels_v1_channels_proto_msgTypes[0] - if protoimpl.UnsafeEnabled && x != nil { + if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -89,7 +89,7 @@ func (*RemoveClientConnectionsRes) ProtoMessage() {} func (x *RemoveClientConnectionsRes) ProtoReflect() protoreflect.Message { mi := &file_channels_v1_channels_proto_msgTypes[1] - if protoimpl.UnsafeEnabled && x != nil { + if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -113,11 +113,9 @@ type UnsetParentGroupFromChannelsReq struct { func (x *UnsetParentGroupFromChannelsReq) Reset() { *x = UnsetParentGroupFromChannelsReq{} - if protoimpl.UnsafeEnabled { - mi := &file_channels_v1_channels_proto_msgTypes[2] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } + mi := &file_channels_v1_channels_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } func (x *UnsetParentGroupFromChannelsReq) String() string { @@ -128,7 +126,7 @@ func (*UnsetParentGroupFromChannelsReq) ProtoMessage() {} func (x *UnsetParentGroupFromChannelsReq) ProtoReflect() protoreflect.Message { mi := &file_channels_v1_channels_proto_msgTypes[2] - if protoimpl.UnsafeEnabled && x != nil { + if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -158,11 +156,9 @@ type UnsetParentGroupFromChannelsRes struct { func (x *UnsetParentGroupFromChannelsRes) Reset() { *x = UnsetParentGroupFromChannelsRes{} - if protoimpl.UnsafeEnabled { - mi := &file_channels_v1_channels_proto_msgTypes[3] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } + mi := &file_channels_v1_channels_proto_msgTypes[3] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } func (x *UnsetParentGroupFromChannelsRes) String() string { @@ -173,7 +169,7 @@ func (*UnsetParentGroupFromChannelsRes) ProtoMessage() {} func (x *UnsetParentGroupFromChannelsRes) ProtoReflect() protoreflect.Message { mi := &file_channels_v1_channels_proto_msgTypes[3] - if protoimpl.UnsafeEnabled && x != nil { + if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -201,11 +197,9 @@ type AuthzReq struct { func (x *AuthzReq) Reset() { *x = AuthzReq{} - if protoimpl.UnsafeEnabled { - mi := &file_channels_v1_channels_proto_msgTypes[4] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } + mi := &file_channels_v1_channels_proto_msgTypes[4] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } func (x *AuthzReq) String() string { @@ -216,7 +210,7 @@ func (*AuthzReq) ProtoMessage() {} func (x *AuthzReq) ProtoReflect() protoreflect.Message { mi := &file_channels_v1_channels_proto_msgTypes[4] - if protoimpl.UnsafeEnabled && x != nil { + if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -275,11 +269,9 @@ type AuthzRes struct { func (x *AuthzRes) Reset() { *x = AuthzRes{} - if protoimpl.UnsafeEnabled { - mi := &file_channels_v1_channels_proto_msgTypes[5] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } + mi := &file_channels_v1_channels_proto_msgTypes[5] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } func (x *AuthzRes) String() string { @@ -290,7 +282,7 @@ func (*AuthzRes) ProtoMessage() {} func (x *AuthzRes) ProtoReflect() protoreflect.Message { mi := &file_channels_v1_channels_proto_msgTypes[5] - if protoimpl.UnsafeEnabled && x != nil { + if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) diff --git a/api/grpc/channels/v1/channels_grpc.pb.go b/api/grpc/channels/v1/channels_grpc.pb.go index 6c4ebeda28..612280b166 100644 --- a/api/grpc/channels/v1/channels_grpc.pb.go +++ b/api/grpc/channels/v1/channels_grpc.pb.go @@ -19,8 +19,8 @@ import ( // This is a compile-time assertion to ensure that this generated file // is compatible with the grpc package it is being compiled against. -// Requires gRPC-Go v1.62.0 or later. -const _ = grpc.SupportPackageIsVersion8 +// Requires gRPC-Go v1.64.0 or later. +const _ = grpc.SupportPackageIsVersion9 const ( ChannelsService_Authorize_FullMethodName = "/channels.v1.ChannelsService/Authorize" @@ -89,7 +89,7 @@ func (c *channelsServiceClient) RetrieveEntity(ctx context.Context, in *v1.Retri // ChannelsServiceServer is the server API for ChannelsService service. // All implementations must embed UnimplementedChannelsServiceServer -// for forward compatibility +// for forward compatibility. type ChannelsServiceServer interface { Authorize(context.Context, *AuthzReq) (*AuthzRes, error) RemoveClientConnections(context.Context, *RemoveClientConnectionsReq) (*RemoveClientConnectionsRes, error) @@ -98,9 +98,12 @@ type ChannelsServiceServer interface { mustEmbedUnimplementedChannelsServiceServer() } -// UnimplementedChannelsServiceServer must be embedded to have forward compatible implementations. -type UnimplementedChannelsServiceServer struct { -} +// UnimplementedChannelsServiceServer must be embedded to have +// forward compatible implementations. +// +// NOTE: this should be embedded by value instead of pointer to avoid a nil +// pointer dereference when methods are called. +type UnimplementedChannelsServiceServer struct{} func (UnimplementedChannelsServiceServer) Authorize(context.Context, *AuthzReq) (*AuthzRes, error) { return nil, status.Errorf(codes.Unimplemented, "method Authorize not implemented") @@ -115,6 +118,7 @@ func (UnimplementedChannelsServiceServer) RetrieveEntity(context.Context, *v1.Re return nil, status.Errorf(codes.Unimplemented, "method RetrieveEntity not implemented") } func (UnimplementedChannelsServiceServer) mustEmbedUnimplementedChannelsServiceServer() {} +func (UnimplementedChannelsServiceServer) testEmbeddedByValue() {} // UnsafeChannelsServiceServer may be embedded to opt out of forward compatibility for this service. // Use of this interface is not recommended, as added methods to ChannelsServiceServer will @@ -124,6 +128,13 @@ type UnsafeChannelsServiceServer interface { } func RegisterChannelsServiceServer(s grpc.ServiceRegistrar, srv ChannelsServiceServer) { + // If the following call pancis, it indicates UnimplementedChannelsServiceServer was + // embedded by pointer and is nil. This will cause panics if an + // unimplemented method is ever invoked, so we test this at initialization + // time to prevent it from happening at runtime later due to I/O. + if t, ok := srv.(interface{ testEmbeddedByValue() }); ok { + t.testEmbeddedByValue() + } s.RegisterService(&ChannelsService_ServiceDesc, srv) } diff --git a/api/grpc/common/v1/common.pb.go b/api/grpc/common/v1/common.pb.go index faa8c157e2..281366d3e6 100644 --- a/api/grpc/common/v1/common.pb.go +++ b/api/grpc/common/v1/common.pb.go @@ -32,11 +32,9 @@ type RetrieveEntitiesReq struct { func (x *RetrieveEntitiesReq) Reset() { *x = RetrieveEntitiesReq{} - if protoimpl.UnsafeEnabled { - mi := &file_common_v1_common_proto_msgTypes[0] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } + mi := &file_common_v1_common_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } func (x *RetrieveEntitiesReq) String() string { @@ -47,7 +45,7 @@ func (*RetrieveEntitiesReq) ProtoMessage() {} func (x *RetrieveEntitiesReq) ProtoReflect() protoreflect.Message { mi := &file_common_v1_common_proto_msgTypes[0] - if protoimpl.UnsafeEnabled && x != nil { + if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -81,11 +79,9 @@ type RetrieveEntitiesRes struct { func (x *RetrieveEntitiesRes) Reset() { *x = RetrieveEntitiesRes{} - if protoimpl.UnsafeEnabled { - mi := &file_common_v1_common_proto_msgTypes[1] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } + mi := &file_common_v1_common_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } func (x *RetrieveEntitiesRes) String() string { @@ -96,7 +92,7 @@ func (*RetrieveEntitiesRes) ProtoMessage() {} func (x *RetrieveEntitiesRes) ProtoReflect() protoreflect.Message { mi := &file_common_v1_common_proto_msgTypes[1] - if protoimpl.UnsafeEnabled && x != nil { + if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -148,11 +144,9 @@ type RetrieveEntityReq struct { func (x *RetrieveEntityReq) Reset() { *x = RetrieveEntityReq{} - if protoimpl.UnsafeEnabled { - mi := &file_common_v1_common_proto_msgTypes[2] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } + mi := &file_common_v1_common_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } func (x *RetrieveEntityReq) String() string { @@ -163,7 +157,7 @@ func (*RetrieveEntityReq) ProtoMessage() {} func (x *RetrieveEntityReq) ProtoReflect() protoreflect.Message { mi := &file_common_v1_common_proto_msgTypes[2] - if protoimpl.UnsafeEnabled && x != nil { + if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -194,11 +188,9 @@ type RetrieveEntityRes struct { func (x *RetrieveEntityRes) Reset() { *x = RetrieveEntityRes{} - if protoimpl.UnsafeEnabled { - mi := &file_common_v1_common_proto_msgTypes[3] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } + mi := &file_common_v1_common_proto_msgTypes[3] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } func (x *RetrieveEntityRes) String() string { @@ -209,7 +201,7 @@ func (*RetrieveEntityRes) ProtoMessage() {} func (x *RetrieveEntityRes) ProtoReflect() protoreflect.Message { mi := &file_common_v1_common_proto_msgTypes[3] - if protoimpl.UnsafeEnabled && x != nil { + if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -243,11 +235,9 @@ type EntityBasic struct { func (x *EntityBasic) Reset() { *x = EntityBasic{} - if protoimpl.UnsafeEnabled { - mi := &file_common_v1_common_proto_msgTypes[4] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } + mi := &file_common_v1_common_proto_msgTypes[4] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } func (x *EntityBasic) String() string { @@ -258,7 +248,7 @@ func (*EntityBasic) ProtoMessage() {} func (x *EntityBasic) ProtoReflect() protoreflect.Message { mi := &file_common_v1_common_proto_msgTypes[4] - if protoimpl.UnsafeEnabled && x != nil { + if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -310,11 +300,9 @@ type AddConnectionsReq struct { func (x *AddConnectionsReq) Reset() { *x = AddConnectionsReq{} - if protoimpl.UnsafeEnabled { - mi := &file_common_v1_common_proto_msgTypes[5] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } + mi := &file_common_v1_common_proto_msgTypes[5] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } func (x *AddConnectionsReq) String() string { @@ -325,7 +313,7 @@ func (*AddConnectionsReq) ProtoMessage() {} func (x *AddConnectionsReq) ProtoReflect() protoreflect.Message { mi := &file_common_v1_common_proto_msgTypes[5] - if protoimpl.UnsafeEnabled && x != nil { + if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -356,11 +344,9 @@ type AddConnectionsRes struct { func (x *AddConnectionsRes) Reset() { *x = AddConnectionsRes{} - if protoimpl.UnsafeEnabled { - mi := &file_common_v1_common_proto_msgTypes[6] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } + mi := &file_common_v1_common_proto_msgTypes[6] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } func (x *AddConnectionsRes) String() string { @@ -371,7 +357,7 @@ func (*AddConnectionsRes) ProtoMessage() {} func (x *AddConnectionsRes) ProtoReflect() protoreflect.Message { mi := &file_common_v1_common_proto_msgTypes[6] - if protoimpl.UnsafeEnabled && x != nil { + if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -402,11 +388,9 @@ type RemoveConnectionsReq struct { func (x *RemoveConnectionsReq) Reset() { *x = RemoveConnectionsReq{} - if protoimpl.UnsafeEnabled { - mi := &file_common_v1_common_proto_msgTypes[7] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } + mi := &file_common_v1_common_proto_msgTypes[7] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } func (x *RemoveConnectionsReq) String() string { @@ -417,7 +401,7 @@ func (*RemoveConnectionsReq) ProtoMessage() {} func (x *RemoveConnectionsReq) ProtoReflect() protoreflect.Message { mi := &file_common_v1_common_proto_msgTypes[7] - if protoimpl.UnsafeEnabled && x != nil { + if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -448,11 +432,9 @@ type RemoveConnectionsRes struct { func (x *RemoveConnectionsRes) Reset() { *x = RemoveConnectionsRes{} - if protoimpl.UnsafeEnabled { - mi := &file_common_v1_common_proto_msgTypes[8] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } + mi := &file_common_v1_common_proto_msgTypes[8] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } func (x *RemoveConnectionsRes) String() string { @@ -463,7 +445,7 @@ func (*RemoveConnectionsRes) ProtoMessage() {} func (x *RemoveConnectionsRes) ProtoReflect() protoreflect.Message { mi := &file_common_v1_common_proto_msgTypes[8] - if protoimpl.UnsafeEnabled && x != nil { + if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -497,11 +479,9 @@ type Connection struct { func (x *Connection) Reset() { *x = Connection{} - if protoimpl.UnsafeEnabled { - mi := &file_common_v1_common_proto_msgTypes[9] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } + mi := &file_common_v1_common_proto_msgTypes[9] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } func (x *Connection) String() string { @@ -512,7 +492,7 @@ func (*Connection) ProtoMessage() {} func (x *Connection) ProtoReflect() protoreflect.Message { mi := &file_common_v1_common_proto_msgTypes[9] - if protoimpl.UnsafeEnabled && x != nil { + if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -657,128 +637,6 @@ func file_common_v1_common_proto_init() { if File_common_v1_common_proto != nil { return } - if !protoimpl.UnsafeEnabled { - file_common_v1_common_proto_msgTypes[0].Exporter = func(v any, i int) any { - switch v := v.(*RetrieveEntitiesReq); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_common_v1_common_proto_msgTypes[1].Exporter = func(v any, i int) any { - switch v := v.(*RetrieveEntitiesRes); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_common_v1_common_proto_msgTypes[2].Exporter = func(v any, i int) any { - switch v := v.(*RetrieveEntityReq); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_common_v1_common_proto_msgTypes[3].Exporter = func(v any, i int) any { - switch v := v.(*RetrieveEntityRes); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_common_v1_common_proto_msgTypes[4].Exporter = func(v any, i int) any { - switch v := v.(*EntityBasic); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_common_v1_common_proto_msgTypes[5].Exporter = func(v any, i int) any { - switch v := v.(*AddConnectionsReq); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_common_v1_common_proto_msgTypes[6].Exporter = func(v any, i int) any { - switch v := v.(*AddConnectionsRes); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_common_v1_common_proto_msgTypes[7].Exporter = func(v any, i int) any { - switch v := v.(*RemoveConnectionsReq); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_common_v1_common_proto_msgTypes[8].Exporter = func(v any, i int) any { - switch v := v.(*RemoveConnectionsRes); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_common_v1_common_proto_msgTypes[9].Exporter = func(v any, i int) any { - switch v := v.(*Connection); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - } type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ diff --git a/api/grpc/domains/v1/domains.pb.go b/api/grpc/domains/v1/domains.pb.go index 8a2dcc211d..897979d4aa 100644 --- a/api/grpc/domains/v1/domains.pb.go +++ b/api/grpc/domains/v1/domains.pb.go @@ -33,11 +33,9 @@ type DeleteUserRes struct { func (x *DeleteUserRes) Reset() { *x = DeleteUserRes{} - if protoimpl.UnsafeEnabled { - mi := &file_domains_v1_domains_proto_msgTypes[0] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } + mi := &file_domains_v1_domains_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } func (x *DeleteUserRes) String() string { @@ -48,7 +46,7 @@ func (*DeleteUserRes) ProtoMessage() {} func (x *DeleteUserRes) ProtoReflect() protoreflect.Message { mi := &file_domains_v1_domains_proto_msgTypes[0] - if protoimpl.UnsafeEnabled && x != nil { + if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -79,11 +77,9 @@ type DeleteUserReq struct { func (x *DeleteUserReq) Reset() { *x = DeleteUserReq{} - if protoimpl.UnsafeEnabled { - mi := &file_domains_v1_domains_proto_msgTypes[1] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } + mi := &file_domains_v1_domains_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } func (x *DeleteUserReq) String() string { @@ -94,7 +90,7 @@ func (*DeleteUserReq) ProtoMessage() {} func (x *DeleteUserReq) ProtoReflect() protoreflect.Message { mi := &file_domains_v1_domains_proto_msgTypes[1] - if protoimpl.UnsafeEnabled && x != nil { + if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -181,32 +177,6 @@ func file_domains_v1_domains_proto_init() { if File_domains_v1_domains_proto != nil { return } - if !protoimpl.UnsafeEnabled { - file_domains_v1_domains_proto_msgTypes[0].Exporter = func(v any, i int) any { - switch v := v.(*DeleteUserRes); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_domains_v1_domains_proto_msgTypes[1].Exporter = func(v any, i int) any { - switch v := v.(*DeleteUserReq); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - } type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ diff --git a/api/grpc/domains/v1/domains_grpc.pb.go b/api/grpc/domains/v1/domains_grpc.pb.go index 1e348de890..90b85349f8 100644 --- a/api/grpc/domains/v1/domains_grpc.pb.go +++ b/api/grpc/domains/v1/domains_grpc.pb.go @@ -19,8 +19,8 @@ import ( // This is a compile-time assertion to ensure that this generated file // is compatible with the grpc package it is being compiled against. -// Requires gRPC-Go v1.62.0 or later. -const _ = grpc.SupportPackageIsVersion8 +// Requires gRPC-Go v1.64.0 or later. +const _ = grpc.SupportPackageIsVersion9 const ( DomainsService_DeleteUserFromDomains_FullMethodName = "/domains.v1.DomainsService/DeleteUserFromDomains" @@ -68,7 +68,7 @@ func (c *domainsServiceClient) RetrieveEntity(ctx context.Context, in *v1.Retrie // DomainsServiceServer is the server API for DomainsService service. // All implementations must embed UnimplementedDomainsServiceServer -// for forward compatibility +// for forward compatibility. // // DomainsService is a service that provides access to // domains functionalities for SuperMQ services. @@ -78,9 +78,12 @@ type DomainsServiceServer interface { mustEmbedUnimplementedDomainsServiceServer() } -// UnimplementedDomainsServiceServer must be embedded to have forward compatible implementations. -type UnimplementedDomainsServiceServer struct { -} +// UnimplementedDomainsServiceServer must be embedded to have +// forward compatible implementations. +// +// NOTE: this should be embedded by value instead of pointer to avoid a nil +// pointer dereference when methods are called. +type UnimplementedDomainsServiceServer struct{} func (UnimplementedDomainsServiceServer) DeleteUserFromDomains(context.Context, *DeleteUserReq) (*DeleteUserRes, error) { return nil, status.Errorf(codes.Unimplemented, "method DeleteUserFromDomains not implemented") @@ -89,6 +92,7 @@ func (UnimplementedDomainsServiceServer) RetrieveEntity(context.Context, *v1.Ret return nil, status.Errorf(codes.Unimplemented, "method RetrieveEntity not implemented") } func (UnimplementedDomainsServiceServer) mustEmbedUnimplementedDomainsServiceServer() {} +func (UnimplementedDomainsServiceServer) testEmbeddedByValue() {} // UnsafeDomainsServiceServer may be embedded to opt out of forward compatibility for this service. // Use of this interface is not recommended, as added methods to DomainsServiceServer will @@ -98,6 +102,13 @@ type UnsafeDomainsServiceServer interface { } func RegisterDomainsServiceServer(s grpc.ServiceRegistrar, srv DomainsServiceServer) { + // If the following call pancis, it indicates UnimplementedDomainsServiceServer was + // embedded by pointer and is nil. This will cause panics if an + // unimplemented method is ever invoked, so we test this at initialization + // time to prevent it from happening at runtime later due to I/O. + if t, ok := srv.(interface{ testEmbeddedByValue() }); ok { + t.testEmbeddedByValue() + } s.RegisterService(&DomainsService_ServiceDesc, srv) } diff --git a/api/grpc/groups/v1/groups_grpc.pb.go b/api/grpc/groups/v1/groups_grpc.pb.go index 86132eea9e..d362f88c2b 100644 --- a/api/grpc/groups/v1/groups_grpc.pb.go +++ b/api/grpc/groups/v1/groups_grpc.pb.go @@ -19,8 +19,8 @@ import ( // This is a compile-time assertion to ensure that this generated file // is compatible with the grpc package it is being compiled against. -// Requires gRPC-Go v1.62.0 or later. -const _ = grpc.SupportPackageIsVersion8 +// Requires gRPC-Go v1.64.0 or later. +const _ = grpc.SupportPackageIsVersion9 const ( GroupsService_RetrieveEntity_FullMethodName = "/groups.v1.GroupsService/RetrieveEntity" @@ -56,7 +56,7 @@ func (c *groupsServiceClient) RetrieveEntity(ctx context.Context, in *v1.Retriev // GroupsServiceServer is the server API for GroupsService service. // All implementations must embed UnimplementedGroupsServiceServer -// for forward compatibility +// for forward compatibility. // // GroupssService is a service that provides groups // functionalities for SuperMQ services. @@ -65,14 +65,18 @@ type GroupsServiceServer interface { mustEmbedUnimplementedGroupsServiceServer() } -// UnimplementedGroupsServiceServer must be embedded to have forward compatible implementations. -type UnimplementedGroupsServiceServer struct { -} +// UnimplementedGroupsServiceServer must be embedded to have +// forward compatible implementations. +// +// NOTE: this should be embedded by value instead of pointer to avoid a nil +// pointer dereference when methods are called. +type UnimplementedGroupsServiceServer struct{} func (UnimplementedGroupsServiceServer) RetrieveEntity(context.Context, *v1.RetrieveEntityReq) (*v1.RetrieveEntityRes, error) { return nil, status.Errorf(codes.Unimplemented, "method RetrieveEntity not implemented") } func (UnimplementedGroupsServiceServer) mustEmbedUnimplementedGroupsServiceServer() {} +func (UnimplementedGroupsServiceServer) testEmbeddedByValue() {} // UnsafeGroupsServiceServer may be embedded to opt out of forward compatibility for this service. // Use of this interface is not recommended, as added methods to GroupsServiceServer will @@ -82,6 +86,13 @@ type UnsafeGroupsServiceServer interface { } func RegisterGroupsServiceServer(s grpc.ServiceRegistrar, srv GroupsServiceServer) { + // If the following call pancis, it indicates UnimplementedGroupsServiceServer was + // embedded by pointer and is nil. This will cause panics if an + // unimplemented method is ever invoked, so we test this at initialization + // time to prevent it from happening at runtime later due to I/O. + if t, ok := srv.(interface{ testEmbeddedByValue() }); ok { + t.testEmbeddedByValue() + } s.RegisterService(&GroupsService_ServiceDesc, srv) } diff --git a/api/grpc/token/v1/token.pb.go b/api/grpc/token/v1/token.pb.go index 9a7722514f..54e897e51f 100644 --- a/api/grpc/token/v1/token.pb.go +++ b/api/grpc/token/v1/token.pb.go @@ -33,11 +33,9 @@ type IssueReq struct { func (x *IssueReq) Reset() { *x = IssueReq{} - if protoimpl.UnsafeEnabled { - mi := &file_token_v1_token_proto_msgTypes[0] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } + mi := &file_token_v1_token_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } func (x *IssueReq) String() string { @@ -48,7 +46,7 @@ func (*IssueReq) ProtoMessage() {} func (x *IssueReq) ProtoReflect() protoreflect.Message { mi := &file_token_v1_token_proto_msgTypes[0] - if protoimpl.UnsafeEnabled && x != nil { + if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -86,11 +84,9 @@ type RefreshReq struct { func (x *RefreshReq) Reset() { *x = RefreshReq{} - if protoimpl.UnsafeEnabled { - mi := &file_token_v1_token_proto_msgTypes[1] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } + mi := &file_token_v1_token_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } func (x *RefreshReq) String() string { @@ -101,7 +97,7 @@ func (*RefreshReq) ProtoMessage() {} func (x *RefreshReq) ProtoReflect() protoreflect.Message { mi := &file_token_v1_token_proto_msgTypes[1] - if protoimpl.UnsafeEnabled && x != nil { + if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -137,11 +133,9 @@ type Token struct { func (x *Token) Reset() { *x = Token{} - if protoimpl.UnsafeEnabled { - mi := &file_token_v1_token_proto_msgTypes[2] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } + mi := &file_token_v1_token_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } func (x *Token) String() string { @@ -152,7 +146,7 @@ func (*Token) ProtoMessage() {} func (x *Token) ProtoReflect() protoreflect.Message { mi := &file_token_v1_token_proto_msgTypes[2] - if protoimpl.UnsafeEnabled && x != nil { + if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -257,44 +251,6 @@ func file_token_v1_token_proto_init() { if File_token_v1_token_proto != nil { return } - if !protoimpl.UnsafeEnabled { - file_token_v1_token_proto_msgTypes[0].Exporter = func(v any, i int) any { - switch v := v.(*IssueReq); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_token_v1_token_proto_msgTypes[1].Exporter = func(v any, i int) any { - switch v := v.(*RefreshReq); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_token_v1_token_proto_msgTypes[2].Exporter = func(v any, i int) any { - switch v := v.(*Token); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - } file_token_v1_token_proto_msgTypes[2].OneofWrappers = []any{} type x struct{} out := protoimpl.TypeBuilder{ diff --git a/api/grpc/token/v1/token_grpc.pb.go b/api/grpc/token/v1/token_grpc.pb.go index 8b8cf03931..f3adacfb70 100644 --- a/api/grpc/token/v1/token_grpc.pb.go +++ b/api/grpc/token/v1/token_grpc.pb.go @@ -18,8 +18,8 @@ import ( // This is a compile-time assertion to ensure that this generated file // is compatible with the grpc package it is being compiled against. -// Requires gRPC-Go v1.62.0 or later. -const _ = grpc.SupportPackageIsVersion8 +// Requires gRPC-Go v1.64.0 or later. +const _ = grpc.SupportPackageIsVersion9 const ( TokenService_Issue_FullMethodName = "/token.v1.TokenService/Issue" @@ -64,16 +64,19 @@ func (c *tokenServiceClient) Refresh(ctx context.Context, in *RefreshReq, opts . // TokenServiceServer is the server API for TokenService service. // All implementations must embed UnimplementedTokenServiceServer -// for forward compatibility +// for forward compatibility. type TokenServiceServer interface { Issue(context.Context, *IssueReq) (*Token, error) Refresh(context.Context, *RefreshReq) (*Token, error) mustEmbedUnimplementedTokenServiceServer() } -// UnimplementedTokenServiceServer must be embedded to have forward compatible implementations. -type UnimplementedTokenServiceServer struct { -} +// UnimplementedTokenServiceServer must be embedded to have +// forward compatible implementations. +// +// NOTE: this should be embedded by value instead of pointer to avoid a nil +// pointer dereference when methods are called. +type UnimplementedTokenServiceServer struct{} func (UnimplementedTokenServiceServer) Issue(context.Context, *IssueReq) (*Token, error) { return nil, status.Errorf(codes.Unimplemented, "method Issue not implemented") @@ -82,6 +85,7 @@ func (UnimplementedTokenServiceServer) Refresh(context.Context, *RefreshReq) (*T return nil, status.Errorf(codes.Unimplemented, "method Refresh not implemented") } func (UnimplementedTokenServiceServer) mustEmbedUnimplementedTokenServiceServer() {} +func (UnimplementedTokenServiceServer) testEmbeddedByValue() {} // UnsafeTokenServiceServer may be embedded to opt out of forward compatibility for this service. // Use of this interface is not recommended, as added methods to TokenServiceServer will @@ -91,6 +95,13 @@ type UnsafeTokenServiceServer interface { } func RegisterTokenServiceServer(s grpc.ServiceRegistrar, srv TokenServiceServer) { + // If the following call pancis, it indicates UnimplementedTokenServiceServer was + // embedded by pointer and is nil. This will cause panics if an + // unimplemented method is ever invoked, so we test this at initialization + // time to prevent it from happening at runtime later due to I/O. + if t, ok := srv.(interface{ testEmbeddedByValue() }); ok { + t.testEmbeddedByValue() + } s.RegisterService(&TokenService_ServiceDesc, srv) } diff --git a/pkg/messaging/message.pb.go b/pkg/messaging/message.pb.go index c2833f72b6..bdf2bb1231 100644 --- a/pkg/messaging/message.pb.go +++ b/pkg/messaging/message.pb.go @@ -38,11 +38,9 @@ type Message struct { func (x *Message) Reset() { *x = Message{} - if protoimpl.UnsafeEnabled { - mi := &file_pkg_messaging_message_proto_msgTypes[0] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } + mi := &file_pkg_messaging_message_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } func (x *Message) String() string { @@ -53,7 +51,7 @@ func (*Message) ProtoMessage() {} func (x *Message) ProtoReflect() protoreflect.Message { mi := &file_pkg_messaging_message_proto_msgTypes[0] - if protoimpl.UnsafeEnabled && x != nil { + if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -159,20 +157,6 @@ func file_pkg_messaging_message_proto_init() { if File_pkg_messaging_message_proto != nil { return } - if !protoimpl.UnsafeEnabled { - file_pkg_messaging_message_proto_msgTypes[0].Exporter = func(v any, i int) any { - switch v := v.(*Message); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - } type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ From 0914dc6e94f4a53d75e86305be33e5476d10b368 Mon Sep 17 00:00:00 2001 From: nyagamunene Date: Wed, 13 Nov 2024 13:46:19 +0300 Subject: [PATCH 38/72] Add authenticate PAT grpc endpoint Signed-off-by: nyagamunene --- auth/api/grpc/auth/responses.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/auth/api/grpc/auth/responses.go b/auth/api/grpc/auth/responses.go index dc9ad1cde5..f7a39deb46 100644 --- a/auth/api/grpc/auth/responses.go +++ b/auth/api/grpc/auth/responses.go @@ -4,9 +4,9 @@ package auth type authenticateRes struct { - id string - userID string - domainID string + id string + userID string + domainID string } type authorizeRes struct { From 9562d31501a36718d175c2c6dcc8195e37fea178 Mon Sep 17 00:00:00 2001 From: nyagamunene Date: Wed, 30 Oct 2024 13:21:26 +0300 Subject: [PATCH 39/72] Add grpc authorizePAT Signed-off-by: nyagamunene --- api/grpc/auth/v1/auth.pb.go | 82 ++++++++- api/grpc/auth/v1/auth_grpc.pb.go | 27 +-- api/grpc/channels/v1/channels.pb.go | 24 ++- api/grpc/channels/v1/channels_grpc.pb.go | 23 +-- api/grpc/common/v1/common.pb.go | 222 +++++++++++++++++++---- api/grpc/domains/v1/domains.pb.go | 46 ++++- api/grpc/domains/v1/domains_grpc.pb.go | 23 +-- api/grpc/groups/v1/groups_grpc.pb.go | 23 +-- api/grpc/token/v1/token.pb.go | 68 +++++-- api/grpc/token/v1/token_grpc.pb.go | 23 +-- auth/service.go | 15 ++ pat/api/http/endpoint.go | 0 pat/api/http/requests.go | 0 pat/api/http/responses.go | 0 pat/api/http/transport.go | 0 pat/api/requests.go | 0 pat/api/responses.go | 0 pat/api/transport.go | 0 pat/events/streams.go | 94 ++++++++++ pkg/messaging/message.pb.go | 24 ++- 20 files changed, 527 insertions(+), 167 deletions(-) create mode 100644 pat/api/http/endpoint.go create mode 100644 pat/api/http/requests.go create mode 100644 pat/api/http/responses.go create mode 100644 pat/api/http/transport.go create mode 100644 pat/api/requests.go create mode 100644 pat/api/responses.go create mode 100644 pat/api/transport.go create mode 100644 pat/events/streams.go diff --git a/api/grpc/auth/v1/auth.pb.go b/api/grpc/auth/v1/auth.pb.go index 1ee360a95f..2d15354ce0 100644 --- a/api/grpc/auth/v1/auth.pb.go +++ b/api/grpc/auth/v1/auth.pb.go @@ -32,9 +32,11 @@ type AuthNReq struct { func (x *AuthNReq) Reset() { *x = AuthNReq{} - mi := &file_auth_v1_auth_proto_msgTypes[0] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) + if protoimpl.UnsafeEnabled { + mi := &file_auth_v1_auth_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } } func (x *AuthNReq) String() string { @@ -45,7 +47,7 @@ func (*AuthNReq) ProtoMessage() {} func (x *AuthNReq) ProtoReflect() protoreflect.Message { mi := &file_auth_v1_auth_proto_msgTypes[0] - if x != nil { + if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -78,9 +80,11 @@ type AuthNRes struct { func (x *AuthNRes) Reset() { *x = AuthNRes{} - mi := &file_auth_v1_auth_proto_msgTypes[1] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) + if protoimpl.UnsafeEnabled { + mi := &file_auth_v1_auth_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } } func (x *AuthNRes) String() string { @@ -91,7 +95,7 @@ func (*AuthNRes) ProtoMessage() {} func (x *AuthNRes) ProtoReflect() protoreflect.Message { mi := &file_auth_v1_auth_proto_msgTypes[1] - if x != nil { + if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -493,6 +497,68 @@ func file_auth_v1_auth_proto_init() { if File_auth_v1_auth_proto != nil { return } + if !protoimpl.UnsafeEnabled { + file_auth_v1_auth_proto_msgTypes[0].Exporter = func(v any, i int) any { + switch v := v.(*AuthNReq); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_auth_v1_auth_proto_msgTypes[1].Exporter = func(v any, i int) any { + switch v := v.(*AuthNRes); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_auth_v1_auth_proto_msgTypes[2].Exporter = func(v any, i int) any { + switch v := v.(*AuthZReq); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_auth_v1_auth_proto_msgTypes[3].Exporter = func(v any, i int) any { + switch v := v.(*AuthZpatReq); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_auth_v1_auth_proto_msgTypes[4].Exporter = func(v any, i int) any { + switch v := v.(*AuthZRes); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ diff --git a/api/grpc/auth/v1/auth_grpc.pb.go b/api/grpc/auth/v1/auth_grpc.pb.go index e0494d597f..eae65dac16 100644 --- a/api/grpc/auth/v1/auth_grpc.pb.go +++ b/api/grpc/auth/v1/auth_grpc.pb.go @@ -18,8 +18,8 @@ import ( // This is a compile-time assertion to ensure that this generated file // is compatible with the grpc package it is being compiled against. -// Requires gRPC-Go v1.64.0 or later. -const _ = grpc.SupportPackageIsVersion9 +// Requires gRPC-Go v1.62.0 or later. +const _ = grpc.SupportPackageIsVersion8 const ( AuthService_Authorize_FullMethodName = "/auth.v1.AuthService/Authorize" @@ -91,7 +91,7 @@ func (c *authServiceClient) AuthenticatePAT(ctx context.Context, in *AuthNReq, o // AuthServiceServer is the server API for AuthService service. // All implementations must embed UnimplementedAuthServiceServer -// for forward compatibility. +// for forward compatibility // // AuthService is a service that provides authentication // and authorization functionalities for SuperMQ services. @@ -103,12 +103,9 @@ type AuthServiceServer interface { mustEmbedUnimplementedAuthServiceServer() } -// UnimplementedAuthServiceServer must be embedded to have -// forward compatible implementations. -// -// NOTE: this should be embedded by value instead of pointer to avoid a nil -// pointer dereference when methods are called. -type UnimplementedAuthServiceServer struct{} +// UnimplementedAuthServiceServer must be embedded to have forward compatible implementations. +type UnimplementedAuthServiceServer struct { +} func (UnimplementedAuthServiceServer) Authorize(context.Context, *AuthZReq) (*AuthZRes, error) { return nil, status.Errorf(codes.Unimplemented, "method Authorize not implemented") @@ -123,7 +120,6 @@ func (UnimplementedAuthServiceServer) AuthenticatePAT(context.Context, *AuthNReq return nil, status.Errorf(codes.Unimplemented, "method AuthenticatePAT not implemented") } func (UnimplementedAuthServiceServer) mustEmbedUnimplementedAuthServiceServer() {} -func (UnimplementedAuthServiceServer) testEmbeddedByValue() {} // UnsafeAuthServiceServer may be embedded to opt out of forward compatibility for this service. // Use of this interface is not recommended, as added methods to AuthServiceServer will @@ -133,13 +129,6 @@ type UnsafeAuthServiceServer interface { } func RegisterAuthServiceServer(s grpc.ServiceRegistrar, srv AuthServiceServer) { - // If the following call pancis, it indicates UnimplementedAuthServiceServer was - // embedded by pointer and is nil. This will cause panics if an - // unimplemented method is ever invoked, so we test this at initialization - // time to prevent it from happening at runtime later due to I/O. - if t, ok := srv.(interface{ testEmbeddedByValue() }); ok { - t.testEmbeddedByValue() - } s.RegisterService(&AuthService_ServiceDesc, srv) } @@ -226,6 +215,10 @@ var AuthService_ServiceDesc = grpc.ServiceDesc{ MethodName: "Authorize", Handler: _AuthService_Authorize_Handler, }, + { + MethodName: "AuthorizePAT", + Handler: _AuthService_AuthorizePAT_Handler, + }, { MethodName: "Authenticate", Handler: _AuthService_Authenticate_Handler, diff --git a/api/grpc/channels/v1/channels.pb.go b/api/grpc/channels/v1/channels.pb.go index 5d607cc219..ab96bac284 100644 --- a/api/grpc/channels/v1/channels.pb.go +++ b/api/grpc/channels/v1/channels.pb.go @@ -46,7 +46,7 @@ func (*RemoveClientConnectionsReq) ProtoMessage() {} func (x *RemoveClientConnectionsReq) ProtoReflect() protoreflect.Message { mi := &file_channels_v1_channels_proto_msgTypes[0] - if x != nil { + if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -89,7 +89,7 @@ func (*RemoveClientConnectionsRes) ProtoMessage() {} func (x *RemoveClientConnectionsRes) ProtoReflect() protoreflect.Message { mi := &file_channels_v1_channels_proto_msgTypes[1] - if x != nil { + if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -113,9 +113,11 @@ type UnsetParentGroupFromChannelsReq struct { func (x *UnsetParentGroupFromChannelsReq) Reset() { *x = UnsetParentGroupFromChannelsReq{} - mi := &file_channels_v1_channels_proto_msgTypes[2] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) + if protoimpl.UnsafeEnabled { + mi := &file_channels_v1_channels_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } } func (x *UnsetParentGroupFromChannelsReq) String() string { @@ -126,7 +128,7 @@ func (*UnsetParentGroupFromChannelsReq) ProtoMessage() {} func (x *UnsetParentGroupFromChannelsReq) ProtoReflect() protoreflect.Message { mi := &file_channels_v1_channels_proto_msgTypes[2] - if x != nil { + if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -156,9 +158,11 @@ type UnsetParentGroupFromChannelsRes struct { func (x *UnsetParentGroupFromChannelsRes) Reset() { *x = UnsetParentGroupFromChannelsRes{} - mi := &file_channels_v1_channels_proto_msgTypes[3] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) + if protoimpl.UnsafeEnabled { + mi := &file_channels_v1_channels_proto_msgTypes[3] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } } func (x *UnsetParentGroupFromChannelsRes) String() string { @@ -169,7 +173,7 @@ func (*UnsetParentGroupFromChannelsRes) ProtoMessage() {} func (x *UnsetParentGroupFromChannelsRes) ProtoReflect() protoreflect.Message { mi := &file_channels_v1_channels_proto_msgTypes[3] - if x != nil { + if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) diff --git a/api/grpc/channels/v1/channels_grpc.pb.go b/api/grpc/channels/v1/channels_grpc.pb.go index 612280b166..6c4ebeda28 100644 --- a/api/grpc/channels/v1/channels_grpc.pb.go +++ b/api/grpc/channels/v1/channels_grpc.pb.go @@ -19,8 +19,8 @@ import ( // This is a compile-time assertion to ensure that this generated file // is compatible with the grpc package it is being compiled against. -// Requires gRPC-Go v1.64.0 or later. -const _ = grpc.SupportPackageIsVersion9 +// Requires gRPC-Go v1.62.0 or later. +const _ = grpc.SupportPackageIsVersion8 const ( ChannelsService_Authorize_FullMethodName = "/channels.v1.ChannelsService/Authorize" @@ -89,7 +89,7 @@ func (c *channelsServiceClient) RetrieveEntity(ctx context.Context, in *v1.Retri // ChannelsServiceServer is the server API for ChannelsService service. // All implementations must embed UnimplementedChannelsServiceServer -// for forward compatibility. +// for forward compatibility type ChannelsServiceServer interface { Authorize(context.Context, *AuthzReq) (*AuthzRes, error) RemoveClientConnections(context.Context, *RemoveClientConnectionsReq) (*RemoveClientConnectionsRes, error) @@ -98,12 +98,9 @@ type ChannelsServiceServer interface { mustEmbedUnimplementedChannelsServiceServer() } -// UnimplementedChannelsServiceServer must be embedded to have -// forward compatible implementations. -// -// NOTE: this should be embedded by value instead of pointer to avoid a nil -// pointer dereference when methods are called. -type UnimplementedChannelsServiceServer struct{} +// UnimplementedChannelsServiceServer must be embedded to have forward compatible implementations. +type UnimplementedChannelsServiceServer struct { +} func (UnimplementedChannelsServiceServer) Authorize(context.Context, *AuthzReq) (*AuthzRes, error) { return nil, status.Errorf(codes.Unimplemented, "method Authorize not implemented") @@ -118,7 +115,6 @@ func (UnimplementedChannelsServiceServer) RetrieveEntity(context.Context, *v1.Re return nil, status.Errorf(codes.Unimplemented, "method RetrieveEntity not implemented") } func (UnimplementedChannelsServiceServer) mustEmbedUnimplementedChannelsServiceServer() {} -func (UnimplementedChannelsServiceServer) testEmbeddedByValue() {} // UnsafeChannelsServiceServer may be embedded to opt out of forward compatibility for this service. // Use of this interface is not recommended, as added methods to ChannelsServiceServer will @@ -128,13 +124,6 @@ type UnsafeChannelsServiceServer interface { } func RegisterChannelsServiceServer(s grpc.ServiceRegistrar, srv ChannelsServiceServer) { - // If the following call pancis, it indicates UnimplementedChannelsServiceServer was - // embedded by pointer and is nil. This will cause panics if an - // unimplemented method is ever invoked, so we test this at initialization - // time to prevent it from happening at runtime later due to I/O. - if t, ok := srv.(interface{ testEmbeddedByValue() }); ok { - t.testEmbeddedByValue() - } s.RegisterService(&ChannelsService_ServiceDesc, srv) } diff --git a/api/grpc/common/v1/common.pb.go b/api/grpc/common/v1/common.pb.go index 281366d3e6..faa8c157e2 100644 --- a/api/grpc/common/v1/common.pb.go +++ b/api/grpc/common/v1/common.pb.go @@ -32,9 +32,11 @@ type RetrieveEntitiesReq struct { func (x *RetrieveEntitiesReq) Reset() { *x = RetrieveEntitiesReq{} - mi := &file_common_v1_common_proto_msgTypes[0] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) + if protoimpl.UnsafeEnabled { + mi := &file_common_v1_common_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } } func (x *RetrieveEntitiesReq) String() string { @@ -45,7 +47,7 @@ func (*RetrieveEntitiesReq) ProtoMessage() {} func (x *RetrieveEntitiesReq) ProtoReflect() protoreflect.Message { mi := &file_common_v1_common_proto_msgTypes[0] - if x != nil { + if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -79,9 +81,11 @@ type RetrieveEntitiesRes struct { func (x *RetrieveEntitiesRes) Reset() { *x = RetrieveEntitiesRes{} - mi := &file_common_v1_common_proto_msgTypes[1] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) + if protoimpl.UnsafeEnabled { + mi := &file_common_v1_common_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } } func (x *RetrieveEntitiesRes) String() string { @@ -92,7 +96,7 @@ func (*RetrieveEntitiesRes) ProtoMessage() {} func (x *RetrieveEntitiesRes) ProtoReflect() protoreflect.Message { mi := &file_common_v1_common_proto_msgTypes[1] - if x != nil { + if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -144,9 +148,11 @@ type RetrieveEntityReq struct { func (x *RetrieveEntityReq) Reset() { *x = RetrieveEntityReq{} - mi := &file_common_v1_common_proto_msgTypes[2] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) + if protoimpl.UnsafeEnabled { + mi := &file_common_v1_common_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } } func (x *RetrieveEntityReq) String() string { @@ -157,7 +163,7 @@ func (*RetrieveEntityReq) ProtoMessage() {} func (x *RetrieveEntityReq) ProtoReflect() protoreflect.Message { mi := &file_common_v1_common_proto_msgTypes[2] - if x != nil { + if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -188,9 +194,11 @@ type RetrieveEntityRes struct { func (x *RetrieveEntityRes) Reset() { *x = RetrieveEntityRes{} - mi := &file_common_v1_common_proto_msgTypes[3] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) + if protoimpl.UnsafeEnabled { + mi := &file_common_v1_common_proto_msgTypes[3] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } } func (x *RetrieveEntityRes) String() string { @@ -201,7 +209,7 @@ func (*RetrieveEntityRes) ProtoMessage() {} func (x *RetrieveEntityRes) ProtoReflect() protoreflect.Message { mi := &file_common_v1_common_proto_msgTypes[3] - if x != nil { + if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -235,9 +243,11 @@ type EntityBasic struct { func (x *EntityBasic) Reset() { *x = EntityBasic{} - mi := &file_common_v1_common_proto_msgTypes[4] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) + if protoimpl.UnsafeEnabled { + mi := &file_common_v1_common_proto_msgTypes[4] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } } func (x *EntityBasic) String() string { @@ -248,7 +258,7 @@ func (*EntityBasic) ProtoMessage() {} func (x *EntityBasic) ProtoReflect() protoreflect.Message { mi := &file_common_v1_common_proto_msgTypes[4] - if x != nil { + if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -300,9 +310,11 @@ type AddConnectionsReq struct { func (x *AddConnectionsReq) Reset() { *x = AddConnectionsReq{} - mi := &file_common_v1_common_proto_msgTypes[5] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) + if protoimpl.UnsafeEnabled { + mi := &file_common_v1_common_proto_msgTypes[5] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } } func (x *AddConnectionsReq) String() string { @@ -313,7 +325,7 @@ func (*AddConnectionsReq) ProtoMessage() {} func (x *AddConnectionsReq) ProtoReflect() protoreflect.Message { mi := &file_common_v1_common_proto_msgTypes[5] - if x != nil { + if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -344,9 +356,11 @@ type AddConnectionsRes struct { func (x *AddConnectionsRes) Reset() { *x = AddConnectionsRes{} - mi := &file_common_v1_common_proto_msgTypes[6] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) + if protoimpl.UnsafeEnabled { + mi := &file_common_v1_common_proto_msgTypes[6] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } } func (x *AddConnectionsRes) String() string { @@ -357,7 +371,7 @@ func (*AddConnectionsRes) ProtoMessage() {} func (x *AddConnectionsRes) ProtoReflect() protoreflect.Message { mi := &file_common_v1_common_proto_msgTypes[6] - if x != nil { + if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -388,9 +402,11 @@ type RemoveConnectionsReq struct { func (x *RemoveConnectionsReq) Reset() { *x = RemoveConnectionsReq{} - mi := &file_common_v1_common_proto_msgTypes[7] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) + if protoimpl.UnsafeEnabled { + mi := &file_common_v1_common_proto_msgTypes[7] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } } func (x *RemoveConnectionsReq) String() string { @@ -401,7 +417,7 @@ func (*RemoveConnectionsReq) ProtoMessage() {} func (x *RemoveConnectionsReq) ProtoReflect() protoreflect.Message { mi := &file_common_v1_common_proto_msgTypes[7] - if x != nil { + if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -432,9 +448,11 @@ type RemoveConnectionsRes struct { func (x *RemoveConnectionsRes) Reset() { *x = RemoveConnectionsRes{} - mi := &file_common_v1_common_proto_msgTypes[8] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) + if protoimpl.UnsafeEnabled { + mi := &file_common_v1_common_proto_msgTypes[8] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } } func (x *RemoveConnectionsRes) String() string { @@ -445,7 +463,7 @@ func (*RemoveConnectionsRes) ProtoMessage() {} func (x *RemoveConnectionsRes) ProtoReflect() protoreflect.Message { mi := &file_common_v1_common_proto_msgTypes[8] - if x != nil { + if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -479,9 +497,11 @@ type Connection struct { func (x *Connection) Reset() { *x = Connection{} - mi := &file_common_v1_common_proto_msgTypes[9] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) + if protoimpl.UnsafeEnabled { + mi := &file_common_v1_common_proto_msgTypes[9] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } } func (x *Connection) String() string { @@ -492,7 +512,7 @@ func (*Connection) ProtoMessage() {} func (x *Connection) ProtoReflect() protoreflect.Message { mi := &file_common_v1_common_proto_msgTypes[9] - if x != nil { + if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -637,6 +657,128 @@ func file_common_v1_common_proto_init() { if File_common_v1_common_proto != nil { return } + if !protoimpl.UnsafeEnabled { + file_common_v1_common_proto_msgTypes[0].Exporter = func(v any, i int) any { + switch v := v.(*RetrieveEntitiesReq); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_common_v1_common_proto_msgTypes[1].Exporter = func(v any, i int) any { + switch v := v.(*RetrieveEntitiesRes); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_common_v1_common_proto_msgTypes[2].Exporter = func(v any, i int) any { + switch v := v.(*RetrieveEntityReq); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_common_v1_common_proto_msgTypes[3].Exporter = func(v any, i int) any { + switch v := v.(*RetrieveEntityRes); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_common_v1_common_proto_msgTypes[4].Exporter = func(v any, i int) any { + switch v := v.(*EntityBasic); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_common_v1_common_proto_msgTypes[5].Exporter = func(v any, i int) any { + switch v := v.(*AddConnectionsReq); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_common_v1_common_proto_msgTypes[6].Exporter = func(v any, i int) any { + switch v := v.(*AddConnectionsRes); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_common_v1_common_proto_msgTypes[7].Exporter = func(v any, i int) any { + switch v := v.(*RemoveConnectionsReq); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_common_v1_common_proto_msgTypes[8].Exporter = func(v any, i int) any { + switch v := v.(*RemoveConnectionsRes); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_common_v1_common_proto_msgTypes[9].Exporter = func(v any, i int) any { + switch v := v.(*Connection); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ diff --git a/api/grpc/domains/v1/domains.pb.go b/api/grpc/domains/v1/domains.pb.go index 897979d4aa..8a2dcc211d 100644 --- a/api/grpc/domains/v1/domains.pb.go +++ b/api/grpc/domains/v1/domains.pb.go @@ -33,9 +33,11 @@ type DeleteUserRes struct { func (x *DeleteUserRes) Reset() { *x = DeleteUserRes{} - mi := &file_domains_v1_domains_proto_msgTypes[0] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) + if protoimpl.UnsafeEnabled { + mi := &file_domains_v1_domains_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } } func (x *DeleteUserRes) String() string { @@ -46,7 +48,7 @@ func (*DeleteUserRes) ProtoMessage() {} func (x *DeleteUserRes) ProtoReflect() protoreflect.Message { mi := &file_domains_v1_domains_proto_msgTypes[0] - if x != nil { + if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -77,9 +79,11 @@ type DeleteUserReq struct { func (x *DeleteUserReq) Reset() { *x = DeleteUserReq{} - mi := &file_domains_v1_domains_proto_msgTypes[1] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) + if protoimpl.UnsafeEnabled { + mi := &file_domains_v1_domains_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } } func (x *DeleteUserReq) String() string { @@ -90,7 +94,7 @@ func (*DeleteUserReq) ProtoMessage() {} func (x *DeleteUserReq) ProtoReflect() protoreflect.Message { mi := &file_domains_v1_domains_proto_msgTypes[1] - if x != nil { + if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -177,6 +181,32 @@ func file_domains_v1_domains_proto_init() { if File_domains_v1_domains_proto != nil { return } + if !protoimpl.UnsafeEnabled { + file_domains_v1_domains_proto_msgTypes[0].Exporter = func(v any, i int) any { + switch v := v.(*DeleteUserRes); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_domains_v1_domains_proto_msgTypes[1].Exporter = func(v any, i int) any { + switch v := v.(*DeleteUserReq); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ diff --git a/api/grpc/domains/v1/domains_grpc.pb.go b/api/grpc/domains/v1/domains_grpc.pb.go index 90b85349f8..1e348de890 100644 --- a/api/grpc/domains/v1/domains_grpc.pb.go +++ b/api/grpc/domains/v1/domains_grpc.pb.go @@ -19,8 +19,8 @@ import ( // This is a compile-time assertion to ensure that this generated file // is compatible with the grpc package it is being compiled against. -// Requires gRPC-Go v1.64.0 or later. -const _ = grpc.SupportPackageIsVersion9 +// Requires gRPC-Go v1.62.0 or later. +const _ = grpc.SupportPackageIsVersion8 const ( DomainsService_DeleteUserFromDomains_FullMethodName = "/domains.v1.DomainsService/DeleteUserFromDomains" @@ -68,7 +68,7 @@ func (c *domainsServiceClient) RetrieveEntity(ctx context.Context, in *v1.Retrie // DomainsServiceServer is the server API for DomainsService service. // All implementations must embed UnimplementedDomainsServiceServer -// for forward compatibility. +// for forward compatibility // // DomainsService is a service that provides access to // domains functionalities for SuperMQ services. @@ -78,12 +78,9 @@ type DomainsServiceServer interface { mustEmbedUnimplementedDomainsServiceServer() } -// UnimplementedDomainsServiceServer must be embedded to have -// forward compatible implementations. -// -// NOTE: this should be embedded by value instead of pointer to avoid a nil -// pointer dereference when methods are called. -type UnimplementedDomainsServiceServer struct{} +// UnimplementedDomainsServiceServer must be embedded to have forward compatible implementations. +type UnimplementedDomainsServiceServer struct { +} func (UnimplementedDomainsServiceServer) DeleteUserFromDomains(context.Context, *DeleteUserReq) (*DeleteUserRes, error) { return nil, status.Errorf(codes.Unimplemented, "method DeleteUserFromDomains not implemented") @@ -92,7 +89,6 @@ func (UnimplementedDomainsServiceServer) RetrieveEntity(context.Context, *v1.Ret return nil, status.Errorf(codes.Unimplemented, "method RetrieveEntity not implemented") } func (UnimplementedDomainsServiceServer) mustEmbedUnimplementedDomainsServiceServer() {} -func (UnimplementedDomainsServiceServer) testEmbeddedByValue() {} // UnsafeDomainsServiceServer may be embedded to opt out of forward compatibility for this service. // Use of this interface is not recommended, as added methods to DomainsServiceServer will @@ -102,13 +98,6 @@ type UnsafeDomainsServiceServer interface { } func RegisterDomainsServiceServer(s grpc.ServiceRegistrar, srv DomainsServiceServer) { - // If the following call pancis, it indicates UnimplementedDomainsServiceServer was - // embedded by pointer and is nil. This will cause panics if an - // unimplemented method is ever invoked, so we test this at initialization - // time to prevent it from happening at runtime later due to I/O. - if t, ok := srv.(interface{ testEmbeddedByValue() }); ok { - t.testEmbeddedByValue() - } s.RegisterService(&DomainsService_ServiceDesc, srv) } diff --git a/api/grpc/groups/v1/groups_grpc.pb.go b/api/grpc/groups/v1/groups_grpc.pb.go index d362f88c2b..86132eea9e 100644 --- a/api/grpc/groups/v1/groups_grpc.pb.go +++ b/api/grpc/groups/v1/groups_grpc.pb.go @@ -19,8 +19,8 @@ import ( // This is a compile-time assertion to ensure that this generated file // is compatible with the grpc package it is being compiled against. -// Requires gRPC-Go v1.64.0 or later. -const _ = grpc.SupportPackageIsVersion9 +// Requires gRPC-Go v1.62.0 or later. +const _ = grpc.SupportPackageIsVersion8 const ( GroupsService_RetrieveEntity_FullMethodName = "/groups.v1.GroupsService/RetrieveEntity" @@ -56,7 +56,7 @@ func (c *groupsServiceClient) RetrieveEntity(ctx context.Context, in *v1.Retriev // GroupsServiceServer is the server API for GroupsService service. // All implementations must embed UnimplementedGroupsServiceServer -// for forward compatibility. +// for forward compatibility // // GroupssService is a service that provides groups // functionalities for SuperMQ services. @@ -65,18 +65,14 @@ type GroupsServiceServer interface { mustEmbedUnimplementedGroupsServiceServer() } -// UnimplementedGroupsServiceServer must be embedded to have -// forward compatible implementations. -// -// NOTE: this should be embedded by value instead of pointer to avoid a nil -// pointer dereference when methods are called. -type UnimplementedGroupsServiceServer struct{} +// UnimplementedGroupsServiceServer must be embedded to have forward compatible implementations. +type UnimplementedGroupsServiceServer struct { +} func (UnimplementedGroupsServiceServer) RetrieveEntity(context.Context, *v1.RetrieveEntityReq) (*v1.RetrieveEntityRes, error) { return nil, status.Errorf(codes.Unimplemented, "method RetrieveEntity not implemented") } func (UnimplementedGroupsServiceServer) mustEmbedUnimplementedGroupsServiceServer() {} -func (UnimplementedGroupsServiceServer) testEmbeddedByValue() {} // UnsafeGroupsServiceServer may be embedded to opt out of forward compatibility for this service. // Use of this interface is not recommended, as added methods to GroupsServiceServer will @@ -86,13 +82,6 @@ type UnsafeGroupsServiceServer interface { } func RegisterGroupsServiceServer(s grpc.ServiceRegistrar, srv GroupsServiceServer) { - // If the following call pancis, it indicates UnimplementedGroupsServiceServer was - // embedded by pointer and is nil. This will cause panics if an - // unimplemented method is ever invoked, so we test this at initialization - // time to prevent it from happening at runtime later due to I/O. - if t, ok := srv.(interface{ testEmbeddedByValue() }); ok { - t.testEmbeddedByValue() - } s.RegisterService(&GroupsService_ServiceDesc, srv) } diff --git a/api/grpc/token/v1/token.pb.go b/api/grpc/token/v1/token.pb.go index 54e897e51f..9a7722514f 100644 --- a/api/grpc/token/v1/token.pb.go +++ b/api/grpc/token/v1/token.pb.go @@ -33,9 +33,11 @@ type IssueReq struct { func (x *IssueReq) Reset() { *x = IssueReq{} - mi := &file_token_v1_token_proto_msgTypes[0] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) + if protoimpl.UnsafeEnabled { + mi := &file_token_v1_token_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } } func (x *IssueReq) String() string { @@ -46,7 +48,7 @@ func (*IssueReq) ProtoMessage() {} func (x *IssueReq) ProtoReflect() protoreflect.Message { mi := &file_token_v1_token_proto_msgTypes[0] - if x != nil { + if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -84,9 +86,11 @@ type RefreshReq struct { func (x *RefreshReq) Reset() { *x = RefreshReq{} - mi := &file_token_v1_token_proto_msgTypes[1] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) + if protoimpl.UnsafeEnabled { + mi := &file_token_v1_token_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } } func (x *RefreshReq) String() string { @@ -97,7 +101,7 @@ func (*RefreshReq) ProtoMessage() {} func (x *RefreshReq) ProtoReflect() protoreflect.Message { mi := &file_token_v1_token_proto_msgTypes[1] - if x != nil { + if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -133,9 +137,11 @@ type Token struct { func (x *Token) Reset() { *x = Token{} - mi := &file_token_v1_token_proto_msgTypes[2] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) + if protoimpl.UnsafeEnabled { + mi := &file_token_v1_token_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } } func (x *Token) String() string { @@ -146,7 +152,7 @@ func (*Token) ProtoMessage() {} func (x *Token) ProtoReflect() protoreflect.Message { mi := &file_token_v1_token_proto_msgTypes[2] - if x != nil { + if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -251,6 +257,44 @@ func file_token_v1_token_proto_init() { if File_token_v1_token_proto != nil { return } + if !protoimpl.UnsafeEnabled { + file_token_v1_token_proto_msgTypes[0].Exporter = func(v any, i int) any { + switch v := v.(*IssueReq); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_token_v1_token_proto_msgTypes[1].Exporter = func(v any, i int) any { + switch v := v.(*RefreshReq); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_token_v1_token_proto_msgTypes[2].Exporter = func(v any, i int) any { + switch v := v.(*Token); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } file_token_v1_token_proto_msgTypes[2].OneofWrappers = []any{} type x struct{} out := protoimpl.TypeBuilder{ diff --git a/api/grpc/token/v1/token_grpc.pb.go b/api/grpc/token/v1/token_grpc.pb.go index f3adacfb70..8b8cf03931 100644 --- a/api/grpc/token/v1/token_grpc.pb.go +++ b/api/grpc/token/v1/token_grpc.pb.go @@ -18,8 +18,8 @@ import ( // This is a compile-time assertion to ensure that this generated file // is compatible with the grpc package it is being compiled against. -// Requires gRPC-Go v1.64.0 or later. -const _ = grpc.SupportPackageIsVersion9 +// Requires gRPC-Go v1.62.0 or later. +const _ = grpc.SupportPackageIsVersion8 const ( TokenService_Issue_FullMethodName = "/token.v1.TokenService/Issue" @@ -64,19 +64,16 @@ func (c *tokenServiceClient) Refresh(ctx context.Context, in *RefreshReq, opts . // TokenServiceServer is the server API for TokenService service. // All implementations must embed UnimplementedTokenServiceServer -// for forward compatibility. +// for forward compatibility type TokenServiceServer interface { Issue(context.Context, *IssueReq) (*Token, error) Refresh(context.Context, *RefreshReq) (*Token, error) mustEmbedUnimplementedTokenServiceServer() } -// UnimplementedTokenServiceServer must be embedded to have -// forward compatible implementations. -// -// NOTE: this should be embedded by value instead of pointer to avoid a nil -// pointer dereference when methods are called. -type UnimplementedTokenServiceServer struct{} +// UnimplementedTokenServiceServer must be embedded to have forward compatible implementations. +type UnimplementedTokenServiceServer struct { +} func (UnimplementedTokenServiceServer) Issue(context.Context, *IssueReq) (*Token, error) { return nil, status.Errorf(codes.Unimplemented, "method Issue not implemented") @@ -85,7 +82,6 @@ func (UnimplementedTokenServiceServer) Refresh(context.Context, *RefreshReq) (*T return nil, status.Errorf(codes.Unimplemented, "method Refresh not implemented") } func (UnimplementedTokenServiceServer) mustEmbedUnimplementedTokenServiceServer() {} -func (UnimplementedTokenServiceServer) testEmbeddedByValue() {} // UnsafeTokenServiceServer may be embedded to opt out of forward compatibility for this service. // Use of this interface is not recommended, as added methods to TokenServiceServer will @@ -95,13 +91,6 @@ type UnsafeTokenServiceServer interface { } func RegisterTokenServiceServer(s grpc.ServiceRegistrar, srv TokenServiceServer) { - // If the following call pancis, it indicates UnimplementedTokenServiceServer was - // embedded by pointer and is nil. This will cause panics if an - // unimplemented method is ever invoked, so we test this at initialization - // time to prevent it from happening at runtime later due to I/O. - if t, ok := srv.(interface{ testEmbeddedByValue() }); ok { - t.testEmbeddedByValue() - } s.RegisterService(&TokenService_ServiceDesc, srv) } diff --git a/auth/service.go b/auth/service.go index 579d8466fd..c266ed2fa7 100644 --- a/auth/service.go +++ b/auth/service.go @@ -167,6 +167,21 @@ func (svc service) RetrieveKey(ctx context.Context, token, id string) (Key, erro } func (svc service) Identify(ctx context.Context, token string) (Key, error) { + if strings.HasPrefix(token, "pat"+"_") { + pat, err := svc.IdentifyPAT(ctx, token) + if err != nil { + return Key{}, err + } + return Key{ + ID: pat.ID, + Type: PersonalAccessToken, + Subject: pat.User, + User: pat.User, + IssuedAt: pat.IssuedAt, + ExpiresAt: pat.ExpiresAt, + }, nil + } + key, err := svc.tokenizer.Parse(token) if errors.Contains(err, ErrExpiry) { err = svc.keys.Remove(ctx, key.Issuer, key.ID) diff --git a/pat/api/http/endpoint.go b/pat/api/http/endpoint.go new file mode 100644 index 0000000000..e69de29bb2 diff --git a/pat/api/http/requests.go b/pat/api/http/requests.go new file mode 100644 index 0000000000..e69de29bb2 diff --git a/pat/api/http/responses.go b/pat/api/http/responses.go new file mode 100644 index 0000000000..e69de29bb2 diff --git a/pat/api/http/transport.go b/pat/api/http/transport.go new file mode 100644 index 0000000000..e69de29bb2 diff --git a/pat/api/requests.go b/pat/api/requests.go new file mode 100644 index 0000000000..e69de29bb2 diff --git a/pat/api/responses.go b/pat/api/responses.go new file mode 100644 index 0000000000..e69de29bb2 diff --git a/pat/api/transport.go b/pat/api/transport.go new file mode 100644 index 0000000000..e69de29bb2 diff --git a/pat/events/streams.go b/pat/events/streams.go new file mode 100644 index 0000000000..713ddea5a6 --- /dev/null +++ b/pat/events/streams.go @@ -0,0 +1,94 @@ +// Copyright (c) Abstract Machines +// SPDX-License-Identifier: Apache-2.0 + +package events + +import ( + "context" + "time" + + "github.com/absmach/magistrala/pat" + "github.com/absmach/magistrala/pkg/authn" + "github.com/absmach/magistrala/pkg/events" + "github.com/absmach/magistrala/pkg/events/store" +) + +const streamID = "magistrala.pat" + +var _ pat.Service = (*eventStore)(nil) + + +type eventStore struct { + events.Publisher + svc pat.Service +} + +// NewEventStoreMiddleware returns wrapper around pat service that sends +// events to event store. +func NewEventStoreMiddleware(ctx context.Context, svc pat.Service, url string) (pat.Service, error) { + publisher, err := store.NewPublisher(ctx, url, streamID) + if err != nil { + return nil, err + } + + return &eventStore{ + svc: svc, + Publisher: publisher, + }, nil +} + +func (es *eventStore) CreatePAT(ctx context.Context, session authn.Session, name, description string, duration time.Duration, scope pat.Scope) (pat.PAT, error) { + return es.svc.CreatePAT(ctx, session, name, description, duration, scope) +} + +func (es *eventStore) UpdatePATName(ctx context.Context, session authn.Session, patID, name string) (pat.PAT, error) { + return es.svc.UpdatePATName(ctx, session, patID, name) +} + +func (es *eventStore) UpdatePATDescription(ctx context.Context, session authn.Session, patID, description string) (pat.PAT, error) { + return es.svc.UpdatePATDescription(ctx, session, patID, description) +} + +func (es *eventStore) RetrievePAT(ctx context.Context, session authn.Session, patID string) (pat.PAT, error) { + return es.svc.RetrievePAT(ctx, session, patID) +} + +func (es *eventStore) ListPATS(ctx context.Context, session authn.Session, pm pat.PATSPageMeta) (pat.PATSPage, error) { + return es.svc.ListPATS(ctx, session, pm) +} + +func (es *eventStore) DeletePAT(ctx context.Context, session authn.Session, patID string) error { + return es.svc.DeletePAT(ctx, session, patID) +} + +func (es *eventStore) ResetPATSecret(ctx context.Context, session authn.Session, patID string, duration time.Duration) (pat.PAT, error) { + return es.svc.ResetPATSecret(ctx, session, patID, duration) +} + +func (es *eventStore) RevokePATSecret(ctx context.Context, session authn.Session, patID string) error { + return es.svc.RevokePATSecret(ctx, session, patID) +} + +func (es *eventStore) AddPATScopeEntry(ctx context.Context, session authn.Session, patID string, platformEntityType pat.PlatformEntityType, optionalDomainID string, optionalDomainEntityType pat.DomainEntityType, operation pat.OperationType, entityIDs ...string) (pat.Scope, error) { + return es.svc.AddPATScopeEntry(ctx, session, patID, platformEntityType, optionalDomainID, optionalDomainEntityType, operation, entityIDs...) +} + +func (es *eventStore) RemovePATScopeEntry(ctx context.Context, session authn.Session, patID string, platformEntityType pat.PlatformEntityType, optionalDomainID string, optionalDomainEntityType pat.DomainEntityType, operation pat.OperationType, entityIDs ...string) (pat.Scope, error) { + return es.svc.RemovePATScopeEntry(ctx, session, patID, platformEntityType, optionalDomainID, optionalDomainEntityType, operation, entityIDs...) +} + +func (es *eventStore) ClearPATAllScopeEntry(ctx context.Context, session authn.Session, patID string) error { + return es.svc.ClearPATAllScopeEntry(ctx, session, patID) +} + +func (es *eventStore) IdentifyPAT(ctx context.Context, paToken string) (pat.PAT, error) { + return es.svc.IdentifyPAT(ctx, paToken) +} + +func (es *eventStore) AuthorizePAT(ctx context.Context, paToken string, platformEntityType pat.PlatformEntityType, optionalDomainID string, optionalDomainEntityType pat.DomainEntityType, operation pat.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 pat.PlatformEntityType, optionalDomainID string, optionalDomainEntityType pat.DomainEntityType, operation pat.OperationType, entityIDs ...string) error { + return es.svc.CheckPAT(ctx, userID, patID, platformEntityType, optionalDomainID, optionalDomainEntityType, operation, entityIDs...) +} \ No newline at end of file diff --git a/pkg/messaging/message.pb.go b/pkg/messaging/message.pb.go index bdf2bb1231..c2833f72b6 100644 --- a/pkg/messaging/message.pb.go +++ b/pkg/messaging/message.pb.go @@ -38,9 +38,11 @@ type Message struct { func (x *Message) Reset() { *x = Message{} - mi := &file_pkg_messaging_message_proto_msgTypes[0] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) + if protoimpl.UnsafeEnabled { + mi := &file_pkg_messaging_message_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } } func (x *Message) String() string { @@ -51,7 +53,7 @@ func (*Message) ProtoMessage() {} func (x *Message) ProtoReflect() protoreflect.Message { mi := &file_pkg_messaging_message_proto_msgTypes[0] - if x != nil { + if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -157,6 +159,20 @@ func file_pkg_messaging_message_proto_init() { if File_pkg_messaging_message_proto != nil { return } + if !protoimpl.UnsafeEnabled { + file_pkg_messaging_message_proto_msgTypes[0].Exporter = func(v any, i int) any { + switch v := v.(*Message); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ From 9fe4e3af875156c9125703e8318eb2135a8755e9 Mon Sep 17 00:00:00 2001 From: nyagamunene Date: Thu, 31 Oct 2024 12:02:35 +0300 Subject: [PATCH 40/72] Update grpc api Signed-off-by: nyagamunene --- api/grpc/auth/v1/auth.pb.go | 12 ------------ api/grpc/auth/v1/auth_grpc.pb.go | 4 ---- api/grpc/channels/v1/channels.pb.go | 20 ++++++++++++-------- 3 files changed, 12 insertions(+), 24 deletions(-) diff --git a/api/grpc/auth/v1/auth.pb.go b/api/grpc/auth/v1/auth.pb.go index 2d15354ce0..16b2b49395 100644 --- a/api/grpc/auth/v1/auth.pb.go +++ b/api/grpc/auth/v1/auth.pb.go @@ -535,18 +535,6 @@ func file_auth_v1_auth_proto_init() { } } file_auth_v1_auth_proto_msgTypes[3].Exporter = func(v any, i int) any { - switch v := v.(*AuthZpatReq); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_auth_v1_auth_proto_msgTypes[4].Exporter = func(v any, i int) any { switch v := v.(*AuthZRes); i { case 0: return &v.state diff --git a/api/grpc/auth/v1/auth_grpc.pb.go b/api/grpc/auth/v1/auth_grpc.pb.go index eae65dac16..73523424d7 100644 --- a/api/grpc/auth/v1/auth_grpc.pb.go +++ b/api/grpc/auth/v1/auth_grpc.pb.go @@ -215,10 +215,6 @@ var AuthService_ServiceDesc = grpc.ServiceDesc{ MethodName: "Authorize", Handler: _AuthService_Authorize_Handler, }, - { - MethodName: "AuthorizePAT", - Handler: _AuthService_AuthorizePAT_Handler, - }, { MethodName: "Authenticate", Handler: _AuthService_Authenticate_Handler, diff --git a/api/grpc/channels/v1/channels.pb.go b/api/grpc/channels/v1/channels.pb.go index ab96bac284..a5208e78ef 100644 --- a/api/grpc/channels/v1/channels.pb.go +++ b/api/grpc/channels/v1/channels.pb.go @@ -201,9 +201,11 @@ type AuthzReq struct { func (x *AuthzReq) Reset() { *x = AuthzReq{} - mi := &file_channels_v1_channels_proto_msgTypes[4] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) + if protoimpl.UnsafeEnabled { + mi := &file_channels_v1_channels_proto_msgTypes[4] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } } func (x *AuthzReq) String() string { @@ -214,7 +216,7 @@ func (*AuthzReq) ProtoMessage() {} func (x *AuthzReq) ProtoReflect() protoreflect.Message { mi := &file_channels_v1_channels_proto_msgTypes[4] - if x != nil { + if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -273,9 +275,11 @@ type AuthzRes struct { func (x *AuthzRes) Reset() { *x = AuthzRes{} - mi := &file_channels_v1_channels_proto_msgTypes[5] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) + if protoimpl.UnsafeEnabled { + mi := &file_channels_v1_channels_proto_msgTypes[5] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } } func (x *AuthzRes) String() string { @@ -286,7 +290,7 @@ func (*AuthzRes) ProtoMessage() {} func (x *AuthzRes) ProtoReflect() protoreflect.Message { mi := &file_channels_v1_channels_proto_msgTypes[5] - if x != nil { + if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) From 9c45bd3a181e794c30029f9e862ee40fcd95373c Mon Sep 17 00:00:00 2001 From: nyagamunene Date: Wed, 30 Oct 2024 13:21:26 +0300 Subject: [PATCH 41/72] Add grpc authorizePAT Signed-off-by: nyagamunene --- api/grpc/auth/v1/auth_grpc.pb.go | 4 ++++ pat/api/endpoint.go | 0 2 files changed, 4 insertions(+) create mode 100644 pat/api/endpoint.go diff --git a/api/grpc/auth/v1/auth_grpc.pb.go b/api/grpc/auth/v1/auth_grpc.pb.go index 73523424d7..eae65dac16 100644 --- a/api/grpc/auth/v1/auth_grpc.pb.go +++ b/api/grpc/auth/v1/auth_grpc.pb.go @@ -215,6 +215,10 @@ var AuthService_ServiceDesc = grpc.ServiceDesc{ MethodName: "Authorize", Handler: _AuthService_Authorize_Handler, }, + { + MethodName: "AuthorizePAT", + Handler: _AuthService_AuthorizePAT_Handler, + }, { MethodName: "Authenticate", Handler: _AuthService_Authenticate_Handler, diff --git a/pat/api/endpoint.go b/pat/api/endpoint.go new file mode 100644 index 0000000000..e69de29bb2 From ede9e50cac0ed22251b96f7cd10426bc8c827c49 Mon Sep 17 00:00:00 2001 From: nyagamunene Date: Thu, 31 Oct 2024 12:12:05 +0300 Subject: [PATCH 42/72] Initial implementation of Authorize Signed-off-by: nyagamunene --- auth/api/grpc/auth/responses.go | 4 + auth/api/grpc/utils.go | 4 +- pat/events/streams.go | 8 +- pat/middleware/authorization.go | 85 +++++++++ pat/middleware/logging.go | 276 ++++++++++++++++++++++++++++++ pat/middleware/metrics.go | 143 ++++++++++++++++ pat/service.go | 260 ++++++++++++++++++++++++++++ pat/tracing/tracing.go | 165 ++++++++++++++++++ pkg/authz/authsvc/authz.go | 19 ++ pkg/authz/authz.go | 1 + pkg/errors/service/types.go | 3 + users/middleware/authorization.go | 165 ++++++++++++++++++ 12 files changed, 1128 insertions(+), 5 deletions(-) create mode 100644 pat/middleware/authorization.go create mode 100644 pat/middleware/logging.go create mode 100644 pat/middleware/metrics.go create mode 100644 pat/service.go create mode 100644 pat/tracing/tracing.go diff --git a/auth/api/grpc/auth/responses.go b/auth/api/grpc/auth/responses.go index f7a39deb46..5cdeb10646 100644 --- a/auth/api/grpc/auth/responses.go +++ b/auth/api/grpc/auth/responses.go @@ -13,3 +13,7 @@ type authorizeRes struct { id string authorized bool } + +type retrievePATRes struct { + pat string +} diff --git a/auth/api/grpc/utils.go b/auth/api/grpc/utils.go index aef3da53c2..5823d853d0 100644 --- a/auth/api/grpc/utils.go +++ b/auth/api/grpc/utils.go @@ -25,7 +25,9 @@ func EncodeError(err error) error { err == apiutil.ErrMissingMemberType, err == apiutil.ErrMissingPolicySub, err == apiutil.ErrMissingPolicyObj, - err == apiutil.ErrMalformedPolicyAct: + err == apiutil.ErrMalformedPolicyAct, + err == apiutil.ErrMissingUserID, + err == apiutil.ErrMissingPATID: return status.Error(codes.InvalidArgument, err.Error()) case errors.Contains(err, svcerr.ErrAuthentication), errors.Contains(err, auth.ErrKeyExpired), diff --git a/pat/events/streams.go b/pat/events/streams.go index 713ddea5a6..a04bcbe0c9 100644 --- a/pat/events/streams.go +++ b/pat/events/streams.go @@ -49,8 +49,8 @@ func (es *eventStore) UpdatePATDescription(ctx context.Context, session authn.Se return es.svc.UpdatePATDescription(ctx, session, patID, description) } -func (es *eventStore) RetrievePAT(ctx context.Context, session authn.Session, patID string) (pat.PAT, error) { - return es.svc.RetrievePAT(ctx, session, patID) +func (es *eventStore) RetrievePAT(ctx context.Context, userID string, patID string) (pat.PAT, error) { + return es.svc.RetrievePAT(ctx, userID, patID) } func (es *eventStore) ListPATS(ctx context.Context, session authn.Session, pm pat.PATSPageMeta) (pat.PATSPage, error) { @@ -85,8 +85,8 @@ func (es *eventStore) IdentifyPAT(ctx context.Context, paToken string) (pat.PAT, return es.svc.IdentifyPAT(ctx, paToken) } -func (es *eventStore) AuthorizePAT(ctx context.Context, paToken string, platformEntityType pat.PlatformEntityType, optionalDomainID string, optionalDomainEntityType pat.DomainEntityType, operation pat.OperationType, entityIDs ...string) error { - return es.svc.AuthorizePAT(ctx, paToken, platformEntityType, optionalDomainID, optionalDomainEntityType, operation, entityIDs...) +func (es *eventStore) AuthorizePAT(ctx context.Context, userID, patID string, platformEntityType pat.PlatformEntityType, optionalDomainID string, optionalDomainEntityType pat.DomainEntityType, operation pat.OperationType, entityIDs ...string) error { + return es.svc.AuthorizePAT(ctx, userID, patID, platformEntityType, optionalDomainID, optionalDomainEntityType, operation, entityIDs...) } func (es *eventStore) CheckPAT(ctx context.Context, userID, patID string, platformEntityType pat.PlatformEntityType, optionalDomainID string, optionalDomainEntityType pat.DomainEntityType, operation pat.OperationType, entityIDs ...string) error { diff --git a/pat/middleware/authorization.go b/pat/middleware/authorization.go new file mode 100644 index 0000000000..cdd27958f1 --- /dev/null +++ b/pat/middleware/authorization.go @@ -0,0 +1,85 @@ +// Copyright (c) Abstract Machines +// SPDX-License-Identifier: Apache-2.0 + +package middleware + +import ( + "context" + "time" + + "github.com/absmach/magistrala/pat" + "github.com/absmach/magistrala/pkg/authn" + mgauthz "github.com/absmach/magistrala/pkg/authz" +) + +var _ pat.Service = (*authorizationMiddleware)(nil) + +type authorizationMiddleware struct { + svc pat.Service + authz mgauthz.Authorization +} + +// AuthorizationMiddleware adds authorization to the clients service. +func AuthorizationMiddleware(entityType string, svc pat.Service, authz mgauthz.Authorization) (pat.Service, error) { + return &authorizationMiddleware{ + svc: svc, + authz: authz, + }, nil +} + +func (am *authorizationMiddleware) CreatePAT(ctx context.Context, session authn.Session, name, description string, duration time.Duration, scope pat.Scope) (pat.PAT, error) { + return am.svc.CreatePAT(ctx, session, name, description, duration, scope) +} + +func (am *authorizationMiddleware) UpdatePATName(ctx context.Context, session authn.Session, patID, name string) (pat.PAT, error) { + return am.svc.UpdatePATName(ctx, session, patID, name) +} + +func (am *authorizationMiddleware) UpdatePATDescription(ctx context.Context, session authn.Session, patID, description string) (pat.PAT, error) { + return am.svc.UpdatePATDescription(ctx, session, patID, description) +} + +func (am *authorizationMiddleware) RetrievePAT(ctx context.Context, userID string, patID string) (pat.PAT, error) { + return am.svc.RetrievePAT(ctx, userID, patID) +} + +func (am *authorizationMiddleware) ListPATS(ctx context.Context, session authn.Session, pm pat.PATSPageMeta) (pat.PATSPage, error) { + return am.svc.ListPATS(ctx, session, pm) +} + +func (am *authorizationMiddleware) DeletePAT(ctx context.Context, session authn.Session, patID string) error { + return am.svc.DeletePAT(ctx, session, patID) +} + +func (am *authorizationMiddleware) ResetPATSecret(ctx context.Context, session authn.Session, patID string, duration time.Duration) (pat.PAT, error) { + return am.svc.ResetPATSecret(ctx, session, patID, duration) +} + +func (am *authorizationMiddleware) RevokePATSecret(ctx context.Context, session authn.Session, patID string) error { + return am.svc.RevokePATSecret(ctx, session, patID) +} + +func (am *authorizationMiddleware) AddPATScopeEntry(ctx context.Context, session authn.Session, patID string, platformEntityType pat.PlatformEntityType, optionalDomainID string, optionalDomainEntityType pat.DomainEntityType, operation pat.OperationType, entityIDs ...string) (pat.Scope, error) { + return am.svc.AddPATScopeEntry(ctx, session, patID, platformEntityType, optionalDomainID, optionalDomainEntityType, operation, entityIDs...) +} + +func (am *authorizationMiddleware) RemovePATScopeEntry(ctx context.Context, session authn.Session, patID string, platformEntityType pat.PlatformEntityType, optionalDomainID string, optionalDomainEntityType pat.DomainEntityType, operation pat.OperationType, entityIDs ...string) (pat.Scope, error) { + return am.svc.RemovePATScopeEntry(ctx, session, patID, platformEntityType, optionalDomainID, optionalDomainEntityType, operation, entityIDs...) +} + +func (am *authorizationMiddleware) ClearPATAllScopeEntry(ctx context.Context, session authn.Session, patID string) error { + return am.svc.ClearPATAllScopeEntry(ctx, session, patID) +} + +func (am *authorizationMiddleware) IdentifyPAT(ctx context.Context, secret string) (pat.PAT, error) { + return am.svc.IdentifyPAT(ctx, secret) +} + +func (am *authorizationMiddleware) AuthorizePAT(ctx context.Context, userID, patID string, platformEntityType pat.PlatformEntityType, optionalDomainID string, optionalDomainEntityType pat.DomainEntityType, operation pat.OperationType, entityIDs ...string) error { + return am.svc.AuthorizePAT(ctx, userID, patID, platformEntityType, optionalDomainID, optionalDomainEntityType, operation, entityIDs...) +} + +func (am *authorizationMiddleware) CheckPAT(ctx context.Context, userID, patID string, platformEntityType pat.PlatformEntityType, optionalDomainID string, optionalDomainEntityType pat.DomainEntityType, operation pat.OperationType, entityIDs ...string) error { + return am.svc.CheckPAT(ctx, userID, patID, platformEntityType, optionalDomainID, optionalDomainEntityType, operation, entityIDs...) +} + diff --git a/pat/middleware/logging.go b/pat/middleware/logging.go new file mode 100644 index 0000000000..b0d096676a --- /dev/null +++ b/pat/middleware/logging.go @@ -0,0 +1,276 @@ +// Copyright (c) Abstract Machines +// SPDX-License-Identifier: Apache-2.0 + +package middleware + +import ( + "context" + "log/slog" + "time" + + "github.com/absmach/magistrala/pat" + "github.com/absmach/magistrala/pkg/authn" +) + +var _ pat.Service = (*loggingMiddleware)(nil) + +type loggingMiddleware struct { + logger *slog.Logger + svc pat.Service +} + +// LoggingMiddleware adds logging facilities to the core service. +func LoggingMiddleware(svc pat.Service, logger *slog.Logger) pat.Service { + return &loggingMiddleware{logger, svc} +} + +func (lm *loggingMiddleware) CreatePAT(ctx context.Context, session authn.Session, name, description string, duration time.Duration, scope pat.Scope) (pa pat.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.CreatePAT(ctx, session, name, description, duration, scope) +} + +func (lm *loggingMiddleware) UpdatePATName(ctx context.Context, session authn.Session, patID, name string) (pa pat.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.UpdatePATName(ctx, session, patID, name) +} + +func (lm *loggingMiddleware) UpdatePATDescription(ctx context.Context, session authn.Session, patID, description string) (pa pat.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.UpdatePATDescription(ctx, session, patID, description) +} + +func (lm *loggingMiddleware) RetrievePAT(ctx context.Context, userID string, patID string) (pa pat.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.RetrievePAT(ctx, userID, patID) +} + +func (lm *loggingMiddleware) ListPATS(ctx context.Context, session authn.Session, pm pat.PATSPageMeta) (pp pat.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.ListPATS(ctx, session, pm) +} + +func (lm *loggingMiddleware) DeletePAT(ctx context.Context, session authn.Session, 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.DeletePAT(ctx, session, patID) +} + +func (lm *loggingMiddleware) ResetPATSecret(ctx context.Context, session authn.Session, patID string, duration time.Duration) (pa pat.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.ResetPATSecret(ctx, session, patID, duration) +} + +func (lm *loggingMiddleware) RevokePATSecret(ctx context.Context, session authn.Session, 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.RevokePATSecret(ctx, session, patID) +} + +func (lm *loggingMiddleware) AddPATScopeEntry(ctx context.Context, session authn.Session, patID string, platformEntityType pat.PlatformEntityType, optionalDomainID string, optionalDomainEntityType pat.DomainEntityType, operation pat.OperationType, entityIDs ...string) (sc pat.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.AddPATScopeEntry(ctx, session, patID, platformEntityType, optionalDomainID, optionalDomainEntityType, operation, entityIDs...) +} + +func (lm *loggingMiddleware) RemovePATScopeEntry(ctx context.Context, session authn.Session, patID string, platformEntityType pat.PlatformEntityType, optionalDomainID string, optionalDomainEntityType pat.DomainEntityType, operation pat.OperationType, entityIDs ...string) (sc pat.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.RemovePATScopeEntry(ctx, session, patID, platformEntityType, optionalDomainID, optionalDomainEntityType, operation, entityIDs...) +} + +func (lm *loggingMiddleware) ClearPATAllScopeEntry(ctx context.Context, session authn.Session, 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.ClearPATAllScopeEntry(ctx, session, patID) +} + +func (lm *loggingMiddleware) IdentifyPAT(ctx context.Context, paToken string) (pa pat.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, userID, patID string, platformEntityType pat.PlatformEntityType, optionalDomainID string, optionalDomainEntityType pat.DomainEntityType, operation pat.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("Authorize PAT failed complete successfully", args...) + return + } + lm.logger.Info("Authorize PAT completed successfully", args...) + }(time.Now()) + return lm.svc.AuthorizePAT(ctx, userID, patID, platformEntityType, optionalDomainID, optionalDomainEntityType, operation, entityIDs...) +} + +func (lm *loggingMiddleware) CheckPAT(ctx context.Context, userID, patID string, platformEntityType pat.PlatformEntityType, optionalDomainID string, optionalDomainEntityType pat.DomainEntityType, operation pat.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("Check PAT failed complete successfully", args...) + return + } + lm.logger.Info("Check PAT completed successfully", args...) + }(time.Now()) + return lm.svc.CheckPAT(ctx, userID, patID, platformEntityType, optionalDomainID, optionalDomainEntityType, operation, entityIDs...) +} + diff --git a/pat/middleware/metrics.go b/pat/middleware/metrics.go new file mode 100644 index 0000000000..d2898f3b29 --- /dev/null +++ b/pat/middleware/metrics.go @@ -0,0 +1,143 @@ +// Copyright (c) Abstract Machines +// SPDX-License-Identifier: Apache-2.0 + +package middleware + +import ( + "context" + "time" + + "github.com/absmach/magistrala/pat" + "github.com/absmach/magistrala/pkg/authn" + "github.com/go-kit/kit/metrics" +) + +var _ pat.Service = (*metricsMiddleware)(nil) + +type metricsMiddleware struct { + counter metrics.Counter + latency metrics.Histogram + svc pat.Service +} + +// MetricsMiddleware instruments core service by tracking request count and latency. +func MetricsMiddleware(svc pat.Service, counter metrics.Counter, latency metrics.Histogram) pat.Service { + return &metricsMiddleware{ + counter: counter, + latency: latency, + svc: svc, + } +} + +func (ms *metricsMiddleware) CreatePAT(ctx context.Context, session authn.Session, name, description string, duration time.Duration, scope pat.Scope) (pat.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.CreatePAT(ctx, session, name, description, duration, scope) +} + +func (ms *metricsMiddleware) UpdatePATName(ctx context.Context, session authn.Session, patID, name string) (pat.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.UpdatePATName(ctx, session, patID, name) +} + +func (ms *metricsMiddleware) UpdatePATDescription(ctx context.Context, session authn.Session, patID, description string) (pat.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.UpdatePATDescription(ctx, session, patID, description) +} + +func (ms *metricsMiddleware) RetrievePAT(ctx context.Context, userID string, patID string) (pat.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.RetrievePAT(ctx, userID, patID) +} + +func (ms *metricsMiddleware) ListPATS(ctx context.Context, session authn.Session, pm pat.PATSPageMeta) (pat.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.ListPATS(ctx, session, pm) +} + +func (ms *metricsMiddleware) DeletePAT(ctx context.Context, session authn.Session, 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.DeletePAT(ctx, session, patID) +} + +func (ms *metricsMiddleware) ResetPATSecret(ctx context.Context, session authn.Session, patID string, duration time.Duration) (pat.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.ResetPATSecret(ctx, session, patID, duration) +} + +func (ms *metricsMiddleware) RevokePATSecret(ctx context.Context, session authn.Session, 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.RevokePATSecret(ctx, session, patID) +} + +func (ms *metricsMiddleware) AddPATScopeEntry(ctx context.Context, session authn.Session, patID string, platformEntityType pat.PlatformEntityType, optionalDomainID string, optionalDomainEntityType pat.DomainEntityType, operation pat.OperationType, entityIDs ...string) (pat.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.AddPATScopeEntry(ctx, session, patID, platformEntityType, optionalDomainID, optionalDomainEntityType, operation, entityIDs...) +} + +func (ms *metricsMiddleware) RemovePATScopeEntry(ctx context.Context, session authn.Session, patID string, platformEntityType pat.PlatformEntityType, optionalDomainID string, optionalDomainEntityType pat.DomainEntityType, operation pat.OperationType, entityIDs ...string) (pat.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.RemovePATScopeEntry(ctx, session, patID, platformEntityType, optionalDomainID, optionalDomainEntityType, operation, entityIDs...) +} + +func (ms *metricsMiddleware) ClearPATAllScopeEntry(ctx context.Context, session authn.Session, 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.ClearPATAllScopeEntry(ctx, session, patID) +} + +func (ms *metricsMiddleware) IdentifyPAT(ctx context.Context, paToken string) (pat.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, userID, patID string, platformEntityType pat.PlatformEntityType, optionalDomainID string, optionalDomainEntityType pat.DomainEntityType, operation pat.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, userID, patID, platformEntityType, optionalDomainID, optionalDomainEntityType, operation, entityIDs...) +} + +func (ms *metricsMiddleware) CheckPAT(ctx context.Context, userID, patID string, platformEntityType pat.PlatformEntityType, optionalDomainID string, optionalDomainEntityType pat.DomainEntityType, operation pat.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/pat/service.go b/pat/service.go new file mode 100644 index 0000000000..571cf6551b --- /dev/null +++ b/pat/service.go @@ -0,0 +1,260 @@ +// Copyright (c) Abstract Machines +// SPDX-License-Identifier: Apache-2.0 + +package pat + +import ( + "context" + "encoding/base64" + "math/rand" + "strings" + "time" + + "github.com/absmach/magistrala" + "github.com/absmach/magistrala/pkg/authn" + "github.com/absmach/magistrala/pkg/errors" + svcerr "github.com/absmach/magistrala/pkg/errors/service" + "github.com/google/uuid" +) + +const ( + randStr = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890!@#$%^&&*|+-=" + patPrefix = "pat" + patSecretSeparator = "_" +) + +var ( + 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") + 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") + errClearAllScope = errors.New("failed to clear all entry in scope") +) + +type service struct { + pats PATSRepository + hasher Hasher + idProvider magistrala.IDProvider +} + +var _ Service = (*service)(nil) + +// New instantiates the auth service implementation. +func New(pats PATSRepository, hasher Hasher, idp magistrala.IDProvider) Service { + return &service{ + pats: pats, + hasher: hasher, + idProvider: idp, + } +} + +func (svc service) CreatePAT(ctx context.Context, session authn.Session, name, description string, duration time.Duration, scope Scope) (PAT, error) { + id, err := svc.idProvider.ID() + if err != nil { + return PAT{}, errors.Wrap(svcerr.ErrCreateEntity, err) + } + secret, hash, err := svc.generateSecretAndHash(session.UserID, id) + if err != nil { + return PAT{}, errors.Wrap(svcerr.ErrCreateEntity, err) + } + + now := time.Now() + pat := PAT{ + ID: id, + User: session.UserID, + 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 +} + +func (svc service) UpdatePATName(ctx context.Context, session authn.Session, patID, name string) (PAT, error) { + pat, err := svc.pats.UpdateName(ctx, session.UserID, patID, name) + if err != nil { + return PAT{}, errors.Wrap(errUpdatePAT, err) + } + return pat, nil +} + +func (svc service) UpdatePATDescription(ctx context.Context, session authn.Session, patID, description string) (PAT, error) { + pat, err := svc.pats.UpdateDescription(ctx, session.UserID, patID, description) + if err != nil { + return PAT{}, errors.Wrap(errUpdatePAT, err) + } + return pat, nil +} + +func (svc service) RetrievePAT(ctx context.Context, userID string, patID string) (PAT, error) { + pat, err := svc.pats.Retrieve(ctx, userID, patID) + if err != nil { + return PAT{}, errors.Wrap(errRetrievePAT, err) + } + return pat, nil +} + +func (svc service) ListPATS(ctx context.Context, session authn.Session, pm PATSPageMeta) (PATSPage, error) { + patsPage, err := svc.pats.RetrieveAll(ctx, session.UserID, pm) + if err != nil { + return PATSPage{}, errors.Wrap(errRetrievePAT, err) + } + return patsPage, nil +} + +func (svc service) DeletePAT(ctx context.Context, session authn.Session, patID string) error { + if err := svc.pats.Remove(ctx, session.UserID, patID); err != nil { + return errors.Wrap(errDeletePAT, err) + } + return nil +} + +func (svc service) ResetPATSecret(ctx context.Context, session authn.Session, patID string, duration time.Duration) (PAT, error) { + // Generate new HashToken take place here + secret, hash, err := svc.generateSecretAndHash(session.UserID, patID) + if err != nil { + return PAT{}, errors.Wrap(svcerr.ErrUpdateEntity, err) + } + + pat, err := svc.pats.UpdateTokenHash(ctx, session.UserID, patID, hash, time.Now().Add(duration)) + if err != nil { + return PAT{}, errors.Wrap(svcerr.ErrUpdateEntity, err) + } + + if err := svc.pats.Reactivate(ctx, session.UserID, patID); err != nil { + return PAT{}, errors.Wrap(svcerr.ErrUpdateEntity, err) + } + pat.Secret = secret + pat.Revoked = false + pat.RevokedAt = time.Time{} + return pat, nil +} + +func (svc service) RevokePATSecret(ctx context.Context, session authn.Session, patID string) error { + if err := svc.pats.Revoke(ctx, session.UserID, patID); err != nil { + return errors.Wrap(errRevokePAT, err) + } + return nil +} + +func (svc service) AddPATScopeEntry(ctx context.Context, session authn.Session, patID string, platformEntityType PlatformEntityType, optionalDomainID string, optionalDomainEntityType DomainEntityType, operation OperationType, entityIDs ...string) (Scope, error) { + scope, err := svc.pats.AddScopeEntry(ctx, session.UserID, 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, session authn.Session, patID string, platformEntityType PlatformEntityType, optionalDomainID string, optionalDomainEntityType DomainEntityType, operation OperationType, entityIDs ...string) (Scope, error) { + scope, err := svc.pats.RemoveScopeEntry(ctx, session.UserID, patID, platformEntityType, optionalDomainID, optionalDomainEntityType, operation, entityIDs...) + if err != nil { + return Scope{}, err + } + return scope, nil +} + +func (svc service) ClearPATAllScopeEntry(ctx context.Context, session authn.Session, patID string) error { + if err := svc.pats.RemoveAllScopeEntry(ctx, session.UserID, patID); err != nil { + return errors.Wrap(errClearAllScope, err) + } + return nil +} + +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, userID, patID string, platformEntityType PlatformEntityType, optionalDomainID string, optionalDomainEntityType DomainEntityType, operation OperationType, entityIDs ...string) error { + res, err := svc.RetrievePAT(ctx, userID, patID) + if 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) 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 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/pat/tracing/tracing.go b/pat/tracing/tracing.go new file mode 100644 index 0000000000..71af03bc56 --- /dev/null +++ b/pat/tracing/tracing.go @@ -0,0 +1,165 @@ +// Copyright (c) Abstract Machines +// SPDX-License-Identifier: Apache-2.0 + +package tracing + +import ( + "context" + "time" + + "github.com/absmach/magistrala/pat" + "github.com/absmach/magistrala/pkg/authn" + "go.opentelemetry.io/otel/attribute" + "go.opentelemetry.io/otel/trace" +) + +var _ pat.Service = (*tracingMiddleware)(nil) + +type tracingMiddleware struct { + tracer trace.Tracer + svc pat.Service +} + +// New returns a new group service with tracing capabilities. +func New(svc pat.Service, tracer trace.Tracer) pat.Service { + return &tracingMiddleware{tracer, svc} +} + +func (tm *tracingMiddleware) CreatePAT(ctx context.Context, session authn.Session, name, description string, duration time.Duration, scope pat.Scope) (pat.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.CreatePAT(ctx, session, name, description, duration, scope) +} + +func (tm *tracingMiddleware) UpdatePATName(ctx context.Context, session authn.Session, patID, name string) (pat.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.UpdatePATName(ctx, session, patID, name) +} + +func (tm *tracingMiddleware) UpdatePATDescription(ctx context.Context, session authn.Session, patID, description string) (pat.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.UpdatePATDescription(ctx, session, patID, description) +} + +func (tm *tracingMiddleware) RetrievePAT(ctx context.Context, userID string, patID string) (pat.PAT, error) { + ctx, span := tm.tracer.Start(ctx, "retrieve_pat", trace.WithAttributes( + attribute.String("pat_id", patID), + )) + defer span.End() + return tm.svc.RetrievePAT(ctx, userID, patID) +} + +func (tm *tracingMiddleware) ListPATS(ctx context.Context, session authn.Session, pm pat.PATSPageMeta) (pat.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.ListPATS(ctx, session, pm) +} + +func (tm *tracingMiddleware) DeletePAT(ctx context.Context, session authn.Session, patID string) error { + ctx, span := tm.tracer.Start(ctx, "delete_pat", trace.WithAttributes( + attribute.String("pat_id", patID), + )) + defer span.End() + return tm.svc.DeletePAT(ctx, session, patID) +} + +func (tm *tracingMiddleware) ResetPATSecret(ctx context.Context, session authn.Session, patID string, duration time.Duration) (pat.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.ResetPATSecret(ctx, session, patID, duration) +} + +func (tm *tracingMiddleware) RevokePATSecret(ctx context.Context, session authn.Session, 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.RevokePATSecret(ctx, session, patID) +} + +func (tm *tracingMiddleware) AddPATScopeEntry(ctx context.Context, session authn.Session, patID string, platformEntityType pat.PlatformEntityType, optionalDomainID string, optionalDomainEntityType pat.DomainEntityType, operation pat.OperationType, entityIDs ...string) (pat.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), + attribute.String("optional_domain_entity", optionalDomainEntityType.String()), + attribute.String("operation", operation.String()), + attribute.StringSlice("entities", entityIDs), + )) + defer span.End() + return tm.svc.AddPATScopeEntry(ctx, session, patID, platformEntityType, optionalDomainID, optionalDomainEntityType, operation, entityIDs...) +} + +func (tm *tracingMiddleware) RemovePATScopeEntry(ctx context.Context, session authn.Session, patID string, platformEntityType pat.PlatformEntityType, optionalDomainID string, optionalDomainEntityType pat.DomainEntityType, operation pat.OperationType, entityIDs ...string) (pat.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), + attribute.String("optional_domain_entity", optionalDomainEntityType.String()), + attribute.String("operation", operation.String()), + attribute.StringSlice("entities", entityIDs), + )) + defer span.End() + return tm.svc.RemovePATScopeEntry(ctx, session, patID, platformEntityType, optionalDomainID, optionalDomainEntityType, operation, entityIDs...) +} + +func (tm *tracingMiddleware) ClearPATAllScopeEntry(ctx context.Context, session authn.Session, 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.ClearPATAllScopeEntry(ctx, session, patID) +} + +func (tm *tracingMiddleware) IdentifyPAT(ctx context.Context, paToken string) (pat.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, userID, patID string, platformEntityType pat.PlatformEntityType, optionalDomainID string, optionalDomainEntityType pat.DomainEntityType, operation pat.OperationType, entityIDs ...string) error { + ctx, span := tm.tracer.Start(ctx, "authorize_pat", 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.AuthorizePAT(ctx, userID, patID, platformEntityType, optionalDomainID, optionalDomainEntityType, operation, entityIDs...) +} + +func (tm *tracingMiddleware) CheckPAT(ctx context.Context, userID, patID string, platformEntityType pat.PlatformEntityType, optionalDomainID string, optionalDomainEntityType pat.DomainEntityType, operation pat.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.CheckPAT(ctx, userID, patID, platformEntityType, optionalDomainID, optionalDomainEntityType, operation, entityIDs...) +} + diff --git a/pkg/authz/authsvc/authz.go b/pkg/authz/authsvc/authz.go index 1c2801c04a..01ad6ee8a4 100644 --- a/pkg/authz/authsvc/authz.go +++ b/pkg/authz/authsvc/authz.go @@ -121,3 +121,22 @@ func (a authorization) checkDomain(ctx context.Context, subjectType, subject, do return svcerr.ErrInvalidStatus } } + +func (a authorization) AuthorizePAT(ctx context.Context, pr authz.PatReq) error { + req := grpcAuthV1.AuthZpatReq{ + PaToken: pr.PaToken, + PlatformEntityType: pr.PlatformEntityType, + OptionalDomainID: pr.OptionalDomainID, + OptionalDomainEntityType: pr.OptionalDomainEntityType, + Operation: pr.Operation, + EntityIDs: pr.EntityIDs, + } + res, err := a.authSvcClient.AuthorizePAT(ctx, &req) + if err != nil { + return errors.Wrap(errors.ErrAuthorization, err) + } + if !res.Authorized { + return errors.ErrAuthorization + } + return nil +} diff --git a/pkg/authz/authz.go b/pkg/authz/authz.go index f6f27e0f2a..06ceb7e03d 100644 --- a/pkg/authz/authz.go +++ b/pkg/authz/authz.go @@ -47,4 +47,5 @@ type PolicyReq struct { //go:generate mockery --name Authorization --output=./mocks --filename authz.go --quiet --note "Copyright (c) Abstract Machines" type Authorization interface { Authorize(ctx context.Context, pr PolicyReq) error + AuthorizePAT(ctx context.Context, pr PatReq) error } diff --git a/pkg/errors/service/types.go b/pkg/errors/service/types.go index 7fe49f6134..928a2268b4 100644 --- a/pkg/errors/service/types.go +++ b/pkg/errors/service/types.go @@ -84,4 +84,7 @@ var ( // ErrRollbackRepo indicates a failure to rollback repository. ErrRollbackRepo = errors.New("failed to rollback repo") + + // ErrUnauthorizedPAT indicates failure occurred while authorizing PAT. + ErrUnauthorizedPAT = errors.New("failed to authorize PAT") ) diff --git a/users/middleware/authorization.go b/users/middleware/authorization.go index e5774f8c79..faf3f3c746 100644 --- a/users/middleware/authorization.go +++ b/users/middleware/authorization.go @@ -44,6 +44,18 @@ func (am *authorizationMiddleware) Register(ctx context.Context, session authn.S } func (am *authorizationMiddleware) View(ctx context.Context, session authn.Session, id string) (users.User, error) { + if session.Type == authn.PersonalAccessToken { + if err := am.authz.AuthorizePAT(ctx, mgauthz.PatReq{ + UserID: session.UserID, + PatID: session.ID, + PlatformEntityType: pat.PlatformUsersScope.String(), + OptionalDomainEntityType: pat.DomainNullScope.String(), + Operation: pat.ReadOp.String(), + }); err != nil { + return users.User{}, errors.Wrap(err, svcerr.ErrUnauthorizedPAT) + } + } + if err := am.checkSuperAdmin(ctx, session.UserID); err == nil { session.SuperAdmin = true } @@ -52,10 +64,32 @@ func (am *authorizationMiddleware) View(ctx context.Context, session authn.Sessi } func (am *authorizationMiddleware) ViewProfile(ctx context.Context, session authn.Session) (users.User, error) { + if session.Type == authn.PersonalAccessToken { + if err := am.authz.AuthorizePAT(ctx, mgauthz.PatReq{ + UserID: session.UserID, + PatID: session.ID, + PlatformEntityType: pat.PlatformUsersScope.String(), + OptionalDomainEntityType: pat.DomainNullScope.String(), + Operation: pat.ReadOp.String(), + }); err != nil { + return users.User{}, errors.Wrap(err, svcerr.ErrUnauthorizedPAT) + } + } return am.svc.ViewProfile(ctx, session) } func (am *authorizationMiddleware) ListUsers(ctx context.Context, session authn.Session, pm users.Page) (users.UsersPage, error) { + if session.Type == authn.PersonalAccessToken { + if err := am.authz.AuthorizePAT(ctx, mgauthz.PatReq{ + UserID: session.UserID, + PatID: session.ID, + PlatformEntityType: pat.PlatformUsersScope.String(), + OptionalDomainEntityType: pat.DomainNullScope.String(), + Operation: pat.ListOp.String(), + }); err != nil { + return users.UsersPage{}, errors.Wrap(err, svcerr.ErrUnauthorizedPAT) + } + } if err := am.checkSuperAdmin(ctx, session.UserID); err == nil { session.SuperAdmin = true } @@ -64,6 +98,18 @@ func (am *authorizationMiddleware) ListUsers(ctx context.Context, session authn. } func (am *authorizationMiddleware) ListMembers(ctx context.Context, session authn.Session, objectKind, objectID string, pm users.Page) (users.MembersPage, error) { + if session.Type == authn.PersonalAccessToken { + if err := am.authz.AuthorizePAT(ctx, mgauthz.PatReq{ + UserID: session.UserID, + PatID: session.ID, + PlatformEntityType: pat.PlatformUsersScope.String(), + OptionalDomainEntityType: pat.DomainNullScope.String(), + Operation: pat.ListOp.String(), + }); err != nil { + return users.MembersPage{}, errors.Wrap(err, svcerr.ErrUnauthorizedPAT) + } + } + if session.DomainUserID == "" { return users.MembersPage{}, svcerr.ErrDomainAuthorization } @@ -96,6 +142,18 @@ func (am *authorizationMiddleware) Update(ctx context.Context, session authn.Ses session.SuperAdmin = true } + if session.Type == authn.PersonalAccessToken { + if err := am.authz.AuthorizePAT(ctx, mgauthz.PatReq{ + UserID: session.UserID, + PatID: session.ID, + PlatformEntityType: pat.PlatformUsersScope.String(), + OptionalDomainEntityType: pat.DomainNullScope.String(), + Operation: pat.UpdateOp.String(), + }); err != nil { + return users.User{}, errors.Wrap(err, svcerr.ErrUnauthorizedPAT) + } + } + return am.svc.Update(ctx, session, user) } @@ -104,10 +162,33 @@ func (am *authorizationMiddleware) UpdateTags(ctx context.Context, session authn session.SuperAdmin = true } + if session.Type == authn.PersonalAccessToken { + if err := am.authz.AuthorizePAT(ctx, mgauthz.PatReq{ + UserID: session.UserID, + PatID: session.ID, + PlatformEntityType: pat.PlatformUsersScope.String(), + OptionalDomainEntityType: pat.DomainNullScope.String(), + Operation: pat.UpdateOp.String(), + }); err != nil { + return users.User{}, errors.Wrap(err, svcerr.ErrUnauthorizedPAT) + } + } + return am.svc.UpdateTags(ctx, session, user) } func (am *authorizationMiddleware) UpdateEmail(ctx context.Context, session authn.Session, id, email string) (users.User, error) { + if session.Type == authn.PersonalAccessToken { + if err := am.authz.AuthorizePAT(ctx, mgauthz.PatReq{ + UserID: session.UserID, + PatID: session.ID, + PlatformEntityType: pat.PlatformUsersScope.String(), + OptionalDomainEntityType: pat.DomainNullScope.String(), + Operation: pat.UpdateOp.String(), + }); err != nil { + return users.User{}, errors.Wrap(err, svcerr.ErrUnauthorizedPAT) + } + } if err := am.checkSuperAdmin(ctx, session.UserID); err == nil { session.SuperAdmin = true } @@ -120,6 +201,18 @@ func (am *authorizationMiddleware) UpdateUsername(ctx context.Context, session a session.SuperAdmin = true } + if session.Type == authn.PersonalAccessToken { + if err := am.authz.AuthorizePAT(ctx, mgauthz.PatReq{ + UserID: session.UserID, + PatID: session.ID, + PlatformEntityType: pat.PlatformUsersScope.String(), + OptionalDomainEntityType: pat.DomainNullScope.String(), + Operation: pat.UpdateOp.String(), + }); err != nil { + return users.User{}, errors.Wrap(err, svcerr.ErrUnauthorizedPAT) + } + } + return am.svc.UpdateUsername(ctx, session, id, username) } @@ -127,6 +220,19 @@ func (am *authorizationMiddleware) UpdateProfilePicture(ctx context.Context, ses if err := am.checkSuperAdmin(ctx, session.UserID); err == nil { session.SuperAdmin = true } + + if session.Type == authn.PersonalAccessToken { + if err := am.authz.AuthorizePAT(ctx, mgauthz.PatReq{ + UserID: session.UserID, + PatID: session.ID, + PlatformEntityType: pat.PlatformUsersScope.String(), + OptionalDomainEntityType: pat.DomainNullScope.String(), + Operation: pat.UpdateOp.String(), + }); err != nil { + return users.User{}, errors.Wrap(err, svcerr.ErrUnauthorizedPAT) + } + } + return am.svc.UpdateProfilePicture(ctx, session, user) } @@ -135,6 +241,18 @@ func (am *authorizationMiddleware) GenerateResetToken(ctx context.Context, email } func (am *authorizationMiddleware) UpdateSecret(ctx context.Context, session authn.Session, oldSecret, newSecret string) (users.User, error) { + if session.Type == authn.PersonalAccessToken { + if err := am.authz.AuthorizePAT(ctx, mgauthz.PatReq{ + UserID: session.UserID, + PatID: session.ID, + PlatformEntityType: pat.PlatformUsersScope.String(), + OptionalDomainEntityType: pat.DomainNullScope.String(), + Operation: pat.UpdateOp.String(), + }); err != nil { + return users.User{}, errors.Wrap(err, svcerr.ErrUnauthorizedPAT) + } + } + return am.svc.UpdateSecret(ctx, session, oldSecret, newSecret) } @@ -150,6 +268,17 @@ func (am *authorizationMiddleware) UpdateRole(ctx context.Context, session authn if err := am.checkSuperAdmin(ctx, session.UserID); err != nil { return users.User{}, err } + if session.Type == authn.PersonalAccessToken { + if err := am.authz.AuthorizePAT(ctx, mgauthz.PatReq{ + UserID: session.UserID, + PatID: session.ID, + PlatformEntityType: pat.PlatformUsersScope.String(), + OptionalDomainEntityType: pat.DomainNullScope.String(), + Operation: pat.UpdateOp.String(), + }); err != nil { + return users.User{}, errors.Wrap(err, svcerr.ErrUnauthorizedPAT) + } + } session.SuperAdmin = true if err := am.authorize(ctx, "", policies.UserType, policies.UsersKind, user.ID, policies.MembershipPermission, policies.PlatformType, policies.SuperMQObject); err != nil { return users.User{}, err @@ -163,6 +292,18 @@ func (am *authorizationMiddleware) Enable(ctx context.Context, session authn.Ses session.SuperAdmin = true } + if session.Type == authn.PersonalAccessToken { + if err := am.authz.AuthorizePAT(ctx, mgauthz.PatReq{ + UserID: session.UserID, + PatID: session.ID, + PlatformEntityType: pat.PlatformUsersScope.String(), + OptionalDomainEntityType: pat.DomainNullScope.String(), + Operation: pat.UpdateOp.String(), + }); err != nil { + return users.User{}, errors.Wrap(err, svcerr.ErrUnauthorizedPAT) + } + } + return am.svc.Enable(ctx, session, id) } @@ -171,6 +312,18 @@ func (am *authorizationMiddleware) Disable(ctx context.Context, session authn.Se session.SuperAdmin = true } + if session.Type == authn.PersonalAccessToken { + if err := am.authz.AuthorizePAT(ctx, mgauthz.PatReq{ + UserID: session.UserID, + PatID: session.ID, + PlatformEntityType: pat.PlatformUsersScope.String(), + OptionalDomainEntityType: pat.DomainNullScope.String(), + Operation: pat.UpdateOp.String(), + }); err != nil { + return users.User{}, errors.Wrap(err, svcerr.ErrUnauthorizedPAT) + } + } + return am.svc.Disable(ctx, session, id) } @@ -179,6 +332,18 @@ func (am *authorizationMiddleware) Delete(ctx context.Context, session authn.Ses session.SuperAdmin = true } + if session.Type == authn.PersonalAccessToken { + if err := am.authz.AuthorizePAT(ctx, mgauthz.PatReq{ + UserID: session.UserID, + PatID: session.ID, + PlatformEntityType: pat.PlatformUsersScope.String(), + OptionalDomainEntityType: pat.DomainNullScope.String(), + Operation: pat.DeleteOp.String(), + }); err != nil { + return errors.Wrap(err, svcerr.ErrUnauthorizedPAT) + } + } + return am.svc.Delete(ctx, session, id) } From 974eedf9de650f235f14afa8cbdfb5797f945de5 Mon Sep 17 00:00:00 2001 From: nyagamunene Date: Thu, 31 Oct 2024 13:57:10 +0300 Subject: [PATCH 43/72] Remove pat folder Signed-off-by: nyagamunene --- pat/api/http/endpoint.go | 1 + pat/api/http/requests.go | 1 + pat/api/http/responses.go | 1 + pat/api/http/transport.go | 1 + pat/events/streams.go | 94 --------------------------------- pat/middleware/authorization.go | 1 + pat/middleware/logging.go | 1 + pat/middleware/metrics.go | 1 + pat/service.go | 1 + pat/tracing/tracing.go | 1 + 10 files changed, 9 insertions(+), 94 deletions(-) delete mode 100644 pat/events/streams.go diff --git a/pat/api/http/endpoint.go b/pat/api/http/endpoint.go index e69de29bb2..8b13789179 100644 --- a/pat/api/http/endpoint.go +++ b/pat/api/http/endpoint.go @@ -0,0 +1 @@ + diff --git a/pat/api/http/requests.go b/pat/api/http/requests.go index e69de29bb2..8b13789179 100644 --- a/pat/api/http/requests.go +++ b/pat/api/http/requests.go @@ -0,0 +1 @@ + diff --git a/pat/api/http/responses.go b/pat/api/http/responses.go index e69de29bb2..8b13789179 100644 --- a/pat/api/http/responses.go +++ b/pat/api/http/responses.go @@ -0,0 +1 @@ + diff --git a/pat/api/http/transport.go b/pat/api/http/transport.go index e69de29bb2..8b13789179 100644 --- a/pat/api/http/transport.go +++ b/pat/api/http/transport.go @@ -0,0 +1 @@ + diff --git a/pat/events/streams.go b/pat/events/streams.go deleted file mode 100644 index a04bcbe0c9..0000000000 --- a/pat/events/streams.go +++ /dev/null @@ -1,94 +0,0 @@ -// Copyright (c) Abstract Machines -// SPDX-License-Identifier: Apache-2.0 - -package events - -import ( - "context" - "time" - - "github.com/absmach/magistrala/pat" - "github.com/absmach/magistrala/pkg/authn" - "github.com/absmach/magistrala/pkg/events" - "github.com/absmach/magistrala/pkg/events/store" -) - -const streamID = "magistrala.pat" - -var _ pat.Service = (*eventStore)(nil) - - -type eventStore struct { - events.Publisher - svc pat.Service -} - -// NewEventStoreMiddleware returns wrapper around pat service that sends -// events to event store. -func NewEventStoreMiddleware(ctx context.Context, svc pat.Service, url string) (pat.Service, error) { - publisher, err := store.NewPublisher(ctx, url, streamID) - if err != nil { - return nil, err - } - - return &eventStore{ - svc: svc, - Publisher: publisher, - }, nil -} - -func (es *eventStore) CreatePAT(ctx context.Context, session authn.Session, name, description string, duration time.Duration, scope pat.Scope) (pat.PAT, error) { - return es.svc.CreatePAT(ctx, session, name, description, duration, scope) -} - -func (es *eventStore) UpdatePATName(ctx context.Context, session authn.Session, patID, name string) (pat.PAT, error) { - return es.svc.UpdatePATName(ctx, session, patID, name) -} - -func (es *eventStore) UpdatePATDescription(ctx context.Context, session authn.Session, patID, description string) (pat.PAT, error) { - return es.svc.UpdatePATDescription(ctx, session, patID, description) -} - -func (es *eventStore) RetrievePAT(ctx context.Context, userID string, patID string) (pat.PAT, error) { - return es.svc.RetrievePAT(ctx, userID, patID) -} - -func (es *eventStore) ListPATS(ctx context.Context, session authn.Session, pm pat.PATSPageMeta) (pat.PATSPage, error) { - return es.svc.ListPATS(ctx, session, pm) -} - -func (es *eventStore) DeletePAT(ctx context.Context, session authn.Session, patID string) error { - return es.svc.DeletePAT(ctx, session, patID) -} - -func (es *eventStore) ResetPATSecret(ctx context.Context, session authn.Session, patID string, duration time.Duration) (pat.PAT, error) { - return es.svc.ResetPATSecret(ctx, session, patID, duration) -} - -func (es *eventStore) RevokePATSecret(ctx context.Context, session authn.Session, patID string) error { - return es.svc.RevokePATSecret(ctx, session, patID) -} - -func (es *eventStore) AddPATScopeEntry(ctx context.Context, session authn.Session, patID string, platformEntityType pat.PlatformEntityType, optionalDomainID string, optionalDomainEntityType pat.DomainEntityType, operation pat.OperationType, entityIDs ...string) (pat.Scope, error) { - return es.svc.AddPATScopeEntry(ctx, session, patID, platformEntityType, optionalDomainID, optionalDomainEntityType, operation, entityIDs...) -} - -func (es *eventStore) RemovePATScopeEntry(ctx context.Context, session authn.Session, patID string, platformEntityType pat.PlatformEntityType, optionalDomainID string, optionalDomainEntityType pat.DomainEntityType, operation pat.OperationType, entityIDs ...string) (pat.Scope, error) { - return es.svc.RemovePATScopeEntry(ctx, session, patID, platformEntityType, optionalDomainID, optionalDomainEntityType, operation, entityIDs...) -} - -func (es *eventStore) ClearPATAllScopeEntry(ctx context.Context, session authn.Session, patID string) error { - return es.svc.ClearPATAllScopeEntry(ctx, session, patID) -} - -func (es *eventStore) IdentifyPAT(ctx context.Context, paToken string) (pat.PAT, error) { - return es.svc.IdentifyPAT(ctx, paToken) -} - -func (es *eventStore) AuthorizePAT(ctx context.Context, userID, patID string, platformEntityType pat.PlatformEntityType, optionalDomainID string, optionalDomainEntityType pat.DomainEntityType, operation pat.OperationType, entityIDs ...string) error { - return es.svc.AuthorizePAT(ctx, userID, patID, platformEntityType, optionalDomainID, optionalDomainEntityType, operation, entityIDs...) -} - -func (es *eventStore) CheckPAT(ctx context.Context, userID, patID string, platformEntityType pat.PlatformEntityType, optionalDomainID string, optionalDomainEntityType pat.DomainEntityType, operation pat.OperationType, entityIDs ...string) error { - return es.svc.CheckPAT(ctx, userID, patID, platformEntityType, optionalDomainID, optionalDomainEntityType, operation, entityIDs...) -} \ No newline at end of file diff --git a/pat/middleware/authorization.go b/pat/middleware/authorization.go index cdd27958f1..2f39fb3928 100644 --- a/pat/middleware/authorization.go +++ b/pat/middleware/authorization.go @@ -83,3 +83,4 @@ func (am *authorizationMiddleware) CheckPAT(ctx context.Context, userID, patID s return am.svc.CheckPAT(ctx, userID, patID, platformEntityType, optionalDomainID, optionalDomainEntityType, operation, entityIDs...) } + diff --git a/pat/middleware/logging.go b/pat/middleware/logging.go index b0d096676a..9d1db1a5e6 100644 --- a/pat/middleware/logging.go +++ b/pat/middleware/logging.go @@ -274,3 +274,4 @@ func (lm *loggingMiddleware) CheckPAT(ctx context.Context, userID, patID string, return lm.svc.CheckPAT(ctx, userID, patID, platformEntityType, optionalDomainID, optionalDomainEntityType, operation, entityIDs...) } + diff --git a/pat/middleware/metrics.go b/pat/middleware/metrics.go index d2898f3b29..4064533a18 100644 --- a/pat/middleware/metrics.go +++ b/pat/middleware/metrics.go @@ -141,3 +141,4 @@ func (ms *metricsMiddleware) CheckPAT(ctx context.Context, userID, patID string, return ms.svc.CheckPAT(ctx, userID, patID, platformEntityType, optionalDomainID, optionalDomainEntityType, operation, entityIDs...) } + diff --git a/pat/service.go b/pat/service.go index 571cf6551b..3c5a7d3a67 100644 --- a/pat/service.go +++ b/pat/service.go @@ -258,3 +258,4 @@ func generateRandomString(n int) string { return string(b) } + diff --git a/pat/tracing/tracing.go b/pat/tracing/tracing.go index 71af03bc56..71e35f82cb 100644 --- a/pat/tracing/tracing.go +++ b/pat/tracing/tracing.go @@ -163,3 +163,4 @@ func (tm *tracingMiddleware) CheckPAT(ctx context.Context, userID, patID string, return tm.svc.CheckPAT(ctx, userID, patID, platformEntityType, optionalDomainID, optionalDomainEntityType, operation, entityIDs...) } + From dff4d04f21939329b8d370b88754b63e2a6d4d74 Mon Sep 17 00:00:00 2001 From: nyagamunene Date: Thu, 31 Oct 2024 15:34:56 +0300 Subject: [PATCH 44/72] Update user and things middleware Signed-off-by: nyagamunene --- api/grpc/auth/v1/auth.pb.go | 12 ++ auth/service.go | 9 +- auth/tracing/tracing.go | 2 +- pat/api/endpoint.go | 0 pat/api/http/endpoint.go | 1 - pat/api/http/requests.go | 1 - pat/api/http/responses.go | 1 - pat/api/http/transport.go | 1 - pat/api/requests.go | 0 pat/api/responses.go | 0 pat/api/transport.go | 0 pat/middleware/authorization.go | 86 ---------- pat/middleware/logging.go | 277 ------------------------------ pat/middleware/metrics.go | 144 ---------------- pat/service.go | 261 ---------------------------- pat/tracing/tracing.go | 166 ------------------ pkg/authz/authsvc/authz.go | 9 +- pkg/authz/authz.go | 6 +- users/middleware/authorization.go | 84 ++++----- 19 files changed, 67 insertions(+), 993 deletions(-) delete mode 100644 pat/api/endpoint.go delete mode 100644 pat/api/http/endpoint.go delete mode 100644 pat/api/http/requests.go delete mode 100644 pat/api/http/responses.go delete mode 100644 pat/api/http/transport.go delete mode 100644 pat/api/requests.go delete mode 100644 pat/api/responses.go delete mode 100644 pat/api/transport.go delete mode 100644 pat/middleware/authorization.go delete mode 100644 pat/middleware/logging.go delete mode 100644 pat/middleware/metrics.go delete mode 100644 pat/service.go delete mode 100644 pat/tracing/tracing.go diff --git a/api/grpc/auth/v1/auth.pb.go b/api/grpc/auth/v1/auth.pb.go index 16b2b49395..2d15354ce0 100644 --- a/api/grpc/auth/v1/auth.pb.go +++ b/api/grpc/auth/v1/auth.pb.go @@ -535,6 +535,18 @@ func file_auth_v1_auth_proto_init() { } } file_auth_v1_auth_proto_msgTypes[3].Exporter = func(v any, i int) any { + switch v := v.(*AuthZpatReq); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_auth_v1_auth_proto_msgTypes[4].Exporter = func(v any, i int) any { switch v := v.(*AuthZRes); i { case 0: return &v.state diff --git a/auth/service.go b/auth/service.go index c266ed2fa7..72f7cc8cb0 100644 --- a/auth/service.go +++ b/auth/service.go @@ -529,13 +529,8 @@ 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 { - return PAT{}, err - } - - pat, err := svc.pats.Retrieve(ctx, key.User, patID) +func (svc service) RetrievePAT(ctx context.Context, userID, patID string) (PAT, error) { + pat, err := svc.pats.Retrieve(ctx, userID, patID) if err != nil { return PAT{}, errors.Wrap(errRetrievePAT, err) } diff --git a/auth/tracing/tracing.go b/auth/tracing/tracing.go index 0321eaf3bd..945bd8f4e8 100644 --- a/auth/tracing/tracing.go +++ b/auth/tracing/tracing.go @@ -203,7 +203,7 @@ func (tm *tracingMiddleware) AuthorizePAT(ctx context.Context, userID, patID str 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("pat_id", patID), attribute.String("platform_entity", platformEntityType.String()), attribute.String("optional_domain_id", optionalDomainID), attribute.String("optional_domain_entity", optionalDomainEntityType.String()), diff --git a/pat/api/endpoint.go b/pat/api/endpoint.go deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/pat/api/http/endpoint.go b/pat/api/http/endpoint.go deleted file mode 100644 index 8b13789179..0000000000 --- a/pat/api/http/endpoint.go +++ /dev/null @@ -1 +0,0 @@ - diff --git a/pat/api/http/requests.go b/pat/api/http/requests.go deleted file mode 100644 index 8b13789179..0000000000 --- a/pat/api/http/requests.go +++ /dev/null @@ -1 +0,0 @@ - diff --git a/pat/api/http/responses.go b/pat/api/http/responses.go deleted file mode 100644 index 8b13789179..0000000000 --- a/pat/api/http/responses.go +++ /dev/null @@ -1 +0,0 @@ - diff --git a/pat/api/http/transport.go b/pat/api/http/transport.go deleted file mode 100644 index 8b13789179..0000000000 --- a/pat/api/http/transport.go +++ /dev/null @@ -1 +0,0 @@ - diff --git a/pat/api/requests.go b/pat/api/requests.go deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/pat/api/responses.go b/pat/api/responses.go deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/pat/api/transport.go b/pat/api/transport.go deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/pat/middleware/authorization.go b/pat/middleware/authorization.go deleted file mode 100644 index 2f39fb3928..0000000000 --- a/pat/middleware/authorization.go +++ /dev/null @@ -1,86 +0,0 @@ -// Copyright (c) Abstract Machines -// SPDX-License-Identifier: Apache-2.0 - -package middleware - -import ( - "context" - "time" - - "github.com/absmach/magistrala/pat" - "github.com/absmach/magistrala/pkg/authn" - mgauthz "github.com/absmach/magistrala/pkg/authz" -) - -var _ pat.Service = (*authorizationMiddleware)(nil) - -type authorizationMiddleware struct { - svc pat.Service - authz mgauthz.Authorization -} - -// AuthorizationMiddleware adds authorization to the clients service. -func AuthorizationMiddleware(entityType string, svc pat.Service, authz mgauthz.Authorization) (pat.Service, error) { - return &authorizationMiddleware{ - svc: svc, - authz: authz, - }, nil -} - -func (am *authorizationMiddleware) CreatePAT(ctx context.Context, session authn.Session, name, description string, duration time.Duration, scope pat.Scope) (pat.PAT, error) { - return am.svc.CreatePAT(ctx, session, name, description, duration, scope) -} - -func (am *authorizationMiddleware) UpdatePATName(ctx context.Context, session authn.Session, patID, name string) (pat.PAT, error) { - return am.svc.UpdatePATName(ctx, session, patID, name) -} - -func (am *authorizationMiddleware) UpdatePATDescription(ctx context.Context, session authn.Session, patID, description string) (pat.PAT, error) { - return am.svc.UpdatePATDescription(ctx, session, patID, description) -} - -func (am *authorizationMiddleware) RetrievePAT(ctx context.Context, userID string, patID string) (pat.PAT, error) { - return am.svc.RetrievePAT(ctx, userID, patID) -} - -func (am *authorizationMiddleware) ListPATS(ctx context.Context, session authn.Session, pm pat.PATSPageMeta) (pat.PATSPage, error) { - return am.svc.ListPATS(ctx, session, pm) -} - -func (am *authorizationMiddleware) DeletePAT(ctx context.Context, session authn.Session, patID string) error { - return am.svc.DeletePAT(ctx, session, patID) -} - -func (am *authorizationMiddleware) ResetPATSecret(ctx context.Context, session authn.Session, patID string, duration time.Duration) (pat.PAT, error) { - return am.svc.ResetPATSecret(ctx, session, patID, duration) -} - -func (am *authorizationMiddleware) RevokePATSecret(ctx context.Context, session authn.Session, patID string) error { - return am.svc.RevokePATSecret(ctx, session, patID) -} - -func (am *authorizationMiddleware) AddPATScopeEntry(ctx context.Context, session authn.Session, patID string, platformEntityType pat.PlatformEntityType, optionalDomainID string, optionalDomainEntityType pat.DomainEntityType, operation pat.OperationType, entityIDs ...string) (pat.Scope, error) { - return am.svc.AddPATScopeEntry(ctx, session, patID, platformEntityType, optionalDomainID, optionalDomainEntityType, operation, entityIDs...) -} - -func (am *authorizationMiddleware) RemovePATScopeEntry(ctx context.Context, session authn.Session, patID string, platformEntityType pat.PlatformEntityType, optionalDomainID string, optionalDomainEntityType pat.DomainEntityType, operation pat.OperationType, entityIDs ...string) (pat.Scope, error) { - return am.svc.RemovePATScopeEntry(ctx, session, patID, platformEntityType, optionalDomainID, optionalDomainEntityType, operation, entityIDs...) -} - -func (am *authorizationMiddleware) ClearPATAllScopeEntry(ctx context.Context, session authn.Session, patID string) error { - return am.svc.ClearPATAllScopeEntry(ctx, session, patID) -} - -func (am *authorizationMiddleware) IdentifyPAT(ctx context.Context, secret string) (pat.PAT, error) { - return am.svc.IdentifyPAT(ctx, secret) -} - -func (am *authorizationMiddleware) AuthorizePAT(ctx context.Context, userID, patID string, platformEntityType pat.PlatformEntityType, optionalDomainID string, optionalDomainEntityType pat.DomainEntityType, operation pat.OperationType, entityIDs ...string) error { - return am.svc.AuthorizePAT(ctx, userID, patID, platformEntityType, optionalDomainID, optionalDomainEntityType, operation, entityIDs...) -} - -func (am *authorizationMiddleware) CheckPAT(ctx context.Context, userID, patID string, platformEntityType pat.PlatformEntityType, optionalDomainID string, optionalDomainEntityType pat.DomainEntityType, operation pat.OperationType, entityIDs ...string) error { - return am.svc.CheckPAT(ctx, userID, patID, platformEntityType, optionalDomainID, optionalDomainEntityType, operation, entityIDs...) -} - - diff --git a/pat/middleware/logging.go b/pat/middleware/logging.go deleted file mode 100644 index 9d1db1a5e6..0000000000 --- a/pat/middleware/logging.go +++ /dev/null @@ -1,277 +0,0 @@ -// Copyright (c) Abstract Machines -// SPDX-License-Identifier: Apache-2.0 - -package middleware - -import ( - "context" - "log/slog" - "time" - - "github.com/absmach/magistrala/pat" - "github.com/absmach/magistrala/pkg/authn" -) - -var _ pat.Service = (*loggingMiddleware)(nil) - -type loggingMiddleware struct { - logger *slog.Logger - svc pat.Service -} - -// LoggingMiddleware adds logging facilities to the core service. -func LoggingMiddleware(svc pat.Service, logger *slog.Logger) pat.Service { - return &loggingMiddleware{logger, svc} -} - -func (lm *loggingMiddleware) CreatePAT(ctx context.Context, session authn.Session, name, description string, duration time.Duration, scope pat.Scope) (pa pat.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.CreatePAT(ctx, session, name, description, duration, scope) -} - -func (lm *loggingMiddleware) UpdatePATName(ctx context.Context, session authn.Session, patID, name string) (pa pat.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.UpdatePATName(ctx, session, patID, name) -} - -func (lm *loggingMiddleware) UpdatePATDescription(ctx context.Context, session authn.Session, patID, description string) (pa pat.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.UpdatePATDescription(ctx, session, patID, description) -} - -func (lm *loggingMiddleware) RetrievePAT(ctx context.Context, userID string, patID string) (pa pat.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.RetrievePAT(ctx, userID, patID) -} - -func (lm *loggingMiddleware) ListPATS(ctx context.Context, session authn.Session, pm pat.PATSPageMeta) (pp pat.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.ListPATS(ctx, session, pm) -} - -func (lm *loggingMiddleware) DeletePAT(ctx context.Context, session authn.Session, 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.DeletePAT(ctx, session, patID) -} - -func (lm *loggingMiddleware) ResetPATSecret(ctx context.Context, session authn.Session, patID string, duration time.Duration) (pa pat.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.ResetPATSecret(ctx, session, patID, duration) -} - -func (lm *loggingMiddleware) RevokePATSecret(ctx context.Context, session authn.Session, 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.RevokePATSecret(ctx, session, patID) -} - -func (lm *loggingMiddleware) AddPATScopeEntry(ctx context.Context, session authn.Session, patID string, platformEntityType pat.PlatformEntityType, optionalDomainID string, optionalDomainEntityType pat.DomainEntityType, operation pat.OperationType, entityIDs ...string) (sc pat.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.AddPATScopeEntry(ctx, session, patID, platformEntityType, optionalDomainID, optionalDomainEntityType, operation, entityIDs...) -} - -func (lm *loggingMiddleware) RemovePATScopeEntry(ctx context.Context, session authn.Session, patID string, platformEntityType pat.PlatformEntityType, optionalDomainID string, optionalDomainEntityType pat.DomainEntityType, operation pat.OperationType, entityIDs ...string) (sc pat.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.RemovePATScopeEntry(ctx, session, patID, platformEntityType, optionalDomainID, optionalDomainEntityType, operation, entityIDs...) -} - -func (lm *loggingMiddleware) ClearPATAllScopeEntry(ctx context.Context, session authn.Session, 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.ClearPATAllScopeEntry(ctx, session, patID) -} - -func (lm *loggingMiddleware) IdentifyPAT(ctx context.Context, paToken string) (pa pat.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, userID, patID string, platformEntityType pat.PlatformEntityType, optionalDomainID string, optionalDomainEntityType pat.DomainEntityType, operation pat.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("Authorize PAT failed complete successfully", args...) - return - } - lm.logger.Info("Authorize PAT completed successfully", args...) - }(time.Now()) - return lm.svc.AuthorizePAT(ctx, userID, patID, platformEntityType, optionalDomainID, optionalDomainEntityType, operation, entityIDs...) -} - -func (lm *loggingMiddleware) CheckPAT(ctx context.Context, userID, patID string, platformEntityType pat.PlatformEntityType, optionalDomainID string, optionalDomainEntityType pat.DomainEntityType, operation pat.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("Check PAT failed complete successfully", args...) - return - } - lm.logger.Info("Check PAT completed successfully", args...) - }(time.Now()) - return lm.svc.CheckPAT(ctx, userID, patID, platformEntityType, optionalDomainID, optionalDomainEntityType, operation, entityIDs...) -} - - diff --git a/pat/middleware/metrics.go b/pat/middleware/metrics.go deleted file mode 100644 index 4064533a18..0000000000 --- a/pat/middleware/metrics.go +++ /dev/null @@ -1,144 +0,0 @@ -// Copyright (c) Abstract Machines -// SPDX-License-Identifier: Apache-2.0 - -package middleware - -import ( - "context" - "time" - - "github.com/absmach/magistrala/pat" - "github.com/absmach/magistrala/pkg/authn" - "github.com/go-kit/kit/metrics" -) - -var _ pat.Service = (*metricsMiddleware)(nil) - -type metricsMiddleware struct { - counter metrics.Counter - latency metrics.Histogram - svc pat.Service -} - -// MetricsMiddleware instruments core service by tracking request count and latency. -func MetricsMiddleware(svc pat.Service, counter metrics.Counter, latency metrics.Histogram) pat.Service { - return &metricsMiddleware{ - counter: counter, - latency: latency, - svc: svc, - } -} - -func (ms *metricsMiddleware) CreatePAT(ctx context.Context, session authn.Session, name, description string, duration time.Duration, scope pat.Scope) (pat.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.CreatePAT(ctx, session, name, description, duration, scope) -} - -func (ms *metricsMiddleware) UpdatePATName(ctx context.Context, session authn.Session, patID, name string) (pat.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.UpdatePATName(ctx, session, patID, name) -} - -func (ms *metricsMiddleware) UpdatePATDescription(ctx context.Context, session authn.Session, patID, description string) (pat.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.UpdatePATDescription(ctx, session, patID, description) -} - -func (ms *metricsMiddleware) RetrievePAT(ctx context.Context, userID string, patID string) (pat.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.RetrievePAT(ctx, userID, patID) -} - -func (ms *metricsMiddleware) ListPATS(ctx context.Context, session authn.Session, pm pat.PATSPageMeta) (pat.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.ListPATS(ctx, session, pm) -} - -func (ms *metricsMiddleware) DeletePAT(ctx context.Context, session authn.Session, 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.DeletePAT(ctx, session, patID) -} - -func (ms *metricsMiddleware) ResetPATSecret(ctx context.Context, session authn.Session, patID string, duration time.Duration) (pat.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.ResetPATSecret(ctx, session, patID, duration) -} - -func (ms *metricsMiddleware) RevokePATSecret(ctx context.Context, session authn.Session, 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.RevokePATSecret(ctx, session, patID) -} - -func (ms *metricsMiddleware) AddPATScopeEntry(ctx context.Context, session authn.Session, patID string, platformEntityType pat.PlatformEntityType, optionalDomainID string, optionalDomainEntityType pat.DomainEntityType, operation pat.OperationType, entityIDs ...string) (pat.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.AddPATScopeEntry(ctx, session, patID, platformEntityType, optionalDomainID, optionalDomainEntityType, operation, entityIDs...) -} - -func (ms *metricsMiddleware) RemovePATScopeEntry(ctx context.Context, session authn.Session, patID string, platformEntityType pat.PlatformEntityType, optionalDomainID string, optionalDomainEntityType pat.DomainEntityType, operation pat.OperationType, entityIDs ...string) (pat.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.RemovePATScopeEntry(ctx, session, patID, platformEntityType, optionalDomainID, optionalDomainEntityType, operation, entityIDs...) -} - -func (ms *metricsMiddleware) ClearPATAllScopeEntry(ctx context.Context, session authn.Session, 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.ClearPATAllScopeEntry(ctx, session, patID) -} - -func (ms *metricsMiddleware) IdentifyPAT(ctx context.Context, paToken string) (pat.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, userID, patID string, platformEntityType pat.PlatformEntityType, optionalDomainID string, optionalDomainEntityType pat.DomainEntityType, operation pat.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, userID, patID, platformEntityType, optionalDomainID, optionalDomainEntityType, operation, entityIDs...) -} - -func (ms *metricsMiddleware) CheckPAT(ctx context.Context, userID, patID string, platformEntityType pat.PlatformEntityType, optionalDomainID string, optionalDomainEntityType pat.DomainEntityType, operation pat.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/pat/service.go b/pat/service.go deleted file mode 100644 index 3c5a7d3a67..0000000000 --- a/pat/service.go +++ /dev/null @@ -1,261 +0,0 @@ -// Copyright (c) Abstract Machines -// SPDX-License-Identifier: Apache-2.0 - -package pat - -import ( - "context" - "encoding/base64" - "math/rand" - "strings" - "time" - - "github.com/absmach/magistrala" - "github.com/absmach/magistrala/pkg/authn" - "github.com/absmach/magistrala/pkg/errors" - svcerr "github.com/absmach/magistrala/pkg/errors/service" - "github.com/google/uuid" -) - -const ( - randStr = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890!@#$%^&&*|+-=" - patPrefix = "pat" - patSecretSeparator = "_" -) - -var ( - 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") - 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") - errClearAllScope = errors.New("failed to clear all entry in scope") -) - -type service struct { - pats PATSRepository - hasher Hasher - idProvider magistrala.IDProvider -} - -var _ Service = (*service)(nil) - -// New instantiates the auth service implementation. -func New(pats PATSRepository, hasher Hasher, idp magistrala.IDProvider) Service { - return &service{ - pats: pats, - hasher: hasher, - idProvider: idp, - } -} - -func (svc service) CreatePAT(ctx context.Context, session authn.Session, name, description string, duration time.Duration, scope Scope) (PAT, error) { - id, err := svc.idProvider.ID() - if err != nil { - return PAT{}, errors.Wrap(svcerr.ErrCreateEntity, err) - } - secret, hash, err := svc.generateSecretAndHash(session.UserID, id) - if err != nil { - return PAT{}, errors.Wrap(svcerr.ErrCreateEntity, err) - } - - now := time.Now() - pat := PAT{ - ID: id, - User: session.UserID, - 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 -} - -func (svc service) UpdatePATName(ctx context.Context, session authn.Session, patID, name string) (PAT, error) { - pat, err := svc.pats.UpdateName(ctx, session.UserID, patID, name) - if err != nil { - return PAT{}, errors.Wrap(errUpdatePAT, err) - } - return pat, nil -} - -func (svc service) UpdatePATDescription(ctx context.Context, session authn.Session, patID, description string) (PAT, error) { - pat, err := svc.pats.UpdateDescription(ctx, session.UserID, patID, description) - if err != nil { - return PAT{}, errors.Wrap(errUpdatePAT, err) - } - return pat, nil -} - -func (svc service) RetrievePAT(ctx context.Context, userID string, patID string) (PAT, error) { - pat, err := svc.pats.Retrieve(ctx, userID, patID) - if err != nil { - return PAT{}, errors.Wrap(errRetrievePAT, err) - } - return pat, nil -} - -func (svc service) ListPATS(ctx context.Context, session authn.Session, pm PATSPageMeta) (PATSPage, error) { - patsPage, err := svc.pats.RetrieveAll(ctx, session.UserID, pm) - if err != nil { - return PATSPage{}, errors.Wrap(errRetrievePAT, err) - } - return patsPage, nil -} - -func (svc service) DeletePAT(ctx context.Context, session authn.Session, patID string) error { - if err := svc.pats.Remove(ctx, session.UserID, patID); err != nil { - return errors.Wrap(errDeletePAT, err) - } - return nil -} - -func (svc service) ResetPATSecret(ctx context.Context, session authn.Session, patID string, duration time.Duration) (PAT, error) { - // Generate new HashToken take place here - secret, hash, err := svc.generateSecretAndHash(session.UserID, patID) - if err != nil { - return PAT{}, errors.Wrap(svcerr.ErrUpdateEntity, err) - } - - pat, err := svc.pats.UpdateTokenHash(ctx, session.UserID, patID, hash, time.Now().Add(duration)) - if err != nil { - return PAT{}, errors.Wrap(svcerr.ErrUpdateEntity, err) - } - - if err := svc.pats.Reactivate(ctx, session.UserID, patID); err != nil { - return PAT{}, errors.Wrap(svcerr.ErrUpdateEntity, err) - } - pat.Secret = secret - pat.Revoked = false - pat.RevokedAt = time.Time{} - return pat, nil -} - -func (svc service) RevokePATSecret(ctx context.Context, session authn.Session, patID string) error { - if err := svc.pats.Revoke(ctx, session.UserID, patID); err != nil { - return errors.Wrap(errRevokePAT, err) - } - return nil -} - -func (svc service) AddPATScopeEntry(ctx context.Context, session authn.Session, patID string, platformEntityType PlatformEntityType, optionalDomainID string, optionalDomainEntityType DomainEntityType, operation OperationType, entityIDs ...string) (Scope, error) { - scope, err := svc.pats.AddScopeEntry(ctx, session.UserID, 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, session authn.Session, patID string, platformEntityType PlatformEntityType, optionalDomainID string, optionalDomainEntityType DomainEntityType, operation OperationType, entityIDs ...string) (Scope, error) { - scope, err := svc.pats.RemoveScopeEntry(ctx, session.UserID, patID, platformEntityType, optionalDomainID, optionalDomainEntityType, operation, entityIDs...) - if err != nil { - return Scope{}, err - } - return scope, nil -} - -func (svc service) ClearPATAllScopeEntry(ctx context.Context, session authn.Session, patID string) error { - if err := svc.pats.RemoveAllScopeEntry(ctx, session.UserID, patID); err != nil { - return errors.Wrap(errClearAllScope, err) - } - return nil -} - -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, userID, patID string, platformEntityType PlatformEntityType, optionalDomainID string, optionalDomainEntityType DomainEntityType, operation OperationType, entityIDs ...string) error { - res, err := svc.RetrievePAT(ctx, userID, patID) - if 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) 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 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/pat/tracing/tracing.go b/pat/tracing/tracing.go deleted file mode 100644 index 71e35f82cb..0000000000 --- a/pat/tracing/tracing.go +++ /dev/null @@ -1,166 +0,0 @@ -// Copyright (c) Abstract Machines -// SPDX-License-Identifier: Apache-2.0 - -package tracing - -import ( - "context" - "time" - - "github.com/absmach/magistrala/pat" - "github.com/absmach/magistrala/pkg/authn" - "go.opentelemetry.io/otel/attribute" - "go.opentelemetry.io/otel/trace" -) - -var _ pat.Service = (*tracingMiddleware)(nil) - -type tracingMiddleware struct { - tracer trace.Tracer - svc pat.Service -} - -// New returns a new group service with tracing capabilities. -func New(svc pat.Service, tracer trace.Tracer) pat.Service { - return &tracingMiddleware{tracer, svc} -} - -func (tm *tracingMiddleware) CreatePAT(ctx context.Context, session authn.Session, name, description string, duration time.Duration, scope pat.Scope) (pat.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.CreatePAT(ctx, session, name, description, duration, scope) -} - -func (tm *tracingMiddleware) UpdatePATName(ctx context.Context, session authn.Session, patID, name string) (pat.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.UpdatePATName(ctx, session, patID, name) -} - -func (tm *tracingMiddleware) UpdatePATDescription(ctx context.Context, session authn.Session, patID, description string) (pat.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.UpdatePATDescription(ctx, session, patID, description) -} - -func (tm *tracingMiddleware) RetrievePAT(ctx context.Context, userID string, patID string) (pat.PAT, error) { - ctx, span := tm.tracer.Start(ctx, "retrieve_pat", trace.WithAttributes( - attribute.String("pat_id", patID), - )) - defer span.End() - return tm.svc.RetrievePAT(ctx, userID, patID) -} - -func (tm *tracingMiddleware) ListPATS(ctx context.Context, session authn.Session, pm pat.PATSPageMeta) (pat.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.ListPATS(ctx, session, pm) -} - -func (tm *tracingMiddleware) DeletePAT(ctx context.Context, session authn.Session, patID string) error { - ctx, span := tm.tracer.Start(ctx, "delete_pat", trace.WithAttributes( - attribute.String("pat_id", patID), - )) - defer span.End() - return tm.svc.DeletePAT(ctx, session, patID) -} - -func (tm *tracingMiddleware) ResetPATSecret(ctx context.Context, session authn.Session, patID string, duration time.Duration) (pat.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.ResetPATSecret(ctx, session, patID, duration) -} - -func (tm *tracingMiddleware) RevokePATSecret(ctx context.Context, session authn.Session, 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.RevokePATSecret(ctx, session, patID) -} - -func (tm *tracingMiddleware) AddPATScopeEntry(ctx context.Context, session authn.Session, patID string, platformEntityType pat.PlatformEntityType, optionalDomainID string, optionalDomainEntityType pat.DomainEntityType, operation pat.OperationType, entityIDs ...string) (pat.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), - attribute.String("optional_domain_entity", optionalDomainEntityType.String()), - attribute.String("operation", operation.String()), - attribute.StringSlice("entities", entityIDs), - )) - defer span.End() - return tm.svc.AddPATScopeEntry(ctx, session, patID, platformEntityType, optionalDomainID, optionalDomainEntityType, operation, entityIDs...) -} - -func (tm *tracingMiddleware) RemovePATScopeEntry(ctx context.Context, session authn.Session, patID string, platformEntityType pat.PlatformEntityType, optionalDomainID string, optionalDomainEntityType pat.DomainEntityType, operation pat.OperationType, entityIDs ...string) (pat.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), - attribute.String("optional_domain_entity", optionalDomainEntityType.String()), - attribute.String("operation", operation.String()), - attribute.StringSlice("entities", entityIDs), - )) - defer span.End() - return tm.svc.RemovePATScopeEntry(ctx, session, patID, platformEntityType, optionalDomainID, optionalDomainEntityType, operation, entityIDs...) -} - -func (tm *tracingMiddleware) ClearPATAllScopeEntry(ctx context.Context, session authn.Session, 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.ClearPATAllScopeEntry(ctx, session, patID) -} - -func (tm *tracingMiddleware) IdentifyPAT(ctx context.Context, paToken string) (pat.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, userID, patID string, platformEntityType pat.PlatformEntityType, optionalDomainID string, optionalDomainEntityType pat.DomainEntityType, operation pat.OperationType, entityIDs ...string) error { - ctx, span := tm.tracer.Start(ctx, "authorize_pat", 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.AuthorizePAT(ctx, userID, patID, platformEntityType, optionalDomainID, optionalDomainEntityType, operation, entityIDs...) -} - -func (tm *tracingMiddleware) CheckPAT(ctx context.Context, userID, patID string, platformEntityType pat.PlatformEntityType, optionalDomainID string, optionalDomainEntityType pat.DomainEntityType, operation pat.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.CheckPAT(ctx, userID, patID, platformEntityType, optionalDomainID, optionalDomainEntityType, operation, entityIDs...) -} - - diff --git a/pkg/authz/authsvc/authz.go b/pkg/authz/authsvc/authz.go index 01ad6ee8a4..4fa6f49688 100644 --- a/pkg/authz/authsvc/authz.go +++ b/pkg/authz/authsvc/authz.go @@ -124,11 +124,12 @@ func (a authorization) checkDomain(ctx context.Context, subjectType, subject, do func (a authorization) AuthorizePAT(ctx context.Context, pr authz.PatReq) error { req := grpcAuthV1.AuthZpatReq{ - PaToken: pr.PaToken, - PlatformEntityType: pr.PlatformEntityType, + UserID: pr.UserID, + PatID: pr.PatID, + PlatformEntityType: uint32(pr.PlatformEntityType), OptionalDomainID: pr.OptionalDomainID, - OptionalDomainEntityType: pr.OptionalDomainEntityType, - Operation: pr.Operation, + OptionalDomainEntityType: uint32(pr.OptionalDomainEntityType), + Operation: uint32(pr.Operation), EntityIDs: pr.EntityIDs, } res, err := a.authSvcClient.AuthorizePAT(ctx, &req) diff --git a/pkg/authz/authz.go b/pkg/authz/authz.go index 06ceb7e03d..79173b8740 100644 --- a/pkg/authz/authz.go +++ b/pkg/authz/authz.go @@ -3,7 +3,11 @@ package authz -import "context" +import ( + "context" + + "github.com/absmach/magistrala/auth" +) type PolicyReq struct { // Domain contains the domain ID. diff --git a/users/middleware/authorization.go b/users/middleware/authorization.go index faf3f3c746..a65584f65c 100644 --- a/users/middleware/authorization.go +++ b/users/middleware/authorization.go @@ -48,9 +48,9 @@ func (am *authorizationMiddleware) View(ctx context.Context, session authn.Sessi if err := am.authz.AuthorizePAT(ctx, mgauthz.PatReq{ UserID: session.UserID, PatID: session.ID, - PlatformEntityType: pat.PlatformUsersScope.String(), - OptionalDomainEntityType: pat.DomainNullScope.String(), - Operation: pat.ReadOp.String(), + PlatformEntityType: mgauth.PlatformUsersScope, + OptionalDomainEntityType: mgauth.DomainNullScope, + Operation: mgauth.ReadOp, }); err != nil { return users.User{}, errors.Wrap(err, svcerr.ErrUnauthorizedPAT) } @@ -68,9 +68,9 @@ func (am *authorizationMiddleware) ViewProfile(ctx context.Context, session auth if err := am.authz.AuthorizePAT(ctx, mgauthz.PatReq{ UserID: session.UserID, PatID: session.ID, - PlatformEntityType: pat.PlatformUsersScope.String(), - OptionalDomainEntityType: pat.DomainNullScope.String(), - Operation: pat.ReadOp.String(), + PlatformEntityType: mgauth.PlatformUsersScope, + OptionalDomainEntityType: mgauth.DomainNullScope, + Operation: mgauth.ReadOp, }); err != nil { return users.User{}, errors.Wrap(err, svcerr.ErrUnauthorizedPAT) } @@ -83,9 +83,9 @@ func (am *authorizationMiddleware) ListUsers(ctx context.Context, session authn. if err := am.authz.AuthorizePAT(ctx, mgauthz.PatReq{ UserID: session.UserID, PatID: session.ID, - PlatformEntityType: pat.PlatformUsersScope.String(), - OptionalDomainEntityType: pat.DomainNullScope.String(), - Operation: pat.ListOp.String(), + PlatformEntityType: mgauth.PlatformUsersScope, + OptionalDomainEntityType: mgauth.DomainNullScope, + Operation: mgauth.ListOp, }); err != nil { return users.UsersPage{}, errors.Wrap(err, svcerr.ErrUnauthorizedPAT) } @@ -102,9 +102,9 @@ func (am *authorizationMiddleware) ListMembers(ctx context.Context, session auth if err := am.authz.AuthorizePAT(ctx, mgauthz.PatReq{ UserID: session.UserID, PatID: session.ID, - PlatformEntityType: pat.PlatformUsersScope.String(), - OptionalDomainEntityType: pat.DomainNullScope.String(), - Operation: pat.ListOp.String(), + PlatformEntityType: mgauth.PlatformUsersScope, + OptionalDomainEntityType: mgauth.DomainNullScope, + Operation: mgauth.ListOp, }); err != nil { return users.MembersPage{}, errors.Wrap(err, svcerr.ErrUnauthorizedPAT) } @@ -146,9 +146,9 @@ func (am *authorizationMiddleware) Update(ctx context.Context, session authn.Ses if err := am.authz.AuthorizePAT(ctx, mgauthz.PatReq{ UserID: session.UserID, PatID: session.ID, - PlatformEntityType: pat.PlatformUsersScope.String(), - OptionalDomainEntityType: pat.DomainNullScope.String(), - Operation: pat.UpdateOp.String(), + PlatformEntityType: mgauth.PlatformUsersScope, + OptionalDomainEntityType: mgauth.DomainNullScope, + Operation: mgauth.UpdateOp, }); err != nil { return users.User{}, errors.Wrap(err, svcerr.ErrUnauthorizedPAT) } @@ -166,9 +166,9 @@ func (am *authorizationMiddleware) UpdateTags(ctx context.Context, session authn if err := am.authz.AuthorizePAT(ctx, mgauthz.PatReq{ UserID: session.UserID, PatID: session.ID, - PlatformEntityType: pat.PlatformUsersScope.String(), - OptionalDomainEntityType: pat.DomainNullScope.String(), - Operation: pat.UpdateOp.String(), + PlatformEntityType: mgauth.PlatformUsersScope, + OptionalDomainEntityType: mgauth.DomainNullScope, + Operation: mgauth.UpdateOp, }); err != nil { return users.User{}, errors.Wrap(err, svcerr.ErrUnauthorizedPAT) } @@ -182,9 +182,9 @@ func (am *authorizationMiddleware) UpdateEmail(ctx context.Context, session auth if err := am.authz.AuthorizePAT(ctx, mgauthz.PatReq{ UserID: session.UserID, PatID: session.ID, - PlatformEntityType: pat.PlatformUsersScope.String(), - OptionalDomainEntityType: pat.DomainNullScope.String(), - Operation: pat.UpdateOp.String(), + PlatformEntityType: mgauth.PlatformUsersScope, + OptionalDomainEntityType: mgauth.DomainNullScope, + Operation: mgauth.UpdateOp, }); err != nil { return users.User{}, errors.Wrap(err, svcerr.ErrUnauthorizedPAT) } @@ -205,9 +205,9 @@ func (am *authorizationMiddleware) UpdateUsername(ctx context.Context, session a if err := am.authz.AuthorizePAT(ctx, mgauthz.PatReq{ UserID: session.UserID, PatID: session.ID, - PlatformEntityType: pat.PlatformUsersScope.String(), - OptionalDomainEntityType: pat.DomainNullScope.String(), - Operation: pat.UpdateOp.String(), + PlatformEntityType: mgauth.PlatformUsersScope, + OptionalDomainEntityType: mgauth.DomainNullScope, + Operation: mgauth.UpdateOp, }); err != nil { return users.User{}, errors.Wrap(err, svcerr.ErrUnauthorizedPAT) } @@ -225,9 +225,9 @@ func (am *authorizationMiddleware) UpdateProfilePicture(ctx context.Context, ses if err := am.authz.AuthorizePAT(ctx, mgauthz.PatReq{ UserID: session.UserID, PatID: session.ID, - PlatformEntityType: pat.PlatformUsersScope.String(), - OptionalDomainEntityType: pat.DomainNullScope.String(), - Operation: pat.UpdateOp.String(), + PlatformEntityType: mgauth.PlatformUsersScope, + OptionalDomainEntityType: mgauth.DomainNullScope, + Operation: mgauth.UpdateOp, }); err != nil { return users.User{}, errors.Wrap(err, svcerr.ErrUnauthorizedPAT) } @@ -245,9 +245,9 @@ func (am *authorizationMiddleware) UpdateSecret(ctx context.Context, session aut if err := am.authz.AuthorizePAT(ctx, mgauthz.PatReq{ UserID: session.UserID, PatID: session.ID, - PlatformEntityType: pat.PlatformUsersScope.String(), - OptionalDomainEntityType: pat.DomainNullScope.String(), - Operation: pat.UpdateOp.String(), + PlatformEntityType: mgauth.PlatformUsersScope, + OptionalDomainEntityType: mgauth.DomainNullScope, + Operation: mgauth.UpdateOp, }); err != nil { return users.User{}, errors.Wrap(err, svcerr.ErrUnauthorizedPAT) } @@ -272,9 +272,9 @@ func (am *authorizationMiddleware) UpdateRole(ctx context.Context, session authn if err := am.authz.AuthorizePAT(ctx, mgauthz.PatReq{ UserID: session.UserID, PatID: session.ID, - PlatformEntityType: pat.PlatformUsersScope.String(), - OptionalDomainEntityType: pat.DomainNullScope.String(), - Operation: pat.UpdateOp.String(), + PlatformEntityType: mgauth.PlatformUsersScope, + OptionalDomainEntityType: mgauth.DomainNullScope, + Operation: mgauth.UpdateOp, }); err != nil { return users.User{}, errors.Wrap(err, svcerr.ErrUnauthorizedPAT) } @@ -296,9 +296,9 @@ func (am *authorizationMiddleware) Enable(ctx context.Context, session authn.Ses if err := am.authz.AuthorizePAT(ctx, mgauthz.PatReq{ UserID: session.UserID, PatID: session.ID, - PlatformEntityType: pat.PlatformUsersScope.String(), - OptionalDomainEntityType: pat.DomainNullScope.String(), - Operation: pat.UpdateOp.String(), + PlatformEntityType: mgauth.PlatformUsersScope, + OptionalDomainEntityType: mgauth.DomainNullScope, + Operation: mgauth.UpdateOp, }); err != nil { return users.User{}, errors.Wrap(err, svcerr.ErrUnauthorizedPAT) } @@ -316,9 +316,9 @@ func (am *authorizationMiddleware) Disable(ctx context.Context, session authn.Se if err := am.authz.AuthorizePAT(ctx, mgauthz.PatReq{ UserID: session.UserID, PatID: session.ID, - PlatformEntityType: pat.PlatformUsersScope.String(), - OptionalDomainEntityType: pat.DomainNullScope.String(), - Operation: pat.UpdateOp.String(), + PlatformEntityType: mgauth.PlatformUsersScope, + OptionalDomainEntityType: mgauth.DomainNullScope, + Operation: mgauth.UpdateOp, }); err != nil { return users.User{}, errors.Wrap(err, svcerr.ErrUnauthorizedPAT) } @@ -336,9 +336,9 @@ func (am *authorizationMiddleware) Delete(ctx context.Context, session authn.Ses if err := am.authz.AuthorizePAT(ctx, mgauthz.PatReq{ UserID: session.UserID, PatID: session.ID, - PlatformEntityType: pat.PlatformUsersScope.String(), - OptionalDomainEntityType: pat.DomainNullScope.String(), - Operation: pat.DeleteOp.String(), + PlatformEntityType: mgauth.PlatformUsersScope, + OptionalDomainEntityType: mgauth.DomainNullScope, + Operation: mgauth.DeleteOp, }); err != nil { return errors.Wrap(err, svcerr.ErrUnauthorizedPAT) } From d9d642a6120c3f915e600330b5b329f2cddf02d4 Mon Sep 17 00:00:00 2001 From: nyagamunene Date: Sun, 3 Nov 2024 20:03:02 +0300 Subject: [PATCH 45/72] Authorize and Authenticate method Signed-off-by: nyagamunene --- api/http/common.go | 1 + auth/api/grpc/auth/endpoint.go | 2 +- auth/api/http/pats/endpoint.go | 7 ++++++- users/middleware/authorization.go | 18 ++++++++++++++++++ 4 files changed, 26 insertions(+), 2 deletions(-) diff --git a/api/http/common.go b/api/http/common.go index 36210d4b5e..802c872e63 100644 --- a/api/http/common.go +++ b/api/http/common.go @@ -127,6 +127,7 @@ func EncodeError(_ context.Context, err error, w http.ResponseWriter) { switch { case errors.Contains(err, svcerr.ErrAuthorization), errors.Contains(err, svcerr.ErrDomainAuthorization), + errors.Contains(err, svcerr.ErrUnauthorizedPAT), errors.Contains(err, bootstrap.ErrExternalKey), errors.Contains(err, bootstrap.ErrExternalKeySecure): err = unwrap(err) diff --git a/auth/api/grpc/auth/endpoint.go b/auth/api/grpc/auth/endpoint.go index 05516b64e6..d665f64153 100644 --- a/auth/api/grpc/auth/endpoint.go +++ b/auth/api/grpc/auth/endpoint.go @@ -23,7 +23,7 @@ func authenticateEndpoint(svc auth.Service) endpoint.Endpoint { return authenticateRes{}, err } - return authenticateRes{id: key.Subject, userID: key.User, domainID: key.Domain}, nil + return authenticateRes{id: key.ID, userID: key.User, domainID: key.Domain}, nil } } diff --git a/auth/api/http/pats/endpoint.go b/auth/api/http/pats/endpoint.go index 45e6b3c607..321eb76bca 100644 --- a/auth/api/http/pats/endpoint.go +++ b/auth/api/http/pats/endpoint.go @@ -33,7 +33,12 @@ func retrievePATEndpoint(svc auth.Service) endpoint.Endpoint { return nil, err } - pat, err := svc.RetrievePAT(ctx, req.token, req.id) + res, err := svc.Identify(ctx, req.token) + if err != nil { + return nil, err + } + + pat, err := svc.RetrievePAT(ctx, res.User, req.id) if err != nil { return nil, err } diff --git a/users/middleware/authorization.go b/users/middleware/authorization.go index a65584f65c..0f9c1c702a 100644 --- a/users/middleware/authorization.go +++ b/users/middleware/authorization.go @@ -16,6 +16,10 @@ import ( "github.com/absmach/supermq/users" ) +var ( + anyEntity = []string{"*"} +) + var _ users.Service = (*authorizationMiddleware)(nil) type authorizationMiddleware struct { @@ -51,6 +55,7 @@ func (am *authorizationMiddleware) View(ctx context.Context, session authn.Sessi PlatformEntityType: mgauth.PlatformUsersScope, OptionalDomainEntityType: mgauth.DomainNullScope, Operation: mgauth.ReadOp, + EntityIDs: []string{id}, }); err != nil { return users.User{}, errors.Wrap(err, svcerr.ErrUnauthorizedPAT) } @@ -71,6 +76,7 @@ func (am *authorizationMiddleware) ViewProfile(ctx context.Context, session auth PlatformEntityType: mgauth.PlatformUsersScope, OptionalDomainEntityType: mgauth.DomainNullScope, Operation: mgauth.ReadOp, + EntityIDs: []string{session.UserID}, }); err != nil { return users.User{}, errors.Wrap(err, svcerr.ErrUnauthorizedPAT) } @@ -86,6 +92,7 @@ func (am *authorizationMiddleware) ListUsers(ctx context.Context, session authn. PlatformEntityType: mgauth.PlatformUsersScope, OptionalDomainEntityType: mgauth.DomainNullScope, Operation: mgauth.ListOp, + EntityIDs: anyEntity, }); err != nil { return users.UsersPage{}, errors.Wrap(err, svcerr.ErrUnauthorizedPAT) } @@ -105,6 +112,7 @@ func (am *authorizationMiddleware) ListMembers(ctx context.Context, session auth PlatformEntityType: mgauth.PlatformUsersScope, OptionalDomainEntityType: mgauth.DomainNullScope, Operation: mgauth.ListOp, + EntityIDs: anyEntity, }); err != nil { return users.MembersPage{}, errors.Wrap(err, svcerr.ErrUnauthorizedPAT) } @@ -149,6 +157,7 @@ func (am *authorizationMiddleware) Update(ctx context.Context, session authn.Ses PlatformEntityType: mgauth.PlatformUsersScope, OptionalDomainEntityType: mgauth.DomainNullScope, Operation: mgauth.UpdateOp, + EntityIDs: []string{user.ID}, }); err != nil { return users.User{}, errors.Wrap(err, svcerr.ErrUnauthorizedPAT) } @@ -169,6 +178,7 @@ func (am *authorizationMiddleware) UpdateTags(ctx context.Context, session authn PlatformEntityType: mgauth.PlatformUsersScope, OptionalDomainEntityType: mgauth.DomainNullScope, Operation: mgauth.UpdateOp, + EntityIDs: []string{user.ID}, }); err != nil { return users.User{}, errors.Wrap(err, svcerr.ErrUnauthorizedPAT) } @@ -185,6 +195,7 @@ func (am *authorizationMiddleware) UpdateEmail(ctx context.Context, session auth PlatformEntityType: mgauth.PlatformUsersScope, OptionalDomainEntityType: mgauth.DomainNullScope, Operation: mgauth.UpdateOp, + EntityIDs: []string{id}, }); err != nil { return users.User{}, errors.Wrap(err, svcerr.ErrUnauthorizedPAT) } @@ -208,6 +219,7 @@ func (am *authorizationMiddleware) UpdateUsername(ctx context.Context, session a PlatformEntityType: mgauth.PlatformUsersScope, OptionalDomainEntityType: mgauth.DomainNullScope, Operation: mgauth.UpdateOp, + EntityIDs: []string{id}, }); err != nil { return users.User{}, errors.Wrap(err, svcerr.ErrUnauthorizedPAT) } @@ -228,6 +240,7 @@ func (am *authorizationMiddleware) UpdateProfilePicture(ctx context.Context, ses PlatformEntityType: mgauth.PlatformUsersScope, OptionalDomainEntityType: mgauth.DomainNullScope, Operation: mgauth.UpdateOp, + EntityIDs: []string{user.ID}, }); err != nil { return users.User{}, errors.Wrap(err, svcerr.ErrUnauthorizedPAT) } @@ -248,6 +261,7 @@ func (am *authorizationMiddleware) UpdateSecret(ctx context.Context, session aut PlatformEntityType: mgauth.PlatformUsersScope, OptionalDomainEntityType: mgauth.DomainNullScope, Operation: mgauth.UpdateOp, + EntityIDs: []string{session.UserID}, }); err != nil { return users.User{}, errors.Wrap(err, svcerr.ErrUnauthorizedPAT) } @@ -275,6 +289,7 @@ func (am *authorizationMiddleware) UpdateRole(ctx context.Context, session authn PlatformEntityType: mgauth.PlatformUsersScope, OptionalDomainEntityType: mgauth.DomainNullScope, Operation: mgauth.UpdateOp, + EntityIDs: []string{user.ID}, }); err != nil { return users.User{}, errors.Wrap(err, svcerr.ErrUnauthorizedPAT) } @@ -299,6 +314,7 @@ func (am *authorizationMiddleware) Enable(ctx context.Context, session authn.Ses PlatformEntityType: mgauth.PlatformUsersScope, OptionalDomainEntityType: mgauth.DomainNullScope, Operation: mgauth.UpdateOp, + EntityIDs: []string{id}, }); err != nil { return users.User{}, errors.Wrap(err, svcerr.ErrUnauthorizedPAT) } @@ -319,6 +335,7 @@ func (am *authorizationMiddleware) Disable(ctx context.Context, session authn.Se PlatformEntityType: mgauth.PlatformUsersScope, OptionalDomainEntityType: mgauth.DomainNullScope, Operation: mgauth.UpdateOp, + EntityIDs: []string{id}, }); err != nil { return users.User{}, errors.Wrap(err, svcerr.ErrUnauthorizedPAT) } @@ -339,6 +356,7 @@ func (am *authorizationMiddleware) Delete(ctx context.Context, session authn.Ses PlatformEntityType: mgauth.PlatformUsersScope, OptionalDomainEntityType: mgauth.DomainNullScope, Operation: mgauth.DeleteOp, + EntityIDs: []string{id}, }); err != nil { return errors.Wrap(err, svcerr.ErrUnauthorizedPAT) } From 95801ed2f21d02fd4fda8d24cfc7d0c4697e1a47 Mon Sep 17 00:00:00 2001 From: nyagamunene Date: Sun, 3 Nov 2024 20:12:20 +0300 Subject: [PATCH 46/72] Update returned errors Signed-off-by: nyagamunene --- users/middleware/authorization.go | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/users/middleware/authorization.go b/users/middleware/authorization.go index 0f9c1c702a..de5e96cfa6 100644 --- a/users/middleware/authorization.go +++ b/users/middleware/authorization.go @@ -57,7 +57,7 @@ func (am *authorizationMiddleware) View(ctx context.Context, session authn.Sessi Operation: mgauth.ReadOp, EntityIDs: []string{id}, }); err != nil { - return users.User{}, errors.Wrap(err, svcerr.ErrUnauthorizedPAT) + return users.User{}, errors.Wrap(svcerr.ErrUnauthorizedPAT, err) } } @@ -78,7 +78,7 @@ func (am *authorizationMiddleware) ViewProfile(ctx context.Context, session auth Operation: mgauth.ReadOp, EntityIDs: []string{session.UserID}, }); err != nil { - return users.User{}, errors.Wrap(err, svcerr.ErrUnauthorizedPAT) + return users.User{}, errors.Wrap(svcerr.ErrUnauthorizedPAT, err) } } return am.svc.ViewProfile(ctx, session) @@ -94,7 +94,7 @@ func (am *authorizationMiddleware) ListUsers(ctx context.Context, session authn. Operation: mgauth.ListOp, EntityIDs: anyEntity, }); err != nil { - return users.UsersPage{}, errors.Wrap(err, svcerr.ErrUnauthorizedPAT) + return users.UsersPage{}, errors.Wrap(svcerr.ErrUnauthorizedPAT, err) } } if err := am.checkSuperAdmin(ctx, session.UserID); err == nil { @@ -114,7 +114,7 @@ func (am *authorizationMiddleware) ListMembers(ctx context.Context, session auth Operation: mgauth.ListOp, EntityIDs: anyEntity, }); err != nil { - return users.MembersPage{}, errors.Wrap(err, svcerr.ErrUnauthorizedPAT) + return users.MembersPage{}, errors.Wrap(svcerr.ErrUnauthorizedPAT, err) } } @@ -159,7 +159,7 @@ func (am *authorizationMiddleware) Update(ctx context.Context, session authn.Ses Operation: mgauth.UpdateOp, EntityIDs: []string{user.ID}, }); err != nil { - return users.User{}, errors.Wrap(err, svcerr.ErrUnauthorizedPAT) + return users.User{}, errors.Wrap(svcerr.ErrUnauthorizedPAT, err) } } @@ -180,7 +180,7 @@ func (am *authorizationMiddleware) UpdateTags(ctx context.Context, session authn Operation: mgauth.UpdateOp, EntityIDs: []string{user.ID}, }); err != nil { - return users.User{}, errors.Wrap(err, svcerr.ErrUnauthorizedPAT) + return users.User{}, errors.Wrap(svcerr.ErrUnauthorizedPAT, err) } } @@ -197,7 +197,7 @@ func (am *authorizationMiddleware) UpdateEmail(ctx context.Context, session auth Operation: mgauth.UpdateOp, EntityIDs: []string{id}, }); err != nil { - return users.User{}, errors.Wrap(err, svcerr.ErrUnauthorizedPAT) + return users.User{}, errors.Wrap(svcerr.ErrUnauthorizedPAT, err) } } if err := am.checkSuperAdmin(ctx, session.UserID); err == nil { @@ -221,7 +221,7 @@ func (am *authorizationMiddleware) UpdateUsername(ctx context.Context, session a Operation: mgauth.UpdateOp, EntityIDs: []string{id}, }); err != nil { - return users.User{}, errors.Wrap(err, svcerr.ErrUnauthorizedPAT) + return users.User{}, errors.Wrap(svcerr.ErrUnauthorizedPAT, err) } } @@ -242,7 +242,7 @@ func (am *authorizationMiddleware) UpdateProfilePicture(ctx context.Context, ses Operation: mgauth.UpdateOp, EntityIDs: []string{user.ID}, }); err != nil { - return users.User{}, errors.Wrap(err, svcerr.ErrUnauthorizedPAT) + return users.User{}, errors.Wrap(svcerr.ErrUnauthorizedPAT, err) } } @@ -263,7 +263,7 @@ func (am *authorizationMiddleware) UpdateSecret(ctx context.Context, session aut Operation: mgauth.UpdateOp, EntityIDs: []string{session.UserID}, }); err != nil { - return users.User{}, errors.Wrap(err, svcerr.ErrUnauthorizedPAT) + return users.User{}, errors.Wrap(svcerr.ErrUnauthorizedPAT, err) } } @@ -291,7 +291,7 @@ func (am *authorizationMiddleware) UpdateRole(ctx context.Context, session authn Operation: mgauth.UpdateOp, EntityIDs: []string{user.ID}, }); err != nil { - return users.User{}, errors.Wrap(err, svcerr.ErrUnauthorizedPAT) + return users.User{}, errors.Wrap(svcerr.ErrUnauthorizedPAT, err) } } session.SuperAdmin = true @@ -316,7 +316,7 @@ func (am *authorizationMiddleware) Enable(ctx context.Context, session authn.Ses Operation: mgauth.UpdateOp, EntityIDs: []string{id}, }); err != nil { - return users.User{}, errors.Wrap(err, svcerr.ErrUnauthorizedPAT) + return users.User{}, errors.Wrap(svcerr.ErrUnauthorizedPAT, err) } } @@ -337,7 +337,7 @@ func (am *authorizationMiddleware) Disable(ctx context.Context, session authn.Se Operation: mgauth.UpdateOp, EntityIDs: []string{id}, }); err != nil { - return users.User{}, errors.Wrap(err, svcerr.ErrUnauthorizedPAT) + return users.User{}, errors.Wrap(svcerr.ErrUnauthorizedPAT, err) } } @@ -358,7 +358,7 @@ func (am *authorizationMiddleware) Delete(ctx context.Context, session authn.Ses Operation: mgauth.DeleteOp, EntityIDs: []string{id}, }); err != nil { - return errors.Wrap(err, svcerr.ErrUnauthorizedPAT) + return errors.Wrap(svcerr.ErrUnauthorizedPAT, err) } } From cb84baf95de5ab05987a007b6b0c907609b8cc9f Mon Sep 17 00:00:00 2001 From: nyagamunene Date: Sun, 3 Nov 2024 20:24:36 +0300 Subject: [PATCH 47/72] Update variable assignment Signed-off-by: nyagamunene --- users/middleware/authorization.go | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/users/middleware/authorization.go b/users/middleware/authorization.go index de5e96cfa6..8836bd53be 100644 --- a/users/middleware/authorization.go +++ b/users/middleware/authorization.go @@ -16,9 +16,7 @@ import ( "github.com/absmach/supermq/users" ) -var ( - anyEntity = []string{"*"} -) +var anyEntity = []string{"*"} var _ users.Service = (*authorizationMiddleware)(nil) From 8bd2afa20b8ea3a9e79f4aeef1c836fe463f8b80 Mon Sep 17 00:00:00 2001 From: nyagamunene Date: Sun, 3 Nov 2024 23:57:52 +0300 Subject: [PATCH 48/72] Update any ids method Signed-off-by: nyagamunene --- users/middleware/authorization.go | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/users/middleware/authorization.go b/users/middleware/authorization.go index 8836bd53be..0b9601571f 100644 --- a/users/middleware/authorization.go +++ b/users/middleware/authorization.go @@ -16,8 +16,6 @@ import ( "github.com/absmach/supermq/users" ) -var anyEntity = []string{"*"} - var _ users.Service = (*authorizationMiddleware)(nil) type authorizationMiddleware struct { @@ -90,7 +88,7 @@ func (am *authorizationMiddleware) ListUsers(ctx context.Context, session authn. PlatformEntityType: mgauth.PlatformUsersScope, OptionalDomainEntityType: mgauth.DomainNullScope, Operation: mgauth.ListOp, - EntityIDs: anyEntity, + EntityIDs: auth.AnyIDs{}.Values(), }); err != nil { return users.UsersPage{}, errors.Wrap(svcerr.ErrUnauthorizedPAT, err) } @@ -110,7 +108,7 @@ func (am *authorizationMiddleware) ListMembers(ctx context.Context, session auth PlatformEntityType: mgauth.PlatformUsersScope, OptionalDomainEntityType: mgauth.DomainNullScope, Operation: mgauth.ListOp, - EntityIDs: anyEntity, + EntityIDs: auth.AnyIDs{}.Values(), }); err != nil { return users.MembersPage{}, errors.Wrap(svcerr.ErrUnauthorizedPAT, err) } From 68def70926800d0e23cfbb0a4316ca13d46cceae Mon Sep 17 00:00:00 2001 From: nyagamunene Date: Mon, 4 Nov 2024 17:17:23 +0300 Subject: [PATCH 49/72] Update users middleware Signed-off-by: nyagamunene --- users/middleware/authorization.go | 48 +++++++++++++++++++++++++------ 1 file changed, 39 insertions(+), 9 deletions(-) diff --git a/users/middleware/authorization.go b/users/middleware/authorization.go index 0b9601571f..772b8df374 100644 --- a/users/middleware/authorization.go +++ b/users/middleware/authorization.go @@ -102,15 +102,45 @@ func (am *authorizationMiddleware) ListUsers(ctx context.Context, session authn. func (am *authorizationMiddleware) ListMembers(ctx context.Context, session authn.Session, objectKind, objectID string, pm users.Page) (users.MembersPage, error) { if session.Type == authn.PersonalAccessToken { - if err := am.authz.AuthorizePAT(ctx, mgauthz.PatReq{ - UserID: session.UserID, - PatID: session.ID, - PlatformEntityType: mgauth.PlatformUsersScope, - OptionalDomainEntityType: mgauth.DomainNullScope, - Operation: mgauth.ListOp, - EntityIDs: auth.AnyIDs{}.Values(), - }); err != nil { - return users.MembersPage{}, errors.Wrap(svcerr.ErrUnauthorizedPAT, err) + switch objectKind { + case policies.GroupsKind: + if err := am.authz.AuthorizePAT(ctx, mgauthz.PatReq{ + UserID: session.UserID, + PatID: session.ID, + OptionalDomainID: session.DomainID, + PlatformEntityType: mgauth.PlatformUsersScope, + OptionalDomainEntityType: mgauth.DomainGroupsScope, + Operation: mgauth.ListOp, + EntityIDs: auth.AnyIDs{}.Values(), + }); err != nil { + return users.MembersPage{}, errors.Wrap(svcerr.ErrUnauthorizedPAT, err) + } + case policies.DomainsKind: + if err := am.authz.AuthorizePAT(ctx, mgauthz.PatReq{ + UserID: session.UserID, + PatID: session.ID, + OptionalDomainID: session.DomainID, + PlatformEntityType: mgauth.PlatformUsersScope, + OptionalDomainEntityType: mgauth.DomainManagementScope, + Operation: mgauth.ListOp, + EntityIDs: auth.AnyIDs{}.Values(), + }); err != nil { + return users.MembersPage{}, errors.Wrap(svcerr.ErrUnauthorizedPAT, err) + } + case policies.ClientsKind: + if err := am.authz.AuthorizePAT(ctx, mgauthz.PatReq{ + UserID: session.UserID, + PatID: session.ID, + OptionalDomainID: session.DomainID, + PlatformEntityType: mgauth.PlatformUsersScope, + OptionalDomainEntityType: mgauth.DomainThingsScope, + Operation: mgauth.ListOp, + EntityIDs: auth.AnyIDs{}.Values(), + }); err != nil { + return users.MembersPage{}, errors.Wrap(svcerr.ErrUnauthorizedPAT, err) + } + default: + return users.MembersPage{}, svcerr.ErrAuthorization } } From 104d8fbf9a6aa2ff3f818edd02486df39025ab70 Mon Sep 17 00:00:00 2001 From: nyagamunene Date: Mon, 28 Oct 2024 20:29:11 +0300 Subject: [PATCH 50/72] Update middleware, service and api Signed-off-by: nyagamunene --- auth/hasher/hasher.go | 5 +- auth/pat.go | 2 +- pat/api/endpoint.go | 259 +++++++++++++++++++++++++++++ pat/api/requests.go | 361 +++++++++++++++++++++++++++++++++++++++++ pat/api/responses.go | 208 ++++++++++++++++++++++++ pat/api/transport.go | 271 +++++++++++++++++++++++++++++++ pat/tracing/tracing.go | 164 +++++++++++++++++++ 7 files changed, 1266 insertions(+), 4 deletions(-) create mode 100644 pat/api/endpoint.go create mode 100644 pat/api/requests.go create mode 100644 pat/api/responses.go create mode 100644 pat/api/transport.go create mode 100644 pat/tracing/tracing.go diff --git a/auth/hasher/hasher.go b/auth/hasher/hasher.go index a9e4b2df08..026f5df306 100644 --- a/auth/hasher/hasher.go +++ b/auth/hasher/hasher.go @@ -10,7 +10,6 @@ import ( "strings" "time" - "github.com/absmach/supermq/auth" "github.com/absmach/supermq/pkg/errors" "golang.org/x/crypto/scrypt" ) @@ -24,12 +23,12 @@ var ( errDecode = errors.New("failed to decode") ) -var _ auth.Hasher = (*bcryptHasher)(nil) +var _ pat.Hasher = (*bcryptHasher)(nil) type bcryptHasher struct{} // New instantiates a bcrypt-based hasher implementation. -func New() auth.Hasher { +func New() pat.Hasher { return &bcryptHasher{} } diff --git a/auth/pat.go b/auth/pat.go index 4a168fa0ec..44a58a0440 100644 --- a/auth/pat.go +++ b/auth/pat.go @@ -727,7 +727,7 @@ type PATS interface { UpdatePATDescription(ctx context.Context, token, patID, description string) (PAT, error) // Retrieve function retrieves the PAT for given ID. - RetrievePAT(ctx context.Context, userID string, patID string) (PAT, error) + RetrievePAT(ctx context.Context, userID, patID string) (PAT, error) // List function lists all the PATs for the user. ListPATS(ctx context.Context, token string, pm PATSPageMeta) (PATSPage, error) diff --git a/pat/api/endpoint.go b/pat/api/endpoint.go new file mode 100644 index 0000000000..94cbdf5e80 --- /dev/null +++ b/pat/api/endpoint.go @@ -0,0 +1,259 @@ +// Copyright (c) Abstract Machines +// SPDX-License-Identifier: Apache-2.0 + +package pats + +import ( + "context" + + "github.com/absmach/magistrala/internal/api" + "github.com/absmach/magistrala/pat" + "github.com/absmach/magistrala/pkg/authn" + svcerr "github.com/absmach/magistrala/pkg/errors/service" + "github.com/go-kit/kit/endpoint" +) + +func createPATEndpoint(svc pat.Service) endpoint.Endpoint { + return func(ctx context.Context, request interface{}) (interface{}, error) { + req := request.(createPatReq) + if err := req.validate(); err != nil { + return nil, err + } + + session, ok := ctx.Value(api.SessionKey).(authn.Session) + if !ok { + return nil, svcerr.ErrAuthentication + } + + pat, err := svc.CreatePAT(ctx, session, req.Name, req.Description, req.Duration, req.Scope) + if err != nil { + return nil, err + } + + return createPatRes{pat}, nil + } +} + +func retrievePATEndpoint(svc pat.Service) endpoint.Endpoint { + return func(ctx context.Context, request interface{}) (interface{}, error) { + req := request.(retrievePatReq) + if err := req.validate(); err != nil { + return nil, err + } + + session, ok := ctx.Value(api.SessionKey).(authn.Session) + if !ok { + return nil, svcerr.ErrAuthentication + } + + pat, err := svc.RetrievePAT(ctx, session, req.id) + if err != nil { + return nil, err + } + + return retrievePatRes{pat}, nil + } +} + +func updatePATNameEndpoint(svc pat.Service) endpoint.Endpoint { + return func(ctx context.Context, request interface{}) (interface{}, error) { + req := request.(updatePatNameReq) + if err := req.validate(); err != nil { + return nil, err + } + + session, ok := ctx.Value(api.SessionKey).(authn.Session) + if !ok { + return nil, svcerr.ErrAuthentication + } + pat, err := svc.UpdatePATName(ctx, session, req.id, req.Name) + if err != nil { + return nil, err + } + + return updatePatNameRes{pat}, nil + } +} + +func updatePATDescriptionEndpoint(svc pat.Service) endpoint.Endpoint { + return func(ctx context.Context, request interface{}) (interface{}, error) { + req := request.(updatePatDescriptionReq) + if err := req.validate(); err != nil { + return nil, err + } + + session, ok := ctx.Value(api.SessionKey).(authn.Session) + if !ok { + return nil, svcerr.ErrAuthentication + } + + pat, err := svc.UpdatePATDescription(ctx, session, req.id, req.Description) + if err != nil { + return nil, err + } + + return updatePatDescriptionRes{pat}, nil + } +} + +func listPATSEndpoint(svc pat.Service) endpoint.Endpoint { + return func(ctx context.Context, request interface{}) (interface{}, error) { + req := request.(listPatsReq) + if err := req.validate(); err != nil { + return nil, err + } + + session, ok := ctx.Value(api.SessionKey).(authn.Session) + if !ok { + return nil, svcerr.ErrAuthentication + } + + pm := pat.PATSPageMeta{ + Limit: req.limit, + Offset: req.offset, + } + patsPage, err := svc.ListPATS(ctx, session, pm) + if err != nil { + return nil, err + } + + return listPatsRes{patsPage}, nil + } +} + +func deletePATEndpoint(svc pat.Service) endpoint.Endpoint { + return func(ctx context.Context, request interface{}) (interface{}, error) { + req := request.(deletePatReq) + if err := req.validate(); err != nil { + return nil, err + } + + session, ok := ctx.Value(api.SessionKey).(authn.Session) + if !ok { + return nil, svcerr.ErrAuthentication + } + + if err := svc.DeletePAT(ctx, session, req.id); err != nil { + return nil, err + } + + return deletePatRes{}, nil + } +} + +func resetPATSecretEndpoint(svc pat.Service) endpoint.Endpoint { + return func(ctx context.Context, request interface{}) (interface{}, error) { + req := request.(resetPatSecretReq) + if err := req.validate(); err != nil { + return nil, err + } + + session, ok := ctx.Value(api.SessionKey).(authn.Session) + if !ok { + return nil, svcerr.ErrAuthentication + } + + pat, err := svc.ResetPATSecret(ctx, session, req.id, req.Duration) + if err != nil { + return nil, err + } + + return resetPatSecretRes{pat}, nil + } +} + +func revokePATSecretEndpoint(svc pat.Service) endpoint.Endpoint { + return func(ctx context.Context, request interface{}) (interface{}, error) { + req := request.(revokePatSecretReq) + if err := req.validate(); err != nil { + return nil, err + } + + session, ok := ctx.Value(api.SessionKey).(authn.Session) + if !ok { + return nil, svcerr.ErrAuthentication + } + + if err := svc.RevokePATSecret(ctx, session, req.id); err != nil { + return nil, err + } + + return revokePatSecretRes{}, nil + } +} + +func addPATScopeEntryEndpoint(svc pat.Service) endpoint.Endpoint { + return func(ctx context.Context, request interface{}) (interface{}, error) { + req := request.(addPatScopeEntryReq) + if err := req.validate(); err != nil { + return nil, err + } + + session, ok := ctx.Value(api.SessionKey).(authn.Session) + if !ok { + return nil, svcerr.ErrAuthentication + } + + scope, err := svc.AddPATScopeEntry(ctx, session, req.id, req.PlatformEntityType, req.OptionalDomainID, req.OptionalDomainEntityType, req.Operation, req.EntityIDs...) + if err != nil { + return nil, err + } + + return addPatScopeEntryRes{scope}, nil + } +} + +func removePATScopeEntryEndpoint(svc pat.Service) endpoint.Endpoint { + return func(ctx context.Context, request interface{}) (interface{}, error) { + req := request.(removePatScopeEntryReq) + if err := req.validate(); err != nil { + return nil, err + } + + session, ok := ctx.Value(api.SessionKey).(authn.Session) + if !ok { + return nil, svcerr.ErrAuthentication + } + + scope, err := svc.RemovePATScopeEntry(ctx, session, req.id, req.PlatformEntityType, req.OptionalDomainID, req.OptionalDomainEntityType, req.Operation, req.EntityIDs...) + if err != nil { + return nil, err + } + return removePatScopeEntryRes{scope}, nil + } +} + +func clearPATAllScopeEntryEndpoint(svc pat.Service) endpoint.Endpoint { + return func(ctx context.Context, request interface{}) (interface{}, error) { + req := request.(clearAllScopeEntryReq) + if err := req.validate(); err != nil { + return nil, err + } + + session, ok := ctx.Value(api.SessionKey).(authn.Session) + if !ok { + return nil, svcerr.ErrAuthentication + } + + if err := svc.ClearPATAllScopeEntry(ctx, session, req.id); err != nil { + return nil, err + } + + return clearAllScopeEntryRes{}, nil + } +} + +func authorizePATEndpoint(svc pat.Service) endpoint.Endpoint { + return func(ctx context.Context, request interface{}) (interface{}, error) { + req := request.(authorizePATReq) + if err := req.validate(); err != nil { + return nil, err + } + + if err := svc.AuthorizePAT(ctx, req.token, req.PlatformEntityType, req.OptionalDomainID, req.OptionalDomainEntityType, req.Operation, req.EntityIDs...); err != nil { + return nil, err + } + + return authorizePATRes{}, nil + } +} diff --git a/pat/api/requests.go b/pat/api/requests.go new file mode 100644 index 0000000000..1822ec457e --- /dev/null +++ b/pat/api/requests.go @@ -0,0 +1,361 @@ +// Copyright (c) Abstract Machines +// SPDX-License-Identifier: Apache-2.0 + +package pats + +import ( + "encoding/json" + "strings" + "time" + + "github.com/absmach/magistrala/pat" + "github.com/absmach/magistrala/pkg/apiutil" +) + +type createPatReq struct { + token string + Name string `json:"name,omitempty"` + Description string `json:"description,omitempty"` + Duration time.Duration `json:"duration,omitempty"` + Scope pat.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 pat.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 + } + + 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 pat.PlatformEntityType `json:"platform_entity_type,omitempty"` + OptionalDomainID string `json:"optional_domain_id,omitempty"` + OptionalDomainEntityType pat.DomainEntityType `json:"optional_domain_entity_type,omitempty"` + Operation pat.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 := pat.ParsePlatformEntityType(temp.PlatformEntityType) + if err != nil { + return err + } + odt, err := pat.ParseDomainEntityType(temp.OptionalDomainEntityType) + if err != nil { + return err + } + op, err := pat.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 pat.PlatformEntityType `json:"platform_entity_type,omitempty"` + OptionalDomainID string `json:"optional_domain_id,omitempty"` + OptionalDomainEntityType pat.DomainEntityType `json:"optional_domain_entity_type,omitempty"` + Operation pat.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 := pat.ParsePlatformEntityType(temp.PlatformEntityType) + if err != nil { + return err + } + odt, err := pat.ParseDomainEntityType(temp.OptionalDomainEntityType) + if err != nil { + return err + } + op, err := pat.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 authorizePATReq struct { + token string + PlatformEntityType pat.PlatformEntityType `json:"platform_entity_type,omitempty"` + OptionalDomainID string `json:"optional_domain_id,omitempty"` + OptionalDomainEntityType pat.DomainEntityType `json:"optional_domain_entity_type,omitempty"` + Operation pat.OperationType `json:"operation,omitempty"` + EntityIDs []string `json:"entity_ids,omitempty"` +} + +func (tcpsr *authorizePATReq) 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 + } + + tcpsr.OptionalDomainID = temp.OptionalDomainID + tcpsr.EntityIDs = temp.EntityIDs + + pet, err := pat.ParsePlatformEntityType(temp.PlatformEntityType) + if err != nil { + return err + } + tcpsr.PlatformEntityType = pet + + if temp.OptionalDomainEntityType != "" { + odt, err := pat.ParseDomainEntityType(temp.OptionalDomainEntityType) + if err != nil { + return err + } + tcpsr.OptionalDomainEntityType = odt + } + + if temp.OptionalDomainID != "" { + op, err := pat.ParseOperationType(temp.Operation) + if err != nil { + return err + } + tcpsr.Operation = op + } + + return nil +} + +func (req authorizePATReq) validate() (err error) { + if req.token == "" { + return apiutil.ErrBearerToken + } + + return nil +} diff --git a/pat/api/responses.go b/pat/api/responses.go new file mode 100644 index 0000000000..b2d0fe57de --- /dev/null +++ b/pat/api/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/pat" +) + +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 { + pat.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 { + pat.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 { + pat.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 { + pat.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 { + pat.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 { + pat.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 { + pat.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 { + pat.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 authorizePATRes struct{} + +func (res authorizePATRes) Code() int { + return http.StatusNoContent +} + +func (res authorizePATRes) Headers() map[string]string { + return map[string]string{} +} + +func (res authorizePATRes) Empty() bool { + return true +} diff --git a/pat/api/transport.go b/pat/api/transport.go new file mode 100644 index 0000000000..95d0f45efa --- /dev/null +++ b/pat/api/transport.go @@ -0,0 +1,271 @@ +// 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/internal/api" + "github.com/absmach/magistrala/pat" + "github.com/absmach/magistrala/pkg/apiutil" + mgauthn "github.com/absmach/magistrala/pkg/authn" + "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 pat.Service, mux *chi.Mux, authn mgauthn.Authentication, logger *slog.Logger) *chi.Mux { + opts := []kithttp.ServerOption{ + kithttp.ServerErrorEncoder(apiutil.LoggingErrorEncoder(logger, api.EncodeError)), + } + mux.Group(func(r chi.Router) { + mux.Use(api.AuthenticateMiddleware(authn, true)) + + mux.Route("/pats", func(r chi.Router) { + r.Post("/", kithttp.NewServer( + createPATEndpoint(svc), + decodeCreatePATRequest, + api.EncodeResponse, + opts..., + ).ServeHTTP) + + r.Get("/{id}", kithttp.NewServer( + (retrievePATEndpoint(svc)), + decodeRetrievePATRequest, + api.EncodeResponse, + opts..., + ).ServeHTTP) + + r.Put("/{id}/name", kithttp.NewServer( + (updatePATNameEndpoint(svc)), + decodeUpdatePATNameRequest, + api.EncodeResponse, + opts..., + ).ServeHTTP) + + r.Put("/{id}/description", kithttp.NewServer( + (updatePATDescriptionEndpoint(svc)), + decodeUpdatePATDescriptionRequest, + api.EncodeResponse, + opts..., + ).ServeHTTP) + + r.Get("/", kithttp.NewServer( + (listPATSEndpoint(svc)), + decodeListPATSRequest, + api.EncodeResponse, + opts..., + ).ServeHTTP) + + r.Delete("/{id}", kithttp.NewServer( + (deletePATEndpoint(svc)), + decodeDeletePATRequest, + api.EncodeResponse, + opts..., + ).ServeHTTP) + + r.Put("/{id}/secret/reset", kithttp.NewServer( + (resetPATSecretEndpoint(svc)), + decodeResetPATSecretRequest, + api.EncodeResponse, + opts..., + ).ServeHTTP) + + r.Put("/{id}/secret/revoke", kithttp.NewServer( + (revokePATSecretEndpoint(svc)), + decodeRevokePATSecretRequest, + api.EncodeResponse, + opts..., + ).ServeHTTP) + + r.Put("/{id}/scope/add", kithttp.NewServer( + (addPATScopeEntryEndpoint(svc)), + decodeAddPATScopeEntryRequest, + api.EncodeResponse, + opts..., + ).ServeHTTP) + + r.Put("/{id}/scope/remove", kithttp.NewServer( + (removePATScopeEntryEndpoint(svc)), + decodeRemovePATScopeEntryRequest, + api.EncodeResponse, + opts..., + ).ServeHTTP) + + r.Delete("/{id}/scope", kithttp.NewServer( + (clearPATAllScopeEntryEndpoint(svc)), + decodeClearPATAllScopeEntryRequest, + api.EncodeResponse, + opts..., + ).ServeHTTP) + + r.Post("/authorize", kithttp.NewServer( + (authorizePATEndpoint(svc)), + decodeAuthorizePATRequest, + 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 decodeUpdatePATNameRequest(_ 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 decodeUpdatePATDescriptionRequest(_ 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 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) + } + 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, + } + return req, nil +} + +func decodeDeletePATRequest(_ context.Context, r *http.Request) (interface{}, error) { + return deletePatReq{ + token: apiutil.ExtractBearerToken(r), + id: chi.URLParam(r, "id"), + }, nil +} + +func decodeResetPATSecretRequest(_ 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 decodeRevokePATSecretRequest(_ context.Context, r *http.Request) (interface{}, error) { + return revokePatSecretReq{ + token: apiutil.ExtractBearerToken(r), + id: chi.URLParam(r, "id"), + }, nil +} + +func decodeAddPATScopeEntryRequest(_ 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 decodeRemovePATScopeEntryRequest(_ 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 decodeClearPATAllScopeEntryRequest(_ 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 decodeAuthorizePATRequest(_ context.Context, r *http.Request) (interface{}, error) { + if !strings.Contains(r.Header.Get("Content-Type"), contentType) { + return nil, apiutil.ErrUnsupportedContentType + } + + req := authorizePATReq{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/pat/tracing/tracing.go b/pat/tracing/tracing.go new file mode 100644 index 0000000000..4fab5fae49 --- /dev/null +++ b/pat/tracing/tracing.go @@ -0,0 +1,164 @@ +// Copyright (c) Abstract Machines +// SPDX-License-Identifier: Apache-2.0 + +package tracing + +import ( + "context" + "time" + + "github.com/absmach/magistrala/pat" + "github.com/absmach/magistrala/pkg/authn" + "go.opentelemetry.io/otel/attribute" + "go.opentelemetry.io/otel/trace" +) + +var _ pat.Service = (*tracingMiddleware)(nil) + +type tracingMiddleware struct { + tracer trace.Tracer + svc pat.Service +} + +// New returns a new group service with tracing capabilities. +func New(svc pat.Service, tracer trace.Tracer) pat.Service { + return &tracingMiddleware{tracer, svc} +} + +func (tm *tracingMiddleware) CreatePAT(ctx context.Context, session authn.Session, name, description string, duration time.Duration, scope pat.Scope) (pat.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.CreatePAT(ctx, session, name, description, duration, scope) +} + +func (tm *tracingMiddleware) UpdatePATName(ctx context.Context, session authn.Session, patID, name string) (pat.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.UpdatePATName(ctx, session, patID, name) +} + +func (tm *tracingMiddleware) UpdatePATDescription(ctx context.Context, session authn.Session, patID, description string) (pat.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.UpdatePATDescription(ctx, session, patID, description) +} + +func (tm *tracingMiddleware) RetrievePAT(ctx context.Context, session authn.Session, patID string) (pat.PAT, error) { + ctx, span := tm.tracer.Start(ctx, "retrieve_pat", trace.WithAttributes( + attribute.String("pat_id", patID), + )) + defer span.End() + return tm.svc.RetrievePAT(ctx, session, patID) +} + +func (tm *tracingMiddleware) ListPATS(ctx context.Context, session authn.Session, pm pat.PATSPageMeta) (pat.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.ListPATS(ctx, session, pm) +} + +func (tm *tracingMiddleware) DeletePAT(ctx context.Context, session authn.Session, patID string) error { + ctx, span := tm.tracer.Start(ctx, "delete_pat", trace.WithAttributes( + attribute.String("pat_id", patID), + )) + defer span.End() + return tm.svc.DeletePAT(ctx, session, patID) +} + +func (tm *tracingMiddleware) ResetPATSecret(ctx context.Context, session authn.Session, patID string, duration time.Duration) (pat.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.ResetPATSecret(ctx, session, patID, duration) +} + +func (tm *tracingMiddleware) RevokePATSecret(ctx context.Context, session authn.Session, 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.RevokePATSecret(ctx, session, patID) +} + +func (tm *tracingMiddleware) AddPATScopeEntry(ctx context.Context, session authn.Session, patID string, platformEntityType pat.PlatformEntityType, optionalDomainID string, optionalDomainEntityType pat.DomainEntityType, operation pat.OperationType, entityIDs ...string) (pat.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), + attribute.String("optional_domain_entity", optionalDomainEntityType.String()), + attribute.String("operation", operation.String()), + attribute.StringSlice("entities", entityIDs), + )) + defer span.End() + return tm.svc.AddPATScopeEntry(ctx, session, patID, platformEntityType, optionalDomainID, optionalDomainEntityType, operation, entityIDs...) +} + +func (tm *tracingMiddleware) RemovePATScopeEntry(ctx context.Context, session authn.Session, patID string, platformEntityType pat.PlatformEntityType, optionalDomainID string, optionalDomainEntityType pat.DomainEntityType, operation pat.OperationType, entityIDs ...string) (pat.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), + attribute.String("optional_domain_entity", optionalDomainEntityType.String()), + attribute.String("operation", operation.String()), + attribute.StringSlice("entities", entityIDs), + )) + defer span.End() + return tm.svc.RemovePATScopeEntry(ctx, session, patID, platformEntityType, optionalDomainID, optionalDomainEntityType, operation, entityIDs...) +} + +func (tm *tracingMiddleware) ClearPATAllScopeEntry(ctx context.Context, session authn.Session, 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.ClearPATAllScopeEntry(ctx, session, patID) +} + +func (tm *tracingMiddleware) IdentifyPAT(ctx context.Context, paToken string) (pat.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 pat.PlatformEntityType, optionalDomainID string, optionalDomainEntityType pat.DomainEntityType, operation pat.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), + 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, platformEntityType, optionalDomainID, optionalDomainEntityType, operation, entityIDs...) +} + +func (tm *tracingMiddleware) CheckPAT(ctx context.Context, userID, patID string, platformEntityType pat.PlatformEntityType, optionalDomainID string, optionalDomainEntityType pat.DomainEntityType, operation pat.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.CheckPAT(ctx, userID, patID, platformEntityType, optionalDomainID, optionalDomainEntityType, operation, entityIDs...) +} From 284c842b2f8fc46fffa43841908da297fd26420a Mon Sep 17 00:00:00 2001 From: nyagamunene Date: Thu, 7 Nov 2024 15:20:22 +0300 Subject: [PATCH 51/72] Update protoc version Signed-off-by: nyagamunene --- api/grpc/auth/v1/auth.pb.go | 20 ++++++++------------ api/grpc/auth/v1/auth_grpc.pb.go | 23 +++++++++++++++++------ 2 files changed, 25 insertions(+), 18 deletions(-) diff --git a/api/grpc/auth/v1/auth.pb.go b/api/grpc/auth/v1/auth.pb.go index 2d15354ce0..a15439a63c 100644 --- a/api/grpc/auth/v1/auth.pb.go +++ b/api/grpc/auth/v1/auth.pb.go @@ -32,11 +32,9 @@ type AuthNReq struct { func (x *AuthNReq) Reset() { *x = AuthNReq{} - if protoimpl.UnsafeEnabled { - mi := &file_auth_v1_auth_proto_msgTypes[0] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } + mi := &file_auth_v1_auth_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } func (x *AuthNReq) String() string { @@ -47,7 +45,7 @@ func (*AuthNReq) ProtoMessage() {} func (x *AuthNReq) ProtoReflect() protoreflect.Message { mi := &file_auth_v1_auth_proto_msgTypes[0] - if protoimpl.UnsafeEnabled && x != nil { + if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -80,11 +78,9 @@ type AuthNRes struct { func (x *AuthNRes) Reset() { *x = AuthNRes{} - if protoimpl.UnsafeEnabled { - mi := &file_auth_v1_auth_proto_msgTypes[1] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } + mi := &file_auth_v1_auth_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } func (x *AuthNRes) String() string { @@ -95,7 +91,7 @@ func (*AuthNRes) ProtoMessage() {} func (x *AuthNRes) ProtoReflect() protoreflect.Message { mi := &file_auth_v1_auth_proto_msgTypes[1] - if protoimpl.UnsafeEnabled && x != nil { + if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) diff --git a/api/grpc/auth/v1/auth_grpc.pb.go b/api/grpc/auth/v1/auth_grpc.pb.go index eae65dac16..d85fabae7c 100644 --- a/api/grpc/auth/v1/auth_grpc.pb.go +++ b/api/grpc/auth/v1/auth_grpc.pb.go @@ -18,8 +18,8 @@ import ( // This is a compile-time assertion to ensure that this generated file // is compatible with the grpc package it is being compiled against. -// Requires gRPC-Go v1.62.0 or later. -const _ = grpc.SupportPackageIsVersion8 +// Requires gRPC-Go v1.64.0 or later. +const _ = grpc.SupportPackageIsVersion9 const ( AuthService_Authorize_FullMethodName = "/auth.v1.AuthService/Authorize" @@ -91,7 +91,7 @@ func (c *authServiceClient) AuthenticatePAT(ctx context.Context, in *AuthNReq, o // AuthServiceServer is the server API for AuthService service. // All implementations must embed UnimplementedAuthServiceServer -// for forward compatibility +// for forward compatibility. // // AuthService is a service that provides authentication // and authorization functionalities for SuperMQ services. @@ -103,9 +103,12 @@ type AuthServiceServer interface { mustEmbedUnimplementedAuthServiceServer() } -// UnimplementedAuthServiceServer must be embedded to have forward compatible implementations. -type UnimplementedAuthServiceServer struct { -} +// UnimplementedAuthServiceServer must be embedded to have +// forward compatible implementations. +// +// NOTE: this should be embedded by value instead of pointer to avoid a nil +// pointer dereference when methods are called. +type UnimplementedAuthServiceServer struct{} func (UnimplementedAuthServiceServer) Authorize(context.Context, *AuthZReq) (*AuthZRes, error) { return nil, status.Errorf(codes.Unimplemented, "method Authorize not implemented") @@ -120,6 +123,7 @@ func (UnimplementedAuthServiceServer) AuthenticatePAT(context.Context, *AuthNReq return nil, status.Errorf(codes.Unimplemented, "method AuthenticatePAT not implemented") } func (UnimplementedAuthServiceServer) mustEmbedUnimplementedAuthServiceServer() {} +func (UnimplementedAuthServiceServer) testEmbeddedByValue() {} // UnsafeAuthServiceServer may be embedded to opt out of forward compatibility for this service. // Use of this interface is not recommended, as added methods to AuthServiceServer will @@ -129,6 +133,13 @@ type UnsafeAuthServiceServer interface { } func RegisterAuthServiceServer(s grpc.ServiceRegistrar, srv AuthServiceServer) { + // If the following call pancis, it indicates UnimplementedAuthServiceServer was + // embedded by pointer and is nil. This will cause panics if an + // unimplemented method is ever invoked, so we test this at initialization + // time to prevent it from happening at runtime later due to I/O. + if t, ok := srv.(interface{ testEmbeddedByValue() }); ok { + t.testEmbeddedByValue() + } s.RegisterService(&AuthService_ServiceDesc, srv) } From 7aed9c5700b6b3cfdac51b927af333c0dfcefb52 Mon Sep 17 00:00:00 2001 From: nyagamunene Date: Mon, 28 Oct 2024 20:29:11 +0300 Subject: [PATCH 52/72] Update middleware, service and api Signed-off-by: nyagamunene --- auth/pat.go | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/auth/pat.go b/auth/pat.go index 44a58a0440..5382dae773 100644 --- a/auth/pat.go +++ b/auth/pat.go @@ -714,41 +714,41 @@ func (pat PAT) Expired() bool { } // 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" +//go:generate mockery --name Service --output=./mocks --filename pats.go --quiet --note "Copyright (c) Abstract Machines" -type PATS interface { +type Service interface { // Create function creates new PAT for given valid inputs. - CreatePAT(ctx context.Context, token, name, description string, duration time.Duration, scope Scope) (PAT, error) + CreatePAT(ctx context.Context, session authn.Session, name, description string, duration time.Duration, scope Scope) (PAT, error) // UpdateName function updates the name for the given PAT ID. - UpdatePATName(ctx context.Context, token, patID, name string) (PAT, error) + UpdatePATName(ctx context.Context, session authn.Session, patID, name string) (PAT, error) // UpdateDescription function updates the description for the given PAT ID. - UpdatePATDescription(ctx context.Context, token, patID, description string) (PAT, error) + UpdatePATDescription(ctx context.Context, session authn.Session, patID, description string) (PAT, error) // Retrieve function retrieves the PAT for given ID. RetrievePAT(ctx context.Context, userID, patID string) (PAT, error) // List function lists all the PATs for the user. - ListPATS(ctx context.Context, token string, pm PATSPageMeta) (PATSPage, error) + ListPATS(ctx context.Context, session authn.Session, pm PATSPageMeta) (PATSPage, error) // Delete function deletes the PAT for given ID. - DeletePAT(ctx context.Context, token, patID string) error + DeletePAT(ctx context.Context, session authn.Session, patID string) error // ResetSecret function reset the secret and creates new secret for the given ID. - ResetPATSecret(ctx context.Context, token, patID string, duration time.Duration) (PAT, error) + ResetPATSecret(ctx context.Context, session authn.Session, patID string, duration time.Duration) (PAT, error) // RevokeSecret function revokes the secret for the given ID. - RevokePATSecret(ctx context.Context, token, patID string) error + RevokePATSecret(ctx context.Context, session authn.Session, patID string) error // AddScope function adds a new scope entry. - AddPATScopeEntry(ctx context.Context, token, patID string, platformEntityType PlatformEntityType, optionalDomainID string, optionalDomainEntityType DomainEntityType, operation OperationType, entityIDs ...string) (Scope, error) + AddPATScopeEntry(ctx context.Context, session authn.Session, patID string, platformEntityType PlatformEntityType, optionalDomainID string, optionalDomainEntityType DomainEntityType, operation OperationType, entityIDs ...string) (Scope, error) // RemoveScope function removes a scope entry. - RemovePATScopeEntry(ctx context.Context, token, patID string, platformEntityType PlatformEntityType, optionalDomainID string, optionalDomainEntityType DomainEntityType, operation OperationType, entityIDs ...string) (Scope, error) + RemovePATScopeEntry(ctx context.Context, session authn.Session, patID string, platformEntityType PlatformEntityType, optionalDomainID string, optionalDomainEntityType DomainEntityType, operation OperationType, entityIDs ...string) (Scope, error) // ClearAllScope function removes all scope entry. - ClearPATAllScopeEntry(ctx context.Context, token, patID string) error + ClearPATAllScopeEntry(ctx context.Context, session authn.Session, patID string) error // IdentifyPAT function will valid the secret. IdentifyPAT(ctx context.Context, paToken string) (PAT, error) From 4d7f16e9d87996ff3ab09496b3615f928ae6519c Mon Sep 17 00:00:00 2001 From: nyagamunene Date: Wed, 30 Oct 2024 13:21:26 +0300 Subject: [PATCH 53/72] Add grpc authorizePAT Signed-off-by: nyagamunene --- pat/api/http/endpoint.go | 0 pat/api/http/requests.go | 0 pat/api/http/responses.go | 0 pat/api/http/transport.go | 0 pat/events/streams.go | 94 +++++++++++++++++++++++++++++++++++++++ 5 files changed, 94 insertions(+) create mode 100644 pat/api/http/endpoint.go create mode 100644 pat/api/http/requests.go create mode 100644 pat/api/http/responses.go create mode 100644 pat/api/http/transport.go create mode 100644 pat/events/streams.go diff --git a/pat/api/http/endpoint.go b/pat/api/http/endpoint.go new file mode 100644 index 0000000000..e69de29bb2 diff --git a/pat/api/http/requests.go b/pat/api/http/requests.go new file mode 100644 index 0000000000..e69de29bb2 diff --git a/pat/api/http/responses.go b/pat/api/http/responses.go new file mode 100644 index 0000000000..e69de29bb2 diff --git a/pat/api/http/transport.go b/pat/api/http/transport.go new file mode 100644 index 0000000000..e69de29bb2 diff --git a/pat/events/streams.go b/pat/events/streams.go new file mode 100644 index 0000000000..713ddea5a6 --- /dev/null +++ b/pat/events/streams.go @@ -0,0 +1,94 @@ +// Copyright (c) Abstract Machines +// SPDX-License-Identifier: Apache-2.0 + +package events + +import ( + "context" + "time" + + "github.com/absmach/magistrala/pat" + "github.com/absmach/magistrala/pkg/authn" + "github.com/absmach/magistrala/pkg/events" + "github.com/absmach/magistrala/pkg/events/store" +) + +const streamID = "magistrala.pat" + +var _ pat.Service = (*eventStore)(nil) + + +type eventStore struct { + events.Publisher + svc pat.Service +} + +// NewEventStoreMiddleware returns wrapper around pat service that sends +// events to event store. +func NewEventStoreMiddleware(ctx context.Context, svc pat.Service, url string) (pat.Service, error) { + publisher, err := store.NewPublisher(ctx, url, streamID) + if err != nil { + return nil, err + } + + return &eventStore{ + svc: svc, + Publisher: publisher, + }, nil +} + +func (es *eventStore) CreatePAT(ctx context.Context, session authn.Session, name, description string, duration time.Duration, scope pat.Scope) (pat.PAT, error) { + return es.svc.CreatePAT(ctx, session, name, description, duration, scope) +} + +func (es *eventStore) UpdatePATName(ctx context.Context, session authn.Session, patID, name string) (pat.PAT, error) { + return es.svc.UpdatePATName(ctx, session, patID, name) +} + +func (es *eventStore) UpdatePATDescription(ctx context.Context, session authn.Session, patID, description string) (pat.PAT, error) { + return es.svc.UpdatePATDescription(ctx, session, patID, description) +} + +func (es *eventStore) RetrievePAT(ctx context.Context, session authn.Session, patID string) (pat.PAT, error) { + return es.svc.RetrievePAT(ctx, session, patID) +} + +func (es *eventStore) ListPATS(ctx context.Context, session authn.Session, pm pat.PATSPageMeta) (pat.PATSPage, error) { + return es.svc.ListPATS(ctx, session, pm) +} + +func (es *eventStore) DeletePAT(ctx context.Context, session authn.Session, patID string) error { + return es.svc.DeletePAT(ctx, session, patID) +} + +func (es *eventStore) ResetPATSecret(ctx context.Context, session authn.Session, patID string, duration time.Duration) (pat.PAT, error) { + return es.svc.ResetPATSecret(ctx, session, patID, duration) +} + +func (es *eventStore) RevokePATSecret(ctx context.Context, session authn.Session, patID string) error { + return es.svc.RevokePATSecret(ctx, session, patID) +} + +func (es *eventStore) AddPATScopeEntry(ctx context.Context, session authn.Session, patID string, platformEntityType pat.PlatformEntityType, optionalDomainID string, optionalDomainEntityType pat.DomainEntityType, operation pat.OperationType, entityIDs ...string) (pat.Scope, error) { + return es.svc.AddPATScopeEntry(ctx, session, patID, platformEntityType, optionalDomainID, optionalDomainEntityType, operation, entityIDs...) +} + +func (es *eventStore) RemovePATScopeEntry(ctx context.Context, session authn.Session, patID string, platformEntityType pat.PlatformEntityType, optionalDomainID string, optionalDomainEntityType pat.DomainEntityType, operation pat.OperationType, entityIDs ...string) (pat.Scope, error) { + return es.svc.RemovePATScopeEntry(ctx, session, patID, platformEntityType, optionalDomainID, optionalDomainEntityType, operation, entityIDs...) +} + +func (es *eventStore) ClearPATAllScopeEntry(ctx context.Context, session authn.Session, patID string) error { + return es.svc.ClearPATAllScopeEntry(ctx, session, patID) +} + +func (es *eventStore) IdentifyPAT(ctx context.Context, paToken string) (pat.PAT, error) { + return es.svc.IdentifyPAT(ctx, paToken) +} + +func (es *eventStore) AuthorizePAT(ctx context.Context, paToken string, platformEntityType pat.PlatformEntityType, optionalDomainID string, optionalDomainEntityType pat.DomainEntityType, operation pat.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 pat.PlatformEntityType, optionalDomainID string, optionalDomainEntityType pat.DomainEntityType, operation pat.OperationType, entityIDs ...string) error { + return es.svc.CheckPAT(ctx, userID, patID, platformEntityType, optionalDomainID, optionalDomainEntityType, operation, entityIDs...) +} \ No newline at end of file From 591e6132e111b8f10f808db18fc852f6f4fc21fd Mon Sep 17 00:00:00 2001 From: nyagamunene Date: Thu, 31 Oct 2024 12:12:05 +0300 Subject: [PATCH 54/72] Initial implementation of Authorize Signed-off-by: nyagamunene --- pat/events/streams.go | 8 +- pat/middleware/authorization.go | 85 +++++++++ pat/middleware/logging.go | 276 ++++++++++++++++++++++++++++++ pat/middleware/metrics.go | 143 ++++++++++++++++ pat/service.go | 260 ++++++++++++++++++++++++++++ users/middleware/authorization.go | 20 ++- 6 files changed, 785 insertions(+), 7 deletions(-) create mode 100644 pat/middleware/authorization.go create mode 100644 pat/middleware/logging.go create mode 100644 pat/middleware/metrics.go create mode 100644 pat/service.go diff --git a/pat/events/streams.go b/pat/events/streams.go index 713ddea5a6..a04bcbe0c9 100644 --- a/pat/events/streams.go +++ b/pat/events/streams.go @@ -49,8 +49,8 @@ func (es *eventStore) UpdatePATDescription(ctx context.Context, session authn.Se return es.svc.UpdatePATDescription(ctx, session, patID, description) } -func (es *eventStore) RetrievePAT(ctx context.Context, session authn.Session, patID string) (pat.PAT, error) { - return es.svc.RetrievePAT(ctx, session, patID) +func (es *eventStore) RetrievePAT(ctx context.Context, userID string, patID string) (pat.PAT, error) { + return es.svc.RetrievePAT(ctx, userID, patID) } func (es *eventStore) ListPATS(ctx context.Context, session authn.Session, pm pat.PATSPageMeta) (pat.PATSPage, error) { @@ -85,8 +85,8 @@ func (es *eventStore) IdentifyPAT(ctx context.Context, paToken string) (pat.PAT, return es.svc.IdentifyPAT(ctx, paToken) } -func (es *eventStore) AuthorizePAT(ctx context.Context, paToken string, platformEntityType pat.PlatformEntityType, optionalDomainID string, optionalDomainEntityType pat.DomainEntityType, operation pat.OperationType, entityIDs ...string) error { - return es.svc.AuthorizePAT(ctx, paToken, platformEntityType, optionalDomainID, optionalDomainEntityType, operation, entityIDs...) +func (es *eventStore) AuthorizePAT(ctx context.Context, userID, patID string, platformEntityType pat.PlatformEntityType, optionalDomainID string, optionalDomainEntityType pat.DomainEntityType, operation pat.OperationType, entityIDs ...string) error { + return es.svc.AuthorizePAT(ctx, userID, patID, platformEntityType, optionalDomainID, optionalDomainEntityType, operation, entityIDs...) } func (es *eventStore) CheckPAT(ctx context.Context, userID, patID string, platformEntityType pat.PlatformEntityType, optionalDomainID string, optionalDomainEntityType pat.DomainEntityType, operation pat.OperationType, entityIDs ...string) error { diff --git a/pat/middleware/authorization.go b/pat/middleware/authorization.go new file mode 100644 index 0000000000..cdd27958f1 --- /dev/null +++ b/pat/middleware/authorization.go @@ -0,0 +1,85 @@ +// Copyright (c) Abstract Machines +// SPDX-License-Identifier: Apache-2.0 + +package middleware + +import ( + "context" + "time" + + "github.com/absmach/magistrala/pat" + "github.com/absmach/magistrala/pkg/authn" + mgauthz "github.com/absmach/magistrala/pkg/authz" +) + +var _ pat.Service = (*authorizationMiddleware)(nil) + +type authorizationMiddleware struct { + svc pat.Service + authz mgauthz.Authorization +} + +// AuthorizationMiddleware adds authorization to the clients service. +func AuthorizationMiddleware(entityType string, svc pat.Service, authz mgauthz.Authorization) (pat.Service, error) { + return &authorizationMiddleware{ + svc: svc, + authz: authz, + }, nil +} + +func (am *authorizationMiddleware) CreatePAT(ctx context.Context, session authn.Session, name, description string, duration time.Duration, scope pat.Scope) (pat.PAT, error) { + return am.svc.CreatePAT(ctx, session, name, description, duration, scope) +} + +func (am *authorizationMiddleware) UpdatePATName(ctx context.Context, session authn.Session, patID, name string) (pat.PAT, error) { + return am.svc.UpdatePATName(ctx, session, patID, name) +} + +func (am *authorizationMiddleware) UpdatePATDescription(ctx context.Context, session authn.Session, patID, description string) (pat.PAT, error) { + return am.svc.UpdatePATDescription(ctx, session, patID, description) +} + +func (am *authorizationMiddleware) RetrievePAT(ctx context.Context, userID string, patID string) (pat.PAT, error) { + return am.svc.RetrievePAT(ctx, userID, patID) +} + +func (am *authorizationMiddleware) ListPATS(ctx context.Context, session authn.Session, pm pat.PATSPageMeta) (pat.PATSPage, error) { + return am.svc.ListPATS(ctx, session, pm) +} + +func (am *authorizationMiddleware) DeletePAT(ctx context.Context, session authn.Session, patID string) error { + return am.svc.DeletePAT(ctx, session, patID) +} + +func (am *authorizationMiddleware) ResetPATSecret(ctx context.Context, session authn.Session, patID string, duration time.Duration) (pat.PAT, error) { + return am.svc.ResetPATSecret(ctx, session, patID, duration) +} + +func (am *authorizationMiddleware) RevokePATSecret(ctx context.Context, session authn.Session, patID string) error { + return am.svc.RevokePATSecret(ctx, session, patID) +} + +func (am *authorizationMiddleware) AddPATScopeEntry(ctx context.Context, session authn.Session, patID string, platformEntityType pat.PlatformEntityType, optionalDomainID string, optionalDomainEntityType pat.DomainEntityType, operation pat.OperationType, entityIDs ...string) (pat.Scope, error) { + return am.svc.AddPATScopeEntry(ctx, session, patID, platformEntityType, optionalDomainID, optionalDomainEntityType, operation, entityIDs...) +} + +func (am *authorizationMiddleware) RemovePATScopeEntry(ctx context.Context, session authn.Session, patID string, platformEntityType pat.PlatformEntityType, optionalDomainID string, optionalDomainEntityType pat.DomainEntityType, operation pat.OperationType, entityIDs ...string) (pat.Scope, error) { + return am.svc.RemovePATScopeEntry(ctx, session, patID, platformEntityType, optionalDomainID, optionalDomainEntityType, operation, entityIDs...) +} + +func (am *authorizationMiddleware) ClearPATAllScopeEntry(ctx context.Context, session authn.Session, patID string) error { + return am.svc.ClearPATAllScopeEntry(ctx, session, patID) +} + +func (am *authorizationMiddleware) IdentifyPAT(ctx context.Context, secret string) (pat.PAT, error) { + return am.svc.IdentifyPAT(ctx, secret) +} + +func (am *authorizationMiddleware) AuthorizePAT(ctx context.Context, userID, patID string, platformEntityType pat.PlatformEntityType, optionalDomainID string, optionalDomainEntityType pat.DomainEntityType, operation pat.OperationType, entityIDs ...string) error { + return am.svc.AuthorizePAT(ctx, userID, patID, platformEntityType, optionalDomainID, optionalDomainEntityType, operation, entityIDs...) +} + +func (am *authorizationMiddleware) CheckPAT(ctx context.Context, userID, patID string, platformEntityType pat.PlatformEntityType, optionalDomainID string, optionalDomainEntityType pat.DomainEntityType, operation pat.OperationType, entityIDs ...string) error { + return am.svc.CheckPAT(ctx, userID, patID, platformEntityType, optionalDomainID, optionalDomainEntityType, operation, entityIDs...) +} + diff --git a/pat/middleware/logging.go b/pat/middleware/logging.go new file mode 100644 index 0000000000..b0d096676a --- /dev/null +++ b/pat/middleware/logging.go @@ -0,0 +1,276 @@ +// Copyright (c) Abstract Machines +// SPDX-License-Identifier: Apache-2.0 + +package middleware + +import ( + "context" + "log/slog" + "time" + + "github.com/absmach/magistrala/pat" + "github.com/absmach/magistrala/pkg/authn" +) + +var _ pat.Service = (*loggingMiddleware)(nil) + +type loggingMiddleware struct { + logger *slog.Logger + svc pat.Service +} + +// LoggingMiddleware adds logging facilities to the core service. +func LoggingMiddleware(svc pat.Service, logger *slog.Logger) pat.Service { + return &loggingMiddleware{logger, svc} +} + +func (lm *loggingMiddleware) CreatePAT(ctx context.Context, session authn.Session, name, description string, duration time.Duration, scope pat.Scope) (pa pat.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.CreatePAT(ctx, session, name, description, duration, scope) +} + +func (lm *loggingMiddleware) UpdatePATName(ctx context.Context, session authn.Session, patID, name string) (pa pat.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.UpdatePATName(ctx, session, patID, name) +} + +func (lm *loggingMiddleware) UpdatePATDescription(ctx context.Context, session authn.Session, patID, description string) (pa pat.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.UpdatePATDescription(ctx, session, patID, description) +} + +func (lm *loggingMiddleware) RetrievePAT(ctx context.Context, userID string, patID string) (pa pat.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.RetrievePAT(ctx, userID, patID) +} + +func (lm *loggingMiddleware) ListPATS(ctx context.Context, session authn.Session, pm pat.PATSPageMeta) (pp pat.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.ListPATS(ctx, session, pm) +} + +func (lm *loggingMiddleware) DeletePAT(ctx context.Context, session authn.Session, 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.DeletePAT(ctx, session, patID) +} + +func (lm *loggingMiddleware) ResetPATSecret(ctx context.Context, session authn.Session, patID string, duration time.Duration) (pa pat.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.ResetPATSecret(ctx, session, patID, duration) +} + +func (lm *loggingMiddleware) RevokePATSecret(ctx context.Context, session authn.Session, 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.RevokePATSecret(ctx, session, patID) +} + +func (lm *loggingMiddleware) AddPATScopeEntry(ctx context.Context, session authn.Session, patID string, platformEntityType pat.PlatformEntityType, optionalDomainID string, optionalDomainEntityType pat.DomainEntityType, operation pat.OperationType, entityIDs ...string) (sc pat.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.AddPATScopeEntry(ctx, session, patID, platformEntityType, optionalDomainID, optionalDomainEntityType, operation, entityIDs...) +} + +func (lm *loggingMiddleware) RemovePATScopeEntry(ctx context.Context, session authn.Session, patID string, platformEntityType pat.PlatformEntityType, optionalDomainID string, optionalDomainEntityType pat.DomainEntityType, operation pat.OperationType, entityIDs ...string) (sc pat.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.RemovePATScopeEntry(ctx, session, patID, platformEntityType, optionalDomainID, optionalDomainEntityType, operation, entityIDs...) +} + +func (lm *loggingMiddleware) ClearPATAllScopeEntry(ctx context.Context, session authn.Session, 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.ClearPATAllScopeEntry(ctx, session, patID) +} + +func (lm *loggingMiddleware) IdentifyPAT(ctx context.Context, paToken string) (pa pat.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, userID, patID string, platformEntityType pat.PlatformEntityType, optionalDomainID string, optionalDomainEntityType pat.DomainEntityType, operation pat.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("Authorize PAT failed complete successfully", args...) + return + } + lm.logger.Info("Authorize PAT completed successfully", args...) + }(time.Now()) + return lm.svc.AuthorizePAT(ctx, userID, patID, platformEntityType, optionalDomainID, optionalDomainEntityType, operation, entityIDs...) +} + +func (lm *loggingMiddleware) CheckPAT(ctx context.Context, userID, patID string, platformEntityType pat.PlatformEntityType, optionalDomainID string, optionalDomainEntityType pat.DomainEntityType, operation pat.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("Check PAT failed complete successfully", args...) + return + } + lm.logger.Info("Check PAT completed successfully", args...) + }(time.Now()) + return lm.svc.CheckPAT(ctx, userID, patID, platformEntityType, optionalDomainID, optionalDomainEntityType, operation, entityIDs...) +} + diff --git a/pat/middleware/metrics.go b/pat/middleware/metrics.go new file mode 100644 index 0000000000..d2898f3b29 --- /dev/null +++ b/pat/middleware/metrics.go @@ -0,0 +1,143 @@ +// Copyright (c) Abstract Machines +// SPDX-License-Identifier: Apache-2.0 + +package middleware + +import ( + "context" + "time" + + "github.com/absmach/magistrala/pat" + "github.com/absmach/magistrala/pkg/authn" + "github.com/go-kit/kit/metrics" +) + +var _ pat.Service = (*metricsMiddleware)(nil) + +type metricsMiddleware struct { + counter metrics.Counter + latency metrics.Histogram + svc pat.Service +} + +// MetricsMiddleware instruments core service by tracking request count and latency. +func MetricsMiddleware(svc pat.Service, counter metrics.Counter, latency metrics.Histogram) pat.Service { + return &metricsMiddleware{ + counter: counter, + latency: latency, + svc: svc, + } +} + +func (ms *metricsMiddleware) CreatePAT(ctx context.Context, session authn.Session, name, description string, duration time.Duration, scope pat.Scope) (pat.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.CreatePAT(ctx, session, name, description, duration, scope) +} + +func (ms *metricsMiddleware) UpdatePATName(ctx context.Context, session authn.Session, patID, name string) (pat.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.UpdatePATName(ctx, session, patID, name) +} + +func (ms *metricsMiddleware) UpdatePATDescription(ctx context.Context, session authn.Session, patID, description string) (pat.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.UpdatePATDescription(ctx, session, patID, description) +} + +func (ms *metricsMiddleware) RetrievePAT(ctx context.Context, userID string, patID string) (pat.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.RetrievePAT(ctx, userID, patID) +} + +func (ms *metricsMiddleware) ListPATS(ctx context.Context, session authn.Session, pm pat.PATSPageMeta) (pat.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.ListPATS(ctx, session, pm) +} + +func (ms *metricsMiddleware) DeletePAT(ctx context.Context, session authn.Session, 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.DeletePAT(ctx, session, patID) +} + +func (ms *metricsMiddleware) ResetPATSecret(ctx context.Context, session authn.Session, patID string, duration time.Duration) (pat.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.ResetPATSecret(ctx, session, patID, duration) +} + +func (ms *metricsMiddleware) RevokePATSecret(ctx context.Context, session authn.Session, 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.RevokePATSecret(ctx, session, patID) +} + +func (ms *metricsMiddleware) AddPATScopeEntry(ctx context.Context, session authn.Session, patID string, platformEntityType pat.PlatformEntityType, optionalDomainID string, optionalDomainEntityType pat.DomainEntityType, operation pat.OperationType, entityIDs ...string) (pat.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.AddPATScopeEntry(ctx, session, patID, platformEntityType, optionalDomainID, optionalDomainEntityType, operation, entityIDs...) +} + +func (ms *metricsMiddleware) RemovePATScopeEntry(ctx context.Context, session authn.Session, patID string, platformEntityType pat.PlatformEntityType, optionalDomainID string, optionalDomainEntityType pat.DomainEntityType, operation pat.OperationType, entityIDs ...string) (pat.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.RemovePATScopeEntry(ctx, session, patID, platformEntityType, optionalDomainID, optionalDomainEntityType, operation, entityIDs...) +} + +func (ms *metricsMiddleware) ClearPATAllScopeEntry(ctx context.Context, session authn.Session, 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.ClearPATAllScopeEntry(ctx, session, patID) +} + +func (ms *metricsMiddleware) IdentifyPAT(ctx context.Context, paToken string) (pat.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, userID, patID string, platformEntityType pat.PlatformEntityType, optionalDomainID string, optionalDomainEntityType pat.DomainEntityType, operation pat.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, userID, patID, platformEntityType, optionalDomainID, optionalDomainEntityType, operation, entityIDs...) +} + +func (ms *metricsMiddleware) CheckPAT(ctx context.Context, userID, patID string, platformEntityType pat.PlatformEntityType, optionalDomainID string, optionalDomainEntityType pat.DomainEntityType, operation pat.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/pat/service.go b/pat/service.go new file mode 100644 index 0000000000..571cf6551b --- /dev/null +++ b/pat/service.go @@ -0,0 +1,260 @@ +// Copyright (c) Abstract Machines +// SPDX-License-Identifier: Apache-2.0 + +package pat + +import ( + "context" + "encoding/base64" + "math/rand" + "strings" + "time" + + "github.com/absmach/magistrala" + "github.com/absmach/magistrala/pkg/authn" + "github.com/absmach/magistrala/pkg/errors" + svcerr "github.com/absmach/magistrala/pkg/errors/service" + "github.com/google/uuid" +) + +const ( + randStr = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890!@#$%^&&*|+-=" + patPrefix = "pat" + patSecretSeparator = "_" +) + +var ( + 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") + 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") + errClearAllScope = errors.New("failed to clear all entry in scope") +) + +type service struct { + pats PATSRepository + hasher Hasher + idProvider magistrala.IDProvider +} + +var _ Service = (*service)(nil) + +// New instantiates the auth service implementation. +func New(pats PATSRepository, hasher Hasher, idp magistrala.IDProvider) Service { + return &service{ + pats: pats, + hasher: hasher, + idProvider: idp, + } +} + +func (svc service) CreatePAT(ctx context.Context, session authn.Session, name, description string, duration time.Duration, scope Scope) (PAT, error) { + id, err := svc.idProvider.ID() + if err != nil { + return PAT{}, errors.Wrap(svcerr.ErrCreateEntity, err) + } + secret, hash, err := svc.generateSecretAndHash(session.UserID, id) + if err != nil { + return PAT{}, errors.Wrap(svcerr.ErrCreateEntity, err) + } + + now := time.Now() + pat := PAT{ + ID: id, + User: session.UserID, + 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 +} + +func (svc service) UpdatePATName(ctx context.Context, session authn.Session, patID, name string) (PAT, error) { + pat, err := svc.pats.UpdateName(ctx, session.UserID, patID, name) + if err != nil { + return PAT{}, errors.Wrap(errUpdatePAT, err) + } + return pat, nil +} + +func (svc service) UpdatePATDescription(ctx context.Context, session authn.Session, patID, description string) (PAT, error) { + pat, err := svc.pats.UpdateDescription(ctx, session.UserID, patID, description) + if err != nil { + return PAT{}, errors.Wrap(errUpdatePAT, err) + } + return pat, nil +} + +func (svc service) RetrievePAT(ctx context.Context, userID string, patID string) (PAT, error) { + pat, err := svc.pats.Retrieve(ctx, userID, patID) + if err != nil { + return PAT{}, errors.Wrap(errRetrievePAT, err) + } + return pat, nil +} + +func (svc service) ListPATS(ctx context.Context, session authn.Session, pm PATSPageMeta) (PATSPage, error) { + patsPage, err := svc.pats.RetrieveAll(ctx, session.UserID, pm) + if err != nil { + return PATSPage{}, errors.Wrap(errRetrievePAT, err) + } + return patsPage, nil +} + +func (svc service) DeletePAT(ctx context.Context, session authn.Session, patID string) error { + if err := svc.pats.Remove(ctx, session.UserID, patID); err != nil { + return errors.Wrap(errDeletePAT, err) + } + return nil +} + +func (svc service) ResetPATSecret(ctx context.Context, session authn.Session, patID string, duration time.Duration) (PAT, error) { + // Generate new HashToken take place here + secret, hash, err := svc.generateSecretAndHash(session.UserID, patID) + if err != nil { + return PAT{}, errors.Wrap(svcerr.ErrUpdateEntity, err) + } + + pat, err := svc.pats.UpdateTokenHash(ctx, session.UserID, patID, hash, time.Now().Add(duration)) + if err != nil { + return PAT{}, errors.Wrap(svcerr.ErrUpdateEntity, err) + } + + if err := svc.pats.Reactivate(ctx, session.UserID, patID); err != nil { + return PAT{}, errors.Wrap(svcerr.ErrUpdateEntity, err) + } + pat.Secret = secret + pat.Revoked = false + pat.RevokedAt = time.Time{} + return pat, nil +} + +func (svc service) RevokePATSecret(ctx context.Context, session authn.Session, patID string) error { + if err := svc.pats.Revoke(ctx, session.UserID, patID); err != nil { + return errors.Wrap(errRevokePAT, err) + } + return nil +} + +func (svc service) AddPATScopeEntry(ctx context.Context, session authn.Session, patID string, platformEntityType PlatformEntityType, optionalDomainID string, optionalDomainEntityType DomainEntityType, operation OperationType, entityIDs ...string) (Scope, error) { + scope, err := svc.pats.AddScopeEntry(ctx, session.UserID, 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, session authn.Session, patID string, platformEntityType PlatformEntityType, optionalDomainID string, optionalDomainEntityType DomainEntityType, operation OperationType, entityIDs ...string) (Scope, error) { + scope, err := svc.pats.RemoveScopeEntry(ctx, session.UserID, patID, platformEntityType, optionalDomainID, optionalDomainEntityType, operation, entityIDs...) + if err != nil { + return Scope{}, err + } + return scope, nil +} + +func (svc service) ClearPATAllScopeEntry(ctx context.Context, session authn.Session, patID string) error { + if err := svc.pats.RemoveAllScopeEntry(ctx, session.UserID, patID); err != nil { + return errors.Wrap(errClearAllScope, err) + } + return nil +} + +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, userID, patID string, platformEntityType PlatformEntityType, optionalDomainID string, optionalDomainEntityType DomainEntityType, operation OperationType, entityIDs ...string) error { + res, err := svc.RetrievePAT(ctx, userID, patID) + if 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) 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 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/users/middleware/authorization.go b/users/middleware/authorization.go index 772b8df374..6116858b49 100644 --- a/users/middleware/authorization.go +++ b/users/middleware/authorization.go @@ -172,6 +172,19 @@ func (am *authorizationMiddleware) SearchUsers(ctx context.Context, pm users.Pag } func (am *authorizationMiddleware) Update(ctx context.Context, session authn.Session, user users.User) (users.User, error) { + if session.Type == authn.PersonalAccessToken { + if err := am.authz.AuthorizePAT(ctx, mgauthz.PatReq{ + UserID: session.UserID, + PatID: session.ID, + PlatformEntityType: mgauth.PlatformUsersScope, + OptionalDomainEntityType: mgauth.DomainNullScope, + Operation: mgauth.UpdateOp, + EntityIDs: []string{user.ID}, + }); err != nil { + return users.User{}, errors.Wrap(svcerr.ErrUnauthorizedPAT, err) + } + } + if err := am.checkSuperAdmin(ctx, session.UserID); err == nil { session.SuperAdmin = true } @@ -305,9 +318,6 @@ func (am *authorizationMiddleware) SendPasswordReset(ctx context.Context, host, } func (am *authorizationMiddleware) UpdateRole(ctx context.Context, session authn.Session, user users.User) (users.User, error) { - if err := am.checkSuperAdmin(ctx, session.UserID); err != nil { - return users.User{}, err - } if session.Type == authn.PersonalAccessToken { if err := am.authz.AuthorizePAT(ctx, mgauthz.PatReq{ UserID: session.UserID, @@ -320,6 +330,10 @@ func (am *authorizationMiddleware) UpdateRole(ctx context.Context, session authn return users.User{}, errors.Wrap(svcerr.ErrUnauthorizedPAT, err) } } + + if err := am.checkSuperAdmin(ctx, session.UserID); err != nil { + return users.User{}, err + } session.SuperAdmin = true if err := am.authorize(ctx, "", policies.UserType, policies.UsersKind, user.ID, policies.MembershipPermission, policies.PlatformType, policies.SuperMQObject); err != nil { return users.User{}, err From 257e03b2b6f8278afb7c3b8418de8a900a504f08 Mon Sep 17 00:00:00 2001 From: nyagamunene Date: Thu, 31 Oct 2024 13:57:10 +0300 Subject: [PATCH 55/72] Remove pat folder Signed-off-by: nyagamunene --- pat/api/http/endpoint.go | 1 + pat/api/http/requests.go | 1 + pat/api/http/responses.go | 1 + pat/api/http/transport.go | 1 + pat/events/streams.go | 94 --------------------------------- pat/middleware/authorization.go | 1 + pat/middleware/logging.go | 1 + pat/middleware/metrics.go | 1 + pat/service.go | 1 + 9 files changed, 8 insertions(+), 94 deletions(-) delete mode 100644 pat/events/streams.go diff --git a/pat/api/http/endpoint.go b/pat/api/http/endpoint.go index e69de29bb2..8b13789179 100644 --- a/pat/api/http/endpoint.go +++ b/pat/api/http/endpoint.go @@ -0,0 +1 @@ + diff --git a/pat/api/http/requests.go b/pat/api/http/requests.go index e69de29bb2..8b13789179 100644 --- a/pat/api/http/requests.go +++ b/pat/api/http/requests.go @@ -0,0 +1 @@ + diff --git a/pat/api/http/responses.go b/pat/api/http/responses.go index e69de29bb2..8b13789179 100644 --- a/pat/api/http/responses.go +++ b/pat/api/http/responses.go @@ -0,0 +1 @@ + diff --git a/pat/api/http/transport.go b/pat/api/http/transport.go index e69de29bb2..8b13789179 100644 --- a/pat/api/http/transport.go +++ b/pat/api/http/transport.go @@ -0,0 +1 @@ + diff --git a/pat/events/streams.go b/pat/events/streams.go deleted file mode 100644 index a04bcbe0c9..0000000000 --- a/pat/events/streams.go +++ /dev/null @@ -1,94 +0,0 @@ -// Copyright (c) Abstract Machines -// SPDX-License-Identifier: Apache-2.0 - -package events - -import ( - "context" - "time" - - "github.com/absmach/magistrala/pat" - "github.com/absmach/magistrala/pkg/authn" - "github.com/absmach/magistrala/pkg/events" - "github.com/absmach/magistrala/pkg/events/store" -) - -const streamID = "magistrala.pat" - -var _ pat.Service = (*eventStore)(nil) - - -type eventStore struct { - events.Publisher - svc pat.Service -} - -// NewEventStoreMiddleware returns wrapper around pat service that sends -// events to event store. -func NewEventStoreMiddleware(ctx context.Context, svc pat.Service, url string) (pat.Service, error) { - publisher, err := store.NewPublisher(ctx, url, streamID) - if err != nil { - return nil, err - } - - return &eventStore{ - svc: svc, - Publisher: publisher, - }, nil -} - -func (es *eventStore) CreatePAT(ctx context.Context, session authn.Session, name, description string, duration time.Duration, scope pat.Scope) (pat.PAT, error) { - return es.svc.CreatePAT(ctx, session, name, description, duration, scope) -} - -func (es *eventStore) UpdatePATName(ctx context.Context, session authn.Session, patID, name string) (pat.PAT, error) { - return es.svc.UpdatePATName(ctx, session, patID, name) -} - -func (es *eventStore) UpdatePATDescription(ctx context.Context, session authn.Session, patID, description string) (pat.PAT, error) { - return es.svc.UpdatePATDescription(ctx, session, patID, description) -} - -func (es *eventStore) RetrievePAT(ctx context.Context, userID string, patID string) (pat.PAT, error) { - return es.svc.RetrievePAT(ctx, userID, patID) -} - -func (es *eventStore) ListPATS(ctx context.Context, session authn.Session, pm pat.PATSPageMeta) (pat.PATSPage, error) { - return es.svc.ListPATS(ctx, session, pm) -} - -func (es *eventStore) DeletePAT(ctx context.Context, session authn.Session, patID string) error { - return es.svc.DeletePAT(ctx, session, patID) -} - -func (es *eventStore) ResetPATSecret(ctx context.Context, session authn.Session, patID string, duration time.Duration) (pat.PAT, error) { - return es.svc.ResetPATSecret(ctx, session, patID, duration) -} - -func (es *eventStore) RevokePATSecret(ctx context.Context, session authn.Session, patID string) error { - return es.svc.RevokePATSecret(ctx, session, patID) -} - -func (es *eventStore) AddPATScopeEntry(ctx context.Context, session authn.Session, patID string, platformEntityType pat.PlatformEntityType, optionalDomainID string, optionalDomainEntityType pat.DomainEntityType, operation pat.OperationType, entityIDs ...string) (pat.Scope, error) { - return es.svc.AddPATScopeEntry(ctx, session, patID, platformEntityType, optionalDomainID, optionalDomainEntityType, operation, entityIDs...) -} - -func (es *eventStore) RemovePATScopeEntry(ctx context.Context, session authn.Session, patID string, platformEntityType pat.PlatformEntityType, optionalDomainID string, optionalDomainEntityType pat.DomainEntityType, operation pat.OperationType, entityIDs ...string) (pat.Scope, error) { - return es.svc.RemovePATScopeEntry(ctx, session, patID, platformEntityType, optionalDomainID, optionalDomainEntityType, operation, entityIDs...) -} - -func (es *eventStore) ClearPATAllScopeEntry(ctx context.Context, session authn.Session, patID string) error { - return es.svc.ClearPATAllScopeEntry(ctx, session, patID) -} - -func (es *eventStore) IdentifyPAT(ctx context.Context, paToken string) (pat.PAT, error) { - return es.svc.IdentifyPAT(ctx, paToken) -} - -func (es *eventStore) AuthorizePAT(ctx context.Context, userID, patID string, platformEntityType pat.PlatformEntityType, optionalDomainID string, optionalDomainEntityType pat.DomainEntityType, operation pat.OperationType, entityIDs ...string) error { - return es.svc.AuthorizePAT(ctx, userID, patID, platformEntityType, optionalDomainID, optionalDomainEntityType, operation, entityIDs...) -} - -func (es *eventStore) CheckPAT(ctx context.Context, userID, patID string, platformEntityType pat.PlatformEntityType, optionalDomainID string, optionalDomainEntityType pat.DomainEntityType, operation pat.OperationType, entityIDs ...string) error { - return es.svc.CheckPAT(ctx, userID, patID, platformEntityType, optionalDomainID, optionalDomainEntityType, operation, entityIDs...) -} \ No newline at end of file diff --git a/pat/middleware/authorization.go b/pat/middleware/authorization.go index cdd27958f1..2f39fb3928 100644 --- a/pat/middleware/authorization.go +++ b/pat/middleware/authorization.go @@ -83,3 +83,4 @@ func (am *authorizationMiddleware) CheckPAT(ctx context.Context, userID, patID s return am.svc.CheckPAT(ctx, userID, patID, platformEntityType, optionalDomainID, optionalDomainEntityType, operation, entityIDs...) } + diff --git a/pat/middleware/logging.go b/pat/middleware/logging.go index b0d096676a..9d1db1a5e6 100644 --- a/pat/middleware/logging.go +++ b/pat/middleware/logging.go @@ -274,3 +274,4 @@ func (lm *loggingMiddleware) CheckPAT(ctx context.Context, userID, patID string, return lm.svc.CheckPAT(ctx, userID, patID, platformEntityType, optionalDomainID, optionalDomainEntityType, operation, entityIDs...) } + diff --git a/pat/middleware/metrics.go b/pat/middleware/metrics.go index d2898f3b29..4064533a18 100644 --- a/pat/middleware/metrics.go +++ b/pat/middleware/metrics.go @@ -141,3 +141,4 @@ func (ms *metricsMiddleware) CheckPAT(ctx context.Context, userID, patID string, return ms.svc.CheckPAT(ctx, userID, patID, platformEntityType, optionalDomainID, optionalDomainEntityType, operation, entityIDs...) } + diff --git a/pat/service.go b/pat/service.go index 571cf6551b..3c5a7d3a67 100644 --- a/pat/service.go +++ b/pat/service.go @@ -258,3 +258,4 @@ func generateRandomString(n int) string { return string(b) } + From 7df363b2fa56edf3f5b637b304581281fb881278 Mon Sep 17 00:00:00 2001 From: nyagamunene Date: Thu, 31 Oct 2024 15:34:56 +0300 Subject: [PATCH 56/72] Update user and things middleware Signed-off-by: nyagamunene --- pat/api/endpoint.go | 259 ----------------------- pat/api/http/endpoint.go | 1 - pat/api/http/requests.go | 1 - pat/api/http/responses.go | 1 - pat/api/http/transport.go | 1 - pat/api/requests.go | 361 -------------------------------- pat/api/responses.go | 208 ------------------ pat/api/transport.go | 271 ------------------------ pat/middleware/authorization.go | 86 -------- pat/middleware/logging.go | 277 ------------------------ pat/middleware/metrics.go | 144 ------------- pat/service.go | 261 ----------------------- pat/tracing/tracing.go | 164 --------------- 13 files changed, 2035 deletions(-) delete mode 100644 pat/api/endpoint.go delete mode 100644 pat/api/http/endpoint.go delete mode 100644 pat/api/http/requests.go delete mode 100644 pat/api/http/responses.go delete mode 100644 pat/api/http/transport.go delete mode 100644 pat/api/requests.go delete mode 100644 pat/api/responses.go delete mode 100644 pat/api/transport.go delete mode 100644 pat/middleware/authorization.go delete mode 100644 pat/middleware/logging.go delete mode 100644 pat/middleware/metrics.go delete mode 100644 pat/service.go delete mode 100644 pat/tracing/tracing.go diff --git a/pat/api/endpoint.go b/pat/api/endpoint.go deleted file mode 100644 index 94cbdf5e80..0000000000 --- a/pat/api/endpoint.go +++ /dev/null @@ -1,259 +0,0 @@ -// Copyright (c) Abstract Machines -// SPDX-License-Identifier: Apache-2.0 - -package pats - -import ( - "context" - - "github.com/absmach/magistrala/internal/api" - "github.com/absmach/magistrala/pat" - "github.com/absmach/magistrala/pkg/authn" - svcerr "github.com/absmach/magistrala/pkg/errors/service" - "github.com/go-kit/kit/endpoint" -) - -func createPATEndpoint(svc pat.Service) endpoint.Endpoint { - return func(ctx context.Context, request interface{}) (interface{}, error) { - req := request.(createPatReq) - if err := req.validate(); err != nil { - return nil, err - } - - session, ok := ctx.Value(api.SessionKey).(authn.Session) - if !ok { - return nil, svcerr.ErrAuthentication - } - - pat, err := svc.CreatePAT(ctx, session, req.Name, req.Description, req.Duration, req.Scope) - if err != nil { - return nil, err - } - - return createPatRes{pat}, nil - } -} - -func retrievePATEndpoint(svc pat.Service) endpoint.Endpoint { - return func(ctx context.Context, request interface{}) (interface{}, error) { - req := request.(retrievePatReq) - if err := req.validate(); err != nil { - return nil, err - } - - session, ok := ctx.Value(api.SessionKey).(authn.Session) - if !ok { - return nil, svcerr.ErrAuthentication - } - - pat, err := svc.RetrievePAT(ctx, session, req.id) - if err != nil { - return nil, err - } - - return retrievePatRes{pat}, nil - } -} - -func updatePATNameEndpoint(svc pat.Service) endpoint.Endpoint { - return func(ctx context.Context, request interface{}) (interface{}, error) { - req := request.(updatePatNameReq) - if err := req.validate(); err != nil { - return nil, err - } - - session, ok := ctx.Value(api.SessionKey).(authn.Session) - if !ok { - return nil, svcerr.ErrAuthentication - } - pat, err := svc.UpdatePATName(ctx, session, req.id, req.Name) - if err != nil { - return nil, err - } - - return updatePatNameRes{pat}, nil - } -} - -func updatePATDescriptionEndpoint(svc pat.Service) endpoint.Endpoint { - return func(ctx context.Context, request interface{}) (interface{}, error) { - req := request.(updatePatDescriptionReq) - if err := req.validate(); err != nil { - return nil, err - } - - session, ok := ctx.Value(api.SessionKey).(authn.Session) - if !ok { - return nil, svcerr.ErrAuthentication - } - - pat, err := svc.UpdatePATDescription(ctx, session, req.id, req.Description) - if err != nil { - return nil, err - } - - return updatePatDescriptionRes{pat}, nil - } -} - -func listPATSEndpoint(svc pat.Service) endpoint.Endpoint { - return func(ctx context.Context, request interface{}) (interface{}, error) { - req := request.(listPatsReq) - if err := req.validate(); err != nil { - return nil, err - } - - session, ok := ctx.Value(api.SessionKey).(authn.Session) - if !ok { - return nil, svcerr.ErrAuthentication - } - - pm := pat.PATSPageMeta{ - Limit: req.limit, - Offset: req.offset, - } - patsPage, err := svc.ListPATS(ctx, session, pm) - if err != nil { - return nil, err - } - - return listPatsRes{patsPage}, nil - } -} - -func deletePATEndpoint(svc pat.Service) endpoint.Endpoint { - return func(ctx context.Context, request interface{}) (interface{}, error) { - req := request.(deletePatReq) - if err := req.validate(); err != nil { - return nil, err - } - - session, ok := ctx.Value(api.SessionKey).(authn.Session) - if !ok { - return nil, svcerr.ErrAuthentication - } - - if err := svc.DeletePAT(ctx, session, req.id); err != nil { - return nil, err - } - - return deletePatRes{}, nil - } -} - -func resetPATSecretEndpoint(svc pat.Service) endpoint.Endpoint { - return func(ctx context.Context, request interface{}) (interface{}, error) { - req := request.(resetPatSecretReq) - if err := req.validate(); err != nil { - return nil, err - } - - session, ok := ctx.Value(api.SessionKey).(authn.Session) - if !ok { - return nil, svcerr.ErrAuthentication - } - - pat, err := svc.ResetPATSecret(ctx, session, req.id, req.Duration) - if err != nil { - return nil, err - } - - return resetPatSecretRes{pat}, nil - } -} - -func revokePATSecretEndpoint(svc pat.Service) endpoint.Endpoint { - return func(ctx context.Context, request interface{}) (interface{}, error) { - req := request.(revokePatSecretReq) - if err := req.validate(); err != nil { - return nil, err - } - - session, ok := ctx.Value(api.SessionKey).(authn.Session) - if !ok { - return nil, svcerr.ErrAuthentication - } - - if err := svc.RevokePATSecret(ctx, session, req.id); err != nil { - return nil, err - } - - return revokePatSecretRes{}, nil - } -} - -func addPATScopeEntryEndpoint(svc pat.Service) endpoint.Endpoint { - return func(ctx context.Context, request interface{}) (interface{}, error) { - req := request.(addPatScopeEntryReq) - if err := req.validate(); err != nil { - return nil, err - } - - session, ok := ctx.Value(api.SessionKey).(authn.Session) - if !ok { - return nil, svcerr.ErrAuthentication - } - - scope, err := svc.AddPATScopeEntry(ctx, session, req.id, req.PlatformEntityType, req.OptionalDomainID, req.OptionalDomainEntityType, req.Operation, req.EntityIDs...) - if err != nil { - return nil, err - } - - return addPatScopeEntryRes{scope}, nil - } -} - -func removePATScopeEntryEndpoint(svc pat.Service) endpoint.Endpoint { - return func(ctx context.Context, request interface{}) (interface{}, error) { - req := request.(removePatScopeEntryReq) - if err := req.validate(); err != nil { - return nil, err - } - - session, ok := ctx.Value(api.SessionKey).(authn.Session) - if !ok { - return nil, svcerr.ErrAuthentication - } - - scope, err := svc.RemovePATScopeEntry(ctx, session, req.id, req.PlatformEntityType, req.OptionalDomainID, req.OptionalDomainEntityType, req.Operation, req.EntityIDs...) - if err != nil { - return nil, err - } - return removePatScopeEntryRes{scope}, nil - } -} - -func clearPATAllScopeEntryEndpoint(svc pat.Service) endpoint.Endpoint { - return func(ctx context.Context, request interface{}) (interface{}, error) { - req := request.(clearAllScopeEntryReq) - if err := req.validate(); err != nil { - return nil, err - } - - session, ok := ctx.Value(api.SessionKey).(authn.Session) - if !ok { - return nil, svcerr.ErrAuthentication - } - - if err := svc.ClearPATAllScopeEntry(ctx, session, req.id); err != nil { - return nil, err - } - - return clearAllScopeEntryRes{}, nil - } -} - -func authorizePATEndpoint(svc pat.Service) endpoint.Endpoint { - return func(ctx context.Context, request interface{}) (interface{}, error) { - req := request.(authorizePATReq) - if err := req.validate(); err != nil { - return nil, err - } - - if err := svc.AuthorizePAT(ctx, req.token, req.PlatformEntityType, req.OptionalDomainID, req.OptionalDomainEntityType, req.Operation, req.EntityIDs...); err != nil { - return nil, err - } - - return authorizePATRes{}, nil - } -} diff --git a/pat/api/http/endpoint.go b/pat/api/http/endpoint.go deleted file mode 100644 index 8b13789179..0000000000 --- a/pat/api/http/endpoint.go +++ /dev/null @@ -1 +0,0 @@ - diff --git a/pat/api/http/requests.go b/pat/api/http/requests.go deleted file mode 100644 index 8b13789179..0000000000 --- a/pat/api/http/requests.go +++ /dev/null @@ -1 +0,0 @@ - diff --git a/pat/api/http/responses.go b/pat/api/http/responses.go deleted file mode 100644 index 8b13789179..0000000000 --- a/pat/api/http/responses.go +++ /dev/null @@ -1 +0,0 @@ - diff --git a/pat/api/http/transport.go b/pat/api/http/transport.go deleted file mode 100644 index 8b13789179..0000000000 --- a/pat/api/http/transport.go +++ /dev/null @@ -1 +0,0 @@ - diff --git a/pat/api/requests.go b/pat/api/requests.go deleted file mode 100644 index 1822ec457e..0000000000 --- a/pat/api/requests.go +++ /dev/null @@ -1,361 +0,0 @@ -// Copyright (c) Abstract Machines -// SPDX-License-Identifier: Apache-2.0 - -package pats - -import ( - "encoding/json" - "strings" - "time" - - "github.com/absmach/magistrala/pat" - "github.com/absmach/magistrala/pkg/apiutil" -) - -type createPatReq struct { - token string - Name string `json:"name,omitempty"` - Description string `json:"description,omitempty"` - Duration time.Duration `json:"duration,omitempty"` - Scope pat.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 pat.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 - } - - 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 pat.PlatformEntityType `json:"platform_entity_type,omitempty"` - OptionalDomainID string `json:"optional_domain_id,omitempty"` - OptionalDomainEntityType pat.DomainEntityType `json:"optional_domain_entity_type,omitempty"` - Operation pat.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 := pat.ParsePlatformEntityType(temp.PlatformEntityType) - if err != nil { - return err - } - odt, err := pat.ParseDomainEntityType(temp.OptionalDomainEntityType) - if err != nil { - return err - } - op, err := pat.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 pat.PlatformEntityType `json:"platform_entity_type,omitempty"` - OptionalDomainID string `json:"optional_domain_id,omitempty"` - OptionalDomainEntityType pat.DomainEntityType `json:"optional_domain_entity_type,omitempty"` - Operation pat.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 := pat.ParsePlatformEntityType(temp.PlatformEntityType) - if err != nil { - return err - } - odt, err := pat.ParseDomainEntityType(temp.OptionalDomainEntityType) - if err != nil { - return err - } - op, err := pat.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 authorizePATReq struct { - token string - PlatformEntityType pat.PlatformEntityType `json:"platform_entity_type,omitempty"` - OptionalDomainID string `json:"optional_domain_id,omitempty"` - OptionalDomainEntityType pat.DomainEntityType `json:"optional_domain_entity_type,omitempty"` - Operation pat.OperationType `json:"operation,omitempty"` - EntityIDs []string `json:"entity_ids,omitempty"` -} - -func (tcpsr *authorizePATReq) 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 - } - - tcpsr.OptionalDomainID = temp.OptionalDomainID - tcpsr.EntityIDs = temp.EntityIDs - - pet, err := pat.ParsePlatformEntityType(temp.PlatformEntityType) - if err != nil { - return err - } - tcpsr.PlatformEntityType = pet - - if temp.OptionalDomainEntityType != "" { - odt, err := pat.ParseDomainEntityType(temp.OptionalDomainEntityType) - if err != nil { - return err - } - tcpsr.OptionalDomainEntityType = odt - } - - if temp.OptionalDomainID != "" { - op, err := pat.ParseOperationType(temp.Operation) - if err != nil { - return err - } - tcpsr.Operation = op - } - - return nil -} - -func (req authorizePATReq) validate() (err error) { - if req.token == "" { - return apiutil.ErrBearerToken - } - - return nil -} diff --git a/pat/api/responses.go b/pat/api/responses.go deleted file mode 100644 index b2d0fe57de..0000000000 --- a/pat/api/responses.go +++ /dev/null @@ -1,208 +0,0 @@ -// Copyright (c) Abstract Machines -// SPDX-License-Identifier: Apache-2.0 - -package pats - -import ( - "net/http" - - "github.com/absmach/magistrala" - "github.com/absmach/magistrala/pat" -) - -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 { - pat.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 { - pat.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 { - pat.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 { - pat.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 { - pat.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 { - pat.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 { - pat.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 { - pat.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 authorizePATRes struct{} - -func (res authorizePATRes) Code() int { - return http.StatusNoContent -} - -func (res authorizePATRes) Headers() map[string]string { - return map[string]string{} -} - -func (res authorizePATRes) Empty() bool { - return true -} diff --git a/pat/api/transport.go b/pat/api/transport.go deleted file mode 100644 index 95d0f45efa..0000000000 --- a/pat/api/transport.go +++ /dev/null @@ -1,271 +0,0 @@ -// 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/internal/api" - "github.com/absmach/magistrala/pat" - "github.com/absmach/magistrala/pkg/apiutil" - mgauthn "github.com/absmach/magistrala/pkg/authn" - "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 pat.Service, mux *chi.Mux, authn mgauthn.Authentication, logger *slog.Logger) *chi.Mux { - opts := []kithttp.ServerOption{ - kithttp.ServerErrorEncoder(apiutil.LoggingErrorEncoder(logger, api.EncodeError)), - } - mux.Group(func(r chi.Router) { - mux.Use(api.AuthenticateMiddleware(authn, true)) - - mux.Route("/pats", func(r chi.Router) { - r.Post("/", kithttp.NewServer( - createPATEndpoint(svc), - decodeCreatePATRequest, - api.EncodeResponse, - opts..., - ).ServeHTTP) - - r.Get("/{id}", kithttp.NewServer( - (retrievePATEndpoint(svc)), - decodeRetrievePATRequest, - api.EncodeResponse, - opts..., - ).ServeHTTP) - - r.Put("/{id}/name", kithttp.NewServer( - (updatePATNameEndpoint(svc)), - decodeUpdatePATNameRequest, - api.EncodeResponse, - opts..., - ).ServeHTTP) - - r.Put("/{id}/description", kithttp.NewServer( - (updatePATDescriptionEndpoint(svc)), - decodeUpdatePATDescriptionRequest, - api.EncodeResponse, - opts..., - ).ServeHTTP) - - r.Get("/", kithttp.NewServer( - (listPATSEndpoint(svc)), - decodeListPATSRequest, - api.EncodeResponse, - opts..., - ).ServeHTTP) - - r.Delete("/{id}", kithttp.NewServer( - (deletePATEndpoint(svc)), - decodeDeletePATRequest, - api.EncodeResponse, - opts..., - ).ServeHTTP) - - r.Put("/{id}/secret/reset", kithttp.NewServer( - (resetPATSecretEndpoint(svc)), - decodeResetPATSecretRequest, - api.EncodeResponse, - opts..., - ).ServeHTTP) - - r.Put("/{id}/secret/revoke", kithttp.NewServer( - (revokePATSecretEndpoint(svc)), - decodeRevokePATSecretRequest, - api.EncodeResponse, - opts..., - ).ServeHTTP) - - r.Put("/{id}/scope/add", kithttp.NewServer( - (addPATScopeEntryEndpoint(svc)), - decodeAddPATScopeEntryRequest, - api.EncodeResponse, - opts..., - ).ServeHTTP) - - r.Put("/{id}/scope/remove", kithttp.NewServer( - (removePATScopeEntryEndpoint(svc)), - decodeRemovePATScopeEntryRequest, - api.EncodeResponse, - opts..., - ).ServeHTTP) - - r.Delete("/{id}/scope", kithttp.NewServer( - (clearPATAllScopeEntryEndpoint(svc)), - decodeClearPATAllScopeEntryRequest, - api.EncodeResponse, - opts..., - ).ServeHTTP) - - r.Post("/authorize", kithttp.NewServer( - (authorizePATEndpoint(svc)), - decodeAuthorizePATRequest, - 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 decodeUpdatePATNameRequest(_ 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 decodeUpdatePATDescriptionRequest(_ 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 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) - } - 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, - } - return req, nil -} - -func decodeDeletePATRequest(_ context.Context, r *http.Request) (interface{}, error) { - return deletePatReq{ - token: apiutil.ExtractBearerToken(r), - id: chi.URLParam(r, "id"), - }, nil -} - -func decodeResetPATSecretRequest(_ 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 decodeRevokePATSecretRequest(_ context.Context, r *http.Request) (interface{}, error) { - return revokePatSecretReq{ - token: apiutil.ExtractBearerToken(r), - id: chi.URLParam(r, "id"), - }, nil -} - -func decodeAddPATScopeEntryRequest(_ 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 decodeRemovePATScopeEntryRequest(_ 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 decodeClearPATAllScopeEntryRequest(_ 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 decodeAuthorizePATRequest(_ context.Context, r *http.Request) (interface{}, error) { - if !strings.Contains(r.Header.Get("Content-Type"), contentType) { - return nil, apiutil.ErrUnsupportedContentType - } - - req := authorizePATReq{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/pat/middleware/authorization.go b/pat/middleware/authorization.go deleted file mode 100644 index 2f39fb3928..0000000000 --- a/pat/middleware/authorization.go +++ /dev/null @@ -1,86 +0,0 @@ -// Copyright (c) Abstract Machines -// SPDX-License-Identifier: Apache-2.0 - -package middleware - -import ( - "context" - "time" - - "github.com/absmach/magistrala/pat" - "github.com/absmach/magistrala/pkg/authn" - mgauthz "github.com/absmach/magistrala/pkg/authz" -) - -var _ pat.Service = (*authorizationMiddleware)(nil) - -type authorizationMiddleware struct { - svc pat.Service - authz mgauthz.Authorization -} - -// AuthorizationMiddleware adds authorization to the clients service. -func AuthorizationMiddleware(entityType string, svc pat.Service, authz mgauthz.Authorization) (pat.Service, error) { - return &authorizationMiddleware{ - svc: svc, - authz: authz, - }, nil -} - -func (am *authorizationMiddleware) CreatePAT(ctx context.Context, session authn.Session, name, description string, duration time.Duration, scope pat.Scope) (pat.PAT, error) { - return am.svc.CreatePAT(ctx, session, name, description, duration, scope) -} - -func (am *authorizationMiddleware) UpdatePATName(ctx context.Context, session authn.Session, patID, name string) (pat.PAT, error) { - return am.svc.UpdatePATName(ctx, session, patID, name) -} - -func (am *authorizationMiddleware) UpdatePATDescription(ctx context.Context, session authn.Session, patID, description string) (pat.PAT, error) { - return am.svc.UpdatePATDescription(ctx, session, patID, description) -} - -func (am *authorizationMiddleware) RetrievePAT(ctx context.Context, userID string, patID string) (pat.PAT, error) { - return am.svc.RetrievePAT(ctx, userID, patID) -} - -func (am *authorizationMiddleware) ListPATS(ctx context.Context, session authn.Session, pm pat.PATSPageMeta) (pat.PATSPage, error) { - return am.svc.ListPATS(ctx, session, pm) -} - -func (am *authorizationMiddleware) DeletePAT(ctx context.Context, session authn.Session, patID string) error { - return am.svc.DeletePAT(ctx, session, patID) -} - -func (am *authorizationMiddleware) ResetPATSecret(ctx context.Context, session authn.Session, patID string, duration time.Duration) (pat.PAT, error) { - return am.svc.ResetPATSecret(ctx, session, patID, duration) -} - -func (am *authorizationMiddleware) RevokePATSecret(ctx context.Context, session authn.Session, patID string) error { - return am.svc.RevokePATSecret(ctx, session, patID) -} - -func (am *authorizationMiddleware) AddPATScopeEntry(ctx context.Context, session authn.Session, patID string, platformEntityType pat.PlatformEntityType, optionalDomainID string, optionalDomainEntityType pat.DomainEntityType, operation pat.OperationType, entityIDs ...string) (pat.Scope, error) { - return am.svc.AddPATScopeEntry(ctx, session, patID, platformEntityType, optionalDomainID, optionalDomainEntityType, operation, entityIDs...) -} - -func (am *authorizationMiddleware) RemovePATScopeEntry(ctx context.Context, session authn.Session, patID string, platformEntityType pat.PlatformEntityType, optionalDomainID string, optionalDomainEntityType pat.DomainEntityType, operation pat.OperationType, entityIDs ...string) (pat.Scope, error) { - return am.svc.RemovePATScopeEntry(ctx, session, patID, platformEntityType, optionalDomainID, optionalDomainEntityType, operation, entityIDs...) -} - -func (am *authorizationMiddleware) ClearPATAllScopeEntry(ctx context.Context, session authn.Session, patID string) error { - return am.svc.ClearPATAllScopeEntry(ctx, session, patID) -} - -func (am *authorizationMiddleware) IdentifyPAT(ctx context.Context, secret string) (pat.PAT, error) { - return am.svc.IdentifyPAT(ctx, secret) -} - -func (am *authorizationMiddleware) AuthorizePAT(ctx context.Context, userID, patID string, platformEntityType pat.PlatformEntityType, optionalDomainID string, optionalDomainEntityType pat.DomainEntityType, operation pat.OperationType, entityIDs ...string) error { - return am.svc.AuthorizePAT(ctx, userID, patID, platformEntityType, optionalDomainID, optionalDomainEntityType, operation, entityIDs...) -} - -func (am *authorizationMiddleware) CheckPAT(ctx context.Context, userID, patID string, platformEntityType pat.PlatformEntityType, optionalDomainID string, optionalDomainEntityType pat.DomainEntityType, operation pat.OperationType, entityIDs ...string) error { - return am.svc.CheckPAT(ctx, userID, patID, platformEntityType, optionalDomainID, optionalDomainEntityType, operation, entityIDs...) -} - - diff --git a/pat/middleware/logging.go b/pat/middleware/logging.go deleted file mode 100644 index 9d1db1a5e6..0000000000 --- a/pat/middleware/logging.go +++ /dev/null @@ -1,277 +0,0 @@ -// Copyright (c) Abstract Machines -// SPDX-License-Identifier: Apache-2.0 - -package middleware - -import ( - "context" - "log/slog" - "time" - - "github.com/absmach/magistrala/pat" - "github.com/absmach/magistrala/pkg/authn" -) - -var _ pat.Service = (*loggingMiddleware)(nil) - -type loggingMiddleware struct { - logger *slog.Logger - svc pat.Service -} - -// LoggingMiddleware adds logging facilities to the core service. -func LoggingMiddleware(svc pat.Service, logger *slog.Logger) pat.Service { - return &loggingMiddleware{logger, svc} -} - -func (lm *loggingMiddleware) CreatePAT(ctx context.Context, session authn.Session, name, description string, duration time.Duration, scope pat.Scope) (pa pat.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.CreatePAT(ctx, session, name, description, duration, scope) -} - -func (lm *loggingMiddleware) UpdatePATName(ctx context.Context, session authn.Session, patID, name string) (pa pat.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.UpdatePATName(ctx, session, patID, name) -} - -func (lm *loggingMiddleware) UpdatePATDescription(ctx context.Context, session authn.Session, patID, description string) (pa pat.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.UpdatePATDescription(ctx, session, patID, description) -} - -func (lm *loggingMiddleware) RetrievePAT(ctx context.Context, userID string, patID string) (pa pat.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.RetrievePAT(ctx, userID, patID) -} - -func (lm *loggingMiddleware) ListPATS(ctx context.Context, session authn.Session, pm pat.PATSPageMeta) (pp pat.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.ListPATS(ctx, session, pm) -} - -func (lm *loggingMiddleware) DeletePAT(ctx context.Context, session authn.Session, 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.DeletePAT(ctx, session, patID) -} - -func (lm *loggingMiddleware) ResetPATSecret(ctx context.Context, session authn.Session, patID string, duration time.Duration) (pa pat.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.ResetPATSecret(ctx, session, patID, duration) -} - -func (lm *loggingMiddleware) RevokePATSecret(ctx context.Context, session authn.Session, 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.RevokePATSecret(ctx, session, patID) -} - -func (lm *loggingMiddleware) AddPATScopeEntry(ctx context.Context, session authn.Session, patID string, platformEntityType pat.PlatformEntityType, optionalDomainID string, optionalDomainEntityType pat.DomainEntityType, operation pat.OperationType, entityIDs ...string) (sc pat.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.AddPATScopeEntry(ctx, session, patID, platformEntityType, optionalDomainID, optionalDomainEntityType, operation, entityIDs...) -} - -func (lm *loggingMiddleware) RemovePATScopeEntry(ctx context.Context, session authn.Session, patID string, platformEntityType pat.PlatformEntityType, optionalDomainID string, optionalDomainEntityType pat.DomainEntityType, operation pat.OperationType, entityIDs ...string) (sc pat.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.RemovePATScopeEntry(ctx, session, patID, platformEntityType, optionalDomainID, optionalDomainEntityType, operation, entityIDs...) -} - -func (lm *loggingMiddleware) ClearPATAllScopeEntry(ctx context.Context, session authn.Session, 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.ClearPATAllScopeEntry(ctx, session, patID) -} - -func (lm *loggingMiddleware) IdentifyPAT(ctx context.Context, paToken string) (pa pat.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, userID, patID string, platformEntityType pat.PlatformEntityType, optionalDomainID string, optionalDomainEntityType pat.DomainEntityType, operation pat.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("Authorize PAT failed complete successfully", args...) - return - } - lm.logger.Info("Authorize PAT completed successfully", args...) - }(time.Now()) - return lm.svc.AuthorizePAT(ctx, userID, patID, platformEntityType, optionalDomainID, optionalDomainEntityType, operation, entityIDs...) -} - -func (lm *loggingMiddleware) CheckPAT(ctx context.Context, userID, patID string, platformEntityType pat.PlatformEntityType, optionalDomainID string, optionalDomainEntityType pat.DomainEntityType, operation pat.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("Check PAT failed complete successfully", args...) - return - } - lm.logger.Info("Check PAT completed successfully", args...) - }(time.Now()) - return lm.svc.CheckPAT(ctx, userID, patID, platformEntityType, optionalDomainID, optionalDomainEntityType, operation, entityIDs...) -} - - diff --git a/pat/middleware/metrics.go b/pat/middleware/metrics.go deleted file mode 100644 index 4064533a18..0000000000 --- a/pat/middleware/metrics.go +++ /dev/null @@ -1,144 +0,0 @@ -// Copyright (c) Abstract Machines -// SPDX-License-Identifier: Apache-2.0 - -package middleware - -import ( - "context" - "time" - - "github.com/absmach/magistrala/pat" - "github.com/absmach/magistrala/pkg/authn" - "github.com/go-kit/kit/metrics" -) - -var _ pat.Service = (*metricsMiddleware)(nil) - -type metricsMiddleware struct { - counter metrics.Counter - latency metrics.Histogram - svc pat.Service -} - -// MetricsMiddleware instruments core service by tracking request count and latency. -func MetricsMiddleware(svc pat.Service, counter metrics.Counter, latency metrics.Histogram) pat.Service { - return &metricsMiddleware{ - counter: counter, - latency: latency, - svc: svc, - } -} - -func (ms *metricsMiddleware) CreatePAT(ctx context.Context, session authn.Session, name, description string, duration time.Duration, scope pat.Scope) (pat.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.CreatePAT(ctx, session, name, description, duration, scope) -} - -func (ms *metricsMiddleware) UpdatePATName(ctx context.Context, session authn.Session, patID, name string) (pat.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.UpdatePATName(ctx, session, patID, name) -} - -func (ms *metricsMiddleware) UpdatePATDescription(ctx context.Context, session authn.Session, patID, description string) (pat.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.UpdatePATDescription(ctx, session, patID, description) -} - -func (ms *metricsMiddleware) RetrievePAT(ctx context.Context, userID string, patID string) (pat.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.RetrievePAT(ctx, userID, patID) -} - -func (ms *metricsMiddleware) ListPATS(ctx context.Context, session authn.Session, pm pat.PATSPageMeta) (pat.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.ListPATS(ctx, session, pm) -} - -func (ms *metricsMiddleware) DeletePAT(ctx context.Context, session authn.Session, 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.DeletePAT(ctx, session, patID) -} - -func (ms *metricsMiddleware) ResetPATSecret(ctx context.Context, session authn.Session, patID string, duration time.Duration) (pat.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.ResetPATSecret(ctx, session, patID, duration) -} - -func (ms *metricsMiddleware) RevokePATSecret(ctx context.Context, session authn.Session, 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.RevokePATSecret(ctx, session, patID) -} - -func (ms *metricsMiddleware) AddPATScopeEntry(ctx context.Context, session authn.Session, patID string, platformEntityType pat.PlatformEntityType, optionalDomainID string, optionalDomainEntityType pat.DomainEntityType, operation pat.OperationType, entityIDs ...string) (pat.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.AddPATScopeEntry(ctx, session, patID, platformEntityType, optionalDomainID, optionalDomainEntityType, operation, entityIDs...) -} - -func (ms *metricsMiddleware) RemovePATScopeEntry(ctx context.Context, session authn.Session, patID string, platformEntityType pat.PlatformEntityType, optionalDomainID string, optionalDomainEntityType pat.DomainEntityType, operation pat.OperationType, entityIDs ...string) (pat.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.RemovePATScopeEntry(ctx, session, patID, platformEntityType, optionalDomainID, optionalDomainEntityType, operation, entityIDs...) -} - -func (ms *metricsMiddleware) ClearPATAllScopeEntry(ctx context.Context, session authn.Session, 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.ClearPATAllScopeEntry(ctx, session, patID) -} - -func (ms *metricsMiddleware) IdentifyPAT(ctx context.Context, paToken string) (pat.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, userID, patID string, platformEntityType pat.PlatformEntityType, optionalDomainID string, optionalDomainEntityType pat.DomainEntityType, operation pat.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, userID, patID, platformEntityType, optionalDomainID, optionalDomainEntityType, operation, entityIDs...) -} - -func (ms *metricsMiddleware) CheckPAT(ctx context.Context, userID, patID string, platformEntityType pat.PlatformEntityType, optionalDomainID string, optionalDomainEntityType pat.DomainEntityType, operation pat.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/pat/service.go b/pat/service.go deleted file mode 100644 index 3c5a7d3a67..0000000000 --- a/pat/service.go +++ /dev/null @@ -1,261 +0,0 @@ -// Copyright (c) Abstract Machines -// SPDX-License-Identifier: Apache-2.0 - -package pat - -import ( - "context" - "encoding/base64" - "math/rand" - "strings" - "time" - - "github.com/absmach/magistrala" - "github.com/absmach/magistrala/pkg/authn" - "github.com/absmach/magistrala/pkg/errors" - svcerr "github.com/absmach/magistrala/pkg/errors/service" - "github.com/google/uuid" -) - -const ( - randStr = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890!@#$%^&&*|+-=" - patPrefix = "pat" - patSecretSeparator = "_" -) - -var ( - 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") - 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") - errClearAllScope = errors.New("failed to clear all entry in scope") -) - -type service struct { - pats PATSRepository - hasher Hasher - idProvider magistrala.IDProvider -} - -var _ Service = (*service)(nil) - -// New instantiates the auth service implementation. -func New(pats PATSRepository, hasher Hasher, idp magistrala.IDProvider) Service { - return &service{ - pats: pats, - hasher: hasher, - idProvider: idp, - } -} - -func (svc service) CreatePAT(ctx context.Context, session authn.Session, name, description string, duration time.Duration, scope Scope) (PAT, error) { - id, err := svc.idProvider.ID() - if err != nil { - return PAT{}, errors.Wrap(svcerr.ErrCreateEntity, err) - } - secret, hash, err := svc.generateSecretAndHash(session.UserID, id) - if err != nil { - return PAT{}, errors.Wrap(svcerr.ErrCreateEntity, err) - } - - now := time.Now() - pat := PAT{ - ID: id, - User: session.UserID, - 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 -} - -func (svc service) UpdatePATName(ctx context.Context, session authn.Session, patID, name string) (PAT, error) { - pat, err := svc.pats.UpdateName(ctx, session.UserID, patID, name) - if err != nil { - return PAT{}, errors.Wrap(errUpdatePAT, err) - } - return pat, nil -} - -func (svc service) UpdatePATDescription(ctx context.Context, session authn.Session, patID, description string) (PAT, error) { - pat, err := svc.pats.UpdateDescription(ctx, session.UserID, patID, description) - if err != nil { - return PAT{}, errors.Wrap(errUpdatePAT, err) - } - return pat, nil -} - -func (svc service) RetrievePAT(ctx context.Context, userID string, patID string) (PAT, error) { - pat, err := svc.pats.Retrieve(ctx, userID, patID) - if err != nil { - return PAT{}, errors.Wrap(errRetrievePAT, err) - } - return pat, nil -} - -func (svc service) ListPATS(ctx context.Context, session authn.Session, pm PATSPageMeta) (PATSPage, error) { - patsPage, err := svc.pats.RetrieveAll(ctx, session.UserID, pm) - if err != nil { - return PATSPage{}, errors.Wrap(errRetrievePAT, err) - } - return patsPage, nil -} - -func (svc service) DeletePAT(ctx context.Context, session authn.Session, patID string) error { - if err := svc.pats.Remove(ctx, session.UserID, patID); err != nil { - return errors.Wrap(errDeletePAT, err) - } - return nil -} - -func (svc service) ResetPATSecret(ctx context.Context, session authn.Session, patID string, duration time.Duration) (PAT, error) { - // Generate new HashToken take place here - secret, hash, err := svc.generateSecretAndHash(session.UserID, patID) - if err != nil { - return PAT{}, errors.Wrap(svcerr.ErrUpdateEntity, err) - } - - pat, err := svc.pats.UpdateTokenHash(ctx, session.UserID, patID, hash, time.Now().Add(duration)) - if err != nil { - return PAT{}, errors.Wrap(svcerr.ErrUpdateEntity, err) - } - - if err := svc.pats.Reactivate(ctx, session.UserID, patID); err != nil { - return PAT{}, errors.Wrap(svcerr.ErrUpdateEntity, err) - } - pat.Secret = secret - pat.Revoked = false - pat.RevokedAt = time.Time{} - return pat, nil -} - -func (svc service) RevokePATSecret(ctx context.Context, session authn.Session, patID string) error { - if err := svc.pats.Revoke(ctx, session.UserID, patID); err != nil { - return errors.Wrap(errRevokePAT, err) - } - return nil -} - -func (svc service) AddPATScopeEntry(ctx context.Context, session authn.Session, patID string, platformEntityType PlatformEntityType, optionalDomainID string, optionalDomainEntityType DomainEntityType, operation OperationType, entityIDs ...string) (Scope, error) { - scope, err := svc.pats.AddScopeEntry(ctx, session.UserID, 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, session authn.Session, patID string, platformEntityType PlatformEntityType, optionalDomainID string, optionalDomainEntityType DomainEntityType, operation OperationType, entityIDs ...string) (Scope, error) { - scope, err := svc.pats.RemoveScopeEntry(ctx, session.UserID, patID, platformEntityType, optionalDomainID, optionalDomainEntityType, operation, entityIDs...) - if err != nil { - return Scope{}, err - } - return scope, nil -} - -func (svc service) ClearPATAllScopeEntry(ctx context.Context, session authn.Session, patID string) error { - if err := svc.pats.RemoveAllScopeEntry(ctx, session.UserID, patID); err != nil { - return errors.Wrap(errClearAllScope, err) - } - return nil -} - -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, userID, patID string, platformEntityType PlatformEntityType, optionalDomainID string, optionalDomainEntityType DomainEntityType, operation OperationType, entityIDs ...string) error { - res, err := svc.RetrievePAT(ctx, userID, patID) - if 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) 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 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/pat/tracing/tracing.go b/pat/tracing/tracing.go deleted file mode 100644 index 4fab5fae49..0000000000 --- a/pat/tracing/tracing.go +++ /dev/null @@ -1,164 +0,0 @@ -// Copyright (c) Abstract Machines -// SPDX-License-Identifier: Apache-2.0 - -package tracing - -import ( - "context" - "time" - - "github.com/absmach/magistrala/pat" - "github.com/absmach/magistrala/pkg/authn" - "go.opentelemetry.io/otel/attribute" - "go.opentelemetry.io/otel/trace" -) - -var _ pat.Service = (*tracingMiddleware)(nil) - -type tracingMiddleware struct { - tracer trace.Tracer - svc pat.Service -} - -// New returns a new group service with tracing capabilities. -func New(svc pat.Service, tracer trace.Tracer) pat.Service { - return &tracingMiddleware{tracer, svc} -} - -func (tm *tracingMiddleware) CreatePAT(ctx context.Context, session authn.Session, name, description string, duration time.Duration, scope pat.Scope) (pat.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.CreatePAT(ctx, session, name, description, duration, scope) -} - -func (tm *tracingMiddleware) UpdatePATName(ctx context.Context, session authn.Session, patID, name string) (pat.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.UpdatePATName(ctx, session, patID, name) -} - -func (tm *tracingMiddleware) UpdatePATDescription(ctx context.Context, session authn.Session, patID, description string) (pat.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.UpdatePATDescription(ctx, session, patID, description) -} - -func (tm *tracingMiddleware) RetrievePAT(ctx context.Context, session authn.Session, patID string) (pat.PAT, error) { - ctx, span := tm.tracer.Start(ctx, "retrieve_pat", trace.WithAttributes( - attribute.String("pat_id", patID), - )) - defer span.End() - return tm.svc.RetrievePAT(ctx, session, patID) -} - -func (tm *tracingMiddleware) ListPATS(ctx context.Context, session authn.Session, pm pat.PATSPageMeta) (pat.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.ListPATS(ctx, session, pm) -} - -func (tm *tracingMiddleware) DeletePAT(ctx context.Context, session authn.Session, patID string) error { - ctx, span := tm.tracer.Start(ctx, "delete_pat", trace.WithAttributes( - attribute.String("pat_id", patID), - )) - defer span.End() - return tm.svc.DeletePAT(ctx, session, patID) -} - -func (tm *tracingMiddleware) ResetPATSecret(ctx context.Context, session authn.Session, patID string, duration time.Duration) (pat.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.ResetPATSecret(ctx, session, patID, duration) -} - -func (tm *tracingMiddleware) RevokePATSecret(ctx context.Context, session authn.Session, 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.RevokePATSecret(ctx, session, patID) -} - -func (tm *tracingMiddleware) AddPATScopeEntry(ctx context.Context, session authn.Session, patID string, platformEntityType pat.PlatformEntityType, optionalDomainID string, optionalDomainEntityType pat.DomainEntityType, operation pat.OperationType, entityIDs ...string) (pat.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), - attribute.String("optional_domain_entity", optionalDomainEntityType.String()), - attribute.String("operation", operation.String()), - attribute.StringSlice("entities", entityIDs), - )) - defer span.End() - return tm.svc.AddPATScopeEntry(ctx, session, patID, platformEntityType, optionalDomainID, optionalDomainEntityType, operation, entityIDs...) -} - -func (tm *tracingMiddleware) RemovePATScopeEntry(ctx context.Context, session authn.Session, patID string, platformEntityType pat.PlatformEntityType, optionalDomainID string, optionalDomainEntityType pat.DomainEntityType, operation pat.OperationType, entityIDs ...string) (pat.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), - attribute.String("optional_domain_entity", optionalDomainEntityType.String()), - attribute.String("operation", operation.String()), - attribute.StringSlice("entities", entityIDs), - )) - defer span.End() - return tm.svc.RemovePATScopeEntry(ctx, session, patID, platformEntityType, optionalDomainID, optionalDomainEntityType, operation, entityIDs...) -} - -func (tm *tracingMiddleware) ClearPATAllScopeEntry(ctx context.Context, session authn.Session, 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.ClearPATAllScopeEntry(ctx, session, patID) -} - -func (tm *tracingMiddleware) IdentifyPAT(ctx context.Context, paToken string) (pat.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 pat.PlatformEntityType, optionalDomainID string, optionalDomainEntityType pat.DomainEntityType, operation pat.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), - 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, platformEntityType, optionalDomainID, optionalDomainEntityType, operation, entityIDs...) -} - -func (tm *tracingMiddleware) CheckPAT(ctx context.Context, userID, patID string, platformEntityType pat.PlatformEntityType, optionalDomainID string, optionalDomainEntityType pat.DomainEntityType, operation pat.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.CheckPAT(ctx, userID, patID, platformEntityType, optionalDomainID, optionalDomainEntityType, operation, entityIDs...) -} From 00a0e69bac717de0b711fad0713d1fcc8a9c2df4 Mon Sep 17 00:00:00 2001 From: nyagamunene Date: Sun, 3 Nov 2024 20:03:02 +0300 Subject: [PATCH 57/72] Authorize and Authenticate method Signed-off-by: nyagamunene --- users/middleware/authorization.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/users/middleware/authorization.go b/users/middleware/authorization.go index 6116858b49..ca14590615 100644 --- a/users/middleware/authorization.go +++ b/users/middleware/authorization.go @@ -16,6 +16,10 @@ import ( "github.com/absmach/supermq/users" ) +var ( + anyEntity = []string{"*"} +) + var _ users.Service = (*authorizationMiddleware)(nil) type authorizationMiddleware struct { From aa216d790b94892dcba6cc93911416cf30d7a26f Mon Sep 17 00:00:00 2001 From: nyagamunene Date: Sun, 3 Nov 2024 23:57:52 +0300 Subject: [PATCH 58/72] Update any ids method Signed-off-by: nyagamunene --- users/middleware/authorization.go | 4 ---- 1 file changed, 4 deletions(-) diff --git a/users/middleware/authorization.go b/users/middleware/authorization.go index ca14590615..6116858b49 100644 --- a/users/middleware/authorization.go +++ b/users/middleware/authorization.go @@ -16,10 +16,6 @@ import ( "github.com/absmach/supermq/users" ) -var ( - anyEntity = []string{"*"} -) - var _ users.Service = (*authorizationMiddleware)(nil) type authorizationMiddleware struct { From 27a41746aad54ce3653cf418ef20d053b08467d1 Mon Sep 17 00:00:00 2001 From: nyagamunene Date: Thu, 7 Nov 2024 20:21:45 +0300 Subject: [PATCH 59/72] Resolve conflicts Signed-off-by: nyagamunene --- auth/hasher/hasher.go | 4 ++-- auth/pat.go | 26 +++++++++++++------------- pkg/authz/mocks/authz.go | 18 ++++++++++++++++++ 3 files changed, 33 insertions(+), 15 deletions(-) diff --git a/auth/hasher/hasher.go b/auth/hasher/hasher.go index 026f5df306..18ea2aebc8 100644 --- a/auth/hasher/hasher.go +++ b/auth/hasher/hasher.go @@ -23,12 +23,12 @@ var ( errDecode = errors.New("failed to decode") ) -var _ pat.Hasher = (*bcryptHasher)(nil) +var _ auth.Hasher = (*bcryptHasher)(nil) type bcryptHasher struct{} // New instantiates a bcrypt-based hasher implementation. -func New() pat.Hasher { +func New() auth.Hasher { return &bcryptHasher{} } diff --git a/auth/pat.go b/auth/pat.go index 5382dae773..4a168fa0ec 100644 --- a/auth/pat.go +++ b/auth/pat.go @@ -714,41 +714,41 @@ func (pat PAT) Expired() bool { } // PATS specifies function which are required for Personal access Token implementation. -//go:generate mockery --name Service --output=./mocks --filename pats.go --quiet --note "Copyright (c) Abstract Machines" +//go:generate mockery --name PATS --output=./mocks --filename pats.go --quiet --note "Copyright (c) Abstract Machines" -type Service interface { +type PATS interface { // Create function creates new PAT for given valid inputs. - CreatePAT(ctx context.Context, session authn.Session, 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. - UpdatePATName(ctx context.Context, session authn.Session, 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. - UpdatePATDescription(ctx context.Context, session authn.Session, patID, description string) (PAT, error) + UpdatePATDescription(ctx context.Context, token, patID, description string) (PAT, error) // Retrieve function retrieves the PAT for given ID. - RetrievePAT(ctx context.Context, userID, patID string) (PAT, error) + RetrievePAT(ctx context.Context, userID string, patID string) (PAT, error) // List function lists all the PATs for the user. - ListPATS(ctx context.Context, session authn.Session, pm PATSPageMeta) (PATSPage, error) + ListPATS(ctx context.Context, token string, pm PATSPageMeta) (PATSPage, error) // Delete function deletes the PAT for given ID. - DeletePAT(ctx context.Context, session authn.Session, patID string) error + DeletePAT(ctx context.Context, token, patID string) error // ResetSecret function reset the secret and creates new secret for the given ID. - ResetPATSecret(ctx context.Context, session authn.Session, 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. - RevokePATSecret(ctx context.Context, session authn.Session, patID string) error + RevokePATSecret(ctx context.Context, token, patID string) error // AddScope function adds a new scope entry. - AddPATScopeEntry(ctx context.Context, session authn.Session, 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. - RemovePATScopeEntry(ctx context.Context, session authn.Session, 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. - ClearPATAllScopeEntry(ctx context.Context, session authn.Session, patID string) error + ClearPATAllScopeEntry(ctx context.Context, token, patID string) error // IdentifyPAT function will valid the secret. IdentifyPAT(ctx context.Context, paToken string) (PAT, error) diff --git a/pkg/authz/mocks/authz.go b/pkg/authz/mocks/authz.go index f3153680c9..69a75517bd 100644 --- a/pkg/authz/mocks/authz.go +++ b/pkg/authz/mocks/authz.go @@ -35,6 +35,24 @@ func (_m *Authorization) Authorize(ctx context.Context, pr authz.PolicyReq) erro return r0 } +// AuthorizePAT provides a mock function with given fields: ctx, pr +func (_m *Authorization) AuthorizePAT(ctx context.Context, pr authz.PatReq) error { + ret := _m.Called(ctx, pr) + + if len(ret) == 0 { + panic("no return value specified for AuthorizePAT") + } + + var r0 error + if rf, ok := ret.Get(0).(func(context.Context, authz.PatReq) error); ok { + r0 = rf(ctx, pr) + } else { + r0 = ret.Error(0) + } + + return r0 +} + // NewAuthorization creates a new instance of Authorization. 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 NewAuthorization(t interface { From 96f43d0123f55db8e7cbb157367b2ea8454b6eaf Mon Sep 17 00:00:00 2001 From: nyagamunene Date: Mon, 11 Nov 2024 17:49:48 +0300 Subject: [PATCH 60/72] Update protoc and mocks Signed-off-by: nyagamunene --- api/grpc/auth/v1/auth.pb.go | 62 ------- api/grpc/channels/v1/channels.pb.go | 44 ++--- api/grpc/channels/v1/channels_grpc.pb.go | 23 ++- api/grpc/common/v1/common.pb.go | 222 ++++------------------- api/grpc/domains/v1/domains.pb.go | 46 +---- api/grpc/domains/v1/domains_grpc.pb.go | 23 ++- api/grpc/groups/v1/groups_grpc.pb.go | 23 ++- api/grpc/token/v1/token.pb.go | 68 ++----- api/grpc/token/v1/token_grpc.pb.go | 23 ++- pkg/messaging/message.pb.go | 24 +-- users/middleware/authorization.go | 8 +- 11 files changed, 154 insertions(+), 412 deletions(-) diff --git a/api/grpc/auth/v1/auth.pb.go b/api/grpc/auth/v1/auth.pb.go index a15439a63c..1ee360a95f 100644 --- a/api/grpc/auth/v1/auth.pb.go +++ b/api/grpc/auth/v1/auth.pb.go @@ -493,68 +493,6 @@ func file_auth_v1_auth_proto_init() { if File_auth_v1_auth_proto != nil { return } - if !protoimpl.UnsafeEnabled { - file_auth_v1_auth_proto_msgTypes[0].Exporter = func(v any, i int) any { - switch v := v.(*AuthNReq); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_auth_v1_auth_proto_msgTypes[1].Exporter = func(v any, i int) any { - switch v := v.(*AuthNRes); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_auth_v1_auth_proto_msgTypes[2].Exporter = func(v any, i int) any { - switch v := v.(*AuthZReq); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_auth_v1_auth_proto_msgTypes[3].Exporter = func(v any, i int) any { - switch v := v.(*AuthZpatReq); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_auth_v1_auth_proto_msgTypes[4].Exporter = func(v any, i int) any { - switch v := v.(*AuthZRes); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - } type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ diff --git a/api/grpc/channels/v1/channels.pb.go b/api/grpc/channels/v1/channels.pb.go index a5208e78ef..5d607cc219 100644 --- a/api/grpc/channels/v1/channels.pb.go +++ b/api/grpc/channels/v1/channels.pb.go @@ -46,7 +46,7 @@ func (*RemoveClientConnectionsReq) ProtoMessage() {} func (x *RemoveClientConnectionsReq) ProtoReflect() protoreflect.Message { mi := &file_channels_v1_channels_proto_msgTypes[0] - if protoimpl.UnsafeEnabled && x != nil { + if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -89,7 +89,7 @@ func (*RemoveClientConnectionsRes) ProtoMessage() {} func (x *RemoveClientConnectionsRes) ProtoReflect() protoreflect.Message { mi := &file_channels_v1_channels_proto_msgTypes[1] - if protoimpl.UnsafeEnabled && x != nil { + if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -113,11 +113,9 @@ type UnsetParentGroupFromChannelsReq struct { func (x *UnsetParentGroupFromChannelsReq) Reset() { *x = UnsetParentGroupFromChannelsReq{} - if protoimpl.UnsafeEnabled { - mi := &file_channels_v1_channels_proto_msgTypes[2] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } + mi := &file_channels_v1_channels_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } func (x *UnsetParentGroupFromChannelsReq) String() string { @@ -128,7 +126,7 @@ func (*UnsetParentGroupFromChannelsReq) ProtoMessage() {} func (x *UnsetParentGroupFromChannelsReq) ProtoReflect() protoreflect.Message { mi := &file_channels_v1_channels_proto_msgTypes[2] - if protoimpl.UnsafeEnabled && x != nil { + if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -158,11 +156,9 @@ type UnsetParentGroupFromChannelsRes struct { func (x *UnsetParentGroupFromChannelsRes) Reset() { *x = UnsetParentGroupFromChannelsRes{} - if protoimpl.UnsafeEnabled { - mi := &file_channels_v1_channels_proto_msgTypes[3] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } + mi := &file_channels_v1_channels_proto_msgTypes[3] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } func (x *UnsetParentGroupFromChannelsRes) String() string { @@ -173,7 +169,7 @@ func (*UnsetParentGroupFromChannelsRes) ProtoMessage() {} func (x *UnsetParentGroupFromChannelsRes) ProtoReflect() protoreflect.Message { mi := &file_channels_v1_channels_proto_msgTypes[3] - if protoimpl.UnsafeEnabled && x != nil { + if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -201,11 +197,9 @@ type AuthzReq struct { func (x *AuthzReq) Reset() { *x = AuthzReq{} - if protoimpl.UnsafeEnabled { - mi := &file_channels_v1_channels_proto_msgTypes[4] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } + mi := &file_channels_v1_channels_proto_msgTypes[4] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } func (x *AuthzReq) String() string { @@ -216,7 +210,7 @@ func (*AuthzReq) ProtoMessage() {} func (x *AuthzReq) ProtoReflect() protoreflect.Message { mi := &file_channels_v1_channels_proto_msgTypes[4] - if protoimpl.UnsafeEnabled && x != nil { + if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -275,11 +269,9 @@ type AuthzRes struct { func (x *AuthzRes) Reset() { *x = AuthzRes{} - if protoimpl.UnsafeEnabled { - mi := &file_channels_v1_channels_proto_msgTypes[5] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } + mi := &file_channels_v1_channels_proto_msgTypes[5] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } func (x *AuthzRes) String() string { @@ -290,7 +282,7 @@ func (*AuthzRes) ProtoMessage() {} func (x *AuthzRes) ProtoReflect() protoreflect.Message { mi := &file_channels_v1_channels_proto_msgTypes[5] - if protoimpl.UnsafeEnabled && x != nil { + if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) diff --git a/api/grpc/channels/v1/channels_grpc.pb.go b/api/grpc/channels/v1/channels_grpc.pb.go index 6c4ebeda28..612280b166 100644 --- a/api/grpc/channels/v1/channels_grpc.pb.go +++ b/api/grpc/channels/v1/channels_grpc.pb.go @@ -19,8 +19,8 @@ import ( // This is a compile-time assertion to ensure that this generated file // is compatible with the grpc package it is being compiled against. -// Requires gRPC-Go v1.62.0 or later. -const _ = grpc.SupportPackageIsVersion8 +// Requires gRPC-Go v1.64.0 or later. +const _ = grpc.SupportPackageIsVersion9 const ( ChannelsService_Authorize_FullMethodName = "/channels.v1.ChannelsService/Authorize" @@ -89,7 +89,7 @@ func (c *channelsServiceClient) RetrieveEntity(ctx context.Context, in *v1.Retri // ChannelsServiceServer is the server API for ChannelsService service. // All implementations must embed UnimplementedChannelsServiceServer -// for forward compatibility +// for forward compatibility. type ChannelsServiceServer interface { Authorize(context.Context, *AuthzReq) (*AuthzRes, error) RemoveClientConnections(context.Context, *RemoveClientConnectionsReq) (*RemoveClientConnectionsRes, error) @@ -98,9 +98,12 @@ type ChannelsServiceServer interface { mustEmbedUnimplementedChannelsServiceServer() } -// UnimplementedChannelsServiceServer must be embedded to have forward compatible implementations. -type UnimplementedChannelsServiceServer struct { -} +// UnimplementedChannelsServiceServer must be embedded to have +// forward compatible implementations. +// +// NOTE: this should be embedded by value instead of pointer to avoid a nil +// pointer dereference when methods are called. +type UnimplementedChannelsServiceServer struct{} func (UnimplementedChannelsServiceServer) Authorize(context.Context, *AuthzReq) (*AuthzRes, error) { return nil, status.Errorf(codes.Unimplemented, "method Authorize not implemented") @@ -115,6 +118,7 @@ func (UnimplementedChannelsServiceServer) RetrieveEntity(context.Context, *v1.Re return nil, status.Errorf(codes.Unimplemented, "method RetrieveEntity not implemented") } func (UnimplementedChannelsServiceServer) mustEmbedUnimplementedChannelsServiceServer() {} +func (UnimplementedChannelsServiceServer) testEmbeddedByValue() {} // UnsafeChannelsServiceServer may be embedded to opt out of forward compatibility for this service. // Use of this interface is not recommended, as added methods to ChannelsServiceServer will @@ -124,6 +128,13 @@ type UnsafeChannelsServiceServer interface { } func RegisterChannelsServiceServer(s grpc.ServiceRegistrar, srv ChannelsServiceServer) { + // If the following call pancis, it indicates UnimplementedChannelsServiceServer was + // embedded by pointer and is nil. This will cause panics if an + // unimplemented method is ever invoked, so we test this at initialization + // time to prevent it from happening at runtime later due to I/O. + if t, ok := srv.(interface{ testEmbeddedByValue() }); ok { + t.testEmbeddedByValue() + } s.RegisterService(&ChannelsService_ServiceDesc, srv) } diff --git a/api/grpc/common/v1/common.pb.go b/api/grpc/common/v1/common.pb.go index faa8c157e2..281366d3e6 100644 --- a/api/grpc/common/v1/common.pb.go +++ b/api/grpc/common/v1/common.pb.go @@ -32,11 +32,9 @@ type RetrieveEntitiesReq struct { func (x *RetrieveEntitiesReq) Reset() { *x = RetrieveEntitiesReq{} - if protoimpl.UnsafeEnabled { - mi := &file_common_v1_common_proto_msgTypes[0] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } + mi := &file_common_v1_common_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } func (x *RetrieveEntitiesReq) String() string { @@ -47,7 +45,7 @@ func (*RetrieveEntitiesReq) ProtoMessage() {} func (x *RetrieveEntitiesReq) ProtoReflect() protoreflect.Message { mi := &file_common_v1_common_proto_msgTypes[0] - if protoimpl.UnsafeEnabled && x != nil { + if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -81,11 +79,9 @@ type RetrieveEntitiesRes struct { func (x *RetrieveEntitiesRes) Reset() { *x = RetrieveEntitiesRes{} - if protoimpl.UnsafeEnabled { - mi := &file_common_v1_common_proto_msgTypes[1] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } + mi := &file_common_v1_common_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } func (x *RetrieveEntitiesRes) String() string { @@ -96,7 +92,7 @@ func (*RetrieveEntitiesRes) ProtoMessage() {} func (x *RetrieveEntitiesRes) ProtoReflect() protoreflect.Message { mi := &file_common_v1_common_proto_msgTypes[1] - if protoimpl.UnsafeEnabled && x != nil { + if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -148,11 +144,9 @@ type RetrieveEntityReq struct { func (x *RetrieveEntityReq) Reset() { *x = RetrieveEntityReq{} - if protoimpl.UnsafeEnabled { - mi := &file_common_v1_common_proto_msgTypes[2] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } + mi := &file_common_v1_common_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } func (x *RetrieveEntityReq) String() string { @@ -163,7 +157,7 @@ func (*RetrieveEntityReq) ProtoMessage() {} func (x *RetrieveEntityReq) ProtoReflect() protoreflect.Message { mi := &file_common_v1_common_proto_msgTypes[2] - if protoimpl.UnsafeEnabled && x != nil { + if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -194,11 +188,9 @@ type RetrieveEntityRes struct { func (x *RetrieveEntityRes) Reset() { *x = RetrieveEntityRes{} - if protoimpl.UnsafeEnabled { - mi := &file_common_v1_common_proto_msgTypes[3] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } + mi := &file_common_v1_common_proto_msgTypes[3] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } func (x *RetrieveEntityRes) String() string { @@ -209,7 +201,7 @@ func (*RetrieveEntityRes) ProtoMessage() {} func (x *RetrieveEntityRes) ProtoReflect() protoreflect.Message { mi := &file_common_v1_common_proto_msgTypes[3] - if protoimpl.UnsafeEnabled && x != nil { + if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -243,11 +235,9 @@ type EntityBasic struct { func (x *EntityBasic) Reset() { *x = EntityBasic{} - if protoimpl.UnsafeEnabled { - mi := &file_common_v1_common_proto_msgTypes[4] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } + mi := &file_common_v1_common_proto_msgTypes[4] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } func (x *EntityBasic) String() string { @@ -258,7 +248,7 @@ func (*EntityBasic) ProtoMessage() {} func (x *EntityBasic) ProtoReflect() protoreflect.Message { mi := &file_common_v1_common_proto_msgTypes[4] - if protoimpl.UnsafeEnabled && x != nil { + if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -310,11 +300,9 @@ type AddConnectionsReq struct { func (x *AddConnectionsReq) Reset() { *x = AddConnectionsReq{} - if protoimpl.UnsafeEnabled { - mi := &file_common_v1_common_proto_msgTypes[5] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } + mi := &file_common_v1_common_proto_msgTypes[5] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } func (x *AddConnectionsReq) String() string { @@ -325,7 +313,7 @@ func (*AddConnectionsReq) ProtoMessage() {} func (x *AddConnectionsReq) ProtoReflect() protoreflect.Message { mi := &file_common_v1_common_proto_msgTypes[5] - if protoimpl.UnsafeEnabled && x != nil { + if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -356,11 +344,9 @@ type AddConnectionsRes struct { func (x *AddConnectionsRes) Reset() { *x = AddConnectionsRes{} - if protoimpl.UnsafeEnabled { - mi := &file_common_v1_common_proto_msgTypes[6] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } + mi := &file_common_v1_common_proto_msgTypes[6] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } func (x *AddConnectionsRes) String() string { @@ -371,7 +357,7 @@ func (*AddConnectionsRes) ProtoMessage() {} func (x *AddConnectionsRes) ProtoReflect() protoreflect.Message { mi := &file_common_v1_common_proto_msgTypes[6] - if protoimpl.UnsafeEnabled && x != nil { + if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -402,11 +388,9 @@ type RemoveConnectionsReq struct { func (x *RemoveConnectionsReq) Reset() { *x = RemoveConnectionsReq{} - if protoimpl.UnsafeEnabled { - mi := &file_common_v1_common_proto_msgTypes[7] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } + mi := &file_common_v1_common_proto_msgTypes[7] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } func (x *RemoveConnectionsReq) String() string { @@ -417,7 +401,7 @@ func (*RemoveConnectionsReq) ProtoMessage() {} func (x *RemoveConnectionsReq) ProtoReflect() protoreflect.Message { mi := &file_common_v1_common_proto_msgTypes[7] - if protoimpl.UnsafeEnabled && x != nil { + if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -448,11 +432,9 @@ type RemoveConnectionsRes struct { func (x *RemoveConnectionsRes) Reset() { *x = RemoveConnectionsRes{} - if protoimpl.UnsafeEnabled { - mi := &file_common_v1_common_proto_msgTypes[8] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } + mi := &file_common_v1_common_proto_msgTypes[8] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } func (x *RemoveConnectionsRes) String() string { @@ -463,7 +445,7 @@ func (*RemoveConnectionsRes) ProtoMessage() {} func (x *RemoveConnectionsRes) ProtoReflect() protoreflect.Message { mi := &file_common_v1_common_proto_msgTypes[8] - if protoimpl.UnsafeEnabled && x != nil { + if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -497,11 +479,9 @@ type Connection struct { func (x *Connection) Reset() { *x = Connection{} - if protoimpl.UnsafeEnabled { - mi := &file_common_v1_common_proto_msgTypes[9] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } + mi := &file_common_v1_common_proto_msgTypes[9] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } func (x *Connection) String() string { @@ -512,7 +492,7 @@ func (*Connection) ProtoMessage() {} func (x *Connection) ProtoReflect() protoreflect.Message { mi := &file_common_v1_common_proto_msgTypes[9] - if protoimpl.UnsafeEnabled && x != nil { + if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -657,128 +637,6 @@ func file_common_v1_common_proto_init() { if File_common_v1_common_proto != nil { return } - if !protoimpl.UnsafeEnabled { - file_common_v1_common_proto_msgTypes[0].Exporter = func(v any, i int) any { - switch v := v.(*RetrieveEntitiesReq); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_common_v1_common_proto_msgTypes[1].Exporter = func(v any, i int) any { - switch v := v.(*RetrieveEntitiesRes); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_common_v1_common_proto_msgTypes[2].Exporter = func(v any, i int) any { - switch v := v.(*RetrieveEntityReq); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_common_v1_common_proto_msgTypes[3].Exporter = func(v any, i int) any { - switch v := v.(*RetrieveEntityRes); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_common_v1_common_proto_msgTypes[4].Exporter = func(v any, i int) any { - switch v := v.(*EntityBasic); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_common_v1_common_proto_msgTypes[5].Exporter = func(v any, i int) any { - switch v := v.(*AddConnectionsReq); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_common_v1_common_proto_msgTypes[6].Exporter = func(v any, i int) any { - switch v := v.(*AddConnectionsRes); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_common_v1_common_proto_msgTypes[7].Exporter = func(v any, i int) any { - switch v := v.(*RemoveConnectionsReq); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_common_v1_common_proto_msgTypes[8].Exporter = func(v any, i int) any { - switch v := v.(*RemoveConnectionsRes); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_common_v1_common_proto_msgTypes[9].Exporter = func(v any, i int) any { - switch v := v.(*Connection); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - } type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ diff --git a/api/grpc/domains/v1/domains.pb.go b/api/grpc/domains/v1/domains.pb.go index 8a2dcc211d..897979d4aa 100644 --- a/api/grpc/domains/v1/domains.pb.go +++ b/api/grpc/domains/v1/domains.pb.go @@ -33,11 +33,9 @@ type DeleteUserRes struct { func (x *DeleteUserRes) Reset() { *x = DeleteUserRes{} - if protoimpl.UnsafeEnabled { - mi := &file_domains_v1_domains_proto_msgTypes[0] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } + mi := &file_domains_v1_domains_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } func (x *DeleteUserRes) String() string { @@ -48,7 +46,7 @@ func (*DeleteUserRes) ProtoMessage() {} func (x *DeleteUserRes) ProtoReflect() protoreflect.Message { mi := &file_domains_v1_domains_proto_msgTypes[0] - if protoimpl.UnsafeEnabled && x != nil { + if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -79,11 +77,9 @@ type DeleteUserReq struct { func (x *DeleteUserReq) Reset() { *x = DeleteUserReq{} - if protoimpl.UnsafeEnabled { - mi := &file_domains_v1_domains_proto_msgTypes[1] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } + mi := &file_domains_v1_domains_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } func (x *DeleteUserReq) String() string { @@ -94,7 +90,7 @@ func (*DeleteUserReq) ProtoMessage() {} func (x *DeleteUserReq) ProtoReflect() protoreflect.Message { mi := &file_domains_v1_domains_proto_msgTypes[1] - if protoimpl.UnsafeEnabled && x != nil { + if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -181,32 +177,6 @@ func file_domains_v1_domains_proto_init() { if File_domains_v1_domains_proto != nil { return } - if !protoimpl.UnsafeEnabled { - file_domains_v1_domains_proto_msgTypes[0].Exporter = func(v any, i int) any { - switch v := v.(*DeleteUserRes); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_domains_v1_domains_proto_msgTypes[1].Exporter = func(v any, i int) any { - switch v := v.(*DeleteUserReq); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - } type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ diff --git a/api/grpc/domains/v1/domains_grpc.pb.go b/api/grpc/domains/v1/domains_grpc.pb.go index 1e348de890..90b85349f8 100644 --- a/api/grpc/domains/v1/domains_grpc.pb.go +++ b/api/grpc/domains/v1/domains_grpc.pb.go @@ -19,8 +19,8 @@ import ( // This is a compile-time assertion to ensure that this generated file // is compatible with the grpc package it is being compiled against. -// Requires gRPC-Go v1.62.0 or later. -const _ = grpc.SupportPackageIsVersion8 +// Requires gRPC-Go v1.64.0 or later. +const _ = grpc.SupportPackageIsVersion9 const ( DomainsService_DeleteUserFromDomains_FullMethodName = "/domains.v1.DomainsService/DeleteUserFromDomains" @@ -68,7 +68,7 @@ func (c *domainsServiceClient) RetrieveEntity(ctx context.Context, in *v1.Retrie // DomainsServiceServer is the server API for DomainsService service. // All implementations must embed UnimplementedDomainsServiceServer -// for forward compatibility +// for forward compatibility. // // DomainsService is a service that provides access to // domains functionalities for SuperMQ services. @@ -78,9 +78,12 @@ type DomainsServiceServer interface { mustEmbedUnimplementedDomainsServiceServer() } -// UnimplementedDomainsServiceServer must be embedded to have forward compatible implementations. -type UnimplementedDomainsServiceServer struct { -} +// UnimplementedDomainsServiceServer must be embedded to have +// forward compatible implementations. +// +// NOTE: this should be embedded by value instead of pointer to avoid a nil +// pointer dereference when methods are called. +type UnimplementedDomainsServiceServer struct{} func (UnimplementedDomainsServiceServer) DeleteUserFromDomains(context.Context, *DeleteUserReq) (*DeleteUserRes, error) { return nil, status.Errorf(codes.Unimplemented, "method DeleteUserFromDomains not implemented") @@ -89,6 +92,7 @@ func (UnimplementedDomainsServiceServer) RetrieveEntity(context.Context, *v1.Ret return nil, status.Errorf(codes.Unimplemented, "method RetrieveEntity not implemented") } func (UnimplementedDomainsServiceServer) mustEmbedUnimplementedDomainsServiceServer() {} +func (UnimplementedDomainsServiceServer) testEmbeddedByValue() {} // UnsafeDomainsServiceServer may be embedded to opt out of forward compatibility for this service. // Use of this interface is not recommended, as added methods to DomainsServiceServer will @@ -98,6 +102,13 @@ type UnsafeDomainsServiceServer interface { } func RegisterDomainsServiceServer(s grpc.ServiceRegistrar, srv DomainsServiceServer) { + // If the following call pancis, it indicates UnimplementedDomainsServiceServer was + // embedded by pointer and is nil. This will cause panics if an + // unimplemented method is ever invoked, so we test this at initialization + // time to prevent it from happening at runtime later due to I/O. + if t, ok := srv.(interface{ testEmbeddedByValue() }); ok { + t.testEmbeddedByValue() + } s.RegisterService(&DomainsService_ServiceDesc, srv) } diff --git a/api/grpc/groups/v1/groups_grpc.pb.go b/api/grpc/groups/v1/groups_grpc.pb.go index 86132eea9e..d362f88c2b 100644 --- a/api/grpc/groups/v1/groups_grpc.pb.go +++ b/api/grpc/groups/v1/groups_grpc.pb.go @@ -19,8 +19,8 @@ import ( // This is a compile-time assertion to ensure that this generated file // is compatible with the grpc package it is being compiled against. -// Requires gRPC-Go v1.62.0 or later. -const _ = grpc.SupportPackageIsVersion8 +// Requires gRPC-Go v1.64.0 or later. +const _ = grpc.SupportPackageIsVersion9 const ( GroupsService_RetrieveEntity_FullMethodName = "/groups.v1.GroupsService/RetrieveEntity" @@ -56,7 +56,7 @@ func (c *groupsServiceClient) RetrieveEntity(ctx context.Context, in *v1.Retriev // GroupsServiceServer is the server API for GroupsService service. // All implementations must embed UnimplementedGroupsServiceServer -// for forward compatibility +// for forward compatibility. // // GroupssService is a service that provides groups // functionalities for SuperMQ services. @@ -65,14 +65,18 @@ type GroupsServiceServer interface { mustEmbedUnimplementedGroupsServiceServer() } -// UnimplementedGroupsServiceServer must be embedded to have forward compatible implementations. -type UnimplementedGroupsServiceServer struct { -} +// UnimplementedGroupsServiceServer must be embedded to have +// forward compatible implementations. +// +// NOTE: this should be embedded by value instead of pointer to avoid a nil +// pointer dereference when methods are called. +type UnimplementedGroupsServiceServer struct{} func (UnimplementedGroupsServiceServer) RetrieveEntity(context.Context, *v1.RetrieveEntityReq) (*v1.RetrieveEntityRes, error) { return nil, status.Errorf(codes.Unimplemented, "method RetrieveEntity not implemented") } func (UnimplementedGroupsServiceServer) mustEmbedUnimplementedGroupsServiceServer() {} +func (UnimplementedGroupsServiceServer) testEmbeddedByValue() {} // UnsafeGroupsServiceServer may be embedded to opt out of forward compatibility for this service. // Use of this interface is not recommended, as added methods to GroupsServiceServer will @@ -82,6 +86,13 @@ type UnsafeGroupsServiceServer interface { } func RegisterGroupsServiceServer(s grpc.ServiceRegistrar, srv GroupsServiceServer) { + // If the following call pancis, it indicates UnimplementedGroupsServiceServer was + // embedded by pointer and is nil. This will cause panics if an + // unimplemented method is ever invoked, so we test this at initialization + // time to prevent it from happening at runtime later due to I/O. + if t, ok := srv.(interface{ testEmbeddedByValue() }); ok { + t.testEmbeddedByValue() + } s.RegisterService(&GroupsService_ServiceDesc, srv) } diff --git a/api/grpc/token/v1/token.pb.go b/api/grpc/token/v1/token.pb.go index 9a7722514f..54e897e51f 100644 --- a/api/grpc/token/v1/token.pb.go +++ b/api/grpc/token/v1/token.pb.go @@ -33,11 +33,9 @@ type IssueReq struct { func (x *IssueReq) Reset() { *x = IssueReq{} - if protoimpl.UnsafeEnabled { - mi := &file_token_v1_token_proto_msgTypes[0] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } + mi := &file_token_v1_token_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } func (x *IssueReq) String() string { @@ -48,7 +46,7 @@ func (*IssueReq) ProtoMessage() {} func (x *IssueReq) ProtoReflect() protoreflect.Message { mi := &file_token_v1_token_proto_msgTypes[0] - if protoimpl.UnsafeEnabled && x != nil { + if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -86,11 +84,9 @@ type RefreshReq struct { func (x *RefreshReq) Reset() { *x = RefreshReq{} - if protoimpl.UnsafeEnabled { - mi := &file_token_v1_token_proto_msgTypes[1] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } + mi := &file_token_v1_token_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } func (x *RefreshReq) String() string { @@ -101,7 +97,7 @@ func (*RefreshReq) ProtoMessage() {} func (x *RefreshReq) ProtoReflect() protoreflect.Message { mi := &file_token_v1_token_proto_msgTypes[1] - if protoimpl.UnsafeEnabled && x != nil { + if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -137,11 +133,9 @@ type Token struct { func (x *Token) Reset() { *x = Token{} - if protoimpl.UnsafeEnabled { - mi := &file_token_v1_token_proto_msgTypes[2] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } + mi := &file_token_v1_token_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } func (x *Token) String() string { @@ -152,7 +146,7 @@ func (*Token) ProtoMessage() {} func (x *Token) ProtoReflect() protoreflect.Message { mi := &file_token_v1_token_proto_msgTypes[2] - if protoimpl.UnsafeEnabled && x != nil { + if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -257,44 +251,6 @@ func file_token_v1_token_proto_init() { if File_token_v1_token_proto != nil { return } - if !protoimpl.UnsafeEnabled { - file_token_v1_token_proto_msgTypes[0].Exporter = func(v any, i int) any { - switch v := v.(*IssueReq); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_token_v1_token_proto_msgTypes[1].Exporter = func(v any, i int) any { - switch v := v.(*RefreshReq); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_token_v1_token_proto_msgTypes[2].Exporter = func(v any, i int) any { - switch v := v.(*Token); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - } file_token_v1_token_proto_msgTypes[2].OneofWrappers = []any{} type x struct{} out := protoimpl.TypeBuilder{ diff --git a/api/grpc/token/v1/token_grpc.pb.go b/api/grpc/token/v1/token_grpc.pb.go index 8b8cf03931..f3adacfb70 100644 --- a/api/grpc/token/v1/token_grpc.pb.go +++ b/api/grpc/token/v1/token_grpc.pb.go @@ -18,8 +18,8 @@ import ( // This is a compile-time assertion to ensure that this generated file // is compatible with the grpc package it is being compiled against. -// Requires gRPC-Go v1.62.0 or later. -const _ = grpc.SupportPackageIsVersion8 +// Requires gRPC-Go v1.64.0 or later. +const _ = grpc.SupportPackageIsVersion9 const ( TokenService_Issue_FullMethodName = "/token.v1.TokenService/Issue" @@ -64,16 +64,19 @@ func (c *tokenServiceClient) Refresh(ctx context.Context, in *RefreshReq, opts . // TokenServiceServer is the server API for TokenService service. // All implementations must embed UnimplementedTokenServiceServer -// for forward compatibility +// for forward compatibility. type TokenServiceServer interface { Issue(context.Context, *IssueReq) (*Token, error) Refresh(context.Context, *RefreshReq) (*Token, error) mustEmbedUnimplementedTokenServiceServer() } -// UnimplementedTokenServiceServer must be embedded to have forward compatible implementations. -type UnimplementedTokenServiceServer struct { -} +// UnimplementedTokenServiceServer must be embedded to have +// forward compatible implementations. +// +// NOTE: this should be embedded by value instead of pointer to avoid a nil +// pointer dereference when methods are called. +type UnimplementedTokenServiceServer struct{} func (UnimplementedTokenServiceServer) Issue(context.Context, *IssueReq) (*Token, error) { return nil, status.Errorf(codes.Unimplemented, "method Issue not implemented") @@ -82,6 +85,7 @@ func (UnimplementedTokenServiceServer) Refresh(context.Context, *RefreshReq) (*T return nil, status.Errorf(codes.Unimplemented, "method Refresh not implemented") } func (UnimplementedTokenServiceServer) mustEmbedUnimplementedTokenServiceServer() {} +func (UnimplementedTokenServiceServer) testEmbeddedByValue() {} // UnsafeTokenServiceServer may be embedded to opt out of forward compatibility for this service. // Use of this interface is not recommended, as added methods to TokenServiceServer will @@ -91,6 +95,13 @@ type UnsafeTokenServiceServer interface { } func RegisterTokenServiceServer(s grpc.ServiceRegistrar, srv TokenServiceServer) { + // If the following call pancis, it indicates UnimplementedTokenServiceServer was + // embedded by pointer and is nil. This will cause panics if an + // unimplemented method is ever invoked, so we test this at initialization + // time to prevent it from happening at runtime later due to I/O. + if t, ok := srv.(interface{ testEmbeddedByValue() }); ok { + t.testEmbeddedByValue() + } s.RegisterService(&TokenService_ServiceDesc, srv) } diff --git a/pkg/messaging/message.pb.go b/pkg/messaging/message.pb.go index c2833f72b6..bdf2bb1231 100644 --- a/pkg/messaging/message.pb.go +++ b/pkg/messaging/message.pb.go @@ -38,11 +38,9 @@ type Message struct { func (x *Message) Reset() { *x = Message{} - if protoimpl.UnsafeEnabled { - mi := &file_pkg_messaging_message_proto_msgTypes[0] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } + mi := &file_pkg_messaging_message_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } func (x *Message) String() string { @@ -53,7 +51,7 @@ func (*Message) ProtoMessage() {} func (x *Message) ProtoReflect() protoreflect.Message { mi := &file_pkg_messaging_message_proto_msgTypes[0] - if protoimpl.UnsafeEnabled && x != nil { + if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -159,20 +157,6 @@ func file_pkg_messaging_message_proto_init() { if File_pkg_messaging_message_proto != nil { return } - if !protoimpl.UnsafeEnabled { - file_pkg_messaging_message_proto_msgTypes[0].Exporter = func(v any, i int) any { - switch v := v.(*Message); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - } type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ diff --git a/users/middleware/authorization.go b/users/middleware/authorization.go index 6116858b49..0101c924cc 100644 --- a/users/middleware/authorization.go +++ b/users/middleware/authorization.go @@ -88,7 +88,7 @@ func (am *authorizationMiddleware) ListUsers(ctx context.Context, session authn. PlatformEntityType: mgauth.PlatformUsersScope, OptionalDomainEntityType: mgauth.DomainNullScope, Operation: mgauth.ListOp, - EntityIDs: auth.AnyIDs{}.Values(), + EntityIDs: mgauth.AnyIDs{}.Values(), }); err != nil { return users.UsersPage{}, errors.Wrap(svcerr.ErrUnauthorizedPAT, err) } @@ -111,7 +111,7 @@ func (am *authorizationMiddleware) ListMembers(ctx context.Context, session auth PlatformEntityType: mgauth.PlatformUsersScope, OptionalDomainEntityType: mgauth.DomainGroupsScope, Operation: mgauth.ListOp, - EntityIDs: auth.AnyIDs{}.Values(), + EntityIDs: mgauth.AnyIDs{}.Values(), }); err != nil { return users.MembersPage{}, errors.Wrap(svcerr.ErrUnauthorizedPAT, err) } @@ -123,7 +123,7 @@ func (am *authorizationMiddleware) ListMembers(ctx context.Context, session auth PlatformEntityType: mgauth.PlatformUsersScope, OptionalDomainEntityType: mgauth.DomainManagementScope, Operation: mgauth.ListOp, - EntityIDs: auth.AnyIDs{}.Values(), + EntityIDs: mgauth.AnyIDs{}.Values(), }); err != nil { return users.MembersPage{}, errors.Wrap(svcerr.ErrUnauthorizedPAT, err) } @@ -135,7 +135,7 @@ func (am *authorizationMiddleware) ListMembers(ctx context.Context, session auth PlatformEntityType: mgauth.PlatformUsersScope, OptionalDomainEntityType: mgauth.DomainThingsScope, Operation: mgauth.ListOp, - EntityIDs: auth.AnyIDs{}.Values(), + EntityIDs: mgauth.AnyIDs{}.Values(), }); err != nil { return users.MembersPage{}, errors.Wrap(svcerr.ErrUnauthorizedPAT, err) } From fd8490f79487644a3f93577954af99bafe49e13c Mon Sep 17 00:00:00 2001 From: nyagamunene Date: Wed, 13 Nov 2024 16:15:33 +0300 Subject: [PATCH 61/72] Address comments Signed-off-by: nyagamunene --- clients/middleware/authorization.go | 210 ++++++++++++++++++++++++---- users/middleware/authorization.go | 48 +++---- 2 files changed, 206 insertions(+), 52 deletions(-) diff --git a/clients/middleware/authorization.go b/clients/middleware/authorization.go index 6a386b1bfc..761ce41856 100644 --- a/clients/middleware/authorization.go +++ b/clients/middleware/authorization.go @@ -18,18 +18,18 @@ import ( ) var ( - errView = errors.New("not authorized to view client") - errUpdate = errors.New("not authorized to update client") - errUpdateTags = errors.New("not authorized to update client tags") - errUpdateSecret = errors.New("not authorized to update client secret") - errEnable = errors.New("not authorized to enable client") - errDisable = errors.New("not authorized to disable client") - errDelete = errors.New("not authorized to delete client") - errSetParentGroup = errors.New("not authorized to set parent group to client") - errRemoveParentGroup = errors.New("not authorized to remove parent group from client") - errDomainCreateClients = errors.New("not authorized to create client in domain") - errGroupSetChildClients = errors.New("not authorized to set child client for group") - errGroupRemoveChildClients = errors.New("not authorized to remove child client for group") + errView = errors.New("not authorized to view thing") + errUpdate = errors.New("not authorized to update thing") + errUpdateTags = errors.New("not authorized to update thing tags") + errUpdateSecret = errors.New("not authorized to update thing secret") + errEnable = errors.New("not authorized to enable thing") + errDisable = errors.New("not authorized to disable thing") + errDelete = errors.New("not authorized to delete thing") + errSetParentGroup = errors.New("not authorized to set parent group to thing") + errRemoveParentGroup = errors.New("not authorized to remove parent group from thing") + errDomainCreateClients = errors.New("not authorized to create thing in domain") + errGroupSetChildClients = errors.New("not authorized to set child thing for group") + errGroupRemoveChildClients = errors.New("not authorized to remove child thing for group") ) var _ clients.Service = (*authorizationMiddleware)(nil) @@ -37,16 +37,16 @@ var _ clients.Service = (*authorizationMiddleware)(nil) type authorizationMiddleware struct { svc clients.Service repo clients.Repository - authz authz.Authorization + authz mgauthz.Authorization opp svcutil.OperationPerm extOpp svcutil.ExternalOperationPerm rmMW.RoleManagerAuthorizationMiddleware } // AuthorizationMiddleware adds authorization to the clients service. -func AuthorizationMiddleware(entityType string, svc clients.Service, authz authz.Authorization, repo clients.Repository, clientsOpPerm, rolesOpPerm map[svcutil.Operation]svcutil.Permission, extOpPerm map[svcutil.ExternalOperation]svcutil.Permission) (clients.Service, error) { +func AuthorizationMiddleware(entityType string, svc clients.Service, authz mgauthz.Authorization, repo clients.Repository, thingsOpPerm, rolesOpPerm map[svcutil.Operation]svcutil.Permission, extOpPerm map[svcutil.ExternalOperation]svcutil.Permission) (clients.Service, error) { opp := clients.NewOperationPerm() - if err := opp.AddOperationPermissionMap(clientsOpPerm); err != nil { + if err := opp.AddOperationPermissionMap(thingsOpPerm); err != nil { return nil, err } if err := opp.Validate(); err != nil { @@ -74,6 +74,20 @@ func AuthorizationMiddleware(entityType string, svc clients.Service, authz authz } func (am *authorizationMiddleware) CreateClients(ctx context.Context, session authn.Session, client ...clients.Client) ([]clients.Client, []roles.RoleProvision, error) { + if session.Type == authn.PersonalAccessToken { + if err := am.authz.AuthorizePAT(ctx, mgauthz.PatReq{ + UserID: session.UserID, + PatID: session.ID, + PlatformEntityType: auth.PlatformDomainsScope, + OptionalDomainID: session.DomainID, + OptionalDomainEntityType: auth.DomainThingsScope, + Operation: auth.CreateOp, + EntityIDs: auth.AnyIDs{}.Values(), + }); err != nil { + return []clients.Client{}, errors.Wrap(svcerr.ErrUnauthorizedPAT, err) + } + } + if err := am.extAuthorize(ctx, clients.DomainOpCreateClient, authz.PolicyReq{ Domain: session.DomainID, SubjectType: policies.UserType, @@ -88,7 +102,21 @@ func (am *authorizationMiddleware) CreateClients(ctx context.Context, session au } func (am *authorizationMiddleware) View(ctx context.Context, session authn.Session, id string) (clients.Client, error) { - if err := am.authorize(ctx, clients.OpViewClient, authz.PolicyReq{ + if session.Type == authn.PersonalAccessToken { + if err := am.authz.AuthorizePAT(ctx, mgauthz.PatReq{ + UserID: session.UserID, + PatID: session.ID, + PlatformEntityType: auth.PlatformDomainsScope, + OptionalDomainID: session.DomainID, + OptionalDomainEntityType: auth.DomainThingsScope, + Operation: auth.ReadOp, + EntityIDs: []string{id}, + }); err != nil { + return clients.Client{}, errors.Wrap(svcerr.ErrUnauthorizedPAT, err) + } + } + + if err := am.authorize(ctx, clients.OpViewThing, authz.PolicyReq{ Domain: session.DomainID, SubjectType: policies.UserType, Subject: session.DomainUserID, @@ -105,11 +133,39 @@ func (am *authorizationMiddleware) ListClients(ctx context.Context, session auth session.SuperAdmin = true } + if session.Type == authn.PersonalAccessToken { + if err := am.authz.AuthorizePAT(ctx, mgauthz.PatReq{ + UserID: session.UserID, + PatID: session.ID, + PlatformEntityType: auth.PlatformDomainsScope, + OptionalDomainID: session.DomainID, + OptionalDomainEntityType: auth.DomainThingsScope, + Operation: auth.ListOp, + EntityIDs: auth.AnyIDs{}.Values(), + }); err != nil { + return clients.ClientsPage{}, errors.Wrap(svcerr.ErrUnauthorizedPAT, err) + } + } + return am.svc.ListClients(ctx, session, reqUserID, pm) } func (am *authorizationMiddleware) Update(ctx context.Context, session authn.Session, client clients.Client) (clients.Client, error) { - if err := am.authorize(ctx, clients.OpUpdateClient, authz.PolicyReq{ + if session.Type == authn.PersonalAccessToken { + if err := am.authz.AuthorizePAT(ctx, mgauthz.PatReq{ + UserID: session.UserID, + PatID: session.ID, + PlatformEntityType: auth.PlatformDomainsScope, + OptionalDomainID: session.DomainID, + OptionalDomainEntityType: auth.DomainThingsScope, + Operation: auth.UpdateOp, + EntityIDs: []string{client.ID}, + }); err != nil { + return clients.Client{}, errors.Wrap(svcerr.ErrUnauthorizedPAT, err) + } + } + + if err := am.authorize(ctx, clients.OpUpdateThing, authz.PolicyReq{ Domain: session.DomainID, SubjectType: policies.UserType, Subject: session.DomainUserID, @@ -123,7 +179,21 @@ func (am *authorizationMiddleware) Update(ctx context.Context, session authn.Ses } func (am *authorizationMiddleware) UpdateTags(ctx context.Context, session authn.Session, client clients.Client) (clients.Client, error) { - if err := am.authorize(ctx, clients.OpUpdateClientTags, authz.PolicyReq{ + if session.Type == authn.PersonalAccessToken { + if err := am.authz.AuthorizePAT(ctx, mgauthz.PatReq{ + UserID: session.UserID, + PatID: session.ID, + PlatformEntityType: auth.PlatformDomainsScope, + OptionalDomainID: session.DomainID, + OptionalDomainEntityType: auth.DomainThingsScope, + Operation: auth.UpdateOp, + EntityIDs: []string{client.ID}, + }); err != nil { + return clients.Client{}, errors.Wrap(svcerr.ErrUnauthorizedPAT, err) + } + } + + if err := am.authorize(ctx, clients.OpUpdateThingTags, authz.PolicyReq{ Domain: session.DomainID, SubjectType: policies.UserType, Subject: session.DomainUserID, @@ -137,7 +207,21 @@ func (am *authorizationMiddleware) UpdateTags(ctx context.Context, session authn } func (am *authorizationMiddleware) UpdateSecret(ctx context.Context, session authn.Session, id, key string) (clients.Client, error) { - if err := am.authorize(ctx, clients.OpUpdateClientSecret, authz.PolicyReq{ + if session.Type == authn.PersonalAccessToken { + if err := am.authz.AuthorizePAT(ctx, mgauthz.PatReq{ + UserID: session.UserID, + PatID: session.ID, + PlatformEntityType: auth.PlatformDomainsScope, + OptionalDomainID: session.DomainID, + OptionalDomainEntityType: auth.DomainThingsScope, + Operation: auth.UpdateOp, + EntityIDs: []string{id}, + }); err != nil { + return clients.Client{}, errors.Wrap(svcerr.ErrUnauthorizedPAT, err) + } + } + + if err := am.authorize(ctx, clients.OpUpdateThingSecret, authz.PolicyReq{ Domain: session.DomainID, SubjectType: policies.UserType, Subject: session.DomainUserID, @@ -150,7 +234,21 @@ func (am *authorizationMiddleware) UpdateSecret(ctx context.Context, session aut } func (am *authorizationMiddleware) Enable(ctx context.Context, session authn.Session, id string) (clients.Client, error) { - if err := am.authorize(ctx, clients.OpEnableClient, authz.PolicyReq{ + if session.Type == authn.PersonalAccessToken { + if err := am.authz.AuthorizePAT(ctx, mgauthz.PatReq{ + UserID: session.UserID, + PatID: session.ID, + PlatformEntityType: auth.PlatformDomainsScope, + OptionalDomainID: session.DomainID, + OptionalDomainEntityType: auth.DomainThingsScope, + Operation: auth.UpdateOp, + EntityIDs: []string{id}, + }); err != nil { + return clients.Client{}, errors.Wrap(svcerr.ErrUnauthorizedPAT, err) + } + } + + if err := am.authorize(ctx, clients.OpEnableThing, authz.PolicyReq{ Domain: session.DomainID, SubjectType: policies.UserType, Subject: session.DomainUserID, @@ -164,7 +262,21 @@ func (am *authorizationMiddleware) Enable(ctx context.Context, session authn.Ses } func (am *authorizationMiddleware) Disable(ctx context.Context, session authn.Session, id string) (clients.Client, error) { - if err := am.authorize(ctx, clients.OpDisableClient, authz.PolicyReq{ + if session.Type == authn.PersonalAccessToken { + if err := am.authz.AuthorizePAT(ctx, mgauthz.PatReq{ + UserID: session.UserID, + PatID: session.ID, + PlatformEntityType: auth.PlatformDomainsScope, + OptionalDomainID: session.DomainID, + OptionalDomainEntityType: auth.DomainThingsScope, + Operation: auth.UpdateOp, + EntityIDs: []string{id}, + }); err != nil { + return clients.Client{}, errors.Wrap(svcerr.ErrUnauthorizedPAT, err) + } + } + + if err := am.authorize(ctx, clients.OpDisableThing, authz.PolicyReq{ Domain: session.DomainID, SubjectType: policies.UserType, Subject: session.DomainUserID, @@ -177,7 +289,20 @@ func (am *authorizationMiddleware) Disable(ctx context.Context, session authn.Se } func (am *authorizationMiddleware) Delete(ctx context.Context, session authn.Session, id string) error { - if err := am.authorize(ctx, clients.OpDeleteClient, authz.PolicyReq{ + if session.Type == authn.PersonalAccessToken { + if err := am.authz.AuthorizePAT(ctx, mgauthz.PatReq{ + UserID: session.UserID, + PatID: session.ID, + PlatformEntityType: auth.PlatformDomainsScope, + OptionalDomainID: session.DomainID, + OptionalDomainEntityType: auth.DomainThingsScope, + Operation: auth.DeleteOp, + EntityIDs: []string{id}, + }); err != nil { + return errors.Wrap(svcerr.ErrUnauthorizedPAT, err) + } + } + if err := am.authorize(ctx, clients.OpDeleteThing, authz.PolicyReq{ Domain: session.DomainID, SubjectType: policies.UserType, Subject: session.DomainUserID, @@ -191,6 +316,20 @@ func (am *authorizationMiddleware) Delete(ctx context.Context, session authn.Ses } func (am *authorizationMiddleware) SetParentGroup(ctx context.Context, session authn.Session, parentGroupID string, id string) error { + if session.Type == authn.PersonalAccessToken { + if err := am.authz.AuthorizePAT(ctx, mgauthz.PatReq{ + UserID: session.UserID, + PatID: session.ID, + PlatformEntityType: auth.PlatformDomainsScope, + OptionalDomainID: session.DomainID, + OptionalDomainEntityType: auth.DomainGroupsScope, + Operation: auth.UpdateOp, + EntityIDs: []string{id}, + }); err != nil { + return errors.Wrap(svcerr.ErrUnauthorizedPAT, err) + } + } + if err := am.authorize(ctx, clients.OpSetParentGroup, authz.PolicyReq{ Domain: session.DomainID, SubjectType: policies.UserType, @@ -201,7 +340,7 @@ func (am *authorizationMiddleware) SetParentGroup(ctx context.Context, session a return errors.Wrap(err, errSetParentGroup) } - if err := am.extAuthorize(ctx, clients.GroupOpSetChildClient, authz.PolicyReq{ + if err := am.extAuthorize(ctx, clients.GroupOpSetChildThing, authz.PolicyReq{ Domain: session.DomainID, SubjectType: policies.UserType, Subject: session.DomainUserID, @@ -214,6 +353,20 @@ func (am *authorizationMiddleware) SetParentGroup(ctx context.Context, session a } func (am *authorizationMiddleware) RemoveParentGroup(ctx context.Context, session authn.Session, id string) error { + if session.Type == authn.PersonalAccessToken { + if err := am.authz.AuthorizePAT(ctx, mgauthz.PatReq{ + UserID: session.UserID, + PatID: session.ID, + PlatformEntityType: auth.PlatformDomainsScope, + OptionalDomainID: session.DomainID, + OptionalDomainEntityType: auth.DomainGroupsScope, + Operation: auth.DeleteOp, + EntityIDs: []string{id}, + }); err != nil { + return errors.Wrap(svcerr.ErrUnauthorizedPAT, err) + } + } + if err := am.authorize(ctx, clients.OpRemoveParentGroup, authz.PolicyReq{ Domain: session.DomainID, SubjectType: policies.UserType, @@ -224,18 +377,18 @@ func (am *authorizationMiddleware) RemoveParentGroup(ctx context.Context, sessio return errors.Wrap(err, errRemoveParentGroup) } - cli, err := am.repo.RetrieveByID(ctx, id) + th, err := am.repo.RetrieveByID(ctx, id) if err != nil { return errors.Wrap(svcerr.ErrRemoveEntity, err) } - if cli.ParentGroup != "" { - if err := am.extAuthorize(ctx, clients.GroupOpSetChildClient, authz.PolicyReq{ + if th.ParentGroup != "" { + if err := am.extAuthorize(ctx, clients.GroupOpSetChildThing, authz.PolicyReq{ Domain: session.DomainID, SubjectType: policies.UserType, Subject: session.DomainUserID, ObjectType: policies.GroupType, - Object: cli.ParentGroup, + Object: th.ParentGroup, }); err != nil { return errors.Wrap(err, errGroupRemoveChildClients) } @@ -264,6 +417,7 @@ func (am *authorizationMiddleware) extAuthorize(ctx context.Context, extOp svcut if err != nil { return err } + req.Permission = perm.String() if err := am.authz.Authorize(ctx, req); err != nil { @@ -274,7 +428,7 @@ func (am *authorizationMiddleware) extAuthorize(ctx context.Context, extOp svcut } func (am *authorizationMiddleware) checkSuperAdmin(ctx context.Context, userID string) error { - if err := am.authz.Authorize(ctx, authz.PolicyReq{ + if err := am.authz.Authorize(ctx, mgauthz.PolicyReq{ SubjectType: policies.UserType, Subject: userID, Permission: policies.AdminPermission, diff --git a/users/middleware/authorization.go b/users/middleware/authorization.go index 0101c924cc..be6caf2320 100644 --- a/users/middleware/authorization.go +++ b/users/middleware/authorization.go @@ -206,10 +206,6 @@ func (am *authorizationMiddleware) Update(ctx context.Context, session authn.Ses } func (am *authorizationMiddleware) UpdateTags(ctx context.Context, session authn.Session, user users.User) (users.User, error) { - if err := am.checkSuperAdmin(ctx, session.UserID); err == nil { - session.SuperAdmin = true - } - if session.Type == authn.PersonalAccessToken { if err := am.authz.AuthorizePAT(ctx, mgauthz.PatReq{ UserID: session.UserID, @@ -223,6 +219,10 @@ func (am *authorizationMiddleware) UpdateTags(ctx context.Context, session authn } } + if err := am.checkSuperAdmin(ctx, session.UserID); err == nil { + session.SuperAdmin = true + } + return am.svc.UpdateTags(ctx, session, user) } @@ -247,10 +247,6 @@ func (am *authorizationMiddleware) UpdateEmail(ctx context.Context, session auth } func (am *authorizationMiddleware) UpdateUsername(ctx context.Context, session authn.Session, id, username string) (users.User, error) { - if err := am.checkSuperAdmin(ctx, session.UserID); err == nil { - session.SuperAdmin = true - } - if session.Type == authn.PersonalAccessToken { if err := am.authz.AuthorizePAT(ctx, mgauthz.PatReq{ UserID: session.UserID, @@ -264,14 +260,14 @@ func (am *authorizationMiddleware) UpdateUsername(ctx context.Context, session a } } - return am.svc.UpdateUsername(ctx, session, id, username) -} - -func (am *authorizationMiddleware) UpdateProfilePicture(ctx context.Context, session authn.Session, user users.User) (users.User, error) { if err := am.checkSuperAdmin(ctx, session.UserID); err == nil { session.SuperAdmin = true } + return am.svc.UpdateUsername(ctx, session, id, username) +} + +func (am *authorizationMiddleware) UpdateProfilePicture(ctx context.Context, session authn.Session, user users.User) (users.User, error) { if session.Type == authn.PersonalAccessToken { if err := am.authz.AuthorizePAT(ctx, mgauthz.PatReq{ UserID: session.UserID, @@ -285,6 +281,10 @@ func (am *authorizationMiddleware) UpdateProfilePicture(ctx context.Context, ses } } + if err := am.checkSuperAdmin(ctx, session.UserID); err == nil { + session.SuperAdmin = true + } + return am.svc.UpdateProfilePicture(ctx, session, user) } @@ -343,10 +343,6 @@ func (am *authorizationMiddleware) UpdateRole(ctx context.Context, session authn } func (am *authorizationMiddleware) Enable(ctx context.Context, session authn.Session, id string) (users.User, error) { - if err := am.checkSuperAdmin(ctx, session.UserID); err == nil { - session.SuperAdmin = true - } - if session.Type == authn.PersonalAccessToken { if err := am.authz.AuthorizePAT(ctx, mgauthz.PatReq{ UserID: session.UserID, @@ -360,14 +356,14 @@ func (am *authorizationMiddleware) Enable(ctx context.Context, session authn.Ses } } - return am.svc.Enable(ctx, session, id) -} - -func (am *authorizationMiddleware) Disable(ctx context.Context, session authn.Session, id string) (users.User, error) { if err := am.checkSuperAdmin(ctx, session.UserID); err == nil { session.SuperAdmin = true } + return am.svc.Enable(ctx, session, id) +} + +func (am *authorizationMiddleware) Disable(ctx context.Context, session authn.Session, id string) (users.User, error) { if session.Type == authn.PersonalAccessToken { if err := am.authz.AuthorizePAT(ctx, mgauthz.PatReq{ UserID: session.UserID, @@ -381,14 +377,14 @@ func (am *authorizationMiddleware) Disable(ctx context.Context, session authn.Se } } - return am.svc.Disable(ctx, session, id) -} - -func (am *authorizationMiddleware) Delete(ctx context.Context, session authn.Session, id string) error { if err := am.checkSuperAdmin(ctx, session.UserID); err == nil { session.SuperAdmin = true } + return am.svc.Disable(ctx, session, id) +} + +func (am *authorizationMiddleware) Delete(ctx context.Context, session authn.Session, id string) error { if session.Type == authn.PersonalAccessToken { if err := am.authz.AuthorizePAT(ctx, mgauthz.PatReq{ UserID: session.UserID, @@ -402,6 +398,10 @@ func (am *authorizationMiddleware) Delete(ctx context.Context, session authn.Ses } } + if err := am.checkSuperAdmin(ctx, session.UserID); err == nil { + session.SuperAdmin = true + } + return am.svc.Delete(ctx, session, id) } From 26e98430b734a39ea41e713f1d9abefc4f259b61 Mon Sep 17 00:00:00 2001 From: nyagamunene Date: Wed, 13 Nov 2024 17:06:18 +0300 Subject: [PATCH 62/72] Resolve conflicts Signed-off-by: nyagamunene --- clients/middleware/authorization.go | 8 ++++---- users/middleware/authorization.go | 13 ------------- 2 files changed, 4 insertions(+), 17 deletions(-) diff --git a/clients/middleware/authorization.go b/clients/middleware/authorization.go index 761ce41856..a654b17334 100644 --- a/clients/middleware/authorization.go +++ b/clients/middleware/authorization.go @@ -129,10 +129,6 @@ func (am *authorizationMiddleware) View(ctx context.Context, session authn.Sessi } func (am *authorizationMiddleware) ListClients(ctx context.Context, session authn.Session, reqUserID string, pm clients.Page) (clients.ClientsPage, error) { - if err := am.checkSuperAdmin(ctx, session.UserID); err != nil { - session.SuperAdmin = true - } - if session.Type == authn.PersonalAccessToken { if err := am.authz.AuthorizePAT(ctx, mgauthz.PatReq{ UserID: session.UserID, @@ -147,6 +143,10 @@ func (am *authorizationMiddleware) ListClients(ctx context.Context, session auth } } + if err := am.checkSuperAdmin(ctx, session.UserID); err != nil { + session.SuperAdmin = true + } + return am.svc.ListClients(ctx, session, reqUserID, pm) } diff --git a/users/middleware/authorization.go b/users/middleware/authorization.go index be6caf2320..df0ab1c7fe 100644 --- a/users/middleware/authorization.go +++ b/users/middleware/authorization.go @@ -189,19 +189,6 @@ func (am *authorizationMiddleware) Update(ctx context.Context, session authn.Ses session.SuperAdmin = true } - if session.Type == authn.PersonalAccessToken { - if err := am.authz.AuthorizePAT(ctx, mgauthz.PatReq{ - UserID: session.UserID, - PatID: session.ID, - PlatformEntityType: mgauth.PlatformUsersScope, - OptionalDomainEntityType: mgauth.DomainNullScope, - Operation: mgauth.UpdateOp, - EntityIDs: []string{user.ID}, - }); err != nil { - return users.User{}, errors.Wrap(svcerr.ErrUnauthorizedPAT, err) - } - } - return am.svc.Update(ctx, session, user) } From 04e5a65c6f992b4582e740cb91e21be10f04159d Mon Sep 17 00:00:00 2001 From: nyagamunene Date: Thu, 5 Dec 2024 20:54:03 +0300 Subject: [PATCH 63/72] rename mg to smq Signed-off-by: nyagamunene --- auth/api/http/transport.go | 2 - auth/hasher/doc.go | 2 +- auth/hasher/hasher.go | 1 + clients/middleware/authorization.go | 59 ++++++------ docker/docker-compose.yml | 2 +- pkg/authz/authsvc/authz.go | 8 +- pkg/authz/authz.go | 12 ++- users/middleware/authorization.go | 142 ++++++++++++++-------------- 8 files changed, 119 insertions(+), 109 deletions(-) diff --git a/auth/api/http/transport.go b/auth/api/http/transport.go index 75b920c253..c3a8d723c0 100644 --- a/auth/api/http/transport.go +++ b/auth/api/http/transport.go @@ -11,8 +11,6 @@ import ( "github.com/absmach/supermq/auth/api/http/keys" "github.com/absmach/supermq/auth/api/http/pats" "github.com/go-chi/chi/v5" - "github.com/absmach/magistrala/auth/api/http/pats" - "github.com/go-chi/chi/v5" "github.com/prometheus/client_golang/prometheus/promhttp" ) diff --git a/auth/hasher/doc.go b/auth/hasher/doc.go index 98be992262..e762b1335e 100644 --- a/auth/hasher/doc.go +++ b/auth/hasher/doc.go @@ -2,5 +2,5 @@ // SPDX-License-Identifier: Apache-2.0 // Package hasher contains the domain concept definitions needed to -// support Magistrala users password hasher sub-service functionality. +// support Supermq users password hasher sub-service functionality. package hasher diff --git a/auth/hasher/hasher.go b/auth/hasher/hasher.go index 18ea2aebc8..a9e4b2df08 100644 --- a/auth/hasher/hasher.go +++ b/auth/hasher/hasher.go @@ -10,6 +10,7 @@ import ( "strings" "time" + "github.com/absmach/supermq/auth" "github.com/absmach/supermq/pkg/errors" "golang.org/x/crypto/scrypt" ) diff --git a/clients/middleware/authorization.go b/clients/middleware/authorization.go index a654b17334..0ce0f29915 100644 --- a/clients/middleware/authorization.go +++ b/clients/middleware/authorization.go @@ -6,9 +6,10 @@ package middleware import ( "context" + "github.com/absmach/supermq/auth" "github.com/absmach/supermq/clients" "github.com/absmach/supermq/pkg/authn" - "github.com/absmach/supermq/pkg/authz" + smqauthz "github.com/absmach/supermq/pkg/authz" "github.com/absmach/supermq/pkg/errors" svcerr "github.com/absmach/supermq/pkg/errors/service" "github.com/absmach/supermq/pkg/policies" @@ -37,14 +38,14 @@ var _ clients.Service = (*authorizationMiddleware)(nil) type authorizationMiddleware struct { svc clients.Service repo clients.Repository - authz mgauthz.Authorization + authz smqauthz.Authorization opp svcutil.OperationPerm extOpp svcutil.ExternalOperationPerm rmMW.RoleManagerAuthorizationMiddleware } // AuthorizationMiddleware adds authorization to the clients service. -func AuthorizationMiddleware(entityType string, svc clients.Service, authz mgauthz.Authorization, repo clients.Repository, thingsOpPerm, rolesOpPerm map[svcutil.Operation]svcutil.Permission, extOpPerm map[svcutil.ExternalOperation]svcutil.Permission) (clients.Service, error) { +func AuthorizationMiddleware(entityType string, svc clients.Service, authz smqauthz.Authorization, repo clients.Repository, thingsOpPerm, rolesOpPerm map[svcutil.Operation]svcutil.Permission, extOpPerm map[svcutil.ExternalOperation]svcutil.Permission) (clients.Service, error) { opp := clients.NewOperationPerm() if err := opp.AddOperationPermissionMap(thingsOpPerm); err != nil { return nil, err @@ -75,7 +76,7 @@ func AuthorizationMiddleware(entityType string, svc clients.Service, authz mgaut func (am *authorizationMiddleware) CreateClients(ctx context.Context, session authn.Session, client ...clients.Client) ([]clients.Client, []roles.RoleProvision, error) { if session.Type == authn.PersonalAccessToken { - if err := am.authz.AuthorizePAT(ctx, mgauthz.PatReq{ + if err := am.authz.AuthorizePAT(ctx, smqauthz.PatReq{ UserID: session.UserID, PatID: session.ID, PlatformEntityType: auth.PlatformDomainsScope, @@ -88,7 +89,7 @@ func (am *authorizationMiddleware) CreateClients(ctx context.Context, session au } } - if err := am.extAuthorize(ctx, clients.DomainOpCreateClient, authz.PolicyReq{ + if err := am.extAuthorize(ctx, clients.DomainOpCreateClient, smqauthz.PolicyReq{ Domain: session.DomainID, SubjectType: policies.UserType, Subject: session.DomainUserID, @@ -103,7 +104,7 @@ func (am *authorizationMiddleware) CreateClients(ctx context.Context, session au func (am *authorizationMiddleware) View(ctx context.Context, session authn.Session, id string) (clients.Client, error) { if session.Type == authn.PersonalAccessToken { - if err := am.authz.AuthorizePAT(ctx, mgauthz.PatReq{ + if err := am.authz.AuthorizePAT(ctx, smqauthz.PatReq{ UserID: session.UserID, PatID: session.ID, PlatformEntityType: auth.PlatformDomainsScope, @@ -116,7 +117,7 @@ func (am *authorizationMiddleware) View(ctx context.Context, session authn.Sessi } } - if err := am.authorize(ctx, clients.OpViewThing, authz.PolicyReq{ + if err := am.authorize(ctx, clients.OpViewClient, smqauthz.PolicyReq{ Domain: session.DomainID, SubjectType: policies.UserType, Subject: session.DomainUserID, @@ -130,7 +131,7 @@ func (am *authorizationMiddleware) View(ctx context.Context, session authn.Sessi func (am *authorizationMiddleware) ListClients(ctx context.Context, session authn.Session, reqUserID string, pm clients.Page) (clients.ClientsPage, error) { if session.Type == authn.PersonalAccessToken { - if err := am.authz.AuthorizePAT(ctx, mgauthz.PatReq{ + if err := am.authz.AuthorizePAT(ctx, smqauthz.PatReq{ UserID: session.UserID, PatID: session.ID, PlatformEntityType: auth.PlatformDomainsScope, @@ -152,7 +153,7 @@ func (am *authorizationMiddleware) ListClients(ctx context.Context, session auth func (am *authorizationMiddleware) Update(ctx context.Context, session authn.Session, client clients.Client) (clients.Client, error) { if session.Type == authn.PersonalAccessToken { - if err := am.authz.AuthorizePAT(ctx, mgauthz.PatReq{ + if err := am.authz.AuthorizePAT(ctx, smqauthz.PatReq{ UserID: session.UserID, PatID: session.ID, PlatformEntityType: auth.PlatformDomainsScope, @@ -165,7 +166,7 @@ func (am *authorizationMiddleware) Update(ctx context.Context, session authn.Ses } } - if err := am.authorize(ctx, clients.OpUpdateThing, authz.PolicyReq{ + if err := am.authorize(ctx, clients.OpUpdateClient, smqauthz.PolicyReq{ Domain: session.DomainID, SubjectType: policies.UserType, Subject: session.DomainUserID, @@ -180,7 +181,7 @@ func (am *authorizationMiddleware) Update(ctx context.Context, session authn.Ses func (am *authorizationMiddleware) UpdateTags(ctx context.Context, session authn.Session, client clients.Client) (clients.Client, error) { if session.Type == authn.PersonalAccessToken { - if err := am.authz.AuthorizePAT(ctx, mgauthz.PatReq{ + if err := am.authz.AuthorizePAT(ctx, smqauthz.PatReq{ UserID: session.UserID, PatID: session.ID, PlatformEntityType: auth.PlatformDomainsScope, @@ -193,7 +194,7 @@ func (am *authorizationMiddleware) UpdateTags(ctx context.Context, session authn } } - if err := am.authorize(ctx, clients.OpUpdateThingTags, authz.PolicyReq{ + if err := am.authorize(ctx, clients.OpUpdateClientTags, smqauthz.PolicyReq{ Domain: session.DomainID, SubjectType: policies.UserType, Subject: session.DomainUserID, @@ -208,7 +209,7 @@ func (am *authorizationMiddleware) UpdateTags(ctx context.Context, session authn func (am *authorizationMiddleware) UpdateSecret(ctx context.Context, session authn.Session, id, key string) (clients.Client, error) { if session.Type == authn.PersonalAccessToken { - if err := am.authz.AuthorizePAT(ctx, mgauthz.PatReq{ + if err := am.authz.AuthorizePAT(ctx, smqauthz.PatReq{ UserID: session.UserID, PatID: session.ID, PlatformEntityType: auth.PlatformDomainsScope, @@ -221,7 +222,7 @@ func (am *authorizationMiddleware) UpdateSecret(ctx context.Context, session aut } } - if err := am.authorize(ctx, clients.OpUpdateThingSecret, authz.PolicyReq{ + if err := am.authorize(ctx, clients.OpUpdateClientSecret, smqauthz.PolicyReq{ Domain: session.DomainID, SubjectType: policies.UserType, Subject: session.DomainUserID, @@ -235,7 +236,7 @@ func (am *authorizationMiddleware) UpdateSecret(ctx context.Context, session aut func (am *authorizationMiddleware) Enable(ctx context.Context, session authn.Session, id string) (clients.Client, error) { if session.Type == authn.PersonalAccessToken { - if err := am.authz.AuthorizePAT(ctx, mgauthz.PatReq{ + if err := am.authz.AuthorizePAT(ctx, smqauthz.PatReq{ UserID: session.UserID, PatID: session.ID, PlatformEntityType: auth.PlatformDomainsScope, @@ -248,7 +249,7 @@ func (am *authorizationMiddleware) Enable(ctx context.Context, session authn.Ses } } - if err := am.authorize(ctx, clients.OpEnableThing, authz.PolicyReq{ + if err := am.authorize(ctx, clients.OpEnableClient, smqauthz.PolicyReq{ Domain: session.DomainID, SubjectType: policies.UserType, Subject: session.DomainUserID, @@ -263,7 +264,7 @@ func (am *authorizationMiddleware) Enable(ctx context.Context, session authn.Ses func (am *authorizationMiddleware) Disable(ctx context.Context, session authn.Session, id string) (clients.Client, error) { if session.Type == authn.PersonalAccessToken { - if err := am.authz.AuthorizePAT(ctx, mgauthz.PatReq{ + if err := am.authz.AuthorizePAT(ctx, smqauthz.PatReq{ UserID: session.UserID, PatID: session.ID, PlatformEntityType: auth.PlatformDomainsScope, @@ -276,7 +277,7 @@ func (am *authorizationMiddleware) Disable(ctx context.Context, session authn.Se } } - if err := am.authorize(ctx, clients.OpDisableThing, authz.PolicyReq{ + if err := am.authorize(ctx, clients.OpDisableClient, smqauthz.PolicyReq{ Domain: session.DomainID, SubjectType: policies.UserType, Subject: session.DomainUserID, @@ -290,7 +291,7 @@ func (am *authorizationMiddleware) Disable(ctx context.Context, session authn.Se func (am *authorizationMiddleware) Delete(ctx context.Context, session authn.Session, id string) error { if session.Type == authn.PersonalAccessToken { - if err := am.authz.AuthorizePAT(ctx, mgauthz.PatReq{ + if err := am.authz.AuthorizePAT(ctx, smqauthz.PatReq{ UserID: session.UserID, PatID: session.ID, PlatformEntityType: auth.PlatformDomainsScope, @@ -302,7 +303,7 @@ func (am *authorizationMiddleware) Delete(ctx context.Context, session authn.Ses return errors.Wrap(svcerr.ErrUnauthorizedPAT, err) } } - if err := am.authorize(ctx, clients.OpDeleteThing, authz.PolicyReq{ + if err := am.authorize(ctx, clients.OpDeleteClient, smqauthz.PolicyReq{ Domain: session.DomainID, SubjectType: policies.UserType, Subject: session.DomainUserID, @@ -317,7 +318,7 @@ func (am *authorizationMiddleware) Delete(ctx context.Context, session authn.Ses func (am *authorizationMiddleware) SetParentGroup(ctx context.Context, session authn.Session, parentGroupID string, id string) error { if session.Type == authn.PersonalAccessToken { - if err := am.authz.AuthorizePAT(ctx, mgauthz.PatReq{ + if err := am.authz.AuthorizePAT(ctx, smqauthz.PatReq{ UserID: session.UserID, PatID: session.ID, PlatformEntityType: auth.PlatformDomainsScope, @@ -330,7 +331,7 @@ func (am *authorizationMiddleware) SetParentGroup(ctx context.Context, session a } } - if err := am.authorize(ctx, clients.OpSetParentGroup, authz.PolicyReq{ + if err := am.authorize(ctx, clients.OpSetParentGroup, smqauthz.PolicyReq{ Domain: session.DomainID, SubjectType: policies.UserType, Subject: session.DomainUserID, @@ -340,7 +341,7 @@ func (am *authorizationMiddleware) SetParentGroup(ctx context.Context, session a return errors.Wrap(err, errSetParentGroup) } - if err := am.extAuthorize(ctx, clients.GroupOpSetChildThing, authz.PolicyReq{ + if err := am.extAuthorize(ctx, clients.GroupOpSetChildClient, smqauthz.PolicyReq{ Domain: session.DomainID, SubjectType: policies.UserType, Subject: session.DomainUserID, @@ -354,7 +355,7 @@ func (am *authorizationMiddleware) SetParentGroup(ctx context.Context, session a func (am *authorizationMiddleware) RemoveParentGroup(ctx context.Context, session authn.Session, id string) error { if session.Type == authn.PersonalAccessToken { - if err := am.authz.AuthorizePAT(ctx, mgauthz.PatReq{ + if err := am.authz.AuthorizePAT(ctx, smqauthz.PatReq{ UserID: session.UserID, PatID: session.ID, PlatformEntityType: auth.PlatformDomainsScope, @@ -367,7 +368,7 @@ func (am *authorizationMiddleware) RemoveParentGroup(ctx context.Context, sessio } } - if err := am.authorize(ctx, clients.OpRemoveParentGroup, authz.PolicyReq{ + if err := am.authorize(ctx, clients.OpRemoveParentGroup, smqauthz.PolicyReq{ Domain: session.DomainID, SubjectType: policies.UserType, Subject: session.DomainUserID, @@ -383,7 +384,7 @@ func (am *authorizationMiddleware) RemoveParentGroup(ctx context.Context, sessio } if th.ParentGroup != "" { - if err := am.extAuthorize(ctx, clients.GroupOpSetChildThing, authz.PolicyReq{ + if err := am.extAuthorize(ctx, clients.GroupOpSetChildClient, smqauthz.PolicyReq{ Domain: session.DomainID, SubjectType: policies.UserType, Subject: session.DomainUserID, @@ -397,7 +398,7 @@ func (am *authorizationMiddleware) RemoveParentGroup(ctx context.Context, sessio return nil } -func (am *authorizationMiddleware) authorize(ctx context.Context, op svcutil.Operation, req authz.PolicyReq) error { +func (am *authorizationMiddleware) authorize(ctx context.Context, op svcutil.Operation, req smqauthz.PolicyReq) error { perm, err := am.opp.GetPermission(op) if err != nil { return err @@ -412,7 +413,7 @@ func (am *authorizationMiddleware) authorize(ctx context.Context, op svcutil.Ope return nil } -func (am *authorizationMiddleware) extAuthorize(ctx context.Context, extOp svcutil.ExternalOperation, req authz.PolicyReq) error { +func (am *authorizationMiddleware) extAuthorize(ctx context.Context, extOp svcutil.ExternalOperation, req smqauthz.PolicyReq) error { perm, err := am.extOpp.GetPermission(extOp) if err != nil { return err @@ -428,7 +429,7 @@ func (am *authorizationMiddleware) extAuthorize(ctx context.Context, extOp svcut } func (am *authorizationMiddleware) checkSuperAdmin(ctx context.Context, userID string) error { - if err := am.authz.Authorize(ctx, mgauthz.PolicyReq{ + if err := am.authz.Authorize(ctx, smqauthz.PolicyReq{ SubjectType: policies.UserType, Subject: userID, Permission: policies.AdminPermission, diff --git a/docker/docker-compose.yml b/docker/docker-compose.yml index 5f105369b7..71dd0268e7 100644 --- a/docker/docker-compose.yml +++ b/docker/docker-compose.yml @@ -1215,7 +1215,7 @@ services: - supermq-base-net ui: - image: magistrala/ui:${SMQ_RELEASE_TAG} + image: supermq/ui:${SMQ_RELEASE_TAG} container_name: supermq-ui restart: on-failure environment: diff --git a/pkg/authz/authsvc/authz.go b/pkg/authz/authsvc/authz.go index 4fa6f49688..c4263db83f 100644 --- a/pkg/authz/authsvc/authz.go +++ b/pkg/authz/authsvc/authz.go @@ -124,13 +124,13 @@ func (a authorization) checkDomain(ctx context.Context, subjectType, subject, do func (a authorization) AuthorizePAT(ctx context.Context, pr authz.PatReq) error { req := grpcAuthV1.AuthZpatReq{ - UserID: pr.UserID, - PatID: pr.PatID, + UserId: pr.UserID, + PatId: pr.PatID, PlatformEntityType: uint32(pr.PlatformEntityType), - OptionalDomainID: pr.OptionalDomainID, + OptionalDomainId: pr.OptionalDomainID, OptionalDomainEntityType: uint32(pr.OptionalDomainEntityType), Operation: uint32(pr.Operation), - EntityIDs: pr.EntityIDs, + EntityIds: pr.EntityIDs, } res, err := a.authSvcClient.AuthorizePAT(ctx, &req) if err != nil { diff --git a/pkg/authz/authz.go b/pkg/authz/authz.go index 79173b8740..93d807ac5e 100644 --- a/pkg/authz/authz.go +++ b/pkg/authz/authz.go @@ -6,7 +6,7 @@ package authz import ( "context" - "github.com/absmach/magistrala/auth" + "github.com/absmach/supermq/auth" ) type PolicyReq struct { @@ -46,6 +46,16 @@ type PolicyReq struct { Permission string `json:"permission,omitempty"` } +type PatReq struct { + UserID string `json:"user_id,omitempty"` // UserID + PatID string `json:"pat_id,omitempty"` // UserID + PlatformEntityType auth.PlatformEntityType `json:"platform_entity_type,omitempty"` // Platform entity type + OptionalDomainID string `json:"optional_domainID,omitempty"` // Optional domain id + OptionalDomainEntityType auth.DomainEntityType `json:"optional_domain_entity_type,omitempty"` // Optional domain entity type + Operation auth.OperationType `json:"operation,omitempty"` // Operation + EntityIDs []string `json:"entityIDs,omitempty"` // EntityIDs +} + // Authz is supermq authorization library. // //go:generate mockery --name Authorization --output=./mocks --filename authz.go --quiet --note "Copyright (c) Abstract Machines" diff --git a/users/middleware/authorization.go b/users/middleware/authorization.go index df0ab1c7fe..20e9dcd239 100644 --- a/users/middleware/authorization.go +++ b/users/middleware/authorization.go @@ -9,8 +9,8 @@ import ( grpcTokenV1 "github.com/absmach/supermq/api/grpc/token/v1" smqauth "github.com/absmach/supermq/auth" "github.com/absmach/supermq/pkg/authn" - "github.com/absmach/supermq/pkg/authz" smqauthz "github.com/absmach/supermq/pkg/authz" + "github.com/absmach/supermq/pkg/errors" svcerr "github.com/absmach/supermq/pkg/errors/service" "github.com/absmach/supermq/pkg/policies" "github.com/absmach/supermq/users" @@ -45,12 +45,12 @@ func (am *authorizationMiddleware) Register(ctx context.Context, session authn.S func (am *authorizationMiddleware) View(ctx context.Context, session authn.Session, id string) (users.User, error) { if session.Type == authn.PersonalAccessToken { - if err := am.authz.AuthorizePAT(ctx, mgauthz.PatReq{ + if err := am.authz.AuthorizePAT(ctx, smqauthz.PatReq{ UserID: session.UserID, PatID: session.ID, - PlatformEntityType: mgauth.PlatformUsersScope, - OptionalDomainEntityType: mgauth.DomainNullScope, - Operation: mgauth.ReadOp, + PlatformEntityType: smqauth.PlatformUsersScope, + OptionalDomainEntityType: smqauth.DomainNullScope, + Operation: smqauth.ReadOp, EntityIDs: []string{id}, }); err != nil { return users.User{}, errors.Wrap(svcerr.ErrUnauthorizedPAT, err) @@ -66,12 +66,12 @@ func (am *authorizationMiddleware) View(ctx context.Context, session authn.Sessi func (am *authorizationMiddleware) ViewProfile(ctx context.Context, session authn.Session) (users.User, error) { if session.Type == authn.PersonalAccessToken { - if err := am.authz.AuthorizePAT(ctx, mgauthz.PatReq{ + if err := am.authz.AuthorizePAT(ctx, smqauthz.PatReq{ UserID: session.UserID, PatID: session.ID, - PlatformEntityType: mgauth.PlatformUsersScope, - OptionalDomainEntityType: mgauth.DomainNullScope, - Operation: mgauth.ReadOp, + PlatformEntityType: smqauth.PlatformUsersScope, + OptionalDomainEntityType: smqauth.DomainNullScope, + Operation: smqauth.ReadOp, EntityIDs: []string{session.UserID}, }); err != nil { return users.User{}, errors.Wrap(svcerr.ErrUnauthorizedPAT, err) @@ -82,13 +82,13 @@ func (am *authorizationMiddleware) ViewProfile(ctx context.Context, session auth func (am *authorizationMiddleware) ListUsers(ctx context.Context, session authn.Session, pm users.Page) (users.UsersPage, error) { if session.Type == authn.PersonalAccessToken { - if err := am.authz.AuthorizePAT(ctx, mgauthz.PatReq{ + if err := am.authz.AuthorizePAT(ctx, smqauthz.PatReq{ UserID: session.UserID, PatID: session.ID, - PlatformEntityType: mgauth.PlatformUsersScope, - OptionalDomainEntityType: mgauth.DomainNullScope, - Operation: mgauth.ListOp, - EntityIDs: mgauth.AnyIDs{}.Values(), + PlatformEntityType: smqauth.PlatformUsersScope, + OptionalDomainEntityType: smqauth.DomainNullScope, + Operation: smqauth.ListOp, + EntityIDs: smqauth.AnyIDs{}.Values(), }); err != nil { return users.UsersPage{}, errors.Wrap(svcerr.ErrUnauthorizedPAT, err) } @@ -104,38 +104,38 @@ func (am *authorizationMiddleware) ListMembers(ctx context.Context, session auth if session.Type == authn.PersonalAccessToken { switch objectKind { case policies.GroupsKind: - if err := am.authz.AuthorizePAT(ctx, mgauthz.PatReq{ + if err := am.authz.AuthorizePAT(ctx, smqauthz.PatReq{ UserID: session.UserID, PatID: session.ID, OptionalDomainID: session.DomainID, - PlatformEntityType: mgauth.PlatformUsersScope, - OptionalDomainEntityType: mgauth.DomainGroupsScope, - Operation: mgauth.ListOp, - EntityIDs: mgauth.AnyIDs{}.Values(), + PlatformEntityType: smqauth.PlatformUsersScope, + OptionalDomainEntityType: smqauth.DomainGroupsScope, + Operation: smqauth.ListOp, + EntityIDs: smqauth.AnyIDs{}.Values(), }); err != nil { return users.MembersPage{}, errors.Wrap(svcerr.ErrUnauthorizedPAT, err) } case policies.DomainsKind: - if err := am.authz.AuthorizePAT(ctx, mgauthz.PatReq{ + if err := am.authz.AuthorizePAT(ctx, smqauthz.PatReq{ UserID: session.UserID, PatID: session.ID, OptionalDomainID: session.DomainID, - PlatformEntityType: mgauth.PlatformUsersScope, - OptionalDomainEntityType: mgauth.DomainManagementScope, - Operation: mgauth.ListOp, - EntityIDs: mgauth.AnyIDs{}.Values(), + PlatformEntityType: smqauth.PlatformUsersScope, + OptionalDomainEntityType: smqauth.DomainManagementScope, + Operation: smqauth.ListOp, + EntityIDs: smqauth.AnyIDs{}.Values(), }); err != nil { return users.MembersPage{}, errors.Wrap(svcerr.ErrUnauthorizedPAT, err) } case policies.ClientsKind: - if err := am.authz.AuthorizePAT(ctx, mgauthz.PatReq{ + if err := am.authz.AuthorizePAT(ctx, smqauthz.PatReq{ UserID: session.UserID, PatID: session.ID, OptionalDomainID: session.DomainID, - PlatformEntityType: mgauth.PlatformUsersScope, - OptionalDomainEntityType: mgauth.DomainThingsScope, - Operation: mgauth.ListOp, - EntityIDs: mgauth.AnyIDs{}.Values(), + PlatformEntityType: smqauth.PlatformUsersScope, + OptionalDomainEntityType: smqauth.DomainThingsScope, + Operation: smqauth.ListOp, + EntityIDs: smqauth.AnyIDs{}.Values(), }); err != nil { return users.MembersPage{}, errors.Wrap(svcerr.ErrUnauthorizedPAT, err) } @@ -173,12 +173,12 @@ func (am *authorizationMiddleware) SearchUsers(ctx context.Context, pm users.Pag func (am *authorizationMiddleware) Update(ctx context.Context, session authn.Session, user users.User) (users.User, error) { if session.Type == authn.PersonalAccessToken { - if err := am.authz.AuthorizePAT(ctx, mgauthz.PatReq{ + if err := am.authz.AuthorizePAT(ctx, smqauthz.PatReq{ UserID: session.UserID, PatID: session.ID, - PlatformEntityType: mgauth.PlatformUsersScope, - OptionalDomainEntityType: mgauth.DomainNullScope, - Operation: mgauth.UpdateOp, + PlatformEntityType: smqauth.PlatformUsersScope, + OptionalDomainEntityType: smqauth.DomainNullScope, + Operation: smqauth.UpdateOp, EntityIDs: []string{user.ID}, }); err != nil { return users.User{}, errors.Wrap(svcerr.ErrUnauthorizedPAT, err) @@ -194,12 +194,12 @@ func (am *authorizationMiddleware) Update(ctx context.Context, session authn.Ses func (am *authorizationMiddleware) UpdateTags(ctx context.Context, session authn.Session, user users.User) (users.User, error) { if session.Type == authn.PersonalAccessToken { - if err := am.authz.AuthorizePAT(ctx, mgauthz.PatReq{ + if err := am.authz.AuthorizePAT(ctx, smqauthz.PatReq{ UserID: session.UserID, PatID: session.ID, - PlatformEntityType: mgauth.PlatformUsersScope, - OptionalDomainEntityType: mgauth.DomainNullScope, - Operation: mgauth.UpdateOp, + PlatformEntityType: smqauth.PlatformUsersScope, + OptionalDomainEntityType: smqauth.DomainNullScope, + Operation: smqauth.UpdateOp, EntityIDs: []string{user.ID}, }); err != nil { return users.User{}, errors.Wrap(svcerr.ErrUnauthorizedPAT, err) @@ -215,12 +215,12 @@ func (am *authorizationMiddleware) UpdateTags(ctx context.Context, session authn func (am *authorizationMiddleware) UpdateEmail(ctx context.Context, session authn.Session, id, email string) (users.User, error) { if session.Type == authn.PersonalAccessToken { - if err := am.authz.AuthorizePAT(ctx, mgauthz.PatReq{ + if err := am.authz.AuthorizePAT(ctx, smqauthz.PatReq{ UserID: session.UserID, PatID: session.ID, - PlatformEntityType: mgauth.PlatformUsersScope, - OptionalDomainEntityType: mgauth.DomainNullScope, - Operation: mgauth.UpdateOp, + PlatformEntityType: smqauth.PlatformUsersScope, + OptionalDomainEntityType: smqauth.DomainNullScope, + Operation: smqauth.UpdateOp, EntityIDs: []string{id}, }); err != nil { return users.User{}, errors.Wrap(svcerr.ErrUnauthorizedPAT, err) @@ -235,12 +235,12 @@ func (am *authorizationMiddleware) UpdateEmail(ctx context.Context, session auth func (am *authorizationMiddleware) UpdateUsername(ctx context.Context, session authn.Session, id, username string) (users.User, error) { if session.Type == authn.PersonalAccessToken { - if err := am.authz.AuthorizePAT(ctx, mgauthz.PatReq{ + if err := am.authz.AuthorizePAT(ctx, smqauthz.PatReq{ UserID: session.UserID, PatID: session.ID, - PlatformEntityType: mgauth.PlatformUsersScope, - OptionalDomainEntityType: mgauth.DomainNullScope, - Operation: mgauth.UpdateOp, + PlatformEntityType: smqauth.PlatformUsersScope, + OptionalDomainEntityType: smqauth.DomainNullScope, + Operation: smqauth.UpdateOp, EntityIDs: []string{id}, }); err != nil { return users.User{}, errors.Wrap(svcerr.ErrUnauthorizedPAT, err) @@ -256,12 +256,12 @@ func (am *authorizationMiddleware) UpdateUsername(ctx context.Context, session a func (am *authorizationMiddleware) UpdateProfilePicture(ctx context.Context, session authn.Session, user users.User) (users.User, error) { if session.Type == authn.PersonalAccessToken { - if err := am.authz.AuthorizePAT(ctx, mgauthz.PatReq{ + if err := am.authz.AuthorizePAT(ctx, smqauthz.PatReq{ UserID: session.UserID, PatID: session.ID, - PlatformEntityType: mgauth.PlatformUsersScope, - OptionalDomainEntityType: mgauth.DomainNullScope, - Operation: mgauth.UpdateOp, + PlatformEntityType: smqauth.PlatformUsersScope, + OptionalDomainEntityType: smqauth.DomainNullScope, + Operation: smqauth.UpdateOp, EntityIDs: []string{user.ID}, }); err != nil { return users.User{}, errors.Wrap(svcerr.ErrUnauthorizedPAT, err) @@ -281,12 +281,12 @@ func (am *authorizationMiddleware) GenerateResetToken(ctx context.Context, email func (am *authorizationMiddleware) UpdateSecret(ctx context.Context, session authn.Session, oldSecret, newSecret string) (users.User, error) { if session.Type == authn.PersonalAccessToken { - if err := am.authz.AuthorizePAT(ctx, mgauthz.PatReq{ + if err := am.authz.AuthorizePAT(ctx, smqauthz.PatReq{ UserID: session.UserID, PatID: session.ID, - PlatformEntityType: mgauth.PlatformUsersScope, - OptionalDomainEntityType: mgauth.DomainNullScope, - Operation: mgauth.UpdateOp, + PlatformEntityType: smqauth.PlatformUsersScope, + OptionalDomainEntityType: smqauth.DomainNullScope, + Operation: smqauth.UpdateOp, EntityIDs: []string{session.UserID}, }); err != nil { return users.User{}, errors.Wrap(svcerr.ErrUnauthorizedPAT, err) @@ -306,12 +306,12 @@ func (am *authorizationMiddleware) SendPasswordReset(ctx context.Context, host, func (am *authorizationMiddleware) UpdateRole(ctx context.Context, session authn.Session, user users.User) (users.User, error) { if session.Type == authn.PersonalAccessToken { - if err := am.authz.AuthorizePAT(ctx, mgauthz.PatReq{ + if err := am.authz.AuthorizePAT(ctx, smqauthz.PatReq{ UserID: session.UserID, PatID: session.ID, - PlatformEntityType: mgauth.PlatformUsersScope, - OptionalDomainEntityType: mgauth.DomainNullScope, - Operation: mgauth.UpdateOp, + PlatformEntityType: smqauth.PlatformUsersScope, + OptionalDomainEntityType: smqauth.DomainNullScope, + Operation: smqauth.UpdateOp, EntityIDs: []string{user.ID}, }); err != nil { return users.User{}, errors.Wrap(svcerr.ErrUnauthorizedPAT, err) @@ -331,12 +331,12 @@ func (am *authorizationMiddleware) UpdateRole(ctx context.Context, session authn func (am *authorizationMiddleware) Enable(ctx context.Context, session authn.Session, id string) (users.User, error) { if session.Type == authn.PersonalAccessToken { - if err := am.authz.AuthorizePAT(ctx, mgauthz.PatReq{ + if err := am.authz.AuthorizePAT(ctx, smqauthz.PatReq{ UserID: session.UserID, PatID: session.ID, - PlatformEntityType: mgauth.PlatformUsersScope, - OptionalDomainEntityType: mgauth.DomainNullScope, - Operation: mgauth.UpdateOp, + PlatformEntityType: smqauth.PlatformUsersScope, + OptionalDomainEntityType: smqauth.DomainNullScope, + Operation: smqauth.UpdateOp, EntityIDs: []string{id}, }); err != nil { return users.User{}, errors.Wrap(svcerr.ErrUnauthorizedPAT, err) @@ -352,12 +352,12 @@ func (am *authorizationMiddleware) Enable(ctx context.Context, session authn.Ses func (am *authorizationMiddleware) Disable(ctx context.Context, session authn.Session, id string) (users.User, error) { if session.Type == authn.PersonalAccessToken { - if err := am.authz.AuthorizePAT(ctx, mgauthz.PatReq{ + if err := am.authz.AuthorizePAT(ctx, smqauthz.PatReq{ UserID: session.UserID, PatID: session.ID, - PlatformEntityType: mgauth.PlatformUsersScope, - OptionalDomainEntityType: mgauth.DomainNullScope, - Operation: mgauth.UpdateOp, + PlatformEntityType: smqauth.PlatformUsersScope, + OptionalDomainEntityType: smqauth.DomainNullScope, + Operation: smqauth.UpdateOp, EntityIDs: []string{id}, }); err != nil { return users.User{}, errors.Wrap(svcerr.ErrUnauthorizedPAT, err) @@ -373,12 +373,12 @@ func (am *authorizationMiddleware) Disable(ctx context.Context, session authn.Se func (am *authorizationMiddleware) Delete(ctx context.Context, session authn.Session, id string) error { if session.Type == authn.PersonalAccessToken { - if err := am.authz.AuthorizePAT(ctx, mgauthz.PatReq{ + if err := am.authz.AuthorizePAT(ctx, smqauthz.PatReq{ UserID: session.UserID, PatID: session.ID, - PlatformEntityType: mgauth.PlatformUsersScope, - OptionalDomainEntityType: mgauth.DomainNullScope, - Operation: mgauth.DeleteOp, + PlatformEntityType: smqauth.PlatformUsersScope, + OptionalDomainEntityType: smqauth.DomainNullScope, + Operation: smqauth.DeleteOp, EntityIDs: []string{id}, }); err != nil { return errors.Wrap(svcerr.ErrUnauthorizedPAT, err) @@ -416,7 +416,7 @@ func (am *authorizationMiddleware) OAuthAddUserPolicy(ctx context.Context, user } func (am *authorizationMiddleware) checkSuperAdmin(ctx context.Context, adminID string) error { - if err := am.authz.Authorize(ctx, authz.PolicyReq{ + if err := am.authz.Authorize(ctx, smqauthz.PolicyReq{ SubjectType: policies.UserType, Subject: adminID, Permission: policies.AdminPermission, @@ -429,7 +429,7 @@ func (am *authorizationMiddleware) checkSuperAdmin(ctx context.Context, adminID } func (am *authorizationMiddleware) authorize(ctx context.Context, domain, subjType, subjKind, subj, perm, objType, obj string) error { - req := authz.PolicyReq{ + req := smqauthz.PolicyReq{ Domain: domain, SubjectType: subjType, SubjectKind: subjKind, From 32cb4f534470f99e0a9e788dc953e08293f7ac6a Mon Sep 17 00:00:00 2001 From: nyagamunene Date: Thu, 5 Dec 2024 21:20:14 +0300 Subject: [PATCH 64/72] update go mod file and proto files Signed-off-by: nyagamunene --- api/grpc/channels/v1/channels.pb.go | 74 -------------- api/grpc/clients/v1/clients.pb.go | 134 +++++-------------------- api/grpc/clients/v1/clients_grpc.pb.go | 23 +++-- go.mod | 2 - 4 files changed, 41 insertions(+), 192 deletions(-) diff --git a/api/grpc/channels/v1/channels.pb.go b/api/grpc/channels/v1/channels.pb.go index 5d607cc219..d9750f5f7d 100644 --- a/api/grpc/channels/v1/channels.pb.go +++ b/api/grpc/channels/v1/channels.pb.go @@ -411,80 +411,6 @@ func file_channels_v1_channels_proto_init() { if File_channels_v1_channels_proto != nil { return } - if !protoimpl.UnsafeEnabled { - file_channels_v1_channels_proto_msgTypes[0].Exporter = func(v any, i int) any { - switch v := v.(*RemoveClientConnectionsReq); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_channels_v1_channels_proto_msgTypes[1].Exporter = func(v any, i int) any { - switch v := v.(*RemoveClientConnectionsRes); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_channels_v1_channels_proto_msgTypes[2].Exporter = func(v any, i int) any { - switch v := v.(*UnsetParentGroupFromChannelsReq); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_channels_v1_channels_proto_msgTypes[3].Exporter = func(v any, i int) any { - switch v := v.(*UnsetParentGroupFromChannelsRes); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_channels_v1_channels_proto_msgTypes[4].Exporter = func(v any, i int) any { - switch v := v.(*AuthzReq); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_channels_v1_channels_proto_msgTypes[5].Exporter = func(v any, i int) any { - switch v := v.(*AuthzRes); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - } type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ diff --git a/api/grpc/clients/v1/clients.pb.go b/api/grpc/clients/v1/clients.pb.go index b525505537..5bf1fc93b4 100644 --- a/api/grpc/clients/v1/clients.pb.go +++ b/api/grpc/clients/v1/clients.pb.go @@ -34,11 +34,9 @@ type AuthnReq struct { func (x *AuthnReq) Reset() { *x = AuthnReq{} - if protoimpl.UnsafeEnabled { - mi := &file_clients_v1_clients_proto_msgTypes[0] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } + mi := &file_clients_v1_clients_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } func (x *AuthnReq) String() string { @@ -49,7 +47,7 @@ func (*AuthnReq) ProtoMessage() {} func (x *AuthnReq) ProtoReflect() protoreflect.Message { mi := &file_clients_v1_clients_proto_msgTypes[0] - if protoimpl.UnsafeEnabled && x != nil { + if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -88,11 +86,9 @@ type AuthnRes struct { func (x *AuthnRes) Reset() { *x = AuthnRes{} - if protoimpl.UnsafeEnabled { - mi := &file_clients_v1_clients_proto_msgTypes[1] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } + mi := &file_clients_v1_clients_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } func (x *AuthnRes) String() string { @@ -103,7 +99,7 @@ func (*AuthnRes) ProtoMessage() {} func (x *AuthnRes) ProtoReflect() protoreflect.Message { mi := &file_clients_v1_clients_proto_msgTypes[1] - if protoimpl.UnsafeEnabled && x != nil { + if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -141,11 +137,9 @@ type RemoveChannelConnectionsReq struct { func (x *RemoveChannelConnectionsReq) Reset() { *x = RemoveChannelConnectionsReq{} - if protoimpl.UnsafeEnabled { - mi := &file_clients_v1_clients_proto_msgTypes[2] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } + mi := &file_clients_v1_clients_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } func (x *RemoveChannelConnectionsReq) String() string { @@ -156,7 +150,7 @@ func (*RemoveChannelConnectionsReq) ProtoMessage() {} func (x *RemoveChannelConnectionsReq) ProtoReflect() protoreflect.Message { mi := &file_clients_v1_clients_proto_msgTypes[2] - if protoimpl.UnsafeEnabled && x != nil { + if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -186,11 +180,9 @@ type RemoveChannelConnectionsRes struct { func (x *RemoveChannelConnectionsRes) Reset() { *x = RemoveChannelConnectionsRes{} - if protoimpl.UnsafeEnabled { - mi := &file_clients_v1_clients_proto_msgTypes[3] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } + mi := &file_clients_v1_clients_proto_msgTypes[3] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } func (x *RemoveChannelConnectionsRes) String() string { @@ -201,7 +193,7 @@ func (*RemoveChannelConnectionsRes) ProtoMessage() {} func (x *RemoveChannelConnectionsRes) ProtoReflect() protoreflect.Message { mi := &file_clients_v1_clients_proto_msgTypes[3] - if protoimpl.UnsafeEnabled && x != nil { + if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -225,11 +217,9 @@ type UnsetParentGroupFromClientReq struct { func (x *UnsetParentGroupFromClientReq) Reset() { *x = UnsetParentGroupFromClientReq{} - if protoimpl.UnsafeEnabled { - mi := &file_clients_v1_clients_proto_msgTypes[4] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } + mi := &file_clients_v1_clients_proto_msgTypes[4] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } func (x *UnsetParentGroupFromClientReq) String() string { @@ -240,7 +230,7 @@ func (*UnsetParentGroupFromClientReq) ProtoMessage() {} func (x *UnsetParentGroupFromClientReq) ProtoReflect() protoreflect.Message { mi := &file_clients_v1_clients_proto_msgTypes[4] - if protoimpl.UnsafeEnabled && x != nil { + if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -270,11 +260,9 @@ type UnsetParentGroupFromClientRes struct { func (x *UnsetParentGroupFromClientRes) Reset() { *x = UnsetParentGroupFromClientRes{} - if protoimpl.UnsafeEnabled { - mi := &file_clients_v1_clients_proto_msgTypes[5] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } + mi := &file_clients_v1_clients_proto_msgTypes[5] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } func (x *UnsetParentGroupFromClientRes) String() string { @@ -285,7 +273,7 @@ func (*UnsetParentGroupFromClientRes) ProtoMessage() {} func (x *UnsetParentGroupFromClientRes) ProtoReflect() protoreflect.Message { mi := &file_clients_v1_clients_proto_msgTypes[5] - if protoimpl.UnsafeEnabled && x != nil { + if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -431,80 +419,6 @@ func file_clients_v1_clients_proto_init() { if File_clients_v1_clients_proto != nil { return } - if !protoimpl.UnsafeEnabled { - file_clients_v1_clients_proto_msgTypes[0].Exporter = func(v any, i int) any { - switch v := v.(*AuthnReq); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_clients_v1_clients_proto_msgTypes[1].Exporter = func(v any, i int) any { - switch v := v.(*AuthnRes); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_clients_v1_clients_proto_msgTypes[2].Exporter = func(v any, i int) any { - switch v := v.(*RemoveChannelConnectionsReq); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_clients_v1_clients_proto_msgTypes[3].Exporter = func(v any, i int) any { - switch v := v.(*RemoveChannelConnectionsRes); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_clients_v1_clients_proto_msgTypes[4].Exporter = func(v any, i int) any { - switch v := v.(*UnsetParentGroupFromClientReq); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_clients_v1_clients_proto_msgTypes[5].Exporter = func(v any, i int) any { - switch v := v.(*UnsetParentGroupFromClientRes); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - } type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ diff --git a/api/grpc/clients/v1/clients_grpc.pb.go b/api/grpc/clients/v1/clients_grpc.pb.go index a2b627b843..bd04a8c47f 100644 --- a/api/grpc/clients/v1/clients_grpc.pb.go +++ b/api/grpc/clients/v1/clients_grpc.pb.go @@ -19,8 +19,8 @@ import ( // This is a compile-time assertion to ensure that this generated file // is compatible with the grpc package it is being compiled against. -// Requires gRPC-Go v1.62.0 or later. -const _ = grpc.SupportPackageIsVersion8 +// Requires gRPC-Go v1.64.0 or later. +const _ = grpc.SupportPackageIsVersion9 const ( ClientsService_Authenticate_FullMethodName = "/clients.v1.ClientsService/Authenticate" @@ -129,7 +129,7 @@ func (c *clientsServiceClient) UnsetParentGroupFromClient(ctx context.Context, i // ClientsServiceServer is the server API for ClientsService service. // All implementations must embed UnimplementedClientsServiceServer -// for forward compatibility +// for forward compatibility. // // ClientsService is a service that provides clients // authorization functionalities for SuperMQ services. @@ -145,9 +145,12 @@ type ClientsServiceServer interface { mustEmbedUnimplementedClientsServiceServer() } -// UnimplementedClientsServiceServer must be embedded to have forward compatible implementations. -type UnimplementedClientsServiceServer struct { -} +// UnimplementedClientsServiceServer must be embedded to have +// forward compatible implementations. +// +// NOTE: this should be embedded by value instead of pointer to avoid a nil +// pointer dereference when methods are called. +type UnimplementedClientsServiceServer struct{} func (UnimplementedClientsServiceServer) Authenticate(context.Context, *AuthnReq) (*AuthnRes, error) { return nil, status.Errorf(codes.Unimplemented, "method Authenticate not implemented") @@ -171,6 +174,7 @@ func (UnimplementedClientsServiceServer) UnsetParentGroupFromClient(context.Cont return nil, status.Errorf(codes.Unimplemented, "method UnsetParentGroupFromClient not implemented") } func (UnimplementedClientsServiceServer) mustEmbedUnimplementedClientsServiceServer() {} +func (UnimplementedClientsServiceServer) testEmbeddedByValue() {} // UnsafeClientsServiceServer may be embedded to opt out of forward compatibility for this service. // Use of this interface is not recommended, as added methods to ClientsServiceServer will @@ -180,6 +184,13 @@ type UnsafeClientsServiceServer interface { } func RegisterClientsServiceServer(s grpc.ServiceRegistrar, srv ClientsServiceServer) { + // If the following call pancis, it indicates UnimplementedClientsServiceServer was + // embedded by pointer and is nil. This will cause panics if an + // unimplemented method is ever invoked, so we test this at initialization + // time to prevent it from happening at runtime later due to I/O. + if t, ok := srv.(interface{ testEmbeddedByValue() }); ok { + t.testEmbeddedByValue() + } s.RegisterService(&ClientsService_ServiceDesc, srv) } diff --git a/go.mod b/go.mod index 52b54b3997..f79cb10647 100644 --- a/go.mod +++ b/go.mod @@ -44,7 +44,6 @@ require ( github.com/spf13/cobra v1.8.1 github.com/spf13/viper v1.19.0 github.com/sqids/sqids-go v0.4.1 - github.com/sqids/sqids-go v0.4.1 github.com/stretchr/testify v1.10.0 go.etcd.io/bbolt v1.3.11 go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.58.0 @@ -170,7 +169,6 @@ require ( github.com/xeipuuv/gojsonschema v1.2.0 // indirect github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect go.opentelemetry.io/auto/sdk v1.1.0 // indirect - go.etcd.io/bbolt v1.3.11 // indirect go.opentelemetry.io/otel/metric v1.33.0 // indirect go.opentelemetry.io/proto/otlp v1.4.0 // indirect go.uber.org/atomic v1.11.0 // indirect From 0f386494eddbfc903180fd69cb40e5293e17986f Mon Sep 17 00:00:00 2001 From: nyagamunene Date: Thu, 5 Dec 2024 21:24:25 +0300 Subject: [PATCH 65/72] remove unused code Signed-off-by: nyagamunene --- auth/api/grpc/auth/responses.go | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/auth/api/grpc/auth/responses.go b/auth/api/grpc/auth/responses.go index 5cdeb10646..dc9ad1cde5 100644 --- a/auth/api/grpc/auth/responses.go +++ b/auth/api/grpc/auth/responses.go @@ -4,16 +4,12 @@ package auth type authenticateRes struct { - id string - userID string - domainID string + id string + userID string + domainID string } type authorizeRes struct { id string authorized bool } - -type retrievePATRes struct { - pat string -} From 052a7712e735c413e94e9830310cf796e6229910 Mon Sep 17 00:00:00 2001 From: nyagamunene Date: Thu, 5 Dec 2024 22:10:48 +0300 Subject: [PATCH 66/72] revert changes in docker-compose.yml Signed-off-by: nyagamunene --- docker/docker-compose.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docker/docker-compose.yml b/docker/docker-compose.yml index 71dd0268e7..5f105369b7 100644 --- a/docker/docker-compose.yml +++ b/docker/docker-compose.yml @@ -1215,7 +1215,7 @@ services: - supermq-base-net ui: - image: supermq/ui:${SMQ_RELEASE_TAG} + image: magistrala/ui:${SMQ_RELEASE_TAG} container_name: supermq-ui restart: on-failure environment: From c94a7880c9805fd434fc812f2d9c76c109501d86 Mon Sep 17 00:00:00 2001 From: nyagamunene Date: Thu, 5 Dec 2024 23:11:45 +0300 Subject: [PATCH 67/72] fix auth tests Signed-off-by: nyagamunene --- auth/api/grpc/auth/endpoint.go | 2 +- docker/nginx/nginx-key.conf | 7 ------- 2 files changed, 1 insertion(+), 8 deletions(-) diff --git a/auth/api/grpc/auth/endpoint.go b/auth/api/grpc/auth/endpoint.go index d665f64153..05516b64e6 100644 --- a/auth/api/grpc/auth/endpoint.go +++ b/auth/api/grpc/auth/endpoint.go @@ -23,7 +23,7 @@ func authenticateEndpoint(svc auth.Service) endpoint.Endpoint { return authenticateRes{}, err } - return authenticateRes{id: key.ID, userID: key.User, domainID: key.Domain}, nil + return authenticateRes{id: key.Subject, userID: key.User, domainID: key.Domain}, nil } } diff --git a/docker/nginx/nginx-key.conf b/docker/nginx/nginx-key.conf index db8a5aaad0..9779466dc2 100644 --- a/docker/nginx/nginx-key.conf +++ b/docker/nginx/nginx-key.conf @@ -64,13 +64,6 @@ http { proxy_pass http://domains:${SMQ_DOMAINS_HTTP_PORT}; } - # Proxy pass to auth service - location ~ ^/(domains|keys|pats) { - include snippets/proxy-headers.conf; - add_header Access-Control-Expose-Headers Location; - proxy_pass http://auth:${MG_AUTH_HTTP_PORT}; - } - # Proxy pass to users service location ~ ^/(users|password|authorize|oauth/callback/[^/]+) { include snippets/proxy-headers.conf; From e1a2d8cfe70bf2710b61b8c3095f00a113c474c6 Mon Sep 17 00:00:00 2001 From: nyagamunene Date: Fri, 6 Dec 2024 20:20:48 +0300 Subject: [PATCH 68/72] solve failing complitation Signed-off-by: nyagamunene --- clients/middleware/authorization.go | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/clients/middleware/authorization.go b/clients/middleware/authorization.go index 0ce0f29915..7e0e357860 100644 --- a/clients/middleware/authorization.go +++ b/clients/middleware/authorization.go @@ -81,7 +81,7 @@ func (am *authorizationMiddleware) CreateClients(ctx context.Context, session au PatID: session.ID, PlatformEntityType: auth.PlatformDomainsScope, OptionalDomainID: session.DomainID, - OptionalDomainEntityType: auth.DomainThingsScope, + OptionalDomainEntityType: auth.DomainClientsScope, Operation: auth.CreateOp, EntityIDs: auth.AnyIDs{}.Values(), }); err != nil { @@ -109,7 +109,7 @@ func (am *authorizationMiddleware) View(ctx context.Context, session authn.Sessi PatID: session.ID, PlatformEntityType: auth.PlatformDomainsScope, OptionalDomainID: session.DomainID, - OptionalDomainEntityType: auth.DomainThingsScope, + OptionalDomainEntityType: auth.DomainClientsScope, Operation: auth.ReadOp, EntityIDs: []string{id}, }); err != nil { @@ -136,7 +136,7 @@ func (am *authorizationMiddleware) ListClients(ctx context.Context, session auth PatID: session.ID, PlatformEntityType: auth.PlatformDomainsScope, OptionalDomainID: session.DomainID, - OptionalDomainEntityType: auth.DomainThingsScope, + OptionalDomainEntityType: auth.DomainClientsScope, Operation: auth.ListOp, EntityIDs: auth.AnyIDs{}.Values(), }); err != nil { @@ -158,7 +158,7 @@ func (am *authorizationMiddleware) Update(ctx context.Context, session authn.Ses PatID: session.ID, PlatformEntityType: auth.PlatformDomainsScope, OptionalDomainID: session.DomainID, - OptionalDomainEntityType: auth.DomainThingsScope, + OptionalDomainEntityType: auth.DomainClientsScope, Operation: auth.UpdateOp, EntityIDs: []string{client.ID}, }); err != nil { @@ -186,7 +186,7 @@ func (am *authorizationMiddleware) UpdateTags(ctx context.Context, session authn PatID: session.ID, PlatformEntityType: auth.PlatformDomainsScope, OptionalDomainID: session.DomainID, - OptionalDomainEntityType: auth.DomainThingsScope, + OptionalDomainEntityType: auth.DomainClientsScope, Operation: auth.UpdateOp, EntityIDs: []string{client.ID}, }); err != nil { @@ -214,7 +214,7 @@ func (am *authorizationMiddleware) UpdateSecret(ctx context.Context, session aut PatID: session.ID, PlatformEntityType: auth.PlatformDomainsScope, OptionalDomainID: session.DomainID, - OptionalDomainEntityType: auth.DomainThingsScope, + OptionalDomainEntityType: auth.DomainClientsScope, Operation: auth.UpdateOp, EntityIDs: []string{id}, }); err != nil { @@ -241,7 +241,7 @@ func (am *authorizationMiddleware) Enable(ctx context.Context, session authn.Ses PatID: session.ID, PlatformEntityType: auth.PlatformDomainsScope, OptionalDomainID: session.DomainID, - OptionalDomainEntityType: auth.DomainThingsScope, + OptionalDomainEntityType: auth.DomainClientsScope, Operation: auth.UpdateOp, EntityIDs: []string{id}, }); err != nil { @@ -269,7 +269,7 @@ func (am *authorizationMiddleware) Disable(ctx context.Context, session authn.Se PatID: session.ID, PlatformEntityType: auth.PlatformDomainsScope, OptionalDomainID: session.DomainID, - OptionalDomainEntityType: auth.DomainThingsScope, + OptionalDomainEntityType: auth.DomainClientsScope, Operation: auth.UpdateOp, EntityIDs: []string{id}, }); err != nil { @@ -296,7 +296,7 @@ func (am *authorizationMiddleware) Delete(ctx context.Context, session authn.Ses PatID: session.ID, PlatformEntityType: auth.PlatformDomainsScope, OptionalDomainID: session.DomainID, - OptionalDomainEntityType: auth.DomainThingsScope, + OptionalDomainEntityType: auth.DomainClientsScope, Operation: auth.DeleteOp, EntityIDs: []string{id}, }); err != nil { From 0b45d76419f72a8593846a9bf937dd13ebdc4dc6 Mon Sep 17 00:00:00 2001 From: nyagamunene Date: Fri, 6 Dec 2024 20:24:21 +0300 Subject: [PATCH 69/72] fix failing linter Signed-off-by: nyagamunene --- users/middleware/authorization.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/users/middleware/authorization.go b/users/middleware/authorization.go index 20e9dcd239..b38e622f03 100644 --- a/users/middleware/authorization.go +++ b/users/middleware/authorization.go @@ -133,7 +133,7 @@ func (am *authorizationMiddleware) ListMembers(ctx context.Context, session auth PatID: session.ID, OptionalDomainID: session.DomainID, PlatformEntityType: smqauth.PlatformUsersScope, - OptionalDomainEntityType: smqauth.DomainThingsScope, + OptionalDomainEntityType: smqauth.DomainClientsScope, Operation: smqauth.ListOp, EntityIDs: smqauth.AnyIDs{}.Values(), }); err != nil { From 18eb5f0670a7fc06c55288bdeba36c08eac853bf Mon Sep 17 00:00:00 2001 From: nyagamunene Date: Tue, 17 Dec 2024 16:06:18 +0300 Subject: [PATCH 70/72] update method name Signed-off-by: nyagamunene --- pkg/authz/authsvc/authz.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/authz/authsvc/authz.go b/pkg/authz/authsvc/authz.go index c4263db83f..f0d1de592b 100644 --- a/pkg/authz/authsvc/authz.go +++ b/pkg/authz/authsvc/authz.go @@ -123,7 +123,7 @@ func (a authorization) checkDomain(ctx context.Context, subjectType, subject, do } func (a authorization) AuthorizePAT(ctx context.Context, pr authz.PatReq) error { - req := grpcAuthV1.AuthZpatReq{ + req := grpcAuthV1.AuthZPatReq{ UserId: pr.UserID, PatId: pr.PatID, PlatformEntityType: uint32(pr.PlatformEntityType), From 8278fa53f8c44c572083de555ea064b18fe4efab Mon Sep 17 00:00:00 2001 From: nyagamunene Date: Wed, 18 Dec 2024 13:57:29 +0300 Subject: [PATCH 71/72] revert changes Signed-off-by: nyagamunene --- auth/api/http/pats/endpoint.go | 7 +------ auth/service.go | 24 +++++++----------------- 2 files changed, 8 insertions(+), 23 deletions(-) diff --git a/auth/api/http/pats/endpoint.go b/auth/api/http/pats/endpoint.go index 321eb76bca..45e6b3c607 100644 --- a/auth/api/http/pats/endpoint.go +++ b/auth/api/http/pats/endpoint.go @@ -33,12 +33,7 @@ func retrievePATEndpoint(svc auth.Service) endpoint.Endpoint { return nil, err } - res, err := svc.Identify(ctx, req.token) - if err != nil { - return nil, err - } - - pat, err := svc.RetrievePAT(ctx, res.User, req.id) + pat, err := svc.RetrievePAT(ctx, req.token, req.id) if err != nil { return nil, err } diff --git a/auth/service.go b/auth/service.go index 72f7cc8cb0..579d8466fd 100644 --- a/auth/service.go +++ b/auth/service.go @@ -167,21 +167,6 @@ func (svc service) RetrieveKey(ctx context.Context, token, id string) (Key, erro } func (svc service) Identify(ctx context.Context, token string) (Key, error) { - if strings.HasPrefix(token, "pat"+"_") { - pat, err := svc.IdentifyPAT(ctx, token) - if err != nil { - return Key{}, err - } - return Key{ - ID: pat.ID, - Type: PersonalAccessToken, - Subject: pat.User, - User: pat.User, - IssuedAt: pat.IssuedAt, - ExpiresAt: pat.ExpiresAt, - }, nil - } - key, err := svc.tokenizer.Parse(token) if errors.Contains(err, ErrExpiry) { err = svc.keys.Remove(ctx, key.Issuer, key.ID) @@ -529,8 +514,13 @@ func (svc service) UpdatePATDescription(ctx context.Context, token, patID, descr return pat, nil } -func (svc service) RetrievePAT(ctx context.Context, userID, patID string) (PAT, error) { - pat, err := svc.pats.Retrieve(ctx, userID, patID) +func (svc service) RetrievePAT(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) } From 40f4a8cff279f19b84e4ff784b91d188dc569f94 Mon Sep 17 00:00:00 2001 From: Arvindh Date: Mon, 30 Dec 2024 20:20:01 +0530 Subject: [PATCH 72/72] fix: rebase Signed-off-by: Arvindh --- clients/middleware/authorization.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clients/middleware/authorization.go b/clients/middleware/authorization.go index 7e0e357860..3729d463c9 100644 --- a/clients/middleware/authorization.go +++ b/clients/middleware/authorization.go @@ -85,7 +85,7 @@ func (am *authorizationMiddleware) CreateClients(ctx context.Context, session au Operation: auth.CreateOp, EntityIDs: auth.AnyIDs{}.Values(), }); err != nil { - return []clients.Client{}, errors.Wrap(svcerr.ErrUnauthorizedPAT, err) + return []clients.Client{}, []roles.RoleProvision{}, errors.Wrap(svcerr.ErrUnauthorizedPAT, err) } }