Thanks to visit codestin.com
Credit goes to github.com

Skip to content

clean up #5357

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 37 additions & 0 deletions coderd/audit/request.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,10 @@ type RequestParams struct {
Request *http.Request
Action database.AuditAction
AdditionalFields json.RawMessage

// specific to Group resource patch requests
HasGroupMemberChange bool
GroupMemberLists json.RawMessage
}

type Request[T Auditable] struct {
Expand Down Expand Up @@ -144,6 +148,12 @@ func InitRequest[T Auditable](w http.ResponseWriter, p *RequestParams) (*Request
if sw.Status < 400 {
diff := Diff(p.Audit, req.Old, req.New)

// Group resource types may have group member changes.
// We track diffs of this nature differently as GroupMember is a distinct table.
if p.HasGroupMemberChange {
diff = addGroupMemberDiff(logCtx, diff, p.GroupMemberLists, p.Log)
}

var err error
diffRaw, err = json.Marshal(diff)
if err != nil {
Expand Down Expand Up @@ -238,3 +248,30 @@ func parseIP(ipStr string) pqtype.Inet {
Valid: ip != nil,
}
}

type GroupMemberLists struct {
OldGroupMembers []string
NewGroupMembers []string
}

// Adds a 'members' key to Group resource diffs
// in order to capture the addition or removal of group members
func addGroupMemberDiff(logCtx context.Context, diff Map, groupMemberLists json.RawMessage, logger slog.Logger) Map {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The context should normally just be called ctx, and the context + logger should always be the first and second fields.

Suggested change
func addGroupMemberDiff(logCtx context.Context, diff Map, groupMemberLists json.RawMessage, logger slog.Logger) Map {
func addGroupMemberDiff(ctx context.Context, logger slog.Logger, diff Map, groupMemberLists json.RawMessage) Map {

var (
groupMemberBytes = []byte(groupMemberLists)
members GroupMemberLists
err = json.Unmarshal(groupMemberBytes, &members)
)

if err == nil {
diff["members"] = OldNew{
Old: members.OldGroupMembers,
New: members.NewGroupMembers,
Secret: false,
}
} else {
logger.Warn(logCtx, "marshal group member diff", slog.Error(err))
}
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a better way to log here so that I don't have to pass through logCtx and logger?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nah, it's on purpose though. The context can potentially hold fields that the logger can use.


return diff
}
78 changes: 64 additions & 14 deletions enterprise/coderd/groups.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package coderd

import (
"database/sql"
"encoding/json"
"fmt"
"net/http"

Expand All @@ -14,6 +15,7 @@ import (
"github.com/coder/coder/coderd/httpapi"
"github.com/coder/coder/coderd/httpmw"
"github.com/coder/coder/coderd/rbac"
"github.com/coder/coder/coderd/util/slice"
"github.com/coder/coder/codersdk"
)

Expand Down Expand Up @@ -72,16 +74,44 @@ func (api *API) postGroupByOrganization(rw http.ResponseWriter, r *http.Request)

func (api *API) patchGroup(rw http.ResponseWriter, r *http.Request) {
var (
ctx = r.Context()
group = httpmw.GroupParam(r)
auditor = api.AGPL.Auditor.Load()
aReq, commitAudit = audit.InitRequest[database.Group](rw, &audit.RequestParams{
Audit: *auditor,
Log: api.Logger,
Request: r,
Action: database.AuditActionWrite,
})
ctx = r.Context()
group = httpmw.GroupParam(r)
auditor = api.AGPL.Auditor.Load()
)

var req codersdk.PatchGroupRequest
if !httpapi.Read(ctx, rw, r, &req) {
return
}

var (
groupResourceInfo = make(map[string][]string)
hasGroupMemberChange = len(req.AddUsers) != 0 || len(req.RemoveUsers) != 0
)

if hasGroupMemberChange {
currentMembers, currentMembersErr := api.Database.GetGroupMembers(ctx, group.ID)
if currentMembersErr != nil {
httpapi.InternalServerError(rw, currentMembersErr)
return
}

groupResourceInfo = createMemberMap(req, groupResourceInfo, currentMembers)
}

wriBytes, marshalErr := json.Marshal(groupResourceInfo)
if marshalErr != nil {
httpapi.InternalServerError(rw, marshalErr)
}
aReq, commitAudit := audit.InitRequest[database.Group](rw, &audit.RequestParams{
Audit: *auditor,
Log: api.Logger,
Request: r,
Action: database.AuditActionWrite,
HasGroupMemberChange: hasGroupMemberChange,
GroupMemberLists: wriBytes,
})

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A pain point here is that if this request fails sometime before line 115, we won't get an audit log signifying the failure. I'm not sure how to get around this, given we want to pass some computational stuff in (GroupMemberLists).

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What if instead of using database.Group here we instead made []database.GroupMember auditable? I think this might solve a lot of the jankiness of needing hardcoded stuff for group members.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This might take some work adding support for slices in the diff code.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@coadler I was trying to avoid having another auditable resource; however, I'm down to make the change because I agree - it would simplify a lot. In general, do you have any opinions about how many auditable resources we should have? I don't want to bloat the feature but maybe that's less of a concern than I think it is.

defer commitAudit()
aReq.Old = group

Expand All @@ -90,11 +120,6 @@ func (api *API) patchGroup(rw http.ResponseWriter, r *http.Request) {
return
}

var req codersdk.PatchGroupRequest
if !httpapi.Read(ctx, rw, r, &req) {
return
}

if req.Name != "" && req.Name == database.AllUsersGroup {
httpapi.Write(ctx, rw, http.StatusBadRequest, codersdk.Response{
Message: fmt.Sprintf("%q is a reserved group name!", database.AllUsersGroup),
Expand Down Expand Up @@ -375,3 +400,28 @@ func convertRole(role rbac.Role) codersdk.Role {
Name: role.Name,
}
}

// Creates a list of current group member IDs
// as well as a list of the patched group member IDs
// in order to form a diff for the group resource audit log entry
func createMemberMap(req codersdk.PatchGroupRequest, groupMemberMap map[string][]string, currentMembers []database.User) map[string][]string {
var oldMemberIds []string
for _, x := range currentMembers {
oldMemberIds = append(oldMemberIds, x.ID.String())
}

var newMemberIds []string
// Although we favor adding users over deleting them,
// users are added and deleted in separate requests on the FE,
// so we won't see duplicate IDs.
newMemberIds = append(newMemberIds, req.AddUsers...)
for _, x := range currentMembers {
if !slice.Contains(newMemberIds, x.ID.String()) && !slice.Contains(req.RemoveUsers, x.ID.String()) {
newMemberIds = append(newMemberIds, x.ID.String())
}
}

groupMemberMap["oldGroupMembers"] = oldMemberIds
groupMemberMap["newGroupMembers"] = newMemberIds
return groupMemberMap
}