Skip to content

Commit

Permalink
Add userid and appid tracking for audit
Browse files Browse the repository at this point in the history
  • Loading branch information
akclace committed Dec 11, 2024
1 parent a0fbd96 commit 0dcd3ab
Show file tree
Hide file tree
Showing 12 changed files with 107 additions and 49 deletions.
17 changes: 8 additions & 9 deletions internal/app/action/action.go
Original file line number Diff line number Diff line change
Expand Up @@ -201,16 +201,18 @@ func (a *Action) execAction(w http.ResponseWriter, r *http.Request, isSuggest, i
RequestId: system.GetContextRequestId(r.Context()),
CreateTime: time.Now(),
UserId: system.GetContextUserId(r.Context()),
AppId: system.GetContextAppId(r.Context()),
EventType: types.EventTypeAction,
Operation: op,
Target: a.name,
Status: "Success",
}

customEvent := types.AuditEvent{
RequestId: system.GetContextUserId(r.Context()),
RequestId: system.GetContextRequestId(r.Context()),
CreateTime: time.Now(),
UserId: system.GetContextUserId(r.Context()),
AppId: system.GetContextAppId(r.Context()),
EventType: types.EventTypeCustom,
Status: "Success",
}
Expand All @@ -221,15 +223,12 @@ func (a *Action) execAction(w http.ResponseWriter, r *http.Request, isSuggest, i
a.Error().Err(err).Msg("error inserting audit event")
}

op := system.GetThreadLocalKey(thread, types.TL_AUDIT_OPERATION)
target := system.GetThreadLocalKey(thread, types.TL_AUDIT_TARGET)
detail := system.GetThreadLocalKey(thread, types.TL_AUDIT_DETAIL)
customEvent.Operation = system.GetThreadLocalKey(thread, types.TL_AUDIT_OPERATION)
customEvent.Target = system.GetThreadLocalKey(thread, types.TL_AUDIT_TARGET)
customEvent.Detail = system.GetThreadLocalKey(thread, types.TL_AUDIT_DETAIL)

