provisioning/scripts/audit-workspace.nu

142 lines
3.8 KiB
Text

#!/usr/bin/env nu
# Workspace Audit Query Commands
# Query and analyze workspace operation audit logs
def get_audit_dir [] {
let home = ($env.HOME | path expand)
$home + "/.local/share/provisioning/audit"
}
def read_audit_logs [filter_fn: closure] {
let audit_dir = (get_audit_dir)
if not ($audit_dir | path exists) {
return (error make { msg: $"Audit directory not found: {$audit_dir}" })
}
glob $"($audit_dir)/*.jsonl" | each { |file|
cat $file | lines | each { |line|
if ($line | is-empty) { return null }
try {
let event = ($line | from json)
(do $filter_fn $event) | if $in { $event } else { null }
}
}
} | compact
}
export def "audit workspace operations" [workspace: string] {
let workspace_ops = ["WorkspaceCreate", "WorkspaceDelete", "WorkspaceUpdate",
"WorkspaceSwitch", "WorkspaceList", "WorkspaceSync"]
let events = (read_audit_logs { |event|
(($event.action.workspace == $workspace) and
($workspace_ops | any { |op| $event.action.action_type == $op }))
})
$events
| sort-by timestamp -r
| each { |e|
{
timestamp: $e.timestamp,
action: $e.action.action_type,
user: $e.user.name,
resource: $e.action.resource,
status: $e.result.status,
duration_ms: $e.result.duration_ms,
}
}
}
export def "audit workspace summary" [days?: int] {
let days = if ($days == null) { 30 } else { $days }
let cutoff_time = ((now) - ($days * 24 * 60 * 60 | into duration))
let workspace_ops = ["WorkspaceCreate", "WorkspaceDelete", "WorkspaceUpdate",
"WorkspaceSwitch", "WorkspaceList", "WorkspaceSync"]
let events = (read_audit_logs { |event|
let event_time = ($event.timestamp | into datetime)
(($event_time > $cutoff_time) and
($workspace_ops | any { |op| $event.action.action_type == $op }))
})
# Simple summary: just list unique workspaces and total ops count
let unique_workspaces = ($events | map { |e| $e.action.workspace } | unique)
$unique_workspaces | each { |ws|
let ws_events = ($events | where { |e| $e.action.workspace == $ws })
{
workspace: $ws,
total_operations: ($ws_events | length),
operations: (
$ws_events
| map { |e| $e.action.action_type }
| unique
| each { |op|
{
action: $op,
count: ($ws_events | where { |e| $e.action.action_type == $op } | length)
}
}
),
}
} | sort-by workspace
}
export def "audit workspace failures" [workspace: string] {
let events = (read_audit_logs { |event|
(($event.action.workspace == $workspace) and
($event.result.status != "Success"))
})
$events
| sort-by timestamp -r
| each { |e|
{
timestamp: $e.timestamp,
action: $e.action.action_type,
resource: $e.action.resource,
status: $e.result.status,
error: $e.result.error,
duration_ms: $e.result.duration_ms,
}
}
}
export def "audit workspace switches" [user_id: string] {
let events = (read_audit_logs { |event|
(($event.user.id == $user_id) and
($event.action.action_type == "WorkspaceSwitch"))
})
$events
| sort-by timestamp -r
| each { |e|
{
timestamp: $e.timestamp,
user: $e.user.name,
to_workspace: $e.action.workspace,
status: $e.result.status,
duration_ms: $e.result.duration_ms,
}
}
}
export def "audit workspace user-actions" [user_id: string] {
let events = (read_audit_logs { |event|
$event.user.id == $user_id
})
$events
| sort-by timestamp -r
| each { |e|
{
timestamp: $e.timestamp,
action: $e.action.action_type,
workspace: $e.action.workspace,
resource: $e.action.resource,
status: $e.result.status,
}
}
}