NOISSUE - Update auth in journal service (#2527)

Signed-off-by: Felix Gateru <felix.gateru@gmail.com>
This commit is contained in:
Felix Gateru
2024-11-25 15:02:27 +03:00
committed by GitHub
parent f42f45e305
commit b20b45023d
22 changed files with 593 additions and 270 deletions
+65 -7
View File
@@ -27,17 +27,56 @@ tags:
url: http://docs.mainflux.io/
paths:
/journal/{entity_type}/{id}:
/journal/user/{userID}:
get:
tags:
- journal-log
summary: List journal log
summary: List user journal log
description: |
Retrieves a list of journal. Due to performance concerns, data
is retrieved in subsets. The API must ensure that the entire
dataset is consumed either by making subsequent requests, or by
increasing the subset size of the initial request.
parameters:
- $ref: "#/components/parameters/user_id"
- $ref: "#/components/parameters/offset"
- $ref: "#/components/parameters/limit"
- $ref: "#/components/parameters/operation"
- $ref: "#/components/parameters/with_attributes"
- $ref: "#/components/parameters/with_metadata"
- $ref: "#/components/parameters/from"
- $ref: "#/components/parameters/to"
- $ref: "#/components/parameters/dir"
security:
- bearerAuth: []
responses:
"200":
$ref: "#/components/responses/JournalsPageRes"
"400":
description: Failed due to malformed query parameters.
"401":
description: Missing or invalid access token provided.
"403":
description: Failed to perform authorization over the entity.
"404":
description: A non-existent entity request.
"422":
description: Database can't process request.
"500":
$ref: "#/components/responses/ServiceError"
/{domainID}/journal/{entityType}/{id}:
get:
tags:
- journal-log
summary: List entity journal log
description: |
Retrieves a list of journal. Due to performance concerns, data
is retrieved in subsets. The API must ensure that the entire
dataset is consumed either by making subsequent requests, or by
increasing the subset size of the initial request.
parameters:
- $ref: "#/components/parameters/domain_id"
- $ref: "#/components/parameters/entity_type"
- $ref: "#/components/parameters/id"
- $ref: "#/components/parameters/offset"
@@ -146,23 +185,42 @@ components:
example: { "error": "malformed entity specification" }
parameters:
domain_id:
name: domainID
description: Unique identifier for a domain.
in: path
schema:
type: string
format: uuid
required: true
example: bb7edb32-2eac-4aad-aebe-ed96fe073879
entity_type:
name: entity_type
description: Type of entity, e.g. user, group, thing, etc.
name: entityType
description: Type of entity, e.g. user, group, thing, etc.entityType
in: path
schema:
type: string
enum:
- user
- group
- thing
- channel
required: true
example: user
example: group
user_id:
name: userID
description: Unique identifier for a user.
in: path
schema:
type: string
format: uuid
required: true
example: bb7edb32-2eac-4aad-aebe-ed96fe073879
id:
name: id
description: Unique identifier for an entity, e.g. user, group, domain, etc. Used together with entity_type.
description: Unique identifier for an entity, e.g. group, channel or thing. Used together with entity_type.
in: path
schema:
type: string
+12 -6
View File
@@ -9,24 +9,30 @@ import (
)
var cmdJournal = cobra.Command{
Use: "get <entity_type> <entity_id> <user_auth_token>",
Use: "get <entity_type> <entity_id> <domain_id> <user_auth_token>",
Short: "Get journal",
Long: "Get journal\n" +
"Usage:\n" +
"\tmagistrala-cli journal get <entity_type> <entity_id> <user_auth_token> - lists journal logs\n" +
"\tmagistrala-cli journal get <entity_type> <entity_id> <user_auth_token> --offset <offset> --limit <limit> - lists journal logs with provided offset and limit\n",
"\tmagistrala-cli journal get user <user_id> <user_auth_token> - lists user journal logs\n" +
"\tmagistrala-cli journal get <entity_type> <entity_id> <domain_id> <user_auth_token> - lists entity journal logs\n" +
"\tmagistrala-cli journal get <entity_type> <entity_id> <domain_id> <user_auth_token> --offset <offset> --limit <limit> - lists user journal logs with provided offset and limit\n",
Run: func(cmd *cobra.Command, args []string) {
if len(args) != 3 {
if len(args) < 3 || len(args) > 4 {
logUsageCmd(*cmd, cmd.Use)
return
}
pageMetadata := mgxsdk.PageMetadata{
Offset: Offset,
Limit: Limit,
}
journal, err := sdk.Journal(args[0], args[1], pageMetadata, args[2])
entityType, entityID, token := args[0], args[1], args[2]
domainID := ""
if len(args) == 4 {
entityType, entityID, domainID, token = args[0], args[1], args[2], args[3]
}
journal, err := sdk.Journal(entityType, entityID, domainID, pageMetadata, token)
if err != nil {
logErrorCmd(*cmd, err)
return
+26 -5
View File
@@ -31,8 +31,9 @@ func TestGetJournalCmd(t *testing.T) {
rootCmd := setFlags(invCmd)
var page mgsdk.JournalsPage
entityType := "entity_type"
entityId := journal.ID
entityType := "group"
entityId := testsutil.GenerateUUID(t)
domainId := testsutil.GenerateUUID(t)
cases := []struct {
desc string
@@ -43,10 +44,26 @@ func TestGetJournalCmd(t *testing.T) {
errLogMessage string
}{
{
desc: "get journal with journal id",
desc: "get user journal",
args: []string{
"user",
entityId,
token,
},
logType: entityLog,
page: mgsdk.JournalsPage{
Total: 1,
Offset: 0,
Limit: 10,
Journals: []mgsdk.Journal{journal},
},
},
{
desc: "get group journal",
args: []string{
entityType,
entityId,
domainId,
token,
},
logType: entityLog,
@@ -63,6 +80,7 @@ func TestGetJournalCmd(t *testing.T) {
entityType,
entityId,
token,
domainId,
extraArg,
},
logType: usageLog,
@@ -72,6 +90,7 @@ func TestGetJournalCmd(t *testing.T) {
args: []string{
entityType,
entityId,
domainId,
invalidToken,
},
logType: errLog,
@@ -82,8 +101,10 @@ func TestGetJournalCmd(t *testing.T) {
for _, tc := range cases {
t.Run(tc.desc, func(t *testing.T) {
sdkCall := sdkMock.On("Journal", tc.args[0], tc.args[1], mock.Anything, tc.args[2]).Return(tc.page, tc.sdkErr)
sdkCall := sdkMock.On("Journal", tc.args[0], tc.args[1], "", mock.Anything, tc.args[2]).Return(tc.page, tc.sdkErr)
if tc.args[0] != "user" {
sdkCall = sdkMock.On("Journal", tc.args[0], tc.args[1], tc.args[2], mock.Anything, tc.args[3]).Return(tc.page, tc.sdkErr)
}
out := executeCommand(t, rootCmd, append([]string{getCmd}, tc.args...)...)
switch tc.logType {
+5 -5
View File
@@ -20,7 +20,6 @@ import (
"github.com/absmach/magistrala/journal/middleware"
journalpg "github.com/absmach/magistrala/journal/postgres"
mglog "github.com/absmach/magistrala/logger"
mgauthn "github.com/absmach/magistrala/pkg/authn"
authsvcAuthn "github.com/absmach/magistrala/pkg/authn/authsvc"
mgauthz "github.com/absmach/magistrala/pkg/authz"
authsvcAuthz "github.com/absmach/magistrala/pkg/authz/authsvc"
@@ -134,7 +133,7 @@ func main() {
}()
tracer := tp.Tracer(svcName)
svc := newService(db, dbConfig, authn, authz, logger, tracer)
svc := newService(db, dbConfig, authz, logger, tracer)
subscriber, err := store.NewSubscriber(ctx, cfg.ESURL, logger)
if err != nil {
@@ -158,7 +157,7 @@ func main() {
return
}
hs := http.NewServer(ctx, cancel, svcName, httpServerConfig, api.MakeHandler(svc, logger, svcName, cfg.InstanceID), logger)
hs := http.NewServer(ctx, cancel, svcName, httpServerConfig, api.MakeHandler(svc, authn, logger, svcName, cfg.InstanceID), logger)
if cfg.SendTelemetry {
chc := chclient.New(svcName, magistrala.Version, logger, cancel)
@@ -178,12 +177,13 @@ func main() {
}
}
func newService(db *sqlx.DB, dbConfig pgclient.Config, authn mgauthn.Authentication, authz mgauthz.Authorization, logger *slog.Logger, tracer trace.Tracer) journal.Service {
func newService(db *sqlx.DB, dbConfig pgclient.Config, authz mgauthz.Authorization, logger *slog.Logger, tracer trace.Tracer) journal.Service {
database := postgres.NewDatabase(db, dbConfig, tracer)
repo := journalpg.NewRepository(database)
idp := uuid.New()
svc := journal.NewService(authn, authz, idp, repo)
svc := journal.NewService(idp, repo)
svc = middleware.AuthorizationMiddleware(svc, authz)
svc = middleware.LoggingMiddleware(svc, logger)
counter, latency := prometheus.MakeMetrics("journal", "journal_writer")
svc = middleware.MetricsMiddleware(svc, counter, latency)
+9 -1
View File
@@ -6,9 +6,12 @@ package api
import (
"context"
"github.com/absmach/magistrala/internal/api"
"github.com/absmach/magistrala/journal"
"github.com/absmach/magistrala/pkg/apiutil"
"github.com/absmach/magistrala/pkg/authn"
"github.com/absmach/magistrala/pkg/errors"
svcerr "github.com/absmach/magistrala/pkg/errors/service"
"github.com/go-kit/kit/endpoint"
)
@@ -19,7 +22,12 @@ func retrieveJournalsEndpoint(svc journal.Service) endpoint.Endpoint {
return nil, errors.Wrap(apiutil.ErrValidation, err)
}
page, err := svc.RetrieveAll(ctx, req.token, req.page)
session, ok := ctx.Value(api.SessionKey).(authn.Session)
if !ok {
return nil, svcerr.ErrAuthorization
}
page, err := svc.RetrieveAll(ctx, session, req.page)
if err != nil {
return nil, err
}
+143 -21
View File
@@ -12,11 +12,14 @@ import (
"testing"
"time"
"github.com/absmach/magistrala/internal/testsutil"
"github.com/absmach/magistrala/journal"
"github.com/absmach/magistrala/journal/api"
"github.com/absmach/magistrala/journal/mocks"
mglog "github.com/absmach/magistrala/logger"
"github.com/absmach/magistrala/pkg/apiutil"
mgauthn "github.com/absmach/magistrala/pkg/authn"
authnmocks "github.com/absmach/magistrala/pkg/authn/mocks"
svcerr "github.com/absmach/magistrala/pkg/errors/service"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/mock"
@@ -45,20 +48,22 @@ func (tr testRequest) make() (*http.Response, error) {
return tr.client.Do(req)
}
func newjournalServer() (*httptest.Server, *mocks.Service) {
func newjournalServer() (*httptest.Server, *mocks.Service, *authnmocks.Authentication) {
svc := new(mocks.Service)
logger := mglog.NewMock()
mux := api.MakeHandler(svc, logger, "journal-log", "test")
return httptest.NewServer(mux), svc
authn := new(authnmocks.Authentication)
mux := api.MakeHandler(svc, authn, logger, "journal-log", "test")
return httptest.NewServer(mux), svc, authn
}
func TestListJournalsEndpoint(t *testing.T) {
es, svc := newjournalServer()
func TestListUserJournalsEndpoint(t *testing.T) {
es, svc, authn := newjournalServer()
cases := []struct {
desc string
token string
session mgauthn.Session
url string
contentType string
status int
@@ -225,20 +230,6 @@ func TestListJournalsEndpoint(t *testing.T) {
status: http.StatusBadRequest,
svcErr: nil,
},
{
desc: "with invalid entity type",
token: validToken,
url: "/invalid/123",
status: http.StatusBadRequest,
svcErr: nil,
},
{
desc: "with all query params",
token: validToken,
url: "/user/123?offset=10&limit=10&operation=user.create&from=0&to=10&with_attributes=true&with_metadata=true&dir=asc",
status: http.StatusOK,
svcErr: nil,
},
{
desc: "with empty url",
token: validToken,
@@ -250,7 +241,7 @@ func TestListJournalsEndpoint(t *testing.T) {
desc: "with empty entity type",
token: validToken,
url: "//123",
status: http.StatusBadRequest,
status: http.StatusNotFound,
svcErr: nil,
},
{
@@ -264,7 +255,13 @@ func TestListJournalsEndpoint(t *testing.T) {
for _, c := range cases {
t.Run(c.desc, func(t *testing.T) {
svcCall := svc.On("RetrieveAll", mock.Anything, c.token, mock.Anything).Return(journal.JournalsPage{}, c.svcErr)
if c.token == validToken {
c.session = mgauthn.Session{
UserID: testsutil.GenerateUUID(t),
}
}
authCall := authn.On("Authenticate", mock.Anything, c.token).Return(c.session, nil)
svcCall := svc.On("RetrieveAll", mock.Anything, c.session, mock.Anything).Return(journal.JournalsPage{}, c.svcErr)
req := testRequest{
client: es.Client(),
method: http.MethodGet,
@@ -277,6 +274,131 @@ func TestListJournalsEndpoint(t *testing.T) {
defer resp.Body.Close()
assert.Equal(t, c.status, resp.StatusCode, c.desc)
svcCall.Unset()
authCall.Unset()
})
}
}
func TestListEntityJournalsEndpoint(t *testing.T) {
es, svc, authn := newjournalServer()
domainID := testsutil.GenerateUUID(t)
userID := testsutil.GenerateUUID(t)
cases := []struct {
desc string
token string
session mgauthn.Session
domainID string
url string
contentType string
status int
authnErr error
svcErr error
}{
{
desc: "with group type successful",
token: validToken,
domainID: domainID,
url: "/group/123",
status: http.StatusOK,
svcErr: nil,
},
{
desc: "with channel type successful",
token: validToken,
domainID: domainID,
url: "/channel/123",
status: http.StatusOK,
svcErr: nil,
},
{
desc: "with thing type successful",
token: validToken,
domainID: domainID,
url: "/thing/123",
status: http.StatusOK,
svcErr: nil,
},
{
desc: "with service error",
token: validToken,
domainID: domainID,
url: "/thing/123",
status: http.StatusForbidden,
svcErr: svcerr.ErrAuthorization,
},
{
desc: "with operation",
token: validToken,
domainID: domainID,
url: "/channel/123?operation=channel.create",
status: http.StatusOK,
svcErr: nil,
},
{
desc: "with malformed operation",
token: validToken,
domainID: domainID,
url: "/user/123?operation=user.create&operation=user.update",
status: http.StatusBadRequest,
svcErr: nil,
},
{
desc: "with invalid entity type",
token: validToken,
domainID: domainID,
url: "/invalid/123",
status: http.StatusBadRequest,
svcErr: nil,
},
{
desc: "with all query params",
token: validToken,
domainID: domainID,
url: "/group/123?offset=10&limit=10&operation=group.create&from=0&to=10&with_attributes=true&with_metadata=true&dir=asc",
status: http.StatusOK,
svcErr: nil,
},
{
desc: " with empty token",
url: "/group/123",
domainID: domainID,
status: http.StatusUnauthorized,
svcErr: nil,
},
{
desc: "with empty domain ID",
token: validToken,
url: "/group/",
status: http.StatusNotFound,
svcErr: nil,
},
}
for _, c := range cases {
t.Run(c.desc, func(t *testing.T) {
if c.token == validToken {
c.session = mgauthn.Session{
UserID: userID,
DomainID: domainID,
DomainUserID: domainID + "_" + userID,
}
}
authCall := authn.On("Authenticate", mock.Anything, c.token).Return(c.session, c.authnErr)
svcCall := svc.On("RetrieveAll", mock.Anything, c.session, mock.Anything).Return(journal.JournalsPage{}, c.svcErr)
req := testRequest{
client: es.Client(),
method: http.MethodGet,
url: fmt.Sprintf("%s/%s/journal%s", es.URL, c.domainID, c.url),
token: c.token,
}
resp, err := req.make()
assert.Nil(t, err, c.desc)
defer resp.Body.Close()
assert.Equal(t, c.status, resp.StatusCode, c.desc)
svcCall.Unset()
authCall.Unset()
})
}
}
+73 -40
View File
@@ -15,6 +15,7 @@ import (
"github.com/absmach/magistrala/internal/api"
"github.com/absmach/magistrala/journal"
"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"
@@ -33,19 +34,26 @@ const (
)
// MakeHandler returns a HTTP API handler with health check and metrics.
func MakeHandler(svc journal.Service, logger *slog.Logger, svcName, instanceID string) http.Handler {
func MakeHandler(svc journal.Service, authn mgauthn.Authentication, logger *slog.Logger, svcName, instanceID string) http.Handler {
opts := []kithttp.ServerOption{
kithttp.ServerErrorEncoder(apiutil.LoggingErrorEncoder(logger, api.EncodeError)),
}
mux := chi.NewRouter()
mux.Get("/journal/{entityType}/{entityID}", otelhttp.NewHandler(kithttp.NewServer(
mux.With(api.AuthenticateMiddleware(authn, false)).Get("/journal/user/{userID}", otelhttp.NewHandler(kithttp.NewServer(
retrieveJournalsEndpoint(svc),
decodeRetrieveJournalReq,
decodeRetrieveUserJournalReq,
api.EncodeResponse,
opts...,
), "list_journals").ServeHTTP)
), "list_user_journals").ServeHTTP)
mux.With(api.AuthenticateMiddleware(authn, true)).Get("/{domainID}/journal/{entityType}/{entityID}", otelhttp.NewHandler(kithttp.NewServer(
retrieveJournalsEndpoint(svc),
decodeRetrieveEntityJournalReq,
api.EncodeResponse,
opts...,
), "list__entity_journals").ServeHTTP)
mux.Get("/health", magistrala.Health(svcName, instanceID))
mux.Handle("/metrics", promhttp.Handler())
@@ -53,25 +61,65 @@ func MakeHandler(svc journal.Service, logger *slog.Logger, svcName, instanceID s
return mux
}
func decodeRetrieveJournalReq(_ context.Context, r *http.Request) (interface{}, error) {
offset, err := apiutil.ReadNumQuery[uint64](r, api.OffsetKey, api.DefOffset)
func decodeRetrieveEntityJournalReq(_ context.Context, r *http.Request) (interface{}, error) {
page, err := decodePageQuery(r)
if err != nil {
return nil, errors.Wrap(apiutil.ErrValidation, err)
}
entityType, err := journal.ToEntityType(chi.URLParam(r, "entityType"))
if err != nil {
return nil, errors.Wrap(apiutil.ErrValidation, err)
}
page.EntityID = chi.URLParam(r, "entityID")
page.EntityType = entityType
if entityType == journal.ChannelEntity {
page.Operation = strings.ReplaceAll(page.Operation, "channel", "group")
}
req := retrieveJournalsReq{
token: apiutil.ExtractBearerToken(r),
page: page,
}
return req, nil
}
func decodeRetrieveUserJournalReq(_ context.Context, r *http.Request) (interface{}, error) {
page, err := decodePageQuery(r)
if err != nil {
return nil, errors.Wrap(apiutil.ErrValidation, err)
}
page.EntityID = chi.URLParam(r, "userID")
page.EntityType = journal.UserEntity
req := retrieveJournalsReq{
token: apiutil.ExtractBearerToken(r),
page: page,
}
return req, nil
}
func decodePageQuery(r *http.Request) (journal.Page, error) {
offset, err := apiutil.ReadNumQuery[uint64](r, api.OffsetKey, api.DefOffset)
if err != nil {
return journal.Page{}, errors.Wrap(apiutil.ErrValidation, err)
}
limit, err := apiutil.ReadNumQuery[uint64](r, api.LimitKey, api.DefLimit)
if err != nil {
return nil, errors.Wrap(apiutil.ErrValidation, err)
return journal.Page{}, errors.Wrap(apiutil.ErrValidation, err)
}
operation, err := apiutil.ReadStringQuery(r, operationKey, "")
if err != nil {
return nil, errors.Wrap(apiutil.ErrValidation, err)
return journal.Page{}, errors.Wrap(apiutil.ErrValidation, err)
}
from, err := apiutil.ReadNumQuery[int64](r, fromKey, 0)
if err != nil {
return nil, errors.Wrap(apiutil.ErrValidation, err)
return journal.Page{}, errors.Wrap(apiutil.ErrValidation, err)
}
if from > math.MaxInt32 {
return nil, errors.Wrap(apiutil.ErrValidation, apiutil.ErrInvalidTimeFormat)
return journal.Page{}, errors.Wrap(apiutil.ErrValidation, apiutil.ErrInvalidTimeFormat)
}
var fromTime time.Time
if from != 0 {
@@ -79,10 +127,10 @@ func decodeRetrieveJournalReq(_ context.Context, r *http.Request) (interface{},
}
to, err := apiutil.ReadNumQuery[int64](r, toKey, 0)
if err != nil {
return nil, errors.Wrap(apiutil.ErrValidation, err)
return journal.Page{}, errors.Wrap(apiutil.ErrValidation, err)
}
if to > math.MaxInt32 {
return nil, errors.Wrap(apiutil.ErrValidation, apiutil.ErrInvalidTimeFormat)
return journal.Page{}, errors.Wrap(apiutil.ErrValidation, apiutil.ErrInvalidTimeFormat)
}
var toTime time.Time
if to != 0 {
@@ -90,40 +138,25 @@ func decodeRetrieveJournalReq(_ context.Context, r *http.Request) (interface{},
}
attributes, err := apiutil.ReadBoolQuery(r, attributesKey, false)
if err != nil {
return nil, errors.Wrap(apiutil.ErrValidation, err)
return journal.Page{}, errors.Wrap(apiutil.ErrValidation, err)
}
metadata, err := apiutil.ReadBoolQuery(r, metadataKey, false)
if err != nil {
return nil, errors.Wrap(apiutil.ErrValidation, err)
return journal.Page{}, errors.Wrap(apiutil.ErrValidation, err)
}
dir, err := apiutil.ReadStringQuery(r, api.DirKey, api.DescDir)
if err != nil {
return nil, errors.Wrap(apiutil.ErrValidation, err)
return journal.Page{}, errors.Wrap(apiutil.ErrValidation, err)
}
entityType, err := journal.ToEntityType(chi.URLParam(r, "entityType"))
if err != nil {
return nil, errors.Wrap(apiutil.ErrValidation, err)
}
if entityType == journal.ChannelEntity {
operation = strings.ReplaceAll(operation, "channel", "group")
}
req := retrieveJournalsReq{
token: apiutil.ExtractBearerToken(r),
page: journal.Page{
Offset: offset,
Limit: limit,
Operation: operation,
From: fromTime,
To: toTime,
WithAttributes: attributes,
WithMetadata: metadata,
EntityID: chi.URLParam(r, "entityID"),
EntityType: entityType,
Direction: dir,
},
}
return req, nil
return journal.Page{
Offset: offset,
Limit: limit,
Operation: operation,
From: fromTime,
To: toTime,
WithAttributes: attributes,
WithMetadata: metadata,
Direction: dir,
}, nil
}
+1 -5
View File
@@ -16,8 +16,6 @@ import (
"github.com/absmach/magistrala/journal"
aevents "github.com/absmach/magistrala/journal/events"
"github.com/absmach/magistrala/journal/mocks"
authnmocks "github.com/absmach/magistrala/pkg/authn/mocks"
authzmocks "github.com/absmach/magistrala/pkg/authz/mocks"
repoerr "github.com/absmach/magistrala/pkg/errors/repository"
"github.com/absmach/magistrala/pkg/uuid"
"github.com/stretchr/testify/assert"
@@ -53,9 +51,7 @@ func NewTestEvent(data map[string]interface{}, err error) testEvent {
func TestHandle(t *testing.T) {
repo := new(mocks.Repository)
authn := new(authnmocks.Authentication)
authz := new(authzmocks.Authorization)
svc := journal.NewService(authn, authz, idProvider, repo)
svc := journal.NewService(idProvider, repo)
cases := []struct {
desc string
+2 -1
View File
@@ -9,6 +9,7 @@ import (
"time"
"github.com/absmach/magistrala/pkg/apiutil"
mgauthn "github.com/absmach/magistrala/pkg/authn"
"github.com/absmach/magistrala/pkg/policies"
)
@@ -143,7 +144,7 @@ type Service interface {
Save(ctx context.Context, journal Journal) error
// RetrieveAll retrieves all journals from the database with the given page.
RetrieveAll(ctx context.Context, token string, page Page) (JournalsPage, error)
RetrieveAll(ctx context.Context, session mgauthn.Session, page Page) (JournalsPage, error)
}
// Repository provides access to the journal log database.
+62
View File
@@ -0,0 +1,62 @@
// Copyright (c) Abstract Machines
// SPDX-License-Identifier: Apache-2.0
package middleware
import (
"context"
"github.com/absmach/magistrala/journal"
mgauthn "github.com/absmach/magistrala/pkg/authn"
mgauthz "github.com/absmach/magistrala/pkg/authz"
"github.com/absmach/magistrala/pkg/policies"
)
var _ journal.Service = (*authorizationMiddleware)(nil)
type authorizationMiddleware struct {
svc journal.Service
authz mgauthz.Authorization
}
// AuthorizationMiddleware adds authorization to the journal service.
func AuthorizationMiddleware(svc journal.Service, authz mgauthz.Authorization) journal.Service {
return &authorizationMiddleware{
svc: svc,
authz: authz,
}
}
func (am *authorizationMiddleware) Save(ctx context.Context, journal journal.Journal) error {
return am.svc.Save(ctx, journal)
}
func (am *authorizationMiddleware) RetrieveAll(ctx context.Context, session mgauthn.Session, page journal.Page) (journal.JournalsPage, error) {
permission := policies.ViewPermission
objectType := page.EntityType.AuthString()
object := page.EntityID
subject := session.DomainUserID
// If the entity is a user, we need to check if the user is an admin
if page.EntityType.AuthString() == policies.UserType {
permission = policies.AdminPermission
objectType = policies.PlatformType
object = policies.MagistralaObject
subject = session.UserID
}
req := mgauthz.PolicyReq{
Domain: session.DomainID,
SubjectType: policies.UserType,
SubjectKind: policies.UsersKind,
Subject: subject,
Permission: permission,
ObjectType: objectType,
Object: object,
}
if err := am.authz.Authorize(ctx, req); err != nil {
return journal.JournalsPage{}, err
}
return am.svc.RetrieveAll(ctx, session, page)
}
+3 -2
View File
@@ -9,6 +9,7 @@ import (
"time"
"github.com/absmach/magistrala/journal"
mgauthn "github.com/absmach/magistrala/pkg/authn"
)
var _ journal.Service = (*loggingMiddleware)(nil)
@@ -46,7 +47,7 @@ func (lm *loggingMiddleware) Save(ctx context.Context, j journal.Journal) (err e
return lm.service.Save(ctx, j)
}
func (lm *loggingMiddleware) RetrieveAll(ctx context.Context, token string, page journal.Page) (journalsPage journal.JournalsPage, err error) {
func (lm *loggingMiddleware) RetrieveAll(ctx context.Context, session mgauthn.Session, page journal.Page) (journalsPage journal.JournalsPage, err error) {
defer func(begin time.Time) {
args := []any{
slog.String("duration", time.Since(begin).String()),
@@ -66,5 +67,5 @@ func (lm *loggingMiddleware) RetrieveAll(ctx context.Context, token string, page
lm.logger.Info("Retrieve all journals completed successfully", args...)
}(time.Now())
return lm.service.RetrieveAll(ctx, token, page)
return lm.service.RetrieveAll(ctx, session, page)
}
+3 -2
View File
@@ -8,6 +8,7 @@ import (
"time"
"github.com/absmach/magistrala/journal"
mgauthn "github.com/absmach/magistrala/pkg/authn"
"github.com/go-kit/kit/metrics"
)
@@ -38,11 +39,11 @@ func (mm *metricsMiddleware) Save(ctx context.Context, j journal.Journal) error
return mm.service.Save(ctx, j)
}
func (mm *metricsMiddleware) RetrieveAll(ctx context.Context, token string, page journal.Page) (journal.JournalsPage, error) {
func (mm *metricsMiddleware) RetrieveAll(ctx context.Context, session mgauthn.Session, page journal.Page) (journal.JournalsPage, error) {
defer func(begin time.Time) {
mm.counter.With("method", "retrieve_all").Add(1)
mm.latency.With("method", "retrieve_all").Observe(time.Since(begin).Seconds())
}(time.Now())
return mm.service.RetrieveAll(ctx, token, page)
return mm.service.RetrieveAll(ctx, session, page)
}
+3 -2
View File
@@ -7,6 +7,7 @@ import (
"context"
"github.com/absmach/magistrala/journal"
mgauthn "github.com/absmach/magistrala/pkg/authn"
"go.opentelemetry.io/otel/attribute"
"go.opentelemetry.io/otel/trace"
)
@@ -32,7 +33,7 @@ func (tm *tracing) Save(ctx context.Context, j journal.Journal) error {
return tm.svc.Save(ctx, j)
}
func (tm *tracing) RetrieveAll(ctx context.Context, token string, page journal.Page) (resp journal.JournalsPage, err error) {
func (tm *tracing) RetrieveAll(ctx context.Context, session mgauthn.Session, page journal.Page) (resp journal.JournalsPage, err error) {
ctx, span := tm.tracer.Start(ctx, "retrieve_all", trace.WithAttributes(
attribute.Int64("offset", int64(page.Offset)),
attribute.Int64("limit", int64(page.Limit)),
@@ -42,5 +43,5 @@ func (tm *tracing) RetrieveAll(ctx context.Context, token string, page journal.P
))
defer span.End()
return tm.svc.RetrieveAll(ctx, token, page)
return tm.svc.RetrieveAll(ctx, session, page)
}
+12 -9
View File
@@ -7,7 +7,10 @@ package mocks
import (
context "context"
authn "github.com/absmach/magistrala/pkg/authn"
journal "github.com/absmach/magistrala/journal"
mock "github.com/stretchr/testify/mock"
)
@@ -16,9 +19,9 @@ type Service struct {
mock.Mock
}
// RetrieveAll provides a mock function with given fields: ctx, token, page
func (_m *Service) RetrieveAll(ctx context.Context, token string, page journal.Page) (journal.JournalsPage, error) {
ret := _m.Called(ctx, token, page)
// RetrieveAll provides a mock function with given fields: ctx, session, page
func (_m *Service) RetrieveAll(ctx context.Context, session authn.Session, page journal.Page) (journal.JournalsPage, error) {
ret := _m.Called(ctx, session, page)
if len(ret) == 0 {
panic("no return value specified for RetrieveAll")
@@ -26,17 +29,17 @@ func (_m *Service) RetrieveAll(ctx context.Context, token string, page journal.P
var r0 journal.JournalsPage
var r1 error
if rf, ok := ret.Get(0).(func(context.Context, string, journal.Page) (journal.JournalsPage, error)); ok {
return rf(ctx, token, page)
if rf, ok := ret.Get(0).(func(context.Context, authn.Session, journal.Page) (journal.JournalsPage, error)); ok {
return rf(ctx, session, page)
}
if rf, ok := ret.Get(0).(func(context.Context, string, journal.Page) journal.JournalsPage); ok {
r0 = rf(ctx, token, page)
if rf, ok := ret.Get(0).(func(context.Context, authn.Session, journal.Page) journal.JournalsPage); ok {
r0 = rf(ctx, session, page)
} else {
r0 = ret.Get(0).(journal.JournalsPage)
}
if rf, ok := ret.Get(1).(func(context.Context, string, journal.Page) error); ok {
r1 = rf(ctx, token, page)
if rf, ok := ret.Get(1).(func(context.Context, authn.Session, journal.Page) error); ok {
r1 = rf(ctx, session, page)
} else {
r1 = ret.Error(1)
}
+3
View File
@@ -72,6 +72,7 @@ var (
"user_id": entityID,
"metadata": payload,
}
validTimeStamp = time.Now().UTC().Truncate(time.Millisecond)
)
func TestJournalSave(t *testing.T) {
@@ -689,6 +690,8 @@ func TestJournalRetrieveAll(t *testing.T) {
page.Journals[i].Attributes = map[string]interface{}{}
tc.response.Journals[i].Metadata = map[string]interface{}{}
page.Journals[i].Metadata = map[string]interface{}{}
tc.response.Journals[i].OccurredAt = validTimeStamp
page.Journals[i].OccurredAt = validTimeStamp
}
assert.ElementsMatch(t, tc.response.Journals, page.Journals)
+7 -46
View File
@@ -8,22 +8,18 @@ import (
"github.com/absmach/magistrala"
mgauthn "github.com/absmach/magistrala/pkg/authn"
mgauthz "github.com/absmach/magistrala/pkg/authz"
"github.com/absmach/magistrala/pkg/policies"
"github.com/absmach/magistrala/pkg/errors"
svcerr "github.com/absmach/magistrala/pkg/errors/service"
)
type service struct {
authn mgauthn.Authentication
authz mgauthz.Authorization
idProvider magistrala.IDProvider
repository Repository
}
func NewService(authn mgauthn.Authentication, authz mgauthz.Authorization, idp magistrala.IDProvider, repository Repository) Service {
func NewService(idp magistrala.IDProvider, repository Repository) Service {
return &service{
idProvider: idp,
authn: authn,
authz: authz,
repository: repository,
}
}
@@ -38,46 +34,11 @@ func (svc *service) Save(ctx context.Context, journal Journal) error {
return svc.repository.Save(ctx, journal)
}
func (svc *service) RetrieveAll(ctx context.Context, token string, page Page) (JournalsPage, error) {
if err := svc.authorize(ctx, token, page.EntityID, page.EntityType.AuthString()); err != nil {
return JournalsPage{}, err
}
return svc.repository.RetrieveAll(ctx, page)
}
func (svc *service) authorize(ctx context.Context, token, entityID, entityType string) error {
session, err := svc.authn.Authenticate(ctx, token)
func (svc *service) RetrieveAll(ctx context.Context, session mgauthn.Session, page Page) (JournalsPage, error) {
journalPage, err := svc.repository.RetrieveAll(ctx, page)
if err != nil {
return err
return JournalsPage{}, errors.Wrap(svcerr.ErrViewEntity, err)
}
permission := policies.ViewPermission
objectType := entityType
object := entityID
subject := session.DomainUserID
// If the entity is a user, we need to check if the user is an admin
if entityType == policies.UserType {
permission = policies.AdminPermission
objectType = policies.PlatformType
object = policies.MagistralaObject
subject = session.UserID
}
req := mgauthz.PolicyReq{
Domain: session.DomainID,
SubjectType: policies.UserType,
SubjectKind: policies.UsersKind,
Subject: subject,
Permission: permission,
ObjectType: objectType,
Object: object,
}
if err := svc.authz.Authorize(ctx, req); err != nil {
return err
}
return nil
return journalPage, nil
}
+28 -89
View File
@@ -14,13 +14,8 @@ import (
"github.com/absmach/magistrala/journal"
"github.com/absmach/magistrala/journal/mocks"
mgauthn "github.com/absmach/magistrala/pkg/authn"
authnmocks "github.com/absmach/magistrala/pkg/authn/mocks"
mgauthz "github.com/absmach/magistrala/pkg/authz"
authzmocks "github.com/absmach/magistrala/pkg/authz/mocks"
"github.com/absmach/magistrala/pkg/errors"
repoerr "github.com/absmach/magistrala/pkg/errors/repository"
svcerr "github.com/absmach/magistrala/pkg/errors/service"
"github.com/absmach/magistrala/pkg/policies"
"github.com/absmach/magistrala/pkg/uuid"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/mock"
@@ -43,9 +38,7 @@ var (
func TestSave(t *testing.T) {
repo := new(mocks.Repository)
authn := new(authnmocks.Authentication)
authz := new(authzmocks.Authorization)
svc := journal.NewService(authn, authz, idProvider, repo)
svc := journal.NewService(idProvider, repo)
cases := []struct {
desc string
@@ -78,11 +71,9 @@ func TestSave(t *testing.T) {
func TestReadAll(t *testing.T) {
repo := new(mocks.Repository)
authn := new(authnmocks.Authentication)
authz := new(authzmocks.Authorization)
svc := journal.NewService(authn, authz, idProvider, repo)
svc := journal.NewService(idProvider, repo)
validToken := "token"
validSession := mgauthn.Session{DomainUserID: testsutil.GenerateUUID(t), UserID: testsutil.GenerateUUID(t), DomainID: testsutil.GenerateUUID(t)}
validPage := journal.Page{
Offset: 0,
Limit: 10,
@@ -91,34 +82,31 @@ func TestReadAll(t *testing.T) {
}
cases := []struct {
desc string
token string
page journal.Page
resp journal.JournalsPage
identifyRes mgauthn.Session
identifyErr error
authErr error
repoErr error
err error
desc string
session mgauthn.Session
page journal.Page
resp journal.JournalsPage
authErr error
repoErr error
err error
}{
{
desc: "successful",
token: validToken,
page: validPage,
desc: "successful",
session: validSession,
page: validPage,
resp: journal.JournalsPage{
Total: 1,
Offset: 0,
Limit: 10,
Journals: []journal.Journal{validJournal},
},
identifyRes: mgauthn.Session{DomainUserID: testsutil.GenerateUUID(t), UserID: testsutil.GenerateUUID(t)},
authErr: nil,
repoErr: nil,
err: nil,
authErr: nil,
repoErr: nil,
err: nil,
},
{
desc: "successful for user",
token: validToken,
desc: "successful for user",
session: validSession,
page: journal.Page{
Offset: 0,
Limit: 10,
@@ -131,78 +119,29 @@ func TestReadAll(t *testing.T) {
Limit: 10,
Journals: []journal.Journal{validJournal},
},
identifyRes: mgauthn.Session{DomainUserID: testsutil.GenerateUUID(t), UserID: testsutil.GenerateUUID(t)},
authErr: nil,
repoErr: nil,
err: nil,
authErr: nil,
repoErr: nil,
err: nil,
},
{
desc: "with identify error",
token: validToken,
page: validPage,
resp: journal.JournalsPage{},
identifyRes: mgauthn.Session{},
identifyErr: svcerr.ErrAuthentication,
err: svcerr.ErrAuthentication,
},
{
desc: "with repo error",
token: validToken,
page: validPage,
resp: journal.JournalsPage{},
identifyRes: mgauthn.Session{DomainUserID: testsutil.GenerateUUID(t), UserID: testsutil.GenerateUUID(t)},
repoErr: repoerr.ErrViewEntity,
err: repoerr.ErrViewEntity,
},
{
desc: "with failed to authorize",
token: validToken,
page: validPage,
resp: journal.JournalsPage{},
identifyRes: mgauthn.Session{DomainUserID: testsutil.GenerateUUID(t), UserID: testsutil.GenerateUUID(t)},
authErr: svcerr.ErrAuthorization,
repoErr: nil,
err: svcerr.ErrAuthorization,
},
{
desc: "with error on authorize",
token: validToken,
page: validPage,
resp: journal.JournalsPage{},
identifyRes: mgauthn.Session{DomainUserID: testsutil.GenerateUUID(t), UserID: testsutil.GenerateUUID(t)},
authErr: svcerr.ErrAuthorization,
repoErr: nil,
err: svcerr.ErrAuthorization,
desc: "with repo error",
session: validSession,
page: validPage,
resp: journal.JournalsPage{},
repoErr: repoerr.ErrViewEntity,
err: repoerr.ErrViewEntity,
},
}
for _, tc := range cases {
t.Run(tc.desc, func(t *testing.T) {
authReq := mgauthz.PolicyReq{
SubjectType: policies.UserType,
SubjectKind: policies.UsersKind,
Subject: tc.identifyRes.DomainUserID,
ObjectType: tc.page.EntityType.AuthString(),
Object: tc.page.EntityID,
Permission: policies.ViewPermission,
}
if tc.page.EntityType == journal.UserEntity {
authReq.Permission = policies.AdminPermission
authReq.ObjectType = policies.PlatformType
authReq.Object = policies.MagistralaObject
authReq.Subject = tc.identifyRes.UserID
}
authCall := authn.On("Authenticate", context.Background(), tc.token).Return(tc.identifyRes, tc.identifyErr)
authCall1 := authz.On("Authorize", context.Background(), authReq).Return(tc.authErr)
repoCall := repo.On("RetrieveAll", context.Background(), tc.page).Return(tc.resp, tc.repoErr)
resp, err := svc.RetrieveAll(context.Background(), tc.token, tc.page)
resp, err := svc.RetrieveAll(context.Background(), tc.session, tc.page)
if tc.err == nil {
assert.Equal(t, tc.resp, resp, tc.desc)
}
assert.True(t, errors.Contains(err, tc.err), fmt.Sprintf("%s: expected %s got %s\n", tc.desc, tc.err, err))
repoCall.Unset()
authCall.Unset()
authCall1.Unset()
})
}
}
+7 -2
View File
@@ -30,7 +30,7 @@ type JournalsPage struct {
Journals []Journal `json:"journals"`
}
func (sdk mgSDK) Journal(entityType, entityID string, pm PageMetadata, token string) (journals JournalsPage, err error) {
func (sdk mgSDK) Journal(entityType, entityID, domainID string, pm PageMetadata, token string) (journals JournalsPage, err error) {
if entityID == "" {
return JournalsPage{}, errors.NewSDKError(apiutil.ErrMissingID)
}
@@ -38,7 +38,12 @@ func (sdk mgSDK) Journal(entityType, entityID string, pm PageMetadata, token str
return JournalsPage{}, errors.NewSDKError(apiutil.ErrMissingEntityType)
}
url, err := sdk.withQueryParams(sdk.journalURL, fmt.Sprintf("%s/%s/%s", journalEndpoint, entityType, entityID), pm)
reqUrl := fmt.Sprintf("%s/%s/%s/%s", domainID, journalEndpoint, entityType, entityID)
if entityType == "user" {
reqUrl = fmt.Sprintf("%s/%s/%s", journalEndpoint, entityType, entityID)
}
url, err := sdk.withQueryParams(sdk.journalURL, reqUrl, pm)
if err != nil {
return JournalsPage{}, errors.NewSDKError(err)
}
+117 -15
View File
@@ -15,6 +15,8 @@ import (
"github.com/absmach/magistrala/journal/mocks"
mglog "github.com/absmach/magistrala/logger"
"github.com/absmach/magistrala/pkg/apiutil"
mgauthn "github.com/absmach/magistrala/pkg/authn"
authnmocks "github.com/absmach/magistrala/pkg/authn/mocks"
"github.com/absmach/magistrala/pkg/errors"
svcerr "github.com/absmach/magistrala/pkg/errors/service"
sdk "github.com/absmach/magistrala/pkg/sdk/go"
@@ -22,20 +24,21 @@ import (
"github.com/stretchr/testify/mock"
)
func setupJournal() (*httptest.Server, *mocks.Service) {
func setupJournal() (*httptest.Server, *mocks.Service, *authnmocks.Authentication) {
svc := new(mocks.Service)
authn := new(authnmocks.Authentication)
logger := mglog.NewMock()
mux := api.MakeHandler(svc, logger, "journal-log", "test")
return httptest.NewServer(mux), svc
mux := api.MakeHandler(svc, authn, logger, "journal-log", "test")
return httptest.NewServer(mux), svc, authn
}
func TestRetrieveJournal(t *testing.T) {
js, svc := setupJournal()
js, svc, authn := setupJournal()
defer js.Close()
testJournal := generateTestJournal(t)
validEntityType := "user"
validEntityType := "group"
sdkConf := sdk.Config{
JournalURL: js.URL,
@@ -46,20 +49,24 @@ func TestRetrieveJournal(t *testing.T) {
cases := []struct {
desc string
token string
session mgauthn.Session
entityType string
entityID string
domainID string
pageMeta sdk.PageMetadata
svcReq journal.Page
svcRes journal.JournalsPage
svcErr error
authnErr error
response sdk.JournalsPage
err error
}{
{
desc: "retrieve journal successfully",
desc: "retrieve user journal successfully",
token: validToken,
entityType: validEntityType,
entityType: "user",
entityID: validID,
domainID: domainID,
pageMeta: sdk.PageMetadata{
Offset: 0,
Limit: 10,
@@ -82,6 +89,90 @@ func TestRetrieveJournal(t *testing.T) {
},
err: nil,
},
{
desc: "retrieve channel journal successfully",
token: validToken,
entityType: "channel",
entityID: validID,
domainID: domainID,
pageMeta: sdk.PageMetadata{
Offset: 0,
Limit: 10,
},
svcReq: journal.Page{
Offset: 0,
Limit: 10,
EntityID: validID,
EntityType: journal.ChannelEntity,
Direction: "desc",
},
svcRes: journal.JournalsPage{
Total: 1,
Journals: []journal.Journal{convertJournal(testJournal)},
},
svcErr: nil,
response: sdk.JournalsPage{
Total: 1,
Journals: []sdk.Journal{testJournal},
},
err: nil,
},
{
desc: "retrieve group journal successfully",
token: validToken,
entityType: "group",
entityID: validID,
domainID: domainID,
pageMeta: sdk.PageMetadata{
Offset: 0,
Limit: 10,
},
svcReq: journal.Page{
Offset: 0,
Limit: 10,
EntityID: validID,
EntityType: journal.GroupEntity,
Direction: "desc",
},
svcRes: journal.JournalsPage{
Total: 1,
Journals: []journal.Journal{convertJournal(testJournal)},
},
svcErr: nil,
response: sdk.JournalsPage{
Total: 1,
Journals: []sdk.Journal{testJournal},
},
err: nil,
},
{
desc: "retrieve thing journal successfully",
token: validToken,
entityType: "thing",
entityID: validID,
domainID: domainID,
pageMeta: sdk.PageMetadata{
Offset: 0,
Limit: 10,
},
svcReq: journal.Page{
Offset: 0,
Limit: 10,
EntityID: validID,
EntityType: journal.ThingEntity,
Direction: "desc",
},
svcRes: journal.JournalsPage{
Total: 1,
Journals: []journal.Journal{convertJournal(testJournal)},
},
svcErr: nil,
response: sdk.JournalsPage{
Total: 1,
Journals: []sdk.Journal{testJournal},
},
err: nil,
},
{
desc: "retrieve journal with invalid token",
token: invalidToken,
@@ -95,11 +186,11 @@ func TestRetrieveJournal(t *testing.T) {
Offset: 0,
Limit: 10,
EntityID: validID,
EntityType: journal.UserEntity,
EntityType: journal.GroupEntity,
Direction: "desc",
},
svcRes: journal.JournalsPage{},
svcErr: svcerr.ErrAuthentication,
authnErr: svcerr.ErrAuthentication,
response: sdk.JournalsPage{},
err: errors.NewSDKErrorWithStatus(svcerr.ErrAuthentication, http.StatusUnauthorized),
},
@@ -116,13 +207,14 @@ func TestRetrieveJournal(t *testing.T) {
svcRes: journal.JournalsPage{},
svcErr: nil,
response: sdk.JournalsPage{},
err: errors.NewSDKErrorWithStatus(errors.Wrap(apiutil.ErrValidation, apiutil.ErrBearerToken), http.StatusUnauthorized),
err: errors.NewSDKErrorWithStatus(apiutil.ErrBearerToken, http.StatusUnauthorized),
},
{
desc: "retrieve journal with invalid entity type",
token: validToken,
entityType: "invalid",
entityID: validID,
domainID: domainID,
pageMeta: sdk.PageMetadata{
Offset: 0,
Limit: 10,
@@ -138,6 +230,7 @@ func TestRetrieveJournal(t *testing.T) {
token: validToken,
entityType: validEntityType,
entityID: "",
domainID: domainID,
pageMeta: sdk.PageMetadata{
Offset: 0,
Limit: 10,
@@ -153,6 +246,7 @@ func TestRetrieveJournal(t *testing.T) {
token: validToken,
entityType: "",
entityID: validID,
domainID: domainID,
pageMeta: sdk.PageMetadata{
Offset: 0,
Limit: 10,
@@ -168,6 +262,7 @@ func TestRetrieveJournal(t *testing.T) {
token: validToken,
entityType: validEntityType,
entityID: validID,
domainID: domainID,
pageMeta: sdk.PageMetadata{
Offset: 0,
Limit: 1000,
@@ -183,6 +278,7 @@ func TestRetrieveJournal(t *testing.T) {
token: validToken,
entityType: validEntityType,
entityID: validID,
domainID: domainID,
pageMeta: sdk.PageMetadata{
Offset: 0,
Limit: 10,
@@ -201,6 +297,7 @@ func TestRetrieveJournal(t *testing.T) {
token: validToken,
entityType: validEntityType,
entityID: validID,
domainID: domainID,
pageMeta: sdk.PageMetadata{
Offset: 0,
Limit: 10,
@@ -209,7 +306,7 @@ func TestRetrieveJournal(t *testing.T) {
Offset: 0,
Limit: 10,
EntityID: validID,
EntityType: journal.UserEntity,
EntityType: journal.GroupEntity,
Direction: "desc",
},
svcRes: journal.JournalsPage{
@@ -231,15 +328,20 @@ func TestRetrieveJournal(t *testing.T) {
}
for _, tc := range cases {
t.Run(tc.desc, func(t *testing.T) {
svcCall := svc.On("RetrieveAll", mock.Anything, tc.token, tc.svcReq).Return(tc.svcRes, tc.svcErr)
resp, err := mgsdk.Journal(tc.entityType, tc.entityID, tc.pageMeta, tc.token)
if tc.token == validToken {
tc.session = mgauthn.Session{DomainUserID: domainID + "_" + validID, UserID: validID, DomainID: domainID}
}
authCall := authn.On("Authenticate", mock.Anything, mock.Anything).Return(tc.session, tc.authnErr)
svcCall := svc.On("RetrieveAll", mock.Anything, tc.session, tc.svcReq).Return(tc.svcRes, tc.svcErr)
resp, err := mgsdk.Journal(tc.entityType, tc.entityID, tc.domainID, tc.pageMeta, tc.token)
assert.Equal(t, tc.err, err)
assert.Equal(t, tc.response, resp)
if tc.err == nil {
ok := svcCall.Parent.AssertCalled(t, "RetrieveAll", mock.Anything, tc.token, tc.svcReq)
ok := svcCall.Parent.AssertCalled(t, "RetrieveAll", mock.Anything, tc.session, tc.svcReq)
assert.True(t, ok)
}
svcCall.Unset()
authCall.Unset()
})
}
}
+2 -2
View File
@@ -1210,9 +1210,9 @@ type SDK interface {
// Journal returns a list of journal logs.
//
// For example:
// journals, _ := sdk.Journal("thing", "thingID", PageMetadata{Offset: 0, Limit: 10, Operation: "users.create"}, "token")
// journals, _ := sdk.Journal("thing", "thingID","domainID", PageMetadata{Offset: 0, Limit: 10, Operation: "thing.create"}, "token")
// fmt.Println(journals)
Journal(entityType, entityID string, pm PageMetadata, token string) (journal JournalsPage, err error)
Journal(entityType, entityID, domainID string, pm PageMetadata, token string) (journal JournalsPage, err error)
}
type mgSDK struct {
+9 -9
View File
@@ -1402,9 +1402,9 @@ func (_m *SDK) IssueCert(thingID string, validity string, domainID string, token
return r0, r1
}
// Journal provides a mock function with given fields: entityType, entityID, pm, token
func (_m *SDK) Journal(entityType string, entityID string, pm sdk.PageMetadata, token string) (sdk.JournalsPage, error) {
ret := _m.Called(entityType, entityID, pm, token)
// Journal provides a mock function with given fields: entityType, entityID, domainID, pm, token
func (_m *SDK) Journal(entityType string, entityID string, domainID string, pm sdk.PageMetadata, token string) (sdk.JournalsPage, error) {
ret := _m.Called(entityType, entityID, domainID, pm, token)
if len(ret) == 0 {
panic("no return value specified for Journal")
@@ -1412,17 +1412,17 @@ func (_m *SDK) Journal(entityType string, entityID string, pm sdk.PageMetadata,
var r0 sdk.JournalsPage
var r1 error
if rf, ok := ret.Get(0).(func(string, string, sdk.PageMetadata, string) (sdk.JournalsPage, error)); ok {
return rf(entityType, entityID, pm, token)
if rf, ok := ret.Get(0).(func(string, string, string, sdk.PageMetadata, string) (sdk.JournalsPage, error)); ok {
return rf(entityType, entityID, domainID, pm, token)
}
if rf, ok := ret.Get(0).(func(string, string, sdk.PageMetadata, string) sdk.JournalsPage); ok {
r0 = rf(entityType, entityID, pm, token)
if rf, ok := ret.Get(0).(func(string, string, string, sdk.PageMetadata, string) sdk.JournalsPage); ok {
r0 = rf(entityType, entityID, domainID, pm, token)
} else {
r0 = ret.Get(0).(sdk.JournalsPage)
}
if rf, ok := ret.Get(1).(func(string, string, sdk.PageMetadata, string) error); ok {
r1 = rf(entityType, entityID, pm, token)
if rf, ok := ret.Get(1).(func(string, string, string, sdk.PageMetadata, string) error); ok {
r1 = rf(entityType, entityID, domainID, pm, token)
} else {
r1 = ret.Error(1)
}
+1 -1
View File
@@ -11,7 +11,7 @@ import (
)
const (
clientPrefix = "client."
clientPrefix = "thing."
clientCreate = clientPrefix + "create"
clientUpdate = clientPrefix + "update"
clientChangeStatus = clientPrefix + "change_status"