From 6c73cf390083acacc2b3694356f37e30f8ea0cbc Mon Sep 17 00:00:00 2001 From: Kira Pilot Date: Mon, 16 Jan 2023 17:48:33 +0000 Subject: [PATCH 01/10] added AuditableGroup type --- coderd/apidoc/docs.go | 2 +- coderd/apidoc/swagger.json | 2 +- coderd/audit.go | 2 ++ coderd/audit/diff.go | 5 +++-- coderd/audit/request.go | 10 +++++----- coderd/database/modelmethods.go | 27 +++++++++++++++++++++++++++ codersdk/audit.go | 8 +++++++- docs/api/schemas.md | 2 +- enterprise/audit/diff.go | 5 +++++ enterprise/audit/table.go | 22 +++++++++++++++------- enterprise/coderd/groups.go | 30 ++++++++++++++++++++---------- site/src/api/typesGenerated.ts | 2 +- 12 files changed, 88 insertions(+), 29 deletions(-) diff --git a/coderd/apidoc/docs.go b/coderd/apidoc/docs.go index 3281865849f36..6a64145e22019 100644 --- a/coderd/apidoc/docs.go +++ b/coderd/apidoc/docs.go @@ -6858,7 +6858,7 @@ const docTemplate = `{ "workspace_build", "git_ssh_key", "api_key", - "group" + "auditable_group" ], "x-enum-varnames": [ "ResourceTypeOrganization", diff --git a/coderd/apidoc/swagger.json b/coderd/apidoc/swagger.json index d97e53fe6be57..8e10203d553c4 100644 --- a/coderd/apidoc/swagger.json +++ b/coderd/apidoc/swagger.json @@ -6145,7 +6145,7 @@ "workspace_build", "git_ssh_key", "api_key", - "group" + "auditable_group" ], "x-enum-varnames": [ "ResourceTypeOrganization", diff --git a/coderd/audit.go b/coderd/audit.go index d8b764c8ea145..185776c2ba1f3 100644 --- a/coderd/audit.go +++ b/coderd/audit.go @@ -464,6 +464,8 @@ func resourceTypeFromString(resourceTypeString string) string { return resourceTypeString case codersdk.ResourceTypeAPIKey: return resourceTypeString + case codersdk.ResourceTypeGroup: + return resourceTypeString } return "" } diff --git a/coderd/audit/diff.go b/coderd/audit/diff.go index 44d5014fb03a1..d01e3d890cfde 100644 --- a/coderd/audit/diff.go +++ b/coderd/audit/diff.go @@ -16,8 +16,9 @@ type Auditable interface { database.User | database.Workspace | database.GitSSHKey | - database.Group | - database.WorkspaceBuild + // database.Group | + database.WorkspaceBuild | + database.AuditableGroup } // Map is a map of changed fields in an audited resource. It maps field names to diff --git a/coderd/audit/request.go b/coderd/audit/request.go index 2bbd93c371d08..4a389836b9fde 100644 --- a/coderd/audit/request.go +++ b/coderd/audit/request.go @@ -64,8 +64,8 @@ func ResourceTarget[T Auditable](tgt T) string { return "" case database.GitSSHKey: return typed.PublicKey - case database.Group: - return typed.Name + case database.AuditableGroup: + return typed.Group.Name default: panic(fmt.Sprintf("unknown resource %T", tgt)) } @@ -87,8 +87,8 @@ func ResourceID[T Auditable](tgt T) uuid.UUID { return typed.ID case database.GitSSHKey: return typed.UserID - case database.Group: - return typed.ID + case database.AuditableGroup: + return typed.Group.ID default: panic(fmt.Sprintf("unknown resource %T", tgt)) } @@ -110,7 +110,7 @@ func ResourceType[T Auditable](tgt T) database.ResourceType { return database.ResourceTypeWorkspaceBuild case database.GitSSHKey: return database.ResourceTypeGitSshKey - case database.Group: + case database.AuditableGroup: return database.ResourceTypeGroup default: panic(fmt.Sprintf("unknown resource %T", tgt)) diff --git a/coderd/database/modelmethods.go b/coderd/database/modelmethods.go index 5257bec1c3a2f..8dd7a48807e9a 100644 --- a/coderd/database/modelmethods.go +++ b/coderd/database/modelmethods.go @@ -1,9 +1,36 @@ package database import ( + "sort" + "github.com/coder/coder/coderd/rbac" ) +type AuditableGroup struct { + Group + Members []GroupMember +} + +func (g Group) Auditable(users []User) AuditableGroup { + members := make([]GroupMember, 0, len(users)) + for _, u := range users { + members = append(members, GroupMember{ + UserID: u.ID, + GroupID: g.ID, + }) + } + + // we sort to ensure the diff order enterprise/audit/diff.go:18 + sort.Slice(members, func(i, j int) bool { + return members[i].UserID.String() < members[j].UserID.String() + }) + + return AuditableGroup{ + Group: g, + Members: members, + } +} + const AllUsersGroup = "Everyone" func (s APIKeyScope) ToRBAC() rbac.Scope { diff --git a/codersdk/audit.go b/codersdk/audit.go index 4ae98466c3798..19c3b41d0259b 100644 --- a/codersdk/audit.go +++ b/codersdk/audit.go @@ -22,7 +22,13 @@ const ( ResourceTypeWorkspaceBuild ResourceType = "workspace_build" ResourceTypeGitSSHKey ResourceType = "git_ssh_key" ResourceTypeAPIKey ResourceType = "api_key" - ResourceTypeGroup ResourceType = "group" + // ResourceTypeGroup ResourceType = "group" + + // ResourceTypeAuditableGroup ResourceType = "auditable_group" // does not work + // ResourceTypeAuditableGroup ResourceType = "group" // does not work + + ResourceTypeGroup ResourceType = "auditable_group" + // ResourceTypeGroup ResourceType = "group" ) func (r ResourceType) FriendlyString() string { diff --git a/docs/api/schemas.md b/docs/api/schemas.md index cb36333e077cd..30b34e43b11fb 100644 --- a/docs/api/schemas.md +++ b/docs/api/schemas.md @@ -3607,7 +3607,7 @@ Parameter represents a set value for the scope. | `workspace_build` | | `git_ssh_key` | | `api_key` | -| `group` | +| `auditable_group` | ## codersdk.Response diff --git a/enterprise/audit/diff.go b/enterprise/audit/diff.go index 05d46499b525a..5fc9c4f3c3078 100644 --- a/enterprise/audit/diff.go +++ b/enterprise/audit/diff.go @@ -27,6 +27,8 @@ func diffValues(left, right any, table Table) audit.Map { diffKey = table[structName(rightT)] ) + fmt.Println("DIFF KEY", diffKey) + if diffKey == nil { panic(fmt.Sprintf("dev error: type %q (type %T) attempted audit but not auditable", rightT.Name(), right)) } @@ -45,6 +47,9 @@ func diffValues(left, right any, table Table) audit.Map { diffName = rightT.Field(i).Tag.Get("json") ) + fmt.Println("rightT.Field(i)", rightT, rightT.Field(i), rightT.Field(i).Tag.Get("json")) + + fmt.Println("DIFF NAME", diffName) atype, ok := diffKey[diffName] if !ok { diff --git a/enterprise/audit/table.go b/enterprise/audit/table.go index 342a80f2c7932..05791b5d6db4d 100644 --- a/enterprise/audit/table.go +++ b/enterprise/audit/table.go @@ -105,13 +105,13 @@ var AuditableResources = auditMap(map[any]map[string]Action{ "ttl": ActionTrack, "last_used_at": ActionIgnore, }, - &database.Group{}: { - "id": ActionTrack, - "name": ActionTrack, - "organization_id": ActionIgnore, // Never changes. - "avatar_url": ActionTrack, - "quota_allowance": ActionTrack, - }, + // &database.Group{}: { + // "id": ActionTrack, + // "name": ActionTrack, + // "organization_id": ActionIgnore, // Never changes. + // "avatar_url": ActionTrack, + // "quota_allowance": ActionTrack, + // }, // We don't show any diff for the WorkspaceBuild resource &database.WorkspaceBuild{}: { "id": ActionIgnore, @@ -128,6 +128,14 @@ var AuditableResources = auditMap(map[any]map[string]Action{ "reason": ActionIgnore, "daily_cost": ActionIgnore, }, + &database.AuditableGroup{}: { + "id": ActionTrack, + "name": ActionTrack, + "organization_id": ActionIgnore, // Never changes. + "avatar_url": ActionTrack, + "quota_allowance": ActionTrack, + "members": ActionTrack, + }, }) // auditMap converts a map of struct pointers to a map of struct names as diff --git a/enterprise/coderd/groups.go b/enterprise/coderd/groups.go index 42148aa1cb405..03e2e6b92703e 100644 --- a/enterprise/coderd/groups.go +++ b/enterprise/coderd/groups.go @@ -32,7 +32,7 @@ func (api *API) postGroupByOrganization(rw http.ResponseWriter, r *http.Request) ctx = r.Context() org = httpmw.OrganizationParam(r) auditor = api.AGPL.Auditor.Load() - aReq, commitAudit = audit.InitRequest[database.Group](rw, &audit.RequestParams{ + aReq, commitAudit = audit.InitRequest[database.AuditableGroup](rw, &audit.RequestParams{ Audit: *auditor, Log: api.Logger, Request: r, @@ -75,7 +75,9 @@ func (api *API) postGroupByOrganization(rw http.ResponseWriter, r *http.Request) httpapi.InternalServerError(rw, err) return } - aReq.New = group + + var emptyUsers []database.User + aReq.New = group.Auditable(emptyUsers) httpapi.Write(ctx, rw, http.StatusCreated, convertGroup(group, nil)) } @@ -93,7 +95,7 @@ func (api *API) patchGroup(rw http.ResponseWriter, r *http.Request) { ctx = r.Context() group = httpmw.GroupParam(r) auditor = api.AGPL.Auditor.Load() - aReq, commitAudit = audit.InitRequest[database.Group](rw, &audit.RequestParams{ + aReq, commitAudit = audit.InitRequest[database.AuditableGroup](rw, &audit.RequestParams{ Audit: *auditor, Log: api.Logger, Request: r, @@ -101,7 +103,14 @@ func (api *API) patchGroup(rw http.ResponseWriter, r *http.Request) { }) ) defer commitAudit() - aReq.Old = group + + currentMembers, currentMembersErr := api.Database.GetGroupMembers(ctx, group.ID) + if currentMembersErr != nil { + httpapi.InternalServerError(rw, currentMembersErr) + return + } + + aReq.Old = group.Auditable(currentMembers) if !api.Authorize(r, rbac.ActionUpdate, group) { http.NotFound(rw, r) @@ -233,15 +242,15 @@ func (api *API) patchGroup(rw http.ResponseWriter, r *http.Request) { return } - members, err := api.Database.GetGroupMembers(ctx, group.ID) - if err != nil { + patchedMembers, patchedMembersErr := api.Database.GetGroupMembers(ctx, group.ID) + if patchedMembersErr != nil { httpapi.InternalServerError(rw, err) return } - aReq.New = group + aReq.New = group.Auditable(patchedMembers) - httpapi.Write(ctx, rw, http.StatusOK, convertGroup(group, members)) + httpapi.Write(ctx, rw, http.StatusOK, convertGroup(group, patchedMembers)) } // @Summary Delete group by name @@ -257,7 +266,7 @@ func (api *API) deleteGroup(rw http.ResponseWriter, r *http.Request) { ctx = r.Context() group = httpmw.GroupParam(r) auditor = api.AGPL.Auditor.Load() - aReq, commitAudit = audit.InitRequest[database.Group](rw, &audit.RequestParams{ + aReq, commitAudit = audit.InitRequest[database.AuditableGroup](rw, &audit.RequestParams{ Audit: *auditor, Log: api.Logger, Request: r, @@ -265,7 +274,8 @@ func (api *API) deleteGroup(rw http.ResponseWriter, r *http.Request) { }) ) defer commitAudit() - aReq.Old = group + var emptyUsers []database.User + aReq.Old = group.Auditable(emptyUsers) if !api.Authorize(r, rbac.ActionDelete, group) { httpapi.ResourceNotFound(rw) diff --git a/site/src/api/typesGenerated.ts b/site/src/api/typesGenerated.ts index 7570a7d9289de..2e5411c89905b 100644 --- a/site/src/api/typesGenerated.ts +++ b/site/src/api/typesGenerated.ts @@ -1075,8 +1075,8 @@ export type ProvisionerType = "echo" | "terraform" // From codersdk/audit.go export type ResourceType = | "api_key" + | "auditable_group" | "git_ssh_key" - | "group" | "organization" | "template" | "template_version" From 40842b9fc3bc0a489ebb8090c67f2252d83ba664 Mon Sep 17 00:00:00 2001 From: Kira Pilot Date: Tue, 17 Jan 2023 14:00:30 +0000 Subject: [PATCH 02/10] added json tags --- coderd/apidoc/docs.go | 2 +- coderd/apidoc/swagger.json | 2 +- coderd/audit.go | 2 +- coderd/database/modelmethods.go | 15 ++++++++++++--- codersdk/audit.go | 10 ++-------- 5 files changed, 17 insertions(+), 14 deletions(-) diff --git a/coderd/apidoc/docs.go b/coderd/apidoc/docs.go index 6a64145e22019..e51d4ab77a15c 100644 --- a/coderd/apidoc/docs.go +++ b/coderd/apidoc/docs.go @@ -6869,7 +6869,7 @@ const docTemplate = `{ "ResourceTypeWorkspaceBuild", "ResourceTypeGitSSHKey", "ResourceTypeAPIKey", - "ResourceTypeGroup" + "ResourceTypeAuditableGroup" ] }, "codersdk.Response": { diff --git a/coderd/apidoc/swagger.json b/coderd/apidoc/swagger.json index 8e10203d553c4..063f98ba3b73e 100644 --- a/coderd/apidoc/swagger.json +++ b/coderd/apidoc/swagger.json @@ -6156,7 +6156,7 @@ "ResourceTypeWorkspaceBuild", "ResourceTypeGitSSHKey", "ResourceTypeAPIKey", - "ResourceTypeGroup" + "ResourceTypeAuditableGroup" ] }, "codersdk.Response": { diff --git a/coderd/audit.go b/coderd/audit.go index 185776c2ba1f3..61287d551095d 100644 --- a/coderd/audit.go +++ b/coderd/audit.go @@ -464,7 +464,7 @@ func resourceTypeFromString(resourceTypeString string) string { return resourceTypeString case codersdk.ResourceTypeAPIKey: return resourceTypeString - case codersdk.ResourceTypeGroup: + case codersdk.ResourceTypeAuditableGroup: return resourceTypeString } return "" diff --git a/coderd/database/modelmethods.go b/coderd/database/modelmethods.go index 8dd7a48807e9a..8a923b07ed73d 100644 --- a/coderd/database/modelmethods.go +++ b/coderd/database/modelmethods.go @@ -7,9 +7,18 @@ import ( ) type AuditableGroup struct { - Group - Members []GroupMember -} + Group Group `json:"group"` + Members []GroupMember `json:"members"` +} + +// type AuditableGroup struct { +// ID Group.ID `json:group_id` +// Name +// OrganizationId +// AvatarUrl +// QuotaAllowance +// Members []GroupMember `json:"members"` +// } func (g Group) Auditable(users []User) AuditableGroup { members := make([]GroupMember, 0, len(users)) diff --git a/codersdk/audit.go b/codersdk/audit.go index 19c3b41d0259b..fdfb799d9efe0 100644 --- a/codersdk/audit.go +++ b/codersdk/audit.go @@ -22,13 +22,7 @@ const ( ResourceTypeWorkspaceBuild ResourceType = "workspace_build" ResourceTypeGitSSHKey ResourceType = "git_ssh_key" ResourceTypeAPIKey ResourceType = "api_key" - // ResourceTypeGroup ResourceType = "group" - - // ResourceTypeAuditableGroup ResourceType = "auditable_group" // does not work - // ResourceTypeAuditableGroup ResourceType = "group" // does not work - - ResourceTypeGroup ResourceType = "auditable_group" - // ResourceTypeGroup ResourceType = "group" + ResourceTypeAuditableGroup ResourceType = "auditable_group" ) func (r ResourceType) FriendlyString() string { @@ -51,7 +45,7 @@ func (r ResourceType) FriendlyString() string { return "git ssh key" case ResourceTypeAPIKey: return "api key" - case ResourceTypeGroup: + case ResourceTypeAuditableGroup: return "group" default: return "unknown" From f4e58019622c41dd26ae947145fa4c456fcafff8 Mon Sep 17 00:00:00 2001 From: Kira Pilot Date: Tue, 17 Jan 2023 15:35:53 +0000 Subject: [PATCH 03/10] Anonymizing gGroup struct --- coderd/audit/diff.go | 1 - coderd/database/modelmethods.go | 11 +---------- enterprise/audit/diff.go | 5 ----- enterprise/audit/table.go | 7 ------- 4 files changed, 1 insertion(+), 23 deletions(-) diff --git a/coderd/audit/diff.go b/coderd/audit/diff.go index d01e3d890cfde..9dc1b3ad8beef 100644 --- a/coderd/audit/diff.go +++ b/coderd/audit/diff.go @@ -16,7 +16,6 @@ type Auditable interface { database.User | database.Workspace | database.GitSSHKey | - // database.Group | database.WorkspaceBuild | database.AuditableGroup } diff --git a/coderd/database/modelmethods.go b/coderd/database/modelmethods.go index 8a923b07ed73d..54fe7bc3ed2bb 100644 --- a/coderd/database/modelmethods.go +++ b/coderd/database/modelmethods.go @@ -7,19 +7,10 @@ import ( ) type AuditableGroup struct { - Group Group `json:"group"` + Group Members []GroupMember `json:"members"` } -// type AuditableGroup struct { -// ID Group.ID `json:group_id` -// Name -// OrganizationId -// AvatarUrl -// QuotaAllowance -// Members []GroupMember `json:"members"` -// } - func (g Group) Auditable(users []User) AuditableGroup { members := make([]GroupMember, 0, len(users)) for _, u := range users { diff --git a/enterprise/audit/diff.go b/enterprise/audit/diff.go index 5fc9c4f3c3078..05d46499b525a 100644 --- a/enterprise/audit/diff.go +++ b/enterprise/audit/diff.go @@ -27,8 +27,6 @@ func diffValues(left, right any, table Table) audit.Map { diffKey = table[structName(rightT)] ) - fmt.Println("DIFF KEY", diffKey) - if diffKey == nil { panic(fmt.Sprintf("dev error: type %q (type %T) attempted audit but not auditable", rightT.Name(), right)) } @@ -47,9 +45,6 @@ func diffValues(left, right any, table Table) audit.Map { diffName = rightT.Field(i).Tag.Get("json") ) - fmt.Println("rightT.Field(i)", rightT, rightT.Field(i), rightT.Field(i).Tag.Get("json")) - - fmt.Println("DIFF NAME", diffName) atype, ok := diffKey[diffName] if !ok { diff --git a/enterprise/audit/table.go b/enterprise/audit/table.go index 05791b5d6db4d..cf1f08b64727b 100644 --- a/enterprise/audit/table.go +++ b/enterprise/audit/table.go @@ -105,13 +105,6 @@ var AuditableResources = auditMap(map[any]map[string]Action{ "ttl": ActionTrack, "last_used_at": ActionIgnore, }, - // &database.Group{}: { - // "id": ActionTrack, - // "name": ActionTrack, - // "organization_id": ActionIgnore, // Never changes. - // "avatar_url": ActionTrack, - // "quota_allowance": ActionTrack, - // }, // We don't show any diff for the WorkspaceBuild resource &database.WorkspaceBuild{}: { "id": ActionIgnore, From c478e9a4c1af6ac756652a676f6732b22a3e6282 Mon Sep 17 00:00:00 2001 From: Kira Pilot Date: Tue, 17 Jan 2023 20:08:12 +0000 Subject: [PATCH 04/10] adding support on the FE for nested group diffs --- coderd/apidoc/docs.go | 4 ++-- coderd/apidoc/swagger.json | 4 ++-- coderd/audit.go | 2 +- codersdk/audit.go | 4 ++-- docs/api/schemas.md | 2 +- site/src/api/typesGenerated.ts | 2 +- .../components/AuditLogRow/AuditLogDiff.tsx | 5 ++--- .../components/AuditLogRow/AuditLogRow.tsx | 21 ++++++++++++++++++- 8 files changed, 31 insertions(+), 13 deletions(-) diff --git a/coderd/apidoc/docs.go b/coderd/apidoc/docs.go index e51d4ab77a15c..3281865849f36 100644 --- a/coderd/apidoc/docs.go +++ b/coderd/apidoc/docs.go @@ -6858,7 +6858,7 @@ const docTemplate = `{ "workspace_build", "git_ssh_key", "api_key", - "auditable_group" + "group" ], "x-enum-varnames": [ "ResourceTypeOrganization", @@ -6869,7 +6869,7 @@ const docTemplate = `{ "ResourceTypeWorkspaceBuild", "ResourceTypeGitSSHKey", "ResourceTypeAPIKey", - "ResourceTypeAuditableGroup" + "ResourceTypeGroup" ] }, "codersdk.Response": { diff --git a/coderd/apidoc/swagger.json b/coderd/apidoc/swagger.json index 063f98ba3b73e..d97e53fe6be57 100644 --- a/coderd/apidoc/swagger.json +++ b/coderd/apidoc/swagger.json @@ -6145,7 +6145,7 @@ "workspace_build", "git_ssh_key", "api_key", - "auditable_group" + "group" ], "x-enum-varnames": [ "ResourceTypeOrganization", @@ -6156,7 +6156,7 @@ "ResourceTypeWorkspaceBuild", "ResourceTypeGitSSHKey", "ResourceTypeAPIKey", - "ResourceTypeAuditableGroup" + "ResourceTypeGroup" ] }, "codersdk.Response": { diff --git a/coderd/audit.go b/coderd/audit.go index 61287d551095d..185776c2ba1f3 100644 --- a/coderd/audit.go +++ b/coderd/audit.go @@ -464,7 +464,7 @@ func resourceTypeFromString(resourceTypeString string) string { return resourceTypeString case codersdk.ResourceTypeAPIKey: return resourceTypeString - case codersdk.ResourceTypeAuditableGroup: + case codersdk.ResourceTypeGroup: return resourceTypeString } return "" diff --git a/codersdk/audit.go b/codersdk/audit.go index fdfb799d9efe0..4ae98466c3798 100644 --- a/codersdk/audit.go +++ b/codersdk/audit.go @@ -22,7 +22,7 @@ const ( ResourceTypeWorkspaceBuild ResourceType = "workspace_build" ResourceTypeGitSSHKey ResourceType = "git_ssh_key" ResourceTypeAPIKey ResourceType = "api_key" - ResourceTypeAuditableGroup ResourceType = "auditable_group" + ResourceTypeGroup ResourceType = "group" ) func (r ResourceType) FriendlyString() string { @@ -45,7 +45,7 @@ func (r ResourceType) FriendlyString() string { return "git ssh key" case ResourceTypeAPIKey: return "api key" - case ResourceTypeAuditableGroup: + case ResourceTypeGroup: return "group" default: return "unknown" diff --git a/docs/api/schemas.md b/docs/api/schemas.md index 30b34e43b11fb..cb36333e077cd 100644 --- a/docs/api/schemas.md +++ b/docs/api/schemas.md @@ -3607,7 +3607,7 @@ Parameter represents a set value for the scope. | `workspace_build` | | `git_ssh_key` | | `api_key` | -| `auditable_group` | +| `group` | ## codersdk.Response diff --git a/site/src/api/typesGenerated.ts b/site/src/api/typesGenerated.ts index 2e5411c89905b..7570a7d9289de 100644 --- a/site/src/api/typesGenerated.ts +++ b/site/src/api/typesGenerated.ts @@ -1075,8 +1075,8 @@ export type ProvisionerType = "echo" | "terraform" // From codersdk/audit.go export type ResourceType = | "api_key" - | "auditable_group" | "git_ssh_key" + | "group" | "organization" | "template" | "template_version" diff --git a/site/src/components/AuditLogRow/AuditLogDiff.tsx b/site/src/components/AuditLogRow/AuditLogDiff.tsx index dcd69aa45c991..ef3629e5b0c1f 100644 --- a/site/src/components/AuditLogRow/AuditLogDiff.tsx +++ b/site/src/components/AuditLogRow/AuditLogDiff.tsx @@ -3,6 +3,7 @@ import { AuditLog } from "api/typesGenerated" import { colors } from "theme/colors" import { MONOSPACE_FONT_FAMILY } from "theme/constants" import { combineClasses } from "util/combineClasses" +import { FC } from "react" const getDiffValue = (value: unknown): string => { if (typeof value === "string") { @@ -21,9 +22,7 @@ const getDiffValue = (value: unknown): string => { return value.toString() } -export const AuditLogDiff: React.FC<{ diff: AuditLog["diff"] }> = ({ - diff, -}) => { +export const AuditLogDiff: FC<{ diff: AuditLog["diff"] }> = ({ diff }) => { const styles = useStyles() const diffEntries = Object.entries(diff) diff --git a/site/src/components/AuditLogRow/AuditLogRow.tsx b/site/src/components/AuditLogRow/AuditLogRow.tsx index 991f0e5303bae..5ca97af19d7ae 100644 --- a/site/src/components/AuditLogRow/AuditLogRow.tsx +++ b/site/src/components/AuditLogRow/AuditLogRow.tsx @@ -49,6 +49,25 @@ export const AuditLogRow: React.FC = ({ ? `${browser.name} ${browser.version}` : t("auditLog:table.logRow.notAvailable") + let auditDiff = auditLog.diff + // groups have nested diffs (group members) + // so we overwrite the member diff such that + // only the user_id is shown. + if (auditLog.resource_type === "group") { + auditDiff = { + ...auditLog.diff, + members: { + old: auditLog.diff.members.old?.map( + (groupMember: any) => groupMember.user_id, + ), + new: auditLog.diff.members.new?.map( + (groupMember: any) => groupMember.user_id, + ), + secret: auditLog.diff.members.secret, + }, + } + } + const toggle = () => { if (shouldDisplayDiff) { setIsDiffOpen((v) => !v) @@ -153,7 +172,7 @@ export const AuditLogRow: React.FC = ({ {shouldDisplayDiff && ( - + )} From 25bbb3a72b2d0595d0465723f6906b45c17fcce4 Mon Sep 17 00:00:00 2001 From: Kira Pilot Date: Tue, 17 Jan 2023 20:39:20 +0000 Subject: [PATCH 05/10] added type for GroupMember --- site/src/components/AuditLogRow/AuditLogRow.tsx | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/site/src/components/AuditLogRow/AuditLogRow.tsx b/site/src/components/AuditLogRow/AuditLogRow.tsx index 5ca97af19d7ae..c98a91ca2c13c 100644 --- a/site/src/components/AuditLogRow/AuditLogRow.tsx +++ b/site/src/components/AuditLogRow/AuditLogRow.tsx @@ -35,6 +35,11 @@ export interface AuditLogRowProps { defaultIsDiffOpen?: boolean } +interface GroupMember { + user_id: string + group_id: string +} + export const AuditLogRow: React.FC = ({ auditLog, defaultIsDiffOpen = false, @@ -58,10 +63,10 @@ export const AuditLogRow: React.FC = ({ ...auditLog.diff, members: { old: auditLog.diff.members.old?.map( - (groupMember: any) => groupMember.user_id, + (groupMember: GroupMember) => groupMember.user_id, ), new: auditLog.diff.members.new?.map( - (groupMember: any) => groupMember.user_id, + (groupMember: GroupMember) => groupMember.user_id, ), secret: auditLog.diff.members.secret, }, From 6b5f13440f0fc09a45fa31a60c10ff8bc71d7fd9 Mon Sep 17 00:00:00 2001 From: Kira Pilot Date: Tue, 17 Jan 2023 16:10:52 -0500 Subject: [PATCH 06/10] Update coderd/database/modelmethods.go Co-authored-by: Steven Masley --- coderd/database/modelmethods.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/coderd/database/modelmethods.go b/coderd/database/modelmethods.go index 54fe7bc3ed2bb..1c3ee61f743cb 100644 --- a/coderd/database/modelmethods.go +++ b/coderd/database/modelmethods.go @@ -11,6 +11,8 @@ type AuditableGroup struct { Members []GroupMember `json:"members"` } +// Auditable returns an object that can be used in audit logs. +// Covers both group and group member changes. func (g Group) Auditable(users []User) AuditableGroup { members := make([]GroupMember, 0, len(users)) for _, u := range users { From d33b33064c5b3cde8796d680e2ae8f82e43b00bd Mon Sep 17 00:00:00 2001 From: Kira Pilot Date: Tue, 17 Jan 2023 16:11:12 -0500 Subject: [PATCH 07/10] Update coderd/database/modelmethods.go Co-authored-by: Steven Masley --- coderd/database/modelmethods.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/coderd/database/modelmethods.go b/coderd/database/modelmethods.go index 1c3ee61f743cb..9c17691d009a5 100644 --- a/coderd/database/modelmethods.go +++ b/coderd/database/modelmethods.go @@ -22,7 +22,7 @@ func (g Group) Auditable(users []User) AuditableGroup { }) } - // we sort to ensure the diff order enterprise/audit/diff.go:18 + // consistent ordering sort.Slice(members, func(i, j int) bool { return members[i].UserID.String() < members[j].UserID.String() }) From 240c00472d9942e9f52dfce8d59dfbf2d9f7eb3c Mon Sep 17 00:00:00 2001 From: Kira Pilot Date: Tue, 17 Jan 2023 21:21:27 +0000 Subject: [PATCH 08/10] fetching group members in group.delete --- enterprise/coderd/groups.go | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/enterprise/coderd/groups.go b/enterprise/coderd/groups.go index 03e2e6b92703e..e507251d0a124 100644 --- a/enterprise/coderd/groups.go +++ b/enterprise/coderd/groups.go @@ -274,8 +274,14 @@ func (api *API) deleteGroup(rw http.ResponseWriter, r *http.Request) { }) ) defer commitAudit() - var emptyUsers []database.User - aReq.Old = group.Auditable(emptyUsers) + + groupMembers, getGroupMembersErr := api.Database.GetGroupMembers(ctx, group.ID) + if getGroupMembersErr != nil { + httpapi.InternalServerError(rw, getGroupMembersErr) + return + } + + aReq.Old = group.Auditable(groupMembers) if !api.Authorize(r, rbac.ActionDelete, group) { httpapi.ResourceNotFound(rw) From 2ffcab8d50dbc71c9b8d7016f0cea8fe6efa14e7 Mon Sep 17 00:00:00 2001 From: Kira Pilot Date: Tue, 17 Jan 2023 22:02:59 +0000 Subject: [PATCH 09/10] passing through right error --- enterprise/coderd/groups.go | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/enterprise/coderd/groups.go b/enterprise/coderd/groups.go index e507251d0a124..7ca5b8e2256cb 100644 --- a/enterprise/coderd/groups.go +++ b/enterprise/coderd/groups.go @@ -242,8 +242,8 @@ func (api *API) patchGroup(rw http.ResponseWriter, r *http.Request) { return } - patchedMembers, patchedMembersErr := api.Database.GetGroupMembers(ctx, group.ID) - if patchedMembersErr != nil { + patchedMembers, err := api.Database.GetGroupMembers(ctx, group.ID) + if err != nil { httpapi.InternalServerError(rw, err) return } @@ -275,9 +275,9 @@ func (api *API) deleteGroup(rw http.ResponseWriter, r *http.Request) { ) defer commitAudit() - groupMembers, getGroupMembersErr := api.Database.GetGroupMembers(ctx, group.ID) - if getGroupMembersErr != nil { - httpapi.InternalServerError(rw, getGroupMembersErr) + groupMembers, getMembersErr := api.Database.GetGroupMembers(ctx, group.ID) + if getMembersErr != nil { + httpapi.InternalServerError(rw, getMembersErr) return } From 66acf5125dc111f37f38e9933cacb0a064d4a1ac Mon Sep 17 00:00:00 2001 From: Kira Pilot Date: Wed, 18 Jan 2023 19:59:55 +0000 Subject: [PATCH 10/10] broke out into util function and added tests --- .../components/AuditLogRow/AuditLogRow.tsx | 22 +--- .../components/AuditLogRow/auditUtils.test.ts | 122 ++++++++++++++++++ site/src/components/AuditLogRow/auditUtils.ts | 26 ++++ 3 files changed, 151 insertions(+), 19 deletions(-) create mode 100644 site/src/components/AuditLogRow/auditUtils.test.ts create mode 100644 site/src/components/AuditLogRow/auditUtils.ts diff --git a/site/src/components/AuditLogRow/AuditLogRow.tsx b/site/src/components/AuditLogRow/AuditLogRow.tsx index c98a91ca2c13c..10719bbe34b4f 100644 --- a/site/src/components/AuditLogRow/AuditLogRow.tsx +++ b/site/src/components/AuditLogRow/AuditLogRow.tsx @@ -16,6 +16,7 @@ import userAgentParser from "ua-parser-js" import { AuditLogDiff } from "./AuditLogDiff" import i18next from "i18next" import { AuditLogDescription } from "./AuditLogDescription" +import { determineGroupDiff } from "./auditUtils" const httpStatusColor = (httpStatus: number): PaletteIndex => { if (httpStatus >= 300 && httpStatus < 500) { @@ -35,11 +36,6 @@ export interface AuditLogRowProps { defaultIsDiffOpen?: boolean } -interface GroupMember { - user_id: string - group_id: string -} - export const AuditLogRow: React.FC = ({ auditLog, defaultIsDiffOpen = false, @@ -55,22 +51,10 @@ export const AuditLogRow: React.FC = ({ : t("auditLog:table.logRow.notAvailable") let auditDiff = auditLog.diff + // groups have nested diffs (group members) - // so we overwrite the member diff such that - // only the user_id is shown. if (auditLog.resource_type === "group") { - auditDiff = { - ...auditLog.diff, - members: { - old: auditLog.diff.members.old?.map( - (groupMember: GroupMember) => groupMember.user_id, - ), - new: auditLog.diff.members.new?.map( - (groupMember: GroupMember) => groupMember.user_id, - ), - secret: auditLog.diff.members.secret, - }, - } + auditDiff = determineGroupDiff(auditLog.diff) } const toggle = () => { diff --git a/site/src/components/AuditLogRow/auditUtils.test.ts b/site/src/components/AuditLogRow/auditUtils.test.ts new file mode 100644 index 0000000000000..957204f9bc358 --- /dev/null +++ b/site/src/components/AuditLogRow/auditUtils.test.ts @@ -0,0 +1,122 @@ +import { determineGroupDiff } from "./auditUtils" + +const auditDiffForNewGroup = { + id: { + old: "", + new: "e22e0eb9-625a-468b-b962-269b19473789", + secret: false, + }, + members: { + new: [], + secret: false, + }, + name: { + old: "", + new: "another-test-group", + secret: false, + }, +} + +const auditDiffForAddedGroupMember = { + members: { + old: [], + new: [ + { + group_id: "e22e0eb9-625a-468b-b962-269b19473789", + user_id: "cea4c2b0-6373-4858-b26a-df3cbfce8845", + }, + ], + secret: false, + }, +} + +const auditDiffForRemovedGroupMember = { + members: { + old: [ + { + group_id: "25793395-b093-4a3c-a473-9ecf9b243478", + user_id: "84d1cd5a-17e1-4022-898c-52e64256e737", + }, + { + group_id: "25793395-b093-4a3c-a473-9ecf9b243478", + user_id: "cea4c2b0-6373-4858-b26a-df3cbfce8845", + }, + ], + new: [ + { + group_id: "25793395-b093-4a3c-a473-9ecf9b243478", + user_id: "84d1cd5a-17e1-4022-898c-52e64256e737", + }, + ], + secret: false, + }, +} + +const AuditDiffForDeletedGroup = { + id: { + old: "25793395-b093-4a3c-a473-9ecf9b243478", + new: "", + secret: false, + }, + members: { + old: [ + { + group_id: "25793395-b093-4a3c-a473-9ecf9b243478", + user_id: "84d1cd5a-17e1-4022-898c-52e64256e737", + }, + ], + secret: false, + }, + name: { + old: "test-group", + new: "", + secret: false, + }, +} + +describe("determineAuditDiff", () => { + it("auditDiffForNewGroup", () => { + // there should be no change as members are not added when a group is created + expect(determineGroupDiff(auditDiffForNewGroup)).toEqual( + auditDiffForNewGroup, + ) + }) + + it("auditDiffForAddedGroupMember", () => { + const result = { + members: { + ...auditDiffForAddedGroupMember.members, + new: ["cea4c2b0-6373-4858-b26a-df3cbfce8845"], + }, + } + + expect(determineGroupDiff(auditDiffForAddedGroupMember)).toEqual(result) + }) + + it("auditDiffForRemovedGroupMember", () => { + const result = { + members: { + ...auditDiffForRemovedGroupMember.members, + old: [ + "84d1cd5a-17e1-4022-898c-52e64256e737", + "cea4c2b0-6373-4858-b26a-df3cbfce8845", + ], + new: ["84d1cd5a-17e1-4022-898c-52e64256e737"], + }, + } + + expect(determineGroupDiff(auditDiffForRemovedGroupMember)).toEqual(result) + }) + + it("AuditDiffForDeletedGroup", () => { + const result = { + ...AuditDiffForDeletedGroup, + members: { + ...AuditDiffForDeletedGroup.members, + old: ["84d1cd5a-17e1-4022-898c-52e64256e737"], + }, + } + + expect(determineGroupDiff(AuditDiffForDeletedGroup)).toEqual(result) + }) +}) diff --git a/site/src/components/AuditLogRow/auditUtils.ts b/site/src/components/AuditLogRow/auditUtils.ts new file mode 100644 index 0000000000000..99dc7286beeca --- /dev/null +++ b/site/src/components/AuditLogRow/auditUtils.ts @@ -0,0 +1,26 @@ +import { AuditDiff } from "api/typesGenerated" + +interface GroupMember { + user_id: string + group_id: string +} + +/** + * + * @param auditLogDiff + * @returns a diff with the 'members' key flattened to be an array of user_ids + */ +export const determineGroupDiff = (auditLogDiff: AuditDiff): AuditDiff => { + return { + ...auditLogDiff, + members: { + old: auditLogDiff.members.old?.map( + (groupMember: GroupMember) => groupMember.user_id, + ), + new: auditLogDiff.members.new?.map( + (groupMember: GroupMember) => groupMember.user_id, + ), + secret: auditLogDiff.members.secret, + }, + } +} pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy