Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
Prev Previous commit
Next Next commit
Add organization to audit log response
This replaces the old ID.  This is a breaking change but organizations
were not being used before.
  • Loading branch information
code-asher committed Jul 20, 2024
commit 2b25f462f5616a640275e3e9b7a7a52adc923a80
6 changes: 4 additions & 2 deletions cli/organization_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,10 @@ func TestCurrentOrganization(t *testing.T) {
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
json.NewEncoder(w).Encode([]codersdk.Organization{
{
ID: orgID,
Name: "not-default",
MinimalOrganization: codersdk.MinimalOrganization{
ID: orgID,
Name: "not-default",
},
CreatedAt: time.Now(),
UpdatedAt: time.Now(),
IsDefault: false,
Expand Down
26 changes: 23 additions & 3 deletions coderd/apidoc/docs.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

24 changes: 21 additions & 3 deletions coderd/apidoc/swagger.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

14 changes: 12 additions & 2 deletions coderd/audit.go
Original file line number Diff line number Diff line change
Expand Up @@ -238,11 +238,10 @@ func (api *API) convertAuditLog(ctx context.Context, dblog database.GetAuditLogs
resourceLink = api.auditLogResourceLink(ctx, dblog, additionalFields)
}

return codersdk.AuditLog{
alog := codersdk.AuditLog{
ID: dblog.ID,
RequestID: dblog.RequestID,
Time: dblog.Time,
OrganizationID: dblog.OrganizationID,
IP: ip,
UserAgent: dblog.UserAgent.String,
ResourceType: codersdk.ResourceType(dblog.ResourceType),
Expand All @@ -258,6 +257,17 @@ func (api *API) convertAuditLog(ctx context.Context, dblog database.GetAuditLogs
ResourceLink: resourceLink,
IsDeleted: isDeleted,
}

if dblog.OrganizationID != uuid.Nil {
alog.Organization = &codersdk.MinimalOrganization{
ID: dblog.OrganizationID,
Name: dblog.OrganizationName,
DisplayName: dblog.OrganizationDisplayName,
Icon: dblog.OrganizationIcon,
}
}

return alog
}

func auditLogDescription(alog database.GetAuditLogsOffsetRow) string {
Expand Down
77 changes: 77 additions & 0 deletions coderd/audit_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,83 @@ func TestAuditLogs(t *testing.T) {
require.Equal(t, foundUser, *alogs.AuditLogs[0].User)
})

t.Run("Organization", func(t *testing.T) {
t.Parallel()

ctx := context.Background()
client := coderdtest.New(t, nil)
user := coderdtest.CreateFirstUser(t, client)

o, err := client.CreateOrganization(ctx, codersdk.CreateOrganizationRequest{
Name: "new-org",
DisplayName: "New organization",
Description: "A new organization to love and cherish until the test is over.",
Icon: "/emojis/1f48f-1f3ff.png",
})
require.NoError(t, err)

err = client.CreateTestAuditLog(ctx, codersdk.CreateTestAuditLogRequest{
OrganizationID: o.ID,
ResourceID: user.UserID,
})
require.NoError(t, err)

alogs, err := client.AuditLogs(ctx, codersdk.AuditLogsRequest{
Pagination: codersdk.Pagination{
Limit: 1,
},
})
require.NoError(t, err)
require.Equal(t, int64(1), alogs.Count)
require.Len(t, alogs.AuditLogs, 1)

// Make sure the organization is fully populated.
require.Equal(t, &codersdk.MinimalOrganization{
ID: o.ID,
Name: o.Name,
DisplayName: o.DisplayName,
Icon: o.Icon,
}, alogs.AuditLogs[0].Organization)

// Delete the org and try again, should be mostly empty.
err = client.DeleteOrganization(ctx, o.ID.String())
require.NoError(t, err)

alogs, err = client.AuditLogs(ctx, codersdk.AuditLogsRequest{
Pagination: codersdk.Pagination{
Limit: 1,
},
})
require.NoError(t, err)
require.Equal(t, int64(1), alogs.Count)
require.Len(t, alogs.AuditLogs, 1)

require.Equal(t, &codersdk.MinimalOrganization{
ID: o.ID,
}, alogs.AuditLogs[0].Organization)

// Some audit entries do not have an organization at all, in which case the
// response omits the organization.
err = client.CreateTestAuditLog(ctx, codersdk.CreateTestAuditLogRequest{
ResourceType: codersdk.ResourceTypeAPIKey,
ResourceID: user.UserID,
})
require.NoError(t, err)

alogs, err = client.AuditLogs(ctx, codersdk.AuditLogsRequest{
SearchQuery: "resource_type:api_key",
Pagination: codersdk.Pagination{
Limit: 1,
},
})
require.NoError(t, err)
require.Equal(t, int64(1), alogs.Count)
require.Len(t, alogs.AuditLogs, 1)

// The other will have no organization.
require.Equal(t, (*codersdk.MinimalOrganization)(nil), alogs.AuditLogs[0].Organization)
})

t.Run("WorkspaceBuildAuditLink", func(t *testing.T) {
t.Parallel()

Expand Down
10 changes: 6 additions & 4 deletions coderd/organizations.go
Original file line number Diff line number Diff line change
Expand Up @@ -315,11 +315,13 @@ func (api *API) deleteOrganization(rw http.ResponseWriter, r *http.Request) {
// convertOrganization consumes the database representation and outputs an API friendly representation.
func convertOrganization(organization database.Organization) codersdk.Organization {
return codersdk.Organization{
ID: organization.ID,
Name: organization.Name,
DisplayName: organization.DisplayName,
MinimalOrganization: codersdk.MinimalOrganization{
ID: organization.ID,
Name: organization.Name,
DisplayName: organization.DisplayName,
Icon: organization.Icon,
},
Description: organization.Description,
Icon: organization.Icon,
CreatedAt: organization.CreatedAt,
UpdatedAt: organization.UpdatedAt,
IsDefault: organization.IsDefault,
Expand Down
17 changes: 9 additions & 8 deletions codersdk/audit.go
Original file line number Diff line number Diff line change
Expand Up @@ -125,14 +125,13 @@ type AuditDiffField struct {
}

type AuditLog struct {
ID uuid.UUID `json:"id" format:"uuid"`
RequestID uuid.UUID `json:"request_id" format:"uuid"`
Time time.Time `json:"time" format:"date-time"`
OrganizationID uuid.UUID `json:"organization_id" format:"uuid"`
IP netip.Addr `json:"ip"`
UserAgent string `json:"user_agent"`
ResourceType ResourceType `json:"resource_type"`
ResourceID uuid.UUID `json:"resource_id" format:"uuid"`
ID uuid.UUID `json:"id" format:"uuid"`
RequestID uuid.UUID `json:"request_id" format:"uuid"`
Time time.Time `json:"time" format:"date-time"`
IP netip.Addr `json:"ip"`
UserAgent string `json:"user_agent"`
ResourceType ResourceType `json:"resource_type"`
ResourceID uuid.UUID `json:"resource_id" format:"uuid"`
// ResourceTarget is the name of the resource.
ResourceTarget string `json:"resource_target"`
ResourceIcon string `json:"resource_icon"`
Expand All @@ -144,6 +143,8 @@ type AuditLog struct {
ResourceLink string `json:"resource_link"`
IsDeleted bool `json:"is_deleted"`

Organization *MinimalOrganization `json:"organization,omitempty"`

User *User `json:"user"`
}

Expand Down
16 changes: 10 additions & 6 deletions codersdk/organizations.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,18 +39,22 @@ func ProvisionerTypeValid[T ProvisionerType | string](pt T) error {
}
}

// Organization is the JSON representation of a Coder organization.
type Organization struct {
type MinimalOrganization struct {
ID uuid.UUID `table:"id" json:"id" validate:"required" format:"uuid"`
Name string `table:"name,default_sort" json:"name"`
DisplayName string `table:"display_name" json:"display_name"`
Description string `table:"description" json:"description"`
CreatedAt time.Time `table:"created_at" json:"created_at" validate:"required" format:"date-time"`
UpdatedAt time.Time `table:"updated_at" json:"updated_at" validate:"required" format:"date-time"`
IsDefault bool `table:"default" json:"is_default" validate:"required"`
Icon string `table:"icon" json:"icon"`
}

// Organization is the JSON representation of a Coder organization.
type Organization struct {
MinimalOrganization `table:"m,recursive_inline"`
Description string `table:"description" json:"description"`
CreatedAt time.Time `table:"created_at" json:"created_at" validate:"required" format:"date-time"`
UpdatedAt time.Time `table:"updated_at" json:"updated_at" validate:"required" format:"date-time"`
IsDefault bool `table:"default" json:"is_default" validate:"required"`
}

func (o Organization) HumanName() string {
if o.DisplayName == "" {
return o.Name
Expand Down
7 changes: 6 additions & 1 deletion docs/api/audit.md

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

74 changes: 52 additions & 22 deletions docs/api/schemas.md

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading
Loading