if op != "" {
// Audit event was set, insert it
customEvent.Operation = op
customEvent.Target = target
customEvent.Detail = detail
if customEvent.Operation != "" {
// Audit event was set in handler, insert it
if err := a.auditInsert(&customEvent); err != nil {
a.Error().Err(err).Msg("error inserting custom audit event")
}
Expand Down
8 changes: 1 addition & 7 deletions internal/app/apptype/builtins.go
Original file line number Diff line number Diff line change
Expand Up @@ -368,13 +368,7 @@ func createAuditBuiltin(thread *starlark.Thread, _ *starlark.Builtin, args starl
thread.SetLocal(types.TL_AUDIT_OPERATION, operation.GoString())
thread.SetLocal(types.TL_AUDIT_TARGET, target.GoString())
thread.SetLocal(types.TL_AUDIT_DETAIL, detail.GoString())

fields := starlark.StringDict{
"operation": operation,
"target": target,
"detail": detail,
}
return starlarkstruct.FromStringDict(starlark.String(AUDIT), fields), nil
return starlark.None, nil
}

func createProxyBuiltin(_ *starlark.Thread, _ *starlark.Builtin, args starlark.Tuple, kwargs []starlark.Tuple) (starlark.Value, error) {
Expand Down
1 change: 1 addition & 0 deletions internal/app/handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,7 @@ func (a *App) createHandlerFunc(fullHtml, fragment string, handler starlark.Call
RequestId: system.GetContextUserId(r.Context()),
CreateTime: time.Now(),
UserId: system.GetContextUserId(r.Context()),
AppId: system.GetContextAppId(r.Context()),
EventType: types.EventTypeCustom,
Status: "Success",
}
Expand Down
17 changes: 11 additions & 6 deletions internal/server/app_apis.go
Original file line number Diff line number Diff line change
Expand Up @@ -454,14 +454,16 @@ func (s *Server) authenticateAndServeApp(w http.ResponseWriter, r *http.Request,
// Create a new context with the user ID
s.Trace().Msgf("Authenticated user %s", userId)
ctx := context.WithValue(r.Context(), types.USER_ID, userId)
r = r.WithContext(ctx)
ctx = context.WithValue(ctx, types.APP_ID, string(app.Id))

contextShared := r.Context().Value(types.USER_ID_SHARED)
contextShared := ctx.Value(types.SHARED)
if contextShared != nil {
// allow audit middleware to access the user id
userIdShared := contextShared.(*ContextUser)
userIdShared.UserId = userId
cs := contextShared.(*ContextShared)
cs.UserId = userId
cs.AppId = string(app.Id)
}
r = r.WithContext(ctx)

// Authentication successful, serve the app
app.ServeHTTP(w, r)
Expand Down Expand Up @@ -593,7 +595,7 @@ func (s *Server) auditApp(ctx context.Context, tx types.Transaction, app *app.Ap
return auditResult, nil
}

func (s *Server) CompleteTransaction(ctx context.Context, tx types.Transaction, entries []types.AppPathDomain, dryRun bool) error {
func (s *Server) CompleteTransaction(ctx context.Context, tx types.Transaction, entries []types.AppPathDomain, dryRun bool, op string) error {
if dryRun {
return nil
}
Expand All @@ -604,8 +606,11 @@ func (s *Server) CompleteTransaction(ctx context.Context, tx types.Transaction,

// Update the in memory cache
if entries != nil {
s.apps.DeleteApps(entries)
if err := s.apps.DeleteAppsAudit(ctx, entries, op); err != nil {
return err
}
}

return nil
}

Expand Down
10 changes: 5 additions & 5 deletions internal/server/app_updates.go
Original file line number Diff line number Diff line change
Expand Up @@ -165,7 +165,7 @@ func (s *Server) ReloadApps(ctx context.Context, appPathGlob string, approve, dr
}

// Commit the transaction if not dry run and update the in memory app store
if err := s.CompleteTransaction(ctx, tx, reloadResults, dryRun); err != nil {
if err := s.CompleteTransaction(ctx, tx, reloadResults, dryRun, "reload"); err != nil {
return nil, err
}

Expand Down Expand Up @@ -197,7 +197,7 @@ func (s *Server) loadAppCode(ctx context.Context, tx types.Transaction, appEntry
return nil
}

func (s *Server) StagedUpdate(ctx context.Context, appPathGlob string, dryRun, promote bool, handler stagedUpdateHandler, args map[string]any) (*types.AppStagedUpdateResponse, error) {
func (s *Server) StagedUpdate(ctx context.Context, appPathGlob string, dryRun, promote bool, handler stagedUpdateHandler, args map[string]any, op string) (*types.AppStagedUpdateResponse, error) {
tx, err := s.db.BeginTransaction(ctx)
if err != nil {
return nil, err
Expand All @@ -215,7 +215,7 @@ func (s *Server) StagedUpdate(ctx context.Context, appPathGlob string, dryRun, p
PromoteResults: promoteResults,
}

if err := s.CompleteTransaction(ctx, tx, entries, dryRun); err != nil {
if err := s.CompleteTransaction(ctx, tx, entries, dryRun, op); err != nil {
return nil, err
}

Expand Down Expand Up @@ -342,7 +342,7 @@ func (s *Server) PromoteApps(ctx context.Context, appPathGlob string, dryRun boo
result = append(result, appInfo.AppPathDomain)
}

if err = s.CompleteTransaction(ctx, tx, result, dryRun); err != nil {
if err = s.CompleteTransaction(ctx, tx, result, dryRun, "promote"); err != nil {
return nil, err
}

Expand Down Expand Up @@ -419,7 +419,7 @@ func (s *Server) UpdateAppSettings(ctx context.Context, appPathGlob string, dryR
return nil, err
}

s.apps.DeleteApps(results) // Delete instead of update to avoid having to initialize all the linked apps
s.apps.DeleteAppsAudit(ctx, results, "update-settings") // Delete instead of update to avoid having to initialize all the linked apps
// Apps will get reloaded on the next request
return ret, nil
}
Expand Down
51 changes: 51 additions & 0 deletions internal/server/apps.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,14 @@
package server

import (
"context"
"fmt"
"strings"
"sync"
"time"

"github.com/claceio/clace/internal/app"
"github.com/claceio/clace/internal/system"
"github.com/claceio/clace/internal/types"
)

Expand Down Expand Up @@ -169,6 +172,54 @@ func (a *AppStore) DeleteApps(pathDomain []types.AppPathDomain) {
a.allDomains = nil
}

func (a *AppStore) DeleteAppsAudit(ctx context.Context, pathDomain []types.AppPathDomain, op string) error {
appInfo, error := a.GetAllApps()
if error != nil {
return error
}
appMap := getAppInfoMap(appInfo)

event := types.AuditEvent{
RequestId: system.GetContextRequestId(ctx),
CreateTime: time.Now(),
UserId: system.GetContextUserId(ctx),
EventType: types.EventTypeSystem,
Operation: op,
Status: "Success",
}

for _, pd := range pathDomain {
appInfo, ok := appMap[pd.String()]
if !ok {
return fmt.Errorf("app not found: %s", pd)
}

event.Target = pd.String()
event.AppId = appInfo.Id

if err := a.server.InsertAuditEvent(&event); err != nil {
return err
}
}

a.mu.Lock()
defer a.mu.Unlock()
for _, pd := range pathDomain {
a.clearApp(pd)
}
a.allApps = nil
a.allDomains = nil
return nil
}

func getAppInfoMap(appInfo []types.AppInfo) map[string]types.AppInfo {
ret := make(map[string]types.AppInfo)
for _, info := range appInfo {
ret[info.AppPathDomain.String()] = info
}
return ret
}

func (a *AppStore) UpdateApps(apps []*app.App) {
a.mu.Lock()
defer a.mu.Unlock()
Expand Down
18 changes: 15 additions & 3 deletions internal/server/audit_middleware.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,13 @@ func (s *Server) insertAuditEvent(event *types.AuditEvent) error {
return err
}

func (s *Server) InsertAuditEvent(event *types.AuditEvent) error {
_, err := s.auditDB.Exec(`insert into audit (rid, app_id, create_time, user_id, event_type, operation, target, status, detail) `+
`values (?, ?, ?, ?, ?, ?, ?, ?, ?)`,
event.RequestId, event.AppId, event.CreateTime, event.UserId, event.EventType, event.Operation, event.Target, event.Status, event.Detail)
return err
}

func (s *Server) cleanupEvents() error {
// TODO: Implement cleanup
return nil
Expand All @@ -74,8 +81,9 @@ func (s *Server) auditCleanup(cleanupTicker *time.Ticker) {
fmt.Fprintf(os.Stderr, "background audit cleanup stopped")
}

type ContextUser struct {
type ContextShared struct {
UserId string
AppId string
}

func (server *Server) handleStatus(next http.Handler) http.Handler {
Expand All @@ -88,12 +96,15 @@ func (server *Server) handleStatus(next http.Handler) http.Handler {
return
}

contextShared := ContextUser{}
contextShared := ContextShared{
UserId: types.ADMIN_USER,
}

rid := "rid_" + id.String()
ctx := r.Context()
ctx = context.WithValue(ctx, types.REQUEST_ID, rid)
ctx = context.WithValue(ctx, types.USER_ID_SHARED, &contextShared)
ctx = context.WithValue(ctx, types.USER_ID, types.ADMIN_USER)
ctx = context.WithValue(ctx, types.SHARED, &contextShared)
r = r.WithContext(ctx)

// Wrap the ResponseWriter
Expand All @@ -116,6 +127,7 @@ func (server *Server) handleStatus(next http.Handler) http.Handler {
RequestId: rid,
CreateTime: time.Now(),
UserId: contextShared.UserId,
AppId: types.AppId(contextShared.AppId),
EventType: types.EventTypeHTTP,
Operation: r.Method,
Target: r.Host + ":" + r.URL.Path,
Expand Down
16 changes: 6 additions & 10 deletions internal/server/router.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
package server

import (
"context"
"crypto/hmac"
"crypto/sha256"
"crypto/subtle"
Expand Down Expand Up @@ -199,11 +198,6 @@ func (h *Handler) callApp(w http.ResponseWriter, r *http.Request) {
}
}

// Add app id to the context
ctx := r.Context()
ctx = context.WithValue(ctx, types.APP_ID, serveApp.Id)
r = r.WithContext(ctx)

h.server.authenticateAndServeApp(w, r, serveApp)
}

Expand Down Expand Up @@ -242,6 +236,7 @@ func (h *Handler) apiHandler(w http.ResponseWriter, r *http.Request, enableBasic
RequestId: system.GetContextRequestId(r.Context()),
CreateTime: time.Now(),
UserId: system.GetContextUserId(r.Context()),
AppId: system.GetContextAppId(r.Context()),
EventType: types.EventTypeSystem,
Operation: operation,
Status: "Success",
Expand Down Expand Up @@ -413,6 +408,7 @@ func (h *Handler) webhookHandler(w http.ResponseWriter, r *http.Request, webhook
RequestId: system.GetContextRequestId(r.Context()),
CreateTime: time.Now(),
UserId: system.GetContextUserId(r.Context()),
AppId: system.GetContextAppId(r.Context()),
EventType: types.EventTypeSystem,
Operation: fmt.Sprintf("webhook_%s", webhookType),
Status: "Success",
Expand Down Expand Up @@ -576,7 +572,7 @@ func (h *Handler) approveApps(r *http.Request) (any, error) {
return nil, types.CreateRequestError("appPathGlob is required", http.StatusBadRequest)
}

approveResult, err := h.server.StagedUpdate(r.Context(), appPathGlob, dryRun, promote, h.server.auditHandler, map[string]any{})
approveResult, err := h.server.StagedUpdate(r.Context(), appPathGlob, dryRun, promote, h.server.auditHandler, map[string]any{}, "approve")
return approveResult, err
}

Expand All @@ -600,7 +596,7 @@ func (h *Handler) accountLink(r *http.Request) (any, error) {
"account": r.URL.Query().Get("account"),
}

linkResult, err := h.server.StagedUpdate(r.Context(), appPathGlob, dryRun, promote, h.server.accountLinkHandler, args)
linkResult, err := h.server.StagedUpdate(r.Context(), appPathGlob, dryRun, promote, h.server.accountLinkHandler, args, "account-link")
return linkResult, err
}

Expand All @@ -624,7 +620,7 @@ func (h *Handler) updateParam(r *http.Request) (any, error) {
"paramValue": r.URL.Query().Get("paramValue"),
}

updateResult, err := h.server.StagedUpdate(r.Context(), appPathGlob, dryRun, promote, h.server.updateParamHandler, args)
updateResult, err := h.server.StagedUpdate(r.Context(), appPathGlob, dryRun, promote, h.server.updateParamHandler, args, "update-param")
return updateResult, err
}

Expand Down Expand Up @@ -780,7 +776,7 @@ func (h *Handler) updateAppMetadata(r *http.Request) (any, error) {
"metadata": updateAppRequest,
}

updateResult, err := h.server.StagedUpdate(r.Context(), appPathGlob, dryRun, promote, h.server.updateMetadataHandler, args)
updateResult, err := h.server.StagedUpdate(r.Context(), appPathGlob, dryRun, promote, h.server.updateMetadataHandler, args, "update-metadata")
return updateResult, err

}
Expand Down
2 changes: 1 addition & 1 deletion internal/server/version_apis.go
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,6 @@ func (s *Server) VersionSwitch(ctx context.Context, mainAppPath string, dryRun b
return nil, err
}

s.apps.DeleteApps([]types.AppPathDomain{appPathDomain})
s.apps.DeleteAppsAudit(ctx, []types.AppPathDomain{appPathDomain}, "version-switch")
return ret, nil
}
4 changes: 2 additions & 2 deletions internal/server/webhook_apis.go
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,7 @@ func (s *Server) TokenCreate(ctx context.Context, appPath string, webhookType ty
return nil, err
}

if err = s.CompleteTransaction(ctx, tx, []types.AppPathDomain{appPathDomain}, dryRun); err != nil {
if err = s.CompleteTransaction(ctx, tx, []types.AppPathDomain{appPathDomain}, dryRun, "webhook-create"); err != nil {
return nil, err
}

Expand Down Expand Up @@ -170,7 +170,7 @@ func (s *Server) TokenDelete(ctx context.Context, appPath string, webhookType ty
return nil, err
}

if err = s.CompleteTransaction(ctx, tx, []types.AppPathDomain{appPathDomain}, dryRun); err != nil {
if err = s.CompleteTransaction(ctx, tx, []types.AppPathDomain{appPathDomain}, dryRun, "webhook-delete"); err != nil {
return nil, err
}

Expand Down
4 changes: 2 additions & 2 deletions internal/system/thread_local.go
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,6 @@ func GetContextRequestId(ctx context.Context) string {
return GetContextValue(ctx, types.REQUEST_ID)
}

func GetContextAppId(ctx context.Context) string {
return GetContextValue(ctx, types.APP_ID)
func GetContextAppId(ctx context.Context) types.AppId {
return types.AppId(GetContextValue(ctx, types.APP_ID))
}
8 changes: 4 additions & 4 deletions internal/types/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,10 +30,10 @@ const (
type ContextKey string

const (
USER_ID ContextKey = "user_id"
USER_ID_SHARED ContextKey = "user_id_shared"
REQUEST_ID ContextKey = "request_id"
APP_ID ContextKey = "app_id"
USER_ID ContextKey = "user_id"
SHARED ContextKey = "shared"
REQUEST_ID ContextKey = "request_id"
APP_ID ContextKey = "app_id"
)

const (
Expand Down

0 comments on commit 0dcd3ab

Please sign in to comment.