Skip to content

Commit

Permalink
feat: logs activity of shield entities (#37)
Browse files Browse the repository at this point in the history
  • Loading branch information
FemiNoviaLina authored Apr 19, 2024
1 parent 81b8706 commit 2e11f66
Show file tree
Hide file tree
Showing 56 changed files with 1,354 additions and 202 deletions.
46 changes: 30 additions & 16 deletions cmd/serve.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import (

"github.com/goto/shield/config"
"github.com/goto/shield/core/action"
"github.com/goto/shield/core/activity"
"github.com/goto/shield/core/group"
"github.com/goto/shield/core/namespace"
"github.com/goto/shield/core/organization"
Expand Down Expand Up @@ -89,19 +90,27 @@ func StartServer(logger *log.Zap, cfg *config.Shield) error {
return err
}

schemaMigrationConfig := schema.NewSchemaMigrationConfig(cfg.App.DefaultSystemEmail)

//
activityRepository := postgres.NewActivityRepository(dbClient)
activityService := activity.NewService(activityRepository)

userRepository := postgres.NewUserRepository(dbClient)
userService := user.NewService(logger, userRepository, activityService)

actionRepository := postgres.NewActionRepository(dbClient)
actionService := action.NewService(actionRepository)
actionService := action.NewService(logger, actionRepository, userService, activityService)

roleRepository := postgres.NewRoleRepository(dbClient)
roleService := role.NewService(roleRepository)
roleService := role.NewService(logger, roleRepository, userService, activityService)

policyPGRepository := postgres.NewPolicyRepository(dbClient)
policySpiceRepository := spicedb.NewPolicyRepository(spiceDBClient)
policyService := policy.NewService(policyPGRepository)
policyService := policy.NewService(logger, policyPGRepository, userService, activityService)

namespaceRepository := postgres.NewNamespaceRepository(dbClient)
namespaceService := namespace.NewService(namespaceRepository)
namespaceService := namespace.NewService(logger, namespaceRepository, userService, activityService)

s := schema.NewSchemaMigrationService(
blob.NewSchemaConfigRepository(resourceBlobFS),
Expand All @@ -110,6 +119,8 @@ func StartServer(logger *log.Zap, cfg *config.Shield) error {
actionService,
policyService,
policySpiceRepository,
userRepository,
schemaMigrationConfig,
)

err = s.RunMigrations(ctx)
Expand Down Expand Up @@ -159,37 +170,40 @@ func BuildAPIDependencies(
dbc *db.Client,
sdb *spicedb.SpiceDB,
) (api.Deps, error) {
activityRepository := postgres.NewActivityRepository(dbc)
activityService := activity.NewService(activityRepository)

userRepository := postgres.NewUserRepository(dbc)
userService := user.NewService(logger, userRepository, activityService)

actionRepository := postgres.NewActionRepository(dbc)
actionService := action.NewService(actionRepository)
actionService := action.NewService(logger, actionRepository, userService, activityService)

namespaceRepository := postgres.NewNamespaceRepository(dbc)
namespaceService := namespace.NewService(namespaceRepository)

userRepository := postgres.NewUserRepository(dbc)
userService := user.NewService(userRepository)
namespaceService := namespace.NewService(logger, namespaceRepository, userService, activityService)

roleRepository := postgres.NewRoleRepository(dbc)
roleService := role.NewService(roleRepository)
roleService := role.NewService(logger, roleRepository, userService, activityService)

relationPGRepository := postgres.NewRelationRepository(dbc)
relationSpiceRepository := spicedb.NewRelationRepository(sdb)
relationService := relation.NewService(relationPGRepository, relationSpiceRepository)
relationService := relation.NewService(logger, relationPGRepository, relationSpiceRepository, userService, activityService)

groupRepository := postgres.NewGroupRepository(dbc)
groupService := group.NewService(groupRepository, relationService, userService)
groupService := group.NewService(logger, groupRepository, relationService, userService, activityService)

organizationRepository := postgres.NewOrganizationRepository(dbc)
organizationService := organization.NewService(organizationRepository, relationService, userService)
organizationService := organization.NewService(logger, organizationRepository, relationService, userService, activityService)

projectRepository := postgres.NewProjectRepository(dbc)
projectService := project.NewService(projectRepository, relationService, userService)
projectService := project.NewService(logger, projectRepository, relationService, userService, activityService)

policyPGRepository := postgres.NewPolicyRepository(dbc)
policyService := policy.NewService(policyPGRepository)
policyService := policy.NewService(logger, policyPGRepository, userService, activityService)

resourcePGRepository := postgres.NewResourceRepository(dbc)
resourceService := resource.NewService(
resourcePGRepository, resourceBlobRepository, relationService, userService, projectService, organizationService, groupService)
logger, resourcePGRepository, resourceBlobRepository, relationService, userService, projectService, organizationService, groupService, activityService)

relationAdapter := adapter.NewRelation(groupService, userService, relationService)

Expand Down
18 changes: 18 additions & 0 deletions core/action/action.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ import (
"time"
)

const auditEntity = "action"

type Repository interface {
Get(ctx context.Context, id string) (Action, error)
Create(ctx context.Context, action Action) (Action, error)
Expand All @@ -19,3 +21,19 @@ type Action struct {
CreatedAt time.Time
UpdatedAt time.Time
}

type ActionLogData struct {
Entity string `mapstructure:"entity"`
ID string `mapstructure:"id"`
Name string `mapstructure:"name"`
NamespaceID string `mapstructure:"namespace_id"`
}

func (action Action) ToActionLogData() ActionLogData {
return ActionLogData{
Entity: auditEntity,
ID: action.ID,
Name: action.Name,
NamespaceID: action.NamespaceID,
}
}
1 change: 1 addition & 0 deletions core/action/errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,5 @@ var (
ErrInvalidID = errors.New("action id is invalid")
ErrNotExist = errors.New("action doesn't exist")
ErrInvalidDetail = errors.New("invalid action detail")
ErrLogActivity = errors.New("error while logging activity")
)
56 changes: 53 additions & 3 deletions core/action/service.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,39 @@ package action

import (
"context"
"fmt"

"github.com/goto/salt/log"
"github.com/goto/shield/core/user"
pkgctx "github.com/goto/shield/pkg/context"
)

const (
auditKeyActionCreate = "action.create"
auditKeyActionUpdate = "action.update"
)

type UserService interface {
FetchCurrentUser(ctx context.Context) (user.User, error)
}

type ActivityService interface {
Log(ctx context.Context, action string, actor string, data any) error
}

type Service struct {
repository Repository
logger log.Logger
repository Repository
userService UserService
activityService ActivityService
}

func NewService(repository Repository) *Service {
func NewService(logger log.Logger, repository Repository, userService UserService, activityService ActivityService) *Service {
return &Service{
repository: repository,
logger: logger,
repository: repository,
userService: userService,
activityService: activityService,
}
}

Expand All @@ -19,11 +43,24 @@ func (s Service) Get(ctx context.Context, id string) (Action, error) {
}

func (s Service) Create(ctx context.Context, action Action) (Action, error) {
currentUser, err := s.userService.FetchCurrentUser(ctx)
if err != nil {
s.logger.Error(fmt.Sprintf("%s: %s", user.ErrInvalidEmail.Error(), err.Error()))
}

newAction, err := s.repository.Create(ctx, action)
if err != nil {
return Action{}, err
}

go func() {
ctx := pkgctx.WithoutCancel(ctx)
actionLogData := newAction.ToActionLogData()
if err := s.activityService.Log(ctx, auditKeyActionCreate, currentUser.ID, actionLogData); err != nil {
s.logger.Error(fmt.Sprintf("%s: %s", ErrLogActivity.Error(), err.Error()))
}
}()

return newAction, nil
}

Expand All @@ -32,6 +69,11 @@ func (s Service) List(ctx context.Context) ([]Action, error) {
}

func (s Service) Update(ctx context.Context, id string, action Action) (Action, error) {
currentUser, err := s.userService.FetchCurrentUser(ctx)
if err != nil {
s.logger.Error(fmt.Sprintf("%s: %s", user.ErrInvalidEmail.Error(), err.Error()))
}

updatedAction, err := s.repository.Update(ctx, Action{
Name: action.Name,
ID: id,
Expand All @@ -41,5 +83,13 @@ func (s Service) Update(ctx context.Context, id string, action Action) (Action,
return Action{}, err
}

go func() {
ctx := pkgctx.WithoutCancel(ctx)
actionLogData := updatedAction.ToActionLogData()
if err := s.activityService.Log(ctx, auditKeyActionUpdate, currentUser.ID, actionLogData); err != nil {
s.logger.Error(fmt.Sprintf("%s: %s", ErrLogActivity.Error(), err.Error()))
}
}()

return updatedAction, nil
}
11 changes: 11 additions & 0 deletions core/activity/activity.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package activity

import (
"context"

"github.com/goto/salt/audit"
)

type Repository interface {
Insert(ctx context.Context, log *audit.Log) error
}
8 changes: 8 additions & 0 deletions core/activity/errors.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package activity

import "errors"

var (
ErrInvalidUUID = errors.New("invalid syntax of uuid")
ErrInvalidData = errors.New("invalid log data")
)
46 changes: 46 additions & 0 deletions core/activity/service.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
package activity

import (
"context"
"time"

"github.com/goto/salt/audit"
"github.com/goto/shield/config"
"github.com/mitchellh/mapstructure"
)

type Service struct {
repository Repository
}

func NewService(repository Repository) *Service {
return &Service{
repository: repository,
}
}

func (s Service) Log(ctx context.Context, action string, actor string, data any) error {
if data == nil {
return ErrInvalidData
}

var logDataMap map[string]interface{}
if err := mapstructure.Decode(data, &logDataMap); err != nil {
return err
}

metadata := map[string]string{
"app_name": "shield",
"app_version": config.Version,
}

log := &audit.Log{
Timestamp: time.Now(),
Action: action,
Data: logDataMap,
Actor: actor,
Metadata: metadata,
}

return s.repository.Insert(ctx, log)
}
1 change: 1 addition & 0 deletions core/group/errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,5 @@ var (
ErrListingGroupRelations = errors.New("error while listing relations")
ErrFetchingUsers = errors.New("error while fetching users")
ErrFetchingGroups = errors.New("error while fetching groups")
ErrLogActivity = errors.New("error while logging activity")
)
22 changes: 22 additions & 0 deletions core/group/group.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ import (
"github.com/goto/shield/pkg/metadata"
)

const auditEntity = "group"

type Repository interface {
Create(ctx context.Context, grp Group) (Group, error)
GetByID(ctx context.Context, id string) (Group, error)
Expand All @@ -29,3 +31,23 @@ type Group struct {
CreatedAt time.Time
UpdatedAt time.Time
}

type GroupLogData struct {
Entity string `mapstructure:"entity"`
ID string `mapstructure:"id"`
Name string `mapstructure:"name"`
Slug string `mapstructure:"slug"`
OrgID string `mapstructure:"organization_id"`
}

func (group Group) ToGroupLogData() GroupLogData {
logData := GroupLogData{
Entity: auditEntity,
ID: group.ID,
Name: group.Name,
Slug: group.Slug,
OrgID: group.OrganizationID,
}

return logData
}
Loading

0 comments on commit 2e11f66

Please sign in to comment.