feat: add organization details to audit log response (#13961)

* Allow creating test audits with nil org

Not all audit entries have organization IDs, so this will allow us to
test those cases.

* Add organization details to audit log queries

* Add organization to audit log response

This replaces the old ID.  This is a breaking change but organizations
were not being used before.
This commit is contained in:
Asher
2024-07-22 13:28:44 -08:00
committed by GitHub
parent 38c7dcda94
commit a8e6e89f65
16 changed files with 348 additions and 120 deletions
+4 -2
View File
@@ -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,
+25
View File
@@ -8368,7 +8368,11 @@ const docTemplate = `{
"is_deleted": {
"type": "boolean"
},
"organization": {
"$ref": "#/definitions/codersdk.MinimalOrganization"
},
"organization_id": {
"description": "Deprecated: Use 'organization.id' instead.",
"type": "string",
"format": "uuid"
},
@@ -10102,6 +10106,27 @@ const docTemplate = `{
}
}
},
"codersdk.MinimalOrganization": {
"type": "object",
"required": [
"id"
],
"properties": {
"display_name": {
"type": "string"
},
"icon": {
"type": "string"
},
"id": {
"type": "string",
"format": "uuid"
},
"name": {
"type": "string"
}
}
},
"codersdk.MinimalUser": {
"type": "object",
"required": [
+23
View File
@@ -7434,7 +7434,11 @@
"is_deleted": {
"type": "boolean"
},
"organization": {
"$ref": "#/definitions/codersdk.MinimalOrganization"
},
"organization_id": {
"description": "Deprecated: Use 'organization.id' instead.",
"type": "string",
"format": "uuid"
},
@@ -9054,6 +9058,25 @@
}
}
},
"codersdk.MinimalOrganization": {
"type": "object",
"required": ["id"],
"properties": {
"display_name": {
"type": "string"
},
"icon": {
"type": "string"
},
"id": {
"type": "string",
"format": "uuid"
},
"name": {
"type": "string"
}
}
},
"codersdk.MinimalUser": {
"type": "object",
"required": ["id", "username"],
+16 -7
View File
@@ -145,9 +145,6 @@ func (api *API) generateFakeAuditLog(rw http.ResponseWriter, r *http.Request) {
if len(params.AdditionalFields) == 0 {
params.AdditionalFields = json.RawMessage("{}")
}
if params.OrganizationID == uuid.Nil {
params.OrganizationID = uuid.New()
}
_, err = api.Database.InsertAuditLog(ctx, database.InsertAuditLogParams{
ID: uuid.New(),
@@ -241,10 +238,11 @@ func (api *API) convertAuditLog(ctx context.Context, dblog database.GetAuditLogs
resourceLink = api.auditLogResourceLink(ctx, dblog, additionalFields)
}
return codersdk.AuditLog{
ID: dblog.ID,
RequestID: dblog.RequestID,
Time: dblog.Time,
alog := codersdk.AuditLog{
ID: dblog.ID,
RequestID: dblog.RequestID,
Time: dblog.Time,
// OrganizationID is deprecated.
OrganizationID: dblog.OrganizationID,
IP: ip,
UserAgent: dblog.UserAgent.String,
@@ -261,6 +259,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 {
+88 -3
View File
@@ -46,7 +46,7 @@ func TestAuditLogs(t *testing.T) {
require.Len(t, alogs.AuditLogs, 1)
})
t.Run("User", func(t *testing.T) {
t.Run("IncludeUser", func(t *testing.T) {
t.Parallel()
ctx := context.Background()
@@ -95,6 +95,92 @@ func TestAuditLogs(t *testing.T) {
require.Equal(t, foundUser, *alogs.AuditLogs[0].User)
})
t.Run("IncludeOrganization", 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)
// OrganizationID is deprecated, but make sure it is set.
require.Equal(t, o.ID, alogs.AuditLogs[0].OrganizationID)
// 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)
// OrganizationID is deprecated, but make sure it is set.
require.Equal(t, o.ID, alogs.AuditLogs[0].OrganizationID)
// 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)
// OrganizationID is deprecated, but make sure it is empty.
require.Equal(t, uuid.Nil, alogs.AuditLogs[0].OrganizationID)
})
t.Run("WorkspaceBuildAuditLink", func(t *testing.T) {
t.Parallel()
@@ -159,8 +245,7 @@ func TestAuditLogs(t *testing.T) {
// Add an extra audit log in another organization
err = client.CreateTestAuditLog(ctx, codersdk.CreateTestAuditLogRequest{
ResourceID: owner.UserID,
OrganizationID: uuid.New(),
ResourceID: owner.UserID,
})
require.NoError(t, err)
+43 -33
View File
@@ -928,6 +928,16 @@ func (q *FakeQuerier) getLatestWorkspaceAppByTemplateIDUserIDSlugNoLock(ctx cont
return database.WorkspaceApp{}, sql.ErrNoRows
}
// getOrganizationByIDNoLock is used by other functions in the database fake.
func (q *FakeQuerier) getOrganizationByIDNoLock(id uuid.UUID) (database.Organization, error) {
for _, organization := range q.organizations {
if organization.ID == id {
return organization, nil
}
}
return database.Organization{}, sql.ErrNoRows
}
func (*FakeQuerier) AcquireLock(_ context.Context, _ int64) error {
return xerrors.New("AcquireLock must only be called within a transaction")
}
@@ -2146,34 +2156,39 @@ func (q *FakeQuerier) GetAuditLogsOffset(_ context.Context, arg database.GetAudi
user, err := q.getUserByIDNoLock(alog.UserID)
userValid := err == nil
org, _ := q.getOrganizationByIDNoLock(alog.OrganizationID)
logs = append(logs, database.GetAuditLogsOffsetRow{
ID: alog.ID,
RequestID: alog.RequestID,
OrganizationID: alog.OrganizationID,
Ip: alog.Ip,
UserAgent: alog.UserAgent,
ResourceType: alog.ResourceType,
ResourceID: alog.ResourceID,
ResourceTarget: alog.ResourceTarget,
ResourceIcon: alog.ResourceIcon,
Action: alog.Action,
Diff: alog.Diff,
StatusCode: alog.StatusCode,
AdditionalFields: alog.AdditionalFields,
UserID: alog.UserID,
UserUsername: sql.NullString{String: user.Username, Valid: userValid},
UserName: sql.NullString{String: user.Name, Valid: userValid},
UserEmail: sql.NullString{String: user.Email, Valid: userValid},
UserCreatedAt: sql.NullTime{Time: user.CreatedAt, Valid: userValid},
UserUpdatedAt: sql.NullTime{Time: user.UpdatedAt, Valid: userValid},
UserLastSeenAt: sql.NullTime{Time: user.LastSeenAt, Valid: userValid},
UserLoginType: database.NullLoginType{LoginType: user.LoginType, Valid: userValid},
UserDeleted: sql.NullBool{Bool: user.Deleted, Valid: userValid},
UserThemePreference: sql.NullString{String: user.ThemePreference, Valid: userValid},
UserQuietHoursSchedule: sql.NullString{String: user.QuietHoursSchedule, Valid: userValid},
UserStatus: database.NullUserStatus{UserStatus: user.Status, Valid: userValid},
UserRoles: user.RBACRoles,
Count: 0,
ID: alog.ID,
RequestID: alog.RequestID,
OrganizationID: alog.OrganizationID,
OrganizationName: org.Name,
OrganizationDisplayName: org.DisplayName,
OrganizationIcon: org.Icon,
Ip: alog.Ip,
UserAgent: alog.UserAgent,
ResourceType: alog.ResourceType,
ResourceID: alog.ResourceID,
ResourceTarget: alog.ResourceTarget,
ResourceIcon: alog.ResourceIcon,
Action: alog.Action,
Diff: alog.Diff,
StatusCode: alog.StatusCode,
AdditionalFields: alog.AdditionalFields,
UserID: alog.UserID,
UserUsername: sql.NullString{String: user.Username, Valid: userValid},
UserName: sql.NullString{String: user.Name, Valid: userValid},
UserEmail: sql.NullString{String: user.Email, Valid: userValid},
UserCreatedAt: sql.NullTime{Time: user.CreatedAt, Valid: userValid},
UserUpdatedAt: sql.NullTime{Time: user.UpdatedAt, Valid: userValid},
UserLastSeenAt: sql.NullTime{Time: user.LastSeenAt, Valid: userValid},
UserLoginType: database.NullLoginType{LoginType: user.LoginType, Valid: userValid},
UserDeleted: sql.NullBool{Bool: user.Deleted, Valid: userValid},
UserThemePreference: sql.NullString{String: user.ThemePreference, Valid: userValid},
UserQuietHoursSchedule: sql.NullString{String: user.QuietHoursSchedule, Valid: userValid},
UserStatus: database.NullUserStatus{UserStatus: user.Status, Valid: userValid},
UserRoles: user.RBACRoles,
Count: 0,
})
if len(logs) >= int(arg.LimitOpt) {
@@ -2969,12 +2984,7 @@ func (q *FakeQuerier) GetOrganizationByID(_ context.Context, id uuid.UUID) (data
q.mutex.RLock()
defer q.mutex.RUnlock()
for _, organization := range q.organizations {
if organization.ID == id {
return organization, nil
}
}
return database.Organization{}, sql.ErrNoRows
return q.getOrganizationByIDNoLock(id)
}
func (q *FakeQuerier) GetOrganizationByName(_ context.Context, name string) (database.Organization, error) {
+39 -29
View File
@@ -459,6 +459,9 @@ SELECT
users.deleted AS user_deleted,
users.theme_preference AS user_theme_preference,
users.quiet_hours_schedule AS user_quiet_hours_schedule,
COALESCE(organizations.name, '') AS organization_name,
COALESCE(organizations.display_name, '') AS organization_display_name,
COALESCE(organizations.icon, '') AS organization_icon,
COUNT(audit_logs.*) OVER () AS count
FROM
audit_logs
@@ -487,6 +490,7 @@ FROM
workspaces.id = workspace_builds.workspace_id AND
workspace_builds.build_number = 1
)
LEFT JOIN organizations ON audit_logs.organization_id = organizations.id
WHERE
-- Filter resource_type
CASE
@@ -582,35 +586,38 @@ type GetAuditLogsOffsetParams struct {
}
type GetAuditLogsOffsetRow struct {
ID uuid.UUID `db:"id" json:"id"`
Time time.Time `db:"time" json:"time"`
UserID uuid.UUID `db:"user_id" json:"user_id"`
OrganizationID uuid.UUID `db:"organization_id" json:"organization_id"`
Ip pqtype.Inet `db:"ip" json:"ip"`
UserAgent sql.NullString `db:"user_agent" json:"user_agent"`
ResourceType ResourceType `db:"resource_type" json:"resource_type"`
ResourceID uuid.UUID `db:"resource_id" json:"resource_id"`
ResourceTarget string `db:"resource_target" json:"resource_target"`
Action AuditAction `db:"action" json:"action"`
Diff json.RawMessage `db:"diff" json:"diff"`
StatusCode int32 `db:"status_code" json:"status_code"`
AdditionalFields json.RawMessage `db:"additional_fields" json:"additional_fields"`
RequestID uuid.UUID `db:"request_id" json:"request_id"`
ResourceIcon string `db:"resource_icon" json:"resource_icon"`
UserUsername sql.NullString `db:"user_username" json:"user_username"`
UserName sql.NullString `db:"user_name" json:"user_name"`
UserEmail sql.NullString `db:"user_email" json:"user_email"`
UserCreatedAt sql.NullTime `db:"user_created_at" json:"user_created_at"`
UserUpdatedAt sql.NullTime `db:"user_updated_at" json:"user_updated_at"`
UserLastSeenAt sql.NullTime `db:"user_last_seen_at" json:"user_last_seen_at"`
UserStatus NullUserStatus `db:"user_status" json:"user_status"`
UserLoginType NullLoginType `db:"user_login_type" json:"user_login_type"`
UserRoles pq.StringArray `db:"user_roles" json:"user_roles"`
UserAvatarUrl sql.NullString `db:"user_avatar_url" json:"user_avatar_url"`
UserDeleted sql.NullBool `db:"user_deleted" json:"user_deleted"`
UserThemePreference sql.NullString `db:"user_theme_preference" json:"user_theme_preference"`
UserQuietHoursSchedule sql.NullString `db:"user_quiet_hours_schedule" json:"user_quiet_hours_schedule"`
Count int64 `db:"count" json:"count"`
ID uuid.UUID `db:"id" json:"id"`
Time time.Time `db:"time" json:"time"`
UserID uuid.UUID `db:"user_id" json:"user_id"`
OrganizationID uuid.UUID `db:"organization_id" json:"organization_id"`
Ip pqtype.Inet `db:"ip" json:"ip"`
UserAgent sql.NullString `db:"user_agent" json:"user_agent"`
ResourceType ResourceType `db:"resource_type" json:"resource_type"`
ResourceID uuid.UUID `db:"resource_id" json:"resource_id"`
ResourceTarget string `db:"resource_target" json:"resource_target"`
Action AuditAction `db:"action" json:"action"`
Diff json.RawMessage `db:"diff" json:"diff"`
StatusCode int32 `db:"status_code" json:"status_code"`
AdditionalFields json.RawMessage `db:"additional_fields" json:"additional_fields"`
RequestID uuid.UUID `db:"request_id" json:"request_id"`
ResourceIcon string `db:"resource_icon" json:"resource_icon"`
UserUsername sql.NullString `db:"user_username" json:"user_username"`
UserName sql.NullString `db:"user_name" json:"user_name"`
UserEmail sql.NullString `db:"user_email" json:"user_email"`
UserCreatedAt sql.NullTime `db:"user_created_at" json:"user_created_at"`
UserUpdatedAt sql.NullTime `db:"user_updated_at" json:"user_updated_at"`
UserLastSeenAt sql.NullTime `db:"user_last_seen_at" json:"user_last_seen_at"`
UserStatus NullUserStatus `db:"user_status" json:"user_status"`
UserLoginType NullLoginType `db:"user_login_type" json:"user_login_type"`
UserRoles pq.StringArray `db:"user_roles" json:"user_roles"`
UserAvatarUrl sql.NullString `db:"user_avatar_url" json:"user_avatar_url"`
UserDeleted sql.NullBool `db:"user_deleted" json:"user_deleted"`
UserThemePreference sql.NullString `db:"user_theme_preference" json:"user_theme_preference"`
UserQuietHoursSchedule sql.NullString `db:"user_quiet_hours_schedule" json:"user_quiet_hours_schedule"`
OrganizationName string `db:"organization_name" json:"organization_name"`
OrganizationDisplayName string `db:"organization_display_name" json:"organization_display_name"`
OrganizationIcon string `db:"organization_icon" json:"organization_icon"`
Count int64 `db:"count" json:"count"`
}
// GetAuditLogsBefore retrieves `row_limit` number of audit logs before the provided
@@ -667,6 +674,9 @@ func (q *sqlQuerier) GetAuditLogsOffset(ctx context.Context, arg GetAuditLogsOff
&i.UserDeleted,
&i.UserThemePreference,
&i.UserQuietHoursSchedule,
&i.OrganizationName,
&i.OrganizationDisplayName,
&i.OrganizationIcon,
&i.Count,
); err != nil {
return nil, err
+4
View File
@@ -18,6 +18,9 @@ SELECT
users.deleted AS user_deleted,
users.theme_preference AS user_theme_preference,
users.quiet_hours_schedule AS user_quiet_hours_schedule,
COALESCE(organizations.name, '') AS organization_name,
COALESCE(organizations.display_name, '') AS organization_display_name,
COALESCE(organizations.icon, '') AS organization_icon,
COUNT(audit_logs.*) OVER () AS count
FROM
audit_logs
@@ -46,6 +49,7 @@ FROM
workspaces.id = workspace_builds.workspace_id AND
workspace_builds.build_number = 1
)
LEFT JOIN organizations ON audit_logs.organization_id = organizations.id
WHERE
-- Filter resource_type
CASE
+6 -4
View File
@@ -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,
+12 -8
View File
@@ -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"`
@@ -144,6 +143,11 @@ type AuditLog struct {
ResourceLink string `json:"resource_link"`
IsDeleted bool `json:"is_deleted"`
// Deprecated: Use 'organization.id' instead.
OrganizationID uuid.UUID `json:"organization_id" format:"uuid"`
Organization *MinimalOrganization `json:"organization,omitempty"`
User *User `json:"user"`
}
+10 -6
View File
@@ -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
+6
View File
@@ -47,6 +47,12 @@ curl -X GET http://coder-server:8080/api/v2/audit?limit=0 \
"id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",
"ip": "string",
"is_deleted": true,
"organization": {
"display_name": "string",
"icon": "string",
"id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",
"name": "string"
},
"organization_id": "7c60d51f-b44e-4682-87d6-449835ea4de6",
"request_id": "266ea41d-adf5-480b-af50-15b940c2b846",
"resource_icon": "string",
+53 -20
View File
@@ -558,6 +558,12 @@
"id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",
"ip": "string",
"is_deleted": true,
"organization": {
"display_name": "string",
"icon": "string",
"id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",
"name": "string"
},
"organization_id": "7c60d51f-b44e-4682-87d6-449835ea4de6",
"request_id": "266ea41d-adf5-480b-af50-15b940c2b846",
"resource_icon": "string",
@@ -594,26 +600,27 @@
### Properties
| Name | Type | Required | Restrictions | Description |
| ------------------- | ---------------------------------------------- | -------- | ------------ | -------------------------------------------- |
| `action` | [codersdk.AuditAction](#codersdkauditaction) | false | | |
| `additional_fields` | array of integer | false | | |
| `description` | string | false | | |
| `diff` | [codersdk.AuditDiff](#codersdkauditdiff) | false | | |
| `id` | string | false | | |
| `ip` | string | false | | |
| `is_deleted` | boolean | false | | |
| `organization_id` | string | false | | |
| `request_id` | string | false | | |
| `resource_icon` | string | false | | |
| `resource_id` | string | false | | |
| `resource_link` | string | false | | |
| `resource_target` | string | false | | Resource target is the name of the resource. |
| `resource_type` | [codersdk.ResourceType](#codersdkresourcetype) | false | | |
| `status_code` | integer | false | | |
| `time` | string | false | | |
| `user` | [codersdk.User](#codersdkuser) | false | | |
| `user_agent` | string | false | | |
| Name | Type | Required | Restrictions | Description |
| ------------------- | ------------------------------------------------------------ | -------- | ------------ | -------------------------------------------- |
| `action` | [codersdk.AuditAction](#codersdkauditaction) | false | | |
| `additional_fields` | array of integer | false | | |
| `description` | string | false | | |
| `diff` | [codersdk.AuditDiff](#codersdkauditdiff) | false | | |
| `id` | string | false | | |
| `ip` | string | false | | |
| `is_deleted` | boolean | false | | |
| `organization` | [codersdk.MinimalOrganization](#codersdkminimalorganization) | false | | |
| `organization_id` | string | false | | Deprecated: Use 'organization.id' instead. |
| `request_id` | string | false | | |
| `resource_icon` | string | false | | |
| `resource_id` | string | false | | |
| `resource_link` | string | false | | |
| `resource_target` | string | false | | Resource target is the name of the resource. |
| `resource_type` | [codersdk.ResourceType](#codersdkresourcetype) | false | | |
| `status_code` | integer | false | | |
| `time` | string | false | | |
| `user` | [codersdk.User](#codersdkuser) | false | | |
| `user_agent` | string | false | | |
## codersdk.AuditLogResponse
@@ -639,6 +646,12 @@
"id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",
"ip": "string",
"is_deleted": true,
"organization": {
"display_name": "string",
"icon": "string",
"id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",
"name": "string"
},
"organization_id": "7c60d51f-b44e-4682-87d6-449835ea4de6",
"request_id": "266ea41d-adf5-480b-af50-15b940c2b846",
"resource_icon": "string",
@@ -3078,6 +3091,26 @@ CreateWorkspaceRequest provides options for creating a new workspace. Only one o
| --------------- | ------ | -------- | ------------ | ----------- |
| `session_token` | string | true | | |
## codersdk.MinimalOrganization
```json
{
"display_name": "string",
"icon": "string",
"id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",
"name": "string"
}
```
### Properties
| Name | Type | Required | Restrictions | Description |
| -------------- | ------ | -------- | ------------ | ----------- |
| `display_name` | string | false | | |
| `icon` | string | false | | |
| `id` | string | true | | |
| `name` | string | false | | |
## codersdk.MinimalUser
```json
+2 -2
View File
@@ -114,7 +114,7 @@ func (r *RootCmd) provisionerDaemonStart() *serpent.Command {
return xerrors.New("must provide a pre-shared key when not authenticated as a user")
}
org = codersdk.Organization{ID: uuid.Nil}
org = codersdk.Organization{MinimalOrganization: codersdk.MinimalOrganization{ID: uuid.Nil}}
if orgContext.FlagSelect != "" {
// If we are using PSK, we can't fetch the organization
// to validate org name so we need the user to provide
@@ -123,7 +123,7 @@ func (r *RootCmd) provisionerDaemonStart() *serpent.Command {
if err != nil {
return xerrors.New("must provide an org ID when not authenticated as a user and organization is specified")
}
org = codersdk.Organization{ID: orgID}
org = codersdk.Organization{MinimalOrganization: codersdk.MinimalOrganization{ID: orgID}}
}
}
+11 -6
View File
@@ -89,7 +89,6 @@ export interface AuditLog {
readonly id: string;
readonly request_id: string;
readonly time: string;
readonly organization_id: string;
// Named type "net/netip.Addr" unknown, using "any"
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- External type
readonly ip: any;
@@ -105,6 +104,8 @@ export interface AuditLog {
readonly description: string;
readonly resource_link: string;
readonly is_deleted: boolean;
readonly organization_id: string;
readonly organization?: MinimalOrganization;
readonly user?: User;
}
@@ -690,6 +691,14 @@ export interface LoginWithPasswordResponse {
readonly session_token: string;
}
// From codersdk/organizations.go
export interface MinimalOrganization {
readonly id: string;
readonly name: string;
readonly display_name: string;
readonly icon: string;
}
// From codersdk/users.go
export interface MinimalUser {
readonly id: string;
@@ -844,15 +853,11 @@ export interface OIDCConfig {
}
// From codersdk/organizations.go
export interface Organization {
readonly id: string;
readonly name: string;
readonly display_name: string;
export interface Organization extends MinimalOrganization {
readonly description: string;
readonly created_at: string;
readonly updated_at: string;
readonly is_default: boolean;
readonly icon: string;
}
// From codersdk/organizations.go
+6
View File
@@ -2153,6 +2153,12 @@ export const MockAuditLog: TypesGen.AuditLog = {
request_id: "53bded77-7b9d-4e82-8771-991a34d759f9",
time: "2022-05-19T16:45:57.122Z",
organization_id: MockOrganization.id,
organization: {
id: MockOrganization.id,
name: "mock name",
display_name: "mock display name",
icon: "/emojis/1f48f-1f3ff.png",
},
ip: "127.0.0.1",
user_agent:
'"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/104.0.0.0 Safari/537.36"',