From db5de9be9793be65e2a77ab71656713f97bc779b Mon Sep 17 00:00:00 2001 From: James Read Date: Thu, 18 Apr 2024 21:52:04 +0100 Subject: [PATCH] feature: #135 Permissions for logs (#273) --- internal/acl/acl.go | 62 ++++++++++++++++++++++++------------- internal/config/config.go | 4 +++ internal/grpcapi/grpcApi.go | 12 +++++-- 3 files changed, 54 insertions(+), 24 deletions(-) diff --git a/internal/acl/acl.go b/internal/acl/acl.go index 20676f7..a8c7b3c 100644 --- a/internal/acl/acl.go +++ b/internal/acl/acl.go @@ -17,24 +17,51 @@ type AuthenticatedUser struct { acls []string } -// IsAllowedExec checks if a AuthenticatedUser is allowed to execute an Action -func IsAllowedExec(cfg *config.Config, user *AuthenticatedUser, action *config.Action) bool { +func logAclNotMatched(cfg *config.Config, aclFunction string, user *AuthenticatedUser, action *config.Action) { + if cfg.LogDebugOptions.AclNotMatched { + log.WithFields(log.Fields{ + "User": user.Username, + "Action": action.Title, + }).Debugf("%v - No ACLs Matched", aclFunction) + } +} + +func logAclMatched(cfg *config.Config, aclFunction string, user *AuthenticatedUser, action *config.Action, acl *config.AccessControlList) { + if cfg.LogDebugOptions.AclMatched { + log.WithFields(log.Fields{ + "User": user.Username, + "Action": action.Title, + "ACL": acl.Name, + }).Debugf("%v - Matched ACL", aclFunction) + } +} + +// IsAllowedLogs checks if a AuthenticatedUser is allowed to view an action's logs +func IsAllowedLogs(cfg *config.Config, user *AuthenticatedUser, action *config.Action) bool { for _, acl := range getRelevantAcls(cfg, action.Acls, user) { - if acl.Permissions.Exec { - log.WithFields(log.Fields{ - "User": user.Username, - "Action": action.Title, - "ACL": acl.Name, - }).Trace("isAllowedExec - Matched ACL") + if acl.Permissions.Logs { + logAclMatched(cfg, "isAllowedLogs", user, action, acl) return true } } - log.WithFields(log.Fields{ - "User": user.Username, - "Action": action.Title, - }).Trace("isAllowedExec - No ACLs matched") + logAclNotMatched(cfg, "isAllowedLogs", user, action) + + return cfg.DefaultPermissions.Logs +} + +// IsAllowedExec checks if a AuthenticatedUser is allowed to execute an Action +func IsAllowedExec(cfg *config.Config, user *AuthenticatedUser, action *config.Action) bool { + for _, acl := range getRelevantAcls(cfg, action.Acls, user) { + if acl.Permissions.Exec { + logAclMatched(cfg, "isAllowedExec", user, action, acl) + + return true + } + } + + logAclNotMatched(cfg, "isAllowedExec", user, action) return cfg.DefaultPermissions.Exec } @@ -47,20 +74,13 @@ func IsAllowedView(cfg *config.Config, user *AuthenticatedUser, action *config.A for _, acl := range getRelevantAcls(cfg, action.Acls, user) { if acl.Permissions.View { - log.WithFields(log.Fields{ - "User": user.Username, - "Action": action.Title, - "ACL": acl.Name, - }).Trace("isAllowedView - Matched ACL") + logAclMatched(cfg, "isAllowedView", user, action, acl) return true } } - log.WithFields(log.Fields{ - "User": user.Username, - "Action": action.Title, - }).Trace("isAllowedView - No ACLs matched") + logAclNotMatched(cfg, "isAllowedView", user, action) return cfg.DefaultPermissions.View } diff --git a/internal/config/config.go b/internal/config/config.go index d3ed735..a569d04 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -53,6 +53,7 @@ type EntityFile struct { type PermissionsList struct { View bool Exec bool + Logs bool } // AccessControlList defines what permissions apply to a user or user group. @@ -117,6 +118,8 @@ type Config struct { type LogDebugOptions struct { SingleFrontendRequests bool SingleFrontendRequestHeaders bool + AclMatched bool + AclNotMatched bool } type DashboardComponent struct { @@ -144,6 +147,7 @@ func DefaultConfig() *Config { config.CheckForUpdates = true config.DefaultPermissions.Exec = true config.DefaultPermissions.View = true + config.DefaultPermissions.Logs = true config.AuthJwtClaimUsername = "name" config.AuthJwtClaimUserGroup = "group" config.WebUIDir = "./webui" diff --git a/internal/grpcapi/grpcApi.go b/internal/grpcapi/grpcApi.go index 25f3baa..f272da3 100644 --- a/internal/grpcapi/grpcApi.go +++ b/internal/grpcapi/grpcApi.go @@ -227,15 +227,21 @@ func (api *oliveTinAPI) GetDashboardComponents(ctx ctx.Context, req *pb.GetDashb } func (api *oliveTinAPI) GetLogs(ctx ctx.Context, req *pb.GetLogsRequest) (*pb.GetLogsResponse, error) { + user := acl.UserFromContext(ctx, cfg) + ret := &pb.GetLogsResponse{} // TODO Limit to 10 entries or something to prevent browser lag. for trackingId, logEntry := range api.executor.Logs { - pbLogEntry := internalLogEntryToPb(logEntry) - pbLogEntry.ExecutionTrackingId = trackingId + action := cfg.FindAction(logEntry.ActionTitle) - ret.Logs = append(ret.Logs, pbLogEntry) + if action == nil || acl.IsAllowedLogs(cfg, user, action) { + pbLogEntry := internalLogEntryToPb(logEntry) + pbLogEntry.ExecutionTrackingId = trackingId + + ret.Logs = append(ret.Logs, pbLogEntry) + } } sorter := func(i, j int) bool {