mirror of
https://github.com/coder/coder.git
synced 2026-09-24 15:04:27 +08:00
This reverts commit 145faf4400.
This commit is contained in:
@@ -219,26 +219,12 @@ func convertAuditLog(dblog database.GetAuditLogsOffsetRow) codersdk.AuditLog {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
type WorkspaceResourceInfo struct {
|
|
||||||
WorkspaceName string
|
|
||||||
}
|
|
||||||
|
|
||||||
func auditLogDescription(alog database.GetAuditLogsOffsetRow) string {
|
func auditLogDescription(alog database.GetAuditLogsOffsetRow) string {
|
||||||
str := fmt.Sprintf("{user} %s %s",
|
str := fmt.Sprintf("{user} %s %s",
|
||||||
codersdk.AuditAction(alog.Action).FriendlyString(),
|
codersdk.AuditAction(alog.Action).FriendlyString(),
|
||||||
codersdk.ResourceType(alog.ResourceType).FriendlyString(),
|
codersdk.ResourceType(alog.ResourceType).FriendlyString(),
|
||||||
)
|
)
|
||||||
|
|
||||||
// Strings for build updates follow the below format:
|
|
||||||
// "{user} started workspace build for workspace {target}"
|
|
||||||
// where target is a workspace instead of the workspace build
|
|
||||||
if alog.ResourceType == database.ResourceTypeWorkspaceBuild {
|
|
||||||
workspaceBytes := []byte(alog.AdditionalFields)
|
|
||||||
var workspaceResourceInfo WorkspaceResourceInfo
|
|
||||||
_ = json.Unmarshal(workspaceBytes, &workspaceResourceInfo)
|
|
||||||
str += " for workspace " + workspaceResourceInfo.WorkspaceName
|
|
||||||
}
|
|
||||||
|
|
||||||
// We don't display the name for git ssh keys. It's fairly long and doesn't
|
// We don't display the name for git ssh keys. It's fairly long and doesn't
|
||||||
// make too much sense to display.
|
// make too much sense to display.
|
||||||
if alog.ResourceType != database.ResourceTypeGitSshKey {
|
if alog.ResourceType != database.ResourceTypeGitSshKey {
|
||||||
@@ -302,8 +288,6 @@ func resourceTypeFromString(resourceTypeString string) string {
|
|||||||
return resourceTypeString
|
return resourceTypeString
|
||||||
case codersdk.ResourceTypeWorkspace:
|
case codersdk.ResourceTypeWorkspace:
|
||||||
return resourceTypeString
|
return resourceTypeString
|
||||||
case codersdk.ResourceTypeWorkspaceBuild:
|
|
||||||
return resourceTypeString
|
|
||||||
case codersdk.ResourceTypeGitSSHKey:
|
case codersdk.ResourceTypeGitSSHKey:
|
||||||
return resourceTypeString
|
return resourceTypeString
|
||||||
case codersdk.ResourceTypeAPIKey:
|
case codersdk.ResourceTypeAPIKey:
|
||||||
|
|||||||
@@ -15,7 +15,6 @@ type Auditable interface {
|
|||||||
database.TemplateVersion |
|
database.TemplateVersion |
|
||||||
database.User |
|
database.User |
|
||||||
database.Workspace |
|
database.Workspace |
|
||||||
database.WorkspaceBuild |
|
|
||||||
database.GitSSHKey |
|
database.GitSSHKey |
|
||||||
database.Group
|
database.Group
|
||||||
}
|
}
|
||||||
|
|||||||
+3
-15
@@ -20,9 +20,8 @@ type RequestParams struct {
|
|||||||
Audit Auditor
|
Audit Auditor
|
||||||
Log slog.Logger
|
Log slog.Logger
|
||||||
|
|
||||||
Request *http.Request
|
Request *http.Request
|
||||||
Action database.AuditAction
|
Action database.AuditAction
|
||||||
AdditionalFields json.RawMessage
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type Request[T Auditable] struct {
|
type Request[T Auditable] struct {
|
||||||
@@ -44,9 +43,6 @@ func ResourceTarget[T Auditable](tgt T) string {
|
|||||||
return typed.Username
|
return typed.Username
|
||||||
case database.Workspace:
|
case database.Workspace:
|
||||||
return typed.Name
|
return typed.Name
|
||||||
case database.WorkspaceBuild:
|
|
||||||
// this isn't used
|
|
||||||
return string(typed.BuildNumber)
|
|
||||||
case database.GitSSHKey:
|
case database.GitSSHKey:
|
||||||
return typed.PublicKey
|
return typed.PublicKey
|
||||||
case database.Group:
|
case database.Group:
|
||||||
@@ -68,8 +64,6 @@ func ResourceID[T Auditable](tgt T) uuid.UUID {
|
|||||||
return typed.ID
|
return typed.ID
|
||||||
case database.Workspace:
|
case database.Workspace:
|
||||||
return typed.ID
|
return typed.ID
|
||||||
case database.WorkspaceBuild:
|
|
||||||
return typed.ID
|
|
||||||
case database.GitSSHKey:
|
case database.GitSSHKey:
|
||||||
return typed.UserID
|
return typed.UserID
|
||||||
case database.Group:
|
case database.Group:
|
||||||
@@ -91,8 +85,6 @@ func ResourceType[T Auditable](tgt T) database.ResourceType {
|
|||||||
return database.ResourceTypeUser
|
return database.ResourceTypeUser
|
||||||
case database.Workspace:
|
case database.Workspace:
|
||||||
return database.ResourceTypeWorkspace
|
return database.ResourceTypeWorkspace
|
||||||
case database.WorkspaceBuild:
|
|
||||||
return database.ResourceTypeWorkspaceBuild
|
|
||||||
case database.GitSSHKey:
|
case database.GitSSHKey:
|
||||||
return database.ResourceTypeGitSshKey
|
return database.ResourceTypeGitSshKey
|
||||||
case database.Group:
|
case database.Group:
|
||||||
@@ -137,10 +129,6 @@ func InitRequest[T Auditable](w http.ResponseWriter, p *RequestParams) (*Request
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if p.AdditionalFields == nil {
|
|
||||||
p.AdditionalFields = json.RawMessage("{}")
|
|
||||||
}
|
|
||||||
|
|
||||||
ip := parseIP(p.Request.RemoteAddr)
|
ip := parseIP(p.Request.RemoteAddr)
|
||||||
err := p.Audit.Export(ctx, database.AuditLog{
|
err := p.Audit.Export(ctx, database.AuditLog{
|
||||||
ID: uuid.New(),
|
ID: uuid.New(),
|
||||||
@@ -155,7 +143,7 @@ func InitRequest[T Auditable](w http.ResponseWriter, p *RequestParams) (*Request
|
|||||||
Diff: diffRaw,
|
Diff: diffRaw,
|
||||||
StatusCode: int32(sw.Status),
|
StatusCode: int32(sw.Status),
|
||||||
RequestID: httpmw.RequestID(p.Request),
|
RequestID: httpmw.RequestID(p.Request),
|
||||||
AdditionalFields: p.AdditionalFields,
|
AdditionalFields: json.RawMessage("{}"),
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
p.Log.Error(logCtx, "export audit log", slog.Error(err))
|
p.Log.Error(logCtx, "export audit log", slog.Error(err))
|
||||||
|
|||||||
Generated
+2
-5
@@ -14,9 +14,7 @@ CREATE TYPE app_sharing_level AS ENUM (
|
|||||||
CREATE TYPE audit_action AS ENUM (
|
CREATE TYPE audit_action AS ENUM (
|
||||||
'create',
|
'create',
|
||||||
'write',
|
'write',
|
||||||
'delete',
|
'delete'
|
||||||
'start',
|
|
||||||
'stop'
|
|
||||||
);
|
);
|
||||||
|
|
||||||
CREATE TYPE build_reason AS ENUM (
|
CREATE TYPE build_reason AS ENUM (
|
||||||
@@ -90,8 +88,7 @@ CREATE TYPE resource_type AS ENUM (
|
|||||||
'workspace',
|
'workspace',
|
||||||
'git_ssh_key',
|
'git_ssh_key',
|
||||||
'api_key',
|
'api_key',
|
||||||
'group',
|
'group'
|
||||||
'workspace_build'
|
|
||||||
);
|
);
|
||||||
|
|
||||||
CREATE TYPE user_status AS ENUM (
|
CREATE TYPE user_status AS ENUM (
|
||||||
|
|||||||
@@ -1,2 +0,0 @@
|
|||||||
-- It's not possible to drop enum values from enum types, so the UP has "IF NOT
|
|
||||||
-- EXISTS".
|
|
||||||
@@ -1,4 +0,0 @@
|
|||||||
ALTER TYPE audit_action ADD VALUE IF NOT EXISTS 'start';
|
|
||||||
ALTER TYPE audit_action ADD VALUE IF NOT EXISTS 'stop';
|
|
||||||
|
|
||||||
ALTER TYPE resource_type ADD VALUE IF NOT EXISTS 'workspace_build';
|
|
||||||
@@ -60,8 +60,6 @@ const (
|
|||||||
AuditActionCreate AuditAction = "create"
|
AuditActionCreate AuditAction = "create"
|
||||||
AuditActionWrite AuditAction = "write"
|
AuditActionWrite AuditAction = "write"
|
||||||
AuditActionDelete AuditAction = "delete"
|
AuditActionDelete AuditAction = "delete"
|
||||||
AuditActionStart AuditAction = "start"
|
|
||||||
AuditActionStop AuditAction = "stop"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func (e *AuditAction) Scan(src interface{}) error {
|
func (e *AuditAction) Scan(src interface{}) error {
|
||||||
@@ -304,7 +302,6 @@ const (
|
|||||||
ResourceTypeGitSshKey ResourceType = "git_ssh_key"
|
ResourceTypeGitSshKey ResourceType = "git_ssh_key"
|
||||||
ResourceTypeApiKey ResourceType = "api_key"
|
ResourceTypeApiKey ResourceType = "api_key"
|
||||||
ResourceTypeGroup ResourceType = "group"
|
ResourceTypeGroup ResourceType = "group"
|
||||||
ResourceTypeWorkspaceBuild ResourceType = "workspace_build"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func (e *ResourceType) Scan(src interface{}) error {
|
func (e *ResourceType) Scan(src interface{}) error {
|
||||||
|
|||||||
+13
-44
@@ -278,59 +278,28 @@ func (api *API) postWorkspaceBuilds(rw http.ResponseWriter, r *http.Request) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
auditor := api.Auditor.Load()
|
// we only want to create audit logs for delete builds right now
|
||||||
|
|
||||||
// if user deletes a workspace, audit the workspace
|
|
||||||
if action == rbac.ActionDelete {
|
if action == rbac.ActionDelete {
|
||||||
aReq, commitAudit := audit.InitRequest[database.Workspace](rw, &audit.RequestParams{
|
var (
|
||||||
Audit: *auditor,
|
auditor = api.Auditor.Load()
|
||||||
Log: api.Logger,
|
aReq, commitAudit = audit.InitRequest[database.Workspace](rw, &audit.RequestParams{
|
||||||
Request: r,
|
Audit: *auditor,
|
||||||
Action: database.AuditActionDelete,
|
Log: api.Logger,
|
||||||
})
|
Request: r,
|
||||||
|
Action: database.AuditActionDelete,
|
||||||
|
})
|
||||||
|
)
|
||||||
|
|
||||||
defer commitAudit()
|
defer commitAudit()
|
||||||
aReq.Old = workspace
|
aReq.Old = workspace
|
||||||
}
|
}
|
||||||
|
|
||||||
latestBuild, latestBuildErr := api.Database.GetLatestWorkspaceBuildByWorkspaceID(ctx, workspace.ID)
|
|
||||||
|
|
||||||
// if a user starts/stops a workspace, audit the workspace build
|
|
||||||
if action == rbac.ActionUpdate {
|
|
||||||
var auditAction database.AuditAction
|
|
||||||
if createBuild.Transition == codersdk.WorkspaceTransitionStart {
|
|
||||||
auditAction = database.AuditActionStart
|
|
||||||
} else if createBuild.Transition == codersdk.WorkspaceTransitionStop {
|
|
||||||
auditAction = database.AuditActionStop
|
|
||||||
} else {
|
|
||||||
auditAction = database.AuditActionWrite
|
|
||||||
}
|
|
||||||
|
|
||||||
// We pass the workspace name to the Auditor so that it
|
|
||||||
// can form a friendly string for the user.
|
|
||||||
workspaceResourceInfo := map[string]string{
|
|
||||||
"workspaceName": workspace.Name,
|
|
||||||
}
|
|
||||||
|
|
||||||
wriBytes, _ := json.Marshal(workspaceResourceInfo)
|
|
||||||
|
|
||||||
aReq, commitAudit := audit.InitRequest[database.WorkspaceBuild](rw, &audit.RequestParams{
|
|
||||||
Audit: *auditor,
|
|
||||||
Log: api.Logger,
|
|
||||||
Request: r,
|
|
||||||
Action: auditAction,
|
|
||||||
AdditionalFields: wriBytes,
|
|
||||||
})
|
|
||||||
|
|
||||||
defer commitAudit()
|
|
||||||
aReq.Old = latestBuild
|
|
||||||
}
|
|
||||||
|
|
||||||
if createBuild.TemplateVersionID == uuid.Nil {
|
if createBuild.TemplateVersionID == uuid.Nil {
|
||||||
if latestBuildErr != nil {
|
latestBuild, err := api.Database.GetLatestWorkspaceBuildByWorkspaceID(ctx, workspace.ID)
|
||||||
|
if err != nil {
|
||||||
httpapi.Write(ctx, rw, http.StatusInternalServerError, codersdk.Response{
|
httpapi.Write(ctx, rw, http.StatusInternalServerError, codersdk.Response{
|
||||||
Message: "Internal error fetching the latest workspace build.",
|
Message: "Internal error fetching the latest workspace build.",
|
||||||
Detail: latestBuildErr.Error(),
|
Detail: err.Error(),
|
||||||
})
|
})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -579,6 +579,6 @@ func TestWorkspaceBuildStatus(t *testing.T) {
|
|||||||
require.EqualValues(t, codersdk.WorkspaceStatusDeleted, workspace.LatestBuild.Status)
|
require.EqualValues(t, codersdk.WorkspaceStatusDeleted, workspace.LatestBuild.Status)
|
||||||
|
|
||||||
// assert an audit log has been created for deletion
|
// assert an audit log has been created for deletion
|
||||||
require.Len(t, auditor.AuditLogs, 7)
|
require.Len(t, auditor.AuditLogs, 5)
|
||||||
assert.Equal(t, database.AuditActionDelete, auditor.AuditLogs[6].Action)
|
assert.Equal(t, database.AuditActionDelete, auditor.AuditLogs[4].Action)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,7 +19,6 @@ const (
|
|||||||
ResourceTypeTemplateVersion ResourceType = "template_version"
|
ResourceTypeTemplateVersion ResourceType = "template_version"
|
||||||
ResourceTypeUser ResourceType = "user"
|
ResourceTypeUser ResourceType = "user"
|
||||||
ResourceTypeWorkspace ResourceType = "workspace"
|
ResourceTypeWorkspace ResourceType = "workspace"
|
||||||
ResourceTypeWorkspaceBuild ResourceType = "workspace_build"
|
|
||||||
ResourceTypeGitSSHKey ResourceType = "git_ssh_key"
|
ResourceTypeGitSSHKey ResourceType = "git_ssh_key"
|
||||||
ResourceTypeAPIKey ResourceType = "api_key"
|
ResourceTypeAPIKey ResourceType = "api_key"
|
||||||
ResourceTypeGroup ResourceType = "group"
|
ResourceTypeGroup ResourceType = "group"
|
||||||
@@ -37,8 +36,6 @@ func (r ResourceType) FriendlyString() string {
|
|||||||
return "user"
|
return "user"
|
||||||
case ResourceTypeWorkspace:
|
case ResourceTypeWorkspace:
|
||||||
return "workspace"
|
return "workspace"
|
||||||
case ResourceTypeWorkspaceBuild:
|
|
||||||
return "workspace build"
|
|
||||||
case ResourceTypeGitSSHKey:
|
case ResourceTypeGitSSHKey:
|
||||||
return "git ssh key"
|
return "git ssh key"
|
||||||
case ResourceTypeAPIKey:
|
case ResourceTypeAPIKey:
|
||||||
@@ -56,8 +53,6 @@ const (
|
|||||||
AuditActionCreate AuditAction = "create"
|
AuditActionCreate AuditAction = "create"
|
||||||
AuditActionWrite AuditAction = "write"
|
AuditActionWrite AuditAction = "write"
|
||||||
AuditActionDelete AuditAction = "delete"
|
AuditActionDelete AuditAction = "delete"
|
||||||
AuditActionStart AuditAction = "start"
|
|
||||||
AuditActionStop AuditAction = "stop"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func (a AuditAction) FriendlyString() string {
|
func (a AuditAction) FriendlyString() string {
|
||||||
@@ -68,10 +63,6 @@ func (a AuditAction) FriendlyString() string {
|
|||||||
return "updated"
|
return "updated"
|
||||||
case AuditActionDelete:
|
case AuditActionDelete:
|
||||||
return "deleted"
|
return "deleted"
|
||||||
case AuditActionStart:
|
|
||||||
return "started"
|
|
||||||
case AuditActionStop:
|
|
||||||
return "stopped"
|
|
||||||
default:
|
default:
|
||||||
return "unknown"
|
return "unknown"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -103,21 +103,6 @@ var AuditableResources = auditMap(map[any]map[string]Action{
|
|||||||
"ttl": ActionTrack,
|
"ttl": ActionTrack,
|
||||||
"last_used_at": ActionIgnore,
|
"last_used_at": ActionIgnore,
|
||||||
},
|
},
|
||||||
// We don't show any diff for the WorkspaceBuild resource
|
|
||||||
&database.WorkspaceBuild{}: {
|
|
||||||
"id": ActionIgnore,
|
|
||||||
"created_at": ActionIgnore,
|
|
||||||
"updated_at": ActionIgnore,
|
|
||||||
"workspace_id": ActionIgnore,
|
|
||||||
"template_version_id": ActionIgnore,
|
|
||||||
"build_number": ActionIgnore,
|
|
||||||
"transition": ActionIgnore,
|
|
||||||
"initiator_id": ActionIgnore,
|
|
||||||
"provisioner_state": ActionIgnore,
|
|
||||||
"job_id": ActionIgnore,
|
|
||||||
"deadline": ActionIgnore,
|
|
||||||
"reason": ActionIgnore,
|
|
||||||
},
|
|
||||||
&database.Group{}: {
|
&database.Group{}: {
|
||||||
"id": ActionTrack,
|
"id": ActionTrack,
|
||||||
"name": ActionTrack,
|
"name": ActionTrack,
|
||||||
|
|||||||
@@ -915,7 +915,7 @@ export interface WorkspacesRequest extends Pagination {
|
|||||||
export type APIKeyScope = "all" | "application_connect"
|
export type APIKeyScope = "all" | "application_connect"
|
||||||
|
|
||||||
// From codersdk/audit.go
|
// From codersdk/audit.go
|
||||||
export type AuditAction = "create" | "delete" | "start" | "stop" | "write"
|
export type AuditAction = "create" | "delete" | "write"
|
||||||
|
|
||||||
// From codersdk/workspacebuilds.go
|
// From codersdk/workspacebuilds.go
|
||||||
export type BuildReason = "autostart" | "autostop" | "initiator"
|
export type BuildReason = "autostart" | "autostop" | "initiator"
|
||||||
@@ -975,7 +975,6 @@ export type ResourceType =
|
|||||||
| "template_version"
|
| "template_version"
|
||||||
| "user"
|
| "user"
|
||||||
| "workspace"
|
| "workspace"
|
||||||
| "workspace_build"
|
|
||||||
|
|
||||||
// From codersdk/sse.go
|
// From codersdk/sse.go
|
||||||
export type ServerSentEventType = "data" | "error" | "ping"
|
export type ServerSentEventType = "data" | "error" | "ping"
|
||||||
|
|||||||
@@ -130,11 +130,13 @@ export const AuditLogRow: React.FC<AuditLogRowProps> = ({
|
|||||||
</Stack>
|
</Stack>
|
||||||
</Stack>
|
</Stack>
|
||||||
|
|
||||||
{shouldDisplayDiff ? (
|
<div
|
||||||
<div> {isDiffOpen ? <CloseDropdown /> : <OpenDropdown />}</div>
|
className={
|
||||||
) : (
|
shouldDisplayDiff ? undefined : styles.disabledDropdownIcon
|
||||||
<div className={styles.columnWithoutDiff}></div>
|
}
|
||||||
)}
|
>
|
||||||
|
{isDiffOpen ? <CloseDropdown /> : <OpenDropdown />}
|
||||||
|
</div>
|
||||||
</Stack>
|
</Stack>
|
||||||
|
|
||||||
{shouldDisplayDiff && (
|
{shouldDisplayDiff && (
|
||||||
@@ -188,8 +190,8 @@ const useStyles = makeStyles((theme) => ({
|
|||||||
color: theme.palette.text.secondary,
|
color: theme.palette.text.secondary,
|
||||||
whiteSpace: "nowrap",
|
whiteSpace: "nowrap",
|
||||||
},
|
},
|
||||||
// offset the absence of the arrow icon on diff-less logs
|
|
||||||
columnWithoutDiff: {
|
disabledDropdownIcon: {
|
||||||
marginLeft: "24px",
|
opacity: 0.5,
|
||||||
},
|
},
|
||||||
}))
|
}))
|
||||||
|
|||||||
Reference in New Issue
Block a user