Skip to content

Commit

Permalink
Fix roles/tenants/clusters values are missing from the audit log user…
Browse files Browse the repository at this point in the history
…s overview (#187)

* fixed missing roles/tenants/clusters values from users overview

Signed-off-by: HaniAlshikh <Alshikh.hani@gmail.com>

* added details constants and made audit log tests more robust

Signed-off-by: HaniAlshikh <Alshikh.hani@gmail.com>
  • Loading branch information
HaniAlshikh committed Jun 29, 2022
1 parent 36cacc5 commit c24c0d5
Show file tree
Hide file tree
Showing 12 changed files with 300 additions and 75 deletions.
91 changes: 70 additions & 21 deletions internal/audit_log_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,38 +16,49 @@ package internal

import (
"context"
"io"
"regexp"
"strings"
"time"

"github.com/finleap-connect/monoskope/internal/gateway/auth"
domainApi "github.com/finleap-connect/monoskope/pkg/api/domain"
cmdData "github.com/finleap-connect/monoskope/pkg/api/domain/commanddata"
esApi "github.com/finleap-connect/monoskope/pkg/api/eventsourcing"
cmd "github.com/finleap-connect/monoskope/pkg/domain/commands"
"github.com/finleap-connect/monoskope/pkg/domain/constants/aggregates"
commandTypes "github.com/finleap-connect/monoskope/pkg/domain/constants/commands"
fConsts "github.com/finleap-connect/monoskope/pkg/domain/constants/formatters"
"github.com/finleap-connect/monoskope/pkg/domain/constants/roles"
"github.com/finleap-connect/monoskope/pkg/domain/constants/scopes"
"github.com/finleap-connect/monoskope/pkg/domain/constants/users"
"github.com/finleap-connect/monoskope/pkg/domain/projections"
"github.com/google/uuid"
"io"
"time"

grpcUtil "github.com/finleap-connect/monoskope/pkg/grpc"
"github.com/finleap-connect/monoskope/pkg/jwt"
"google.golang.org/protobuf/types/known/timestamppb"
"google.golang.org/protobuf/types/known/wrapperspb"

"github.com/finleap-connect/monoskope/internal/gateway/auth"
domainApi "github.com/finleap-connect/monoskope/pkg/api/domain"
esApi "github.com/finleap-connect/monoskope/pkg/api/eventsourcing"
"github.com/google/uuid"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
"google.golang.org/protobuf/types/known/timestamppb"
"google.golang.org/protobuf/types/known/wrapperspb"
)

var _ = Describe("AuditLog Test", func() {
ctx := context.Background()
userId := uuid.New()
userEmail := "jane.dou@monoskope.io"
expectedValidity := time.Hour * 1
expectedNumUsers := 2 // SUPER_USERS
expectedNumEventsDoneOnUser := 0 // to be counted see initEvents
expectedNumEventsDoneByAdmin := 0 // to be counted see initEvents
expectedNumEventsDoneByAdminMidTime := 0 // to be counted see initEvents

var (
userId = uuid.New()
userEmail = "jane.dou@monoskope.io"
expectedValidity = time.Hour * 1
// see initEvents
expectedNumUsers = 0
expectedNumEventsDoneOnUser = 0
expectedNumEventsDoneByAdmin = 0
expectedNumEventsDoneByAdminMidTime = 0
expectedDetailMsgs []string
expectedUserOverviewDetailMsgs []string
expectedUserOverviewRoleMsgs []string
expectedUserOverviewTenantMsgs []string
)

getAdminAuthToken := func() string {
signer := testEnv.gatewayTestEnv.JwtTestEnv.CreateSigner()
Expand Down Expand Up @@ -109,6 +120,14 @@ var _ = Describe("AuditLog Test", func() {
initEvents := func(commandHandlerClient func() esApi.CommandHandlerClient) time.Time {
adminWorkaround() // remove when issue #182 is resolved

// SUPER_USERS
for _, su := range testEnv.superUsers {
expectedUserOverviewDetailMsgs = append(expectedUserOverviewDetailMsgs, strings.ReplaceAll(fConsts.UserCreatedOverviewDetailsFormat.Sprint(su, "system@"+users.BASE_DOMAIN, "x"), fConsts.Quote("x"), ""))
expectedUserOverviewRoleMsgs = append(expectedUserOverviewRoleMsgs, fConsts.UserRoleBindingOverviewDetailsFormat.Sprint(scopes.System, roles.Admin))
expectedUserOverviewTenantMsgs = append(expectedUserOverviewTenantMsgs, "")
expectedNumUsers++
}

// CreateUser
command, err := cmd.AddCommandData(
cmd.CreateCommand(uuid.Nil, commandTypes.CreateUser),
Expand All @@ -124,6 +143,7 @@ var _ = Describe("AuditLog Test", func() {
expectedNumEventsDoneByAdmin++
expectedNumEventsDoneOnUser++
expectedNumUsers++
expectedDetailMsgs = append(expectedDetailMsgs, fConsts.UserCreatedDetailsFormat.Sprint(testEnv.gatewayTestEnv.AdminUser.Email, userEmail))

// CreateUserRoleBinding on system level
command, err = cmd.AddCommandData(
Expand All @@ -138,6 +158,7 @@ var _ = Describe("AuditLog Test", func() {
userRoleBindingId := uuid.MustParse(reply.AggregateId)
expectedNumEventsDoneByAdmin++
expectedNumEventsDoneOnUser++
expectedDetailMsgs = append(expectedDetailMsgs, fConsts.UserRoleAddedDetailsFormat.Sprint(testEnv.gatewayTestEnv.AdminUser.Email, roles.Admin, scopes.System, userEmail))

// UpdateUser
command, err = cmd.AddCommandData(
Expand All @@ -151,6 +172,7 @@ var _ = Describe("AuditLog Test", func() {
}).Should(Succeed())
expectedNumEventsDoneByAdmin++
expectedNumEventsDoneOnUser++
expectedDetailMsgs = append(expectedDetailMsgs, fConsts.UserUpdatedDetailsFormat.Sprint(testEnv.gatewayTestEnv.AdminUser.Email))

// CreateTenant
command, err = cmd.AddCommandData(
Expand All @@ -164,6 +186,7 @@ var _ = Describe("AuditLog Test", func() {
}).Should(Succeed())
tenantId := uuid.MustParse(reply.AggregateId)
expectedNumEventsDoneByAdmin++
expectedDetailMsgs = append(expectedDetailMsgs, fConsts.TenantCreatedDetailsFormat.Sprint(testEnv.gatewayTestEnv.AdminUser.Email, "Tenant Y", "ty"))

// CreateUserRoleBinding on tenant level
command, err = cmd.AddCommandData(
Expand All @@ -178,6 +201,9 @@ var _ = Describe("AuditLog Test", func() {
_ = uuid.MustParse(reply.AggregateId)
expectedNumEventsDoneByAdmin++
expectedNumEventsDoneOnUser++
expectedDetailMsgs = append(expectedDetailMsgs, fConsts.UserRoleAddedDetailsFormat.Sprint(testEnv.gatewayTestEnv.AdminUser.Email, roles.User, scopes.Tenant, userEmail))
expectedUserOverviewRoleMsgs = append(expectedUserOverviewRoleMsgs, fConsts.UserRoleBindingOverviewDetailsFormat.Sprint(scopes.Tenant, roles.User))
expectedUserOverviewTenantMsgs = append(expectedUserOverviewTenantMsgs, fConsts.TenantUserRoleBindingOverviewDetailsFormat.Sprint("Tenant Z", roles.User))

// UpdateTenant
command, err = cmd.AddCommandData(
Expand All @@ -190,6 +216,7 @@ var _ = Describe("AuditLog Test", func() {
g.Expect(err).ToNot(HaveOccurred())
}).Should(Succeed())
expectedNumEventsDoneByAdmin++
expectedDetailMsgs = append(expectedDetailMsgs, fConsts.TenantUpdatedDetailsFormat.Sprint(testEnv.gatewayTestEnv.AdminUser.Email))

midTime := time.Now().UTC()
expectedNumEventsDoneByAdminMidTime = expectedNumEventsDoneByAdmin
Expand All @@ -206,6 +233,7 @@ var _ = Describe("AuditLog Test", func() {
}).Should(Succeed())
clusterId := uuid.MustParse(reply.AggregateId)
expectedNumEventsDoneByAdmin++
expectedDetailMsgs = append(expectedDetailMsgs, fConsts.ClusterCreatedDetailsFormat.Sprint(testEnv.gatewayTestEnv.AdminUser.Email, "cluster-y"))

// UpdateCluster
command, err = cmd.AddCommandData(
Expand All @@ -218,6 +246,7 @@ var _ = Describe("AuditLog Test", func() {
g.Expect(err).ToNot(HaveOccurred())
}).Should(Succeed())
expectedNumEventsDoneByAdmin++
expectedDetailMsgs = append(expectedDetailMsgs, fConsts.ClusterUpdatedDetailsFormat.Sprint(testEnv.gatewayTestEnv.AdminUser.Email))

// CreateTenantClusterBinding
command, err = cmd.AddCommandData(
Expand All @@ -231,6 +260,7 @@ var _ = Describe("AuditLog Test", func() {
}).Should(Succeed())
tenantClusterBindingId := uuid.MustParse(reply.AggregateId)
expectedNumEventsDoneByAdmin++
expectedDetailMsgs = append(expectedDetailMsgs, fConsts.TenantClusterBindingCreatedDetailsFormat.Sprint(testEnv.gatewayTestEnv.AdminUser.Email, "Tenant Z", "Cluster Z"))

// RequestCertificate
command, err = cmd.AddCommandData(
Expand All @@ -247,37 +277,44 @@ var _ = Describe("AuditLog Test", func() {
g.Expect(err).ToNot(HaveOccurred())
}).Should(Succeed())
expectedNumEventsDoneByAdmin++
expectedDetailMsgs = append(expectedDetailMsgs, fConsts.CertificateRequestedDetailsFormat.Sprint(testEnv.gatewayTestEnv.AdminUser.Email))

// DeleteUser
_, err = commandHandlerClient().Execute(ctx,
cmd.CreateCommand(userId, commandTypes.DeleteUser))
Expect(err).ToNot(HaveOccurred())
expectedNumEventsDoneByAdmin++
expectedNumEventsDoneOnUser++
expectedDetailMsgs = append(expectedDetailMsgs, fConsts.UserDeletedDetailsFormat.Sprint(testEnv.gatewayTestEnv.AdminUser.Email, userEmail))
expectedUserOverviewDetailMsgs = append(expectedUserOverviewDetailMsgs, strings.ReplaceAll(fConsts.UserDeletedOverviewDetailsFormat.Sprint(testEnv.gatewayTestEnv.AdminUser.Email, "x"), fConsts.Quote("x"), ""))

// DeleteUserRoleBinding
_, err = commandHandlerClient().Execute(ctx,
cmd.CreateCommand(userRoleBindingId, commandTypes.DeleteUserRoleBinding))
Expect(err).ToNot(HaveOccurred())
expectedNumEventsDoneByAdmin++
expectedDetailMsgs = append(expectedDetailMsgs, fConsts.UserRoleBindingDeletedDetailsFormat.Sprint(testEnv.gatewayTestEnv.AdminUser.Email, roles.Admin, scopes.System, userEmail))

// DeleteTenant
_, err = commandHandlerClient().Execute(ctx,
cmd.CreateCommand(tenantId, commandTypes.DeleteTenant))
Expect(err).ToNot(HaveOccurred())
expectedNumEventsDoneByAdmin++
expectedDetailMsgs = append(expectedDetailMsgs, fConsts.TenantDeletedDetailsFormat.Sprint(testEnv.gatewayTestEnv.AdminUser.Email, "Tenant Z"))

// DeleteTenantClusterBinding
reply, err = commandHandlerClient().Execute(ctx,
cmd.CreateCommand(tenantClusterBindingId, commandTypes.DeleteTenantClusterBinding))
Expect(err).ToNot(HaveOccurred())
expectedNumEventsDoneByAdmin++
expectedDetailMsgs = append(expectedDetailMsgs, fConsts.TenantClusterBindingDeletedDetailsFormat.Sprint(testEnv.gatewayTestEnv.AdminUser.Email, "Cluster Z", "Tenant Z"))

// DeleteCluster
reply, err = commandHandlerClient().Execute(ctx,
cmd.CreateCommand(clusterId, commandTypes.DeleteCluster))
Expect(err).ToNot(HaveOccurred())
expectedNumEventsDoneByAdmin++
expectedDetailMsgs = append(expectedDetailMsgs, fConsts.ClusterDeletedDetailsFormat.Sprint(testEnv.gatewayTestEnv.AdminUser.Email, "Cluster Z"))

return midTime
}
Expand Down Expand Up @@ -309,7 +346,7 @@ var _ = Describe("AuditLog Test", func() {
Expect(e.Issuer).ToNot(BeEmpty())
Expect(e.IssuerId).ToNot(BeEmpty())
Expect(e.EventType).ToNot(BeEmpty())
Expect(e.Details).ToNot(BeEmpty())
Expect(regexp.MatchString(`.*`+regexp.QuoteMeta(strings.TrimSpace(expectedDetailMsgs[counter]))+`.*`, e.Details)).To(BeTrue())
counter++
}
Expect(counter).To(Equal(expectedNumEventsDoneByAdmin))
Expand Down Expand Up @@ -388,6 +425,14 @@ var _ = Describe("AuditLog Test", func() {
})
Expect(err).ToNot(HaveOccurred())

// "shared" testEnv workaround
// ginkgo v2 should solve this by utilizing baforeAll/afterAll?
knownUsersSet := make(map[string]struct{}, len(testEnv.superUsers)+1)
for _, s := range testEnv.superUsers {
knownUsersSet[s] = struct{}{}
}
knownUsersSet[userEmail] = struct{}{}

counter := 0
for {
o, err := overviews.Recv()
Expand All @@ -398,10 +443,14 @@ var _ = Describe("AuditLog Test", func() {

Expect(o.Name).ToNot(BeEmpty())
Expect(o.Email).ToNot(BeEmpty())
Expect(o.Details).ToNot(BeEmpty())
counter++
if _, known := knownUsersSet[o.Email]; known {
Expect(regexp.MatchString(`.*`+regexp.QuoteMeta(strings.TrimSpace(expectedUserOverviewRoleMsgs[counter]))+`.*`, o.Roles)).To(BeTrue())
Expect(regexp.MatchString(`.*`+regexp.QuoteMeta(strings.TrimSpace(expectedUserOverviewTenantMsgs[counter]))+`.*`, o.Tenants)).To(BeTrue())
Expect(regexp.MatchString(`.*`+regexp.QuoteMeta(strings.TrimSpace(expectedUserOverviewDetailMsgs[counter]))+`.*`, o.Details)).To(BeTrue())
counter++
}
}
Expect(counter).To(BeNumerically(">=", expectedNumUsers))
Expect(counter).To(Equal(expectedNumUsers))
})
})

Expand Down
12 changes: 6 additions & 6 deletions internal/queryhandler/audit_log_server.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,19 +21,19 @@ import (
"time"

"github.com/finleap-connect/monoskope/internal/gateway/auth"
doApi "github.com/finleap-connect/monoskope/pkg/api/domain"
esApi "github.com/finleap-connect/monoskope/pkg/api/eventsourcing"
"github.com/finleap-connect/monoskope/pkg/audit/formatters/audit"
"github.com/finleap-connect/monoskope/pkg/audit/formatters/event"
"github.com/finleap-connect/monoskope/pkg/domain/constants/aggregates"
"github.com/finleap-connect/monoskope/pkg/domain/constants/events"
"github.com/finleap-connect/monoskope/pkg/domain/repositories"
"github.com/google/uuid"
"google.golang.org/protobuf/types/known/wrapperspb"

doApi "github.com/finleap-connect/monoskope/pkg/api/domain"
fConsts "github.com/finleap-connect/monoskope/pkg/domain/constants/formatters"
"github.com/finleap-connect/monoskope/pkg/domain/errors"
"github.com/finleap-connect/monoskope/pkg/domain/repositories"
grpcUtil "github.com/finleap-connect/monoskope/pkg/grpc"
"github.com/google/uuid"
"google.golang.org/grpc"
"google.golang.org/protobuf/types/known/wrapperspb"
)

// auditLogServer is the implementation of the auditLogService API
Expand Down Expand Up @@ -119,7 +119,7 @@ func (s *auditLogServer) GetByUser(request *doApi.GetByUserRequest, stream doApi
}

hre := s.auditFormatter.NewHumanReadableEvent(stream.Context(), e)
if !strings.Contains(hre.Details, "“"+user.Email+"“") || hre.IssuerId == user.Id {
if !strings.Contains(hre.Details, fConsts.Quote(user.Email)) || hre.IssuerId == user.Id {
continue // skip e.g. UserRoleBindings that doesn't affect the given user or were created by him
}
err = stream.Send(hre)
Expand Down
9 changes: 6 additions & 3 deletions internal/testenv.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,17 +15,19 @@
package internal

import (
"os"
"strings"

"github.com/finleap-connect/monoskope/internal/commandhandler"
"github.com/finleap-connect/monoskope/internal/eventstore"
"github.com/finleap-connect/monoskope/internal/gateway"
"github.com/finleap-connect/monoskope/internal/queryhandler"
"os"

"github.com/finleap-connect/monoskope/internal/test"
)

type TestEnv struct {
*test.TestEnv
superUsers []string
gatewayTestEnv *gateway.TestEnv
eventStoreTestEnv *eventstore.TestEnv
queryHandlerTestEnv *queryhandler.TestEnv
Expand All @@ -38,7 +40,8 @@ func NewTestEnv(testEnv *test.TestEnv) (*TestEnv, error) {
TestEnv: testEnv,
}

os.Setenv("SUPER_USERS", "admin@monoskope.io,other-admin@monoskope.io")
env.superUsers = []string{"admin@monoskope.io", "other-admin@monoskope.io"}
os.Setenv("SUPER_USERS", strings.Join(env.superUsers, ","))

env.gatewayTestEnv, err = gateway.NewTestEnvWithParent(testEnv)
if err != nil {
Expand Down
16 changes: 10 additions & 6 deletions pkg/audit/formatters/audit/audit_formatter.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import (
esApi "github.com/finleap-connect/monoskope/pkg/api/eventsourcing"
"github.com/finleap-connect/monoskope/pkg/audit/formatters"
"github.com/finleap-connect/monoskope/pkg/audit/formatters/event"
fConsts "github.com/finleap-connect/monoskope/pkg/domain/constants/formatters"
_ "github.com/finleap-connect/monoskope/pkg/domain/formatters/events"
"github.com/finleap-connect/monoskope/pkg/domain/formatters/overviews"
"github.com/finleap-connect/monoskope/pkg/domain/projectors"
Expand Down Expand Up @@ -56,16 +57,16 @@ func NewAuditFormatter(esClient esApi.EventStoreClient, efRegistry event.EventFo
// NewHumanReadableEvent creates a HumanReadableEvent of a given event
func (f *auditFormatter) NewHumanReadableEvent(ctx context.Context, event *esApi.Event) *audit.HumanReadableEvent {
humanReadableEvent := &audit.HumanReadableEvent{
When: event.Timestamp.AsTime().Format(time.RFC822),
When: event.Timestamp.AsTime().Format(fConsts.TimeFormat),
Issuer: event.Metadata[auth.HeaderAuthEmail],
IssuerId: event.Metadata[auth.HeaderAuthId],
EventType: event.Type,
}

eventFormatter, err := f.efRegistry.CreateEventFormatter(f.esClient, es.EventType(event.Type))
if err != nil {
return humanReadableEvent
}

humanReadableEvent.Details, err = eventFormatter.GetFormattedDetails(ctx, event)
if err != nil {
f.log.Error(err, "failed to format event details",
Expand All @@ -79,9 +80,10 @@ func (f *auditFormatter) NewHumanReadableEvent(ctx context.Context, event *esApi
// NewUserOverview creates a UserOverview of the given user by its id according to the given timestamp
func (f *auditFormatter) NewUserOverview(ctx context.Context, userId uuid.UUID, timestamp time.Time) *audit.UserOverview {
userOverview := &audit.UserOverview{}
overviewFormatter := overviews.NewUserOverviewFormatter(f.esClient)
userSnapshotter := formatters.NewUserSnapshotter(f.esClient, projectors.NewUserProjector())

snapshotter := formatters.NewSnapshotter(f.esClient, projectors.NewUserProjector())
user, err := snapshotter.CreateSnapshot(ctx, &esApi.EventFilter{
user, err := userSnapshotter.CreateSnapshot(ctx, &esApi.EventFilter{
MaxTimestamp: timestamppb.New(timestamp),
AggregateId: wrapperspb.String(userId.String()),
})
Expand All @@ -90,10 +92,12 @@ func (f *auditFormatter) NewUserOverview(ctx context.Context, userId uuid.UUID,
return userOverview
}

for _, role := range userSnapshotter.CreateRoleBindingSnapshots(ctx, userId, timestamp) {
user.Roles = append(user.Roles, role.Proto())
}

userOverview.Name = user.Name
userOverview.Email = user.Email

overviewFormatter := overviews.NewUserOverviewFormatter(f.esClient)
userOverview.Roles, userOverview.Tenants, userOverview.Clusters, err = overviewFormatter.GetRolesDetails(ctx, user, timestamp)
if err != nil {
f.log.Error(err, "failed to format roles details", "userId", user, "timeStamp", timestamp)
Expand Down
14 changes: 11 additions & 3 deletions pkg/audit/formatters/snapshotter.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ package formatters

import (
"context"
"errors"
"io"

esApi "github.com/finleap-connect/monoskope/pkg/api/eventsourcing"
Expand All @@ -38,10 +39,14 @@ func NewSnapshotter[T es.Projection](esClient esApi.EventStoreClient, projector
// This is a temporary implementation until snapshots are fully implemented,
// and it is not meant to be used extensively.
func (s *Snapshotter[T]) CreateSnapshot(ctx context.Context, eventFilter *esApi.EventFilter) (T, error) {
projection := s.projector.NewProjection(uuid.New())
aggregateEvents, err := s.esClient.Retrieve(ctx, eventFilter)

var nilResult T

id, err := uuid.Parse(eventFilter.AggregateId.Value)
if err != nil {
id = uuid.New()
}
projection := s.projector.NewProjection(id)
aggregateEvents, err := s.esClient.Retrieve(ctx, eventFilter)
if err != nil {
return nilResult, err
}
Expand All @@ -66,5 +71,8 @@ func (s *Snapshotter[T]) CreateSnapshot(ctx context.Context, eventFilter *esApi.
}
}

if projection.Version() == 0 {
return nilResult, errors.New("no events found to create a snapshot for aggregate ID: " + eventFilter.AggregateId.Value)
}
return projection, nil
}
Loading

0 comments on commit c24c0d5

Please sign in to comment.