142 lines
3.8 KiB
Text
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,
|
|
}
|
|
}
|
|
}
|