mirror of
https://github.com/coder/coder.git
synced 2026-09-21 20:51:01 +08:00
chore: audit log entries for all idp sync changes (#15919)
This commit is contained in:
Generated
+12
-2
@@ -12937,7 +12937,12 @@ const docTemplate = `{
|
||||
"organization",
|
||||
"oauth2_provider_app",
|
||||
"oauth2_provider_app_secret",
|
||||
"custom_role"
|
||||
"custom_role",
|
||||
"organization_member",
|
||||
"notification_template",
|
||||
"idp_sync_settings_organization",
|
||||
"idp_sync_settings_group",
|
||||
"idp_sync_settings_role"
|
||||
],
|
||||
"x-enum-varnames": [
|
||||
"ResourceTypeTemplate",
|
||||
@@ -12956,7 +12961,12 @@ const docTemplate = `{
|
||||
"ResourceTypeOrganization",
|
||||
"ResourceTypeOAuth2ProviderApp",
|
||||
"ResourceTypeOAuth2ProviderAppSecret",
|
||||
"ResourceTypeCustomRole"
|
||||
"ResourceTypeCustomRole",
|
||||
"ResourceTypeOrganizationMember",
|
||||
"ResourceTypeNotificationTemplate",
|
||||
"ResourceTypeIdpSyncSettingsOrganization",
|
||||
"ResourceTypeIdpSyncSettingsGroup",
|
||||
"ResourceTypeIdpSyncSettingsRole"
|
||||
]
|
||||
},
|
||||
"codersdk.Response": {
|
||||
|
||||
Generated
+12
-2
@@ -11694,7 +11694,12 @@
|
||||
"organization",
|
||||
"oauth2_provider_app",
|
||||
"oauth2_provider_app_secret",
|
||||
"custom_role"
|
||||
"custom_role",
|
||||
"organization_member",
|
||||
"notification_template",
|
||||
"idp_sync_settings_organization",
|
||||
"idp_sync_settings_group",
|
||||
"idp_sync_settings_role"
|
||||
],
|
||||
"x-enum-varnames": [
|
||||
"ResourceTypeTemplate",
|
||||
@@ -11713,7 +11718,12 @@
|
||||
"ResourceTypeOrganization",
|
||||
"ResourceTypeOAuth2ProviderApp",
|
||||
"ResourceTypeOAuth2ProviderAppSecret",
|
||||
"ResourceTypeCustomRole"
|
||||
"ResourceTypeCustomRole",
|
||||
"ResourceTypeOrganizationMember",
|
||||
"ResourceTypeNotificationTemplate",
|
||||
"ResourceTypeIdpSyncSettingsOrganization",
|
||||
"ResourceTypeIdpSyncSettingsGroup",
|
||||
"ResourceTypeIdpSyncSettingsRole"
|
||||
]
|
||||
},
|
||||
"codersdk.Response": {
|
||||
|
||||
@@ -2,6 +2,7 @@ package audit
|
||||
|
||||
import (
|
||||
"github.com/coder/coder/v2/coderd/database"
|
||||
"github.com/coder/coder/v2/coderd/idpsync"
|
||||
)
|
||||
|
||||
// Auditable is mostly a marker interface. It contains a definitive list of all
|
||||
@@ -26,7 +27,10 @@ type Auditable interface {
|
||||
database.CustomRole |
|
||||
database.AuditableOrganizationMember |
|
||||
database.Organization |
|
||||
database.NotificationTemplate
|
||||
database.NotificationTemplate |
|
||||
idpsync.OrganizationSyncSettings |
|
||||
idpsync.GroupSyncSettings |
|
||||
idpsync.RoleSyncSettings
|
||||
}
|
||||
|
||||
// Map is a map of changed fields in an audited resource. It maps field names to
|
||||
|
||||
@@ -20,6 +20,7 @@ import (
|
||||
"github.com/coder/coder/v2/coderd/database"
|
||||
"github.com/coder/coder/v2/coderd/database/dbtime"
|
||||
"github.com/coder/coder/v2/coderd/httpmw"
|
||||
"github.com/coder/coder/v2/coderd/idpsync"
|
||||
"github.com/coder/coder/v2/coderd/tracing"
|
||||
)
|
||||
|
||||
@@ -121,11 +122,22 @@ func ResourceTarget[T Auditable](tgt T) string {
|
||||
return typed.Name
|
||||
case database.NotificationTemplate:
|
||||
return typed.Name
|
||||
case idpsync.OrganizationSyncSettings:
|
||||
return "Organization Sync"
|
||||
case idpsync.GroupSyncSettings:
|
||||
return "Organization Group Sync"
|
||||
case idpsync.RoleSyncSettings:
|
||||
return "Organization Role Sync"
|
||||
default:
|
||||
panic(fmt.Sprintf("unknown resource %T for ResourceTarget", tgt))
|
||||
}
|
||||
}
|
||||
|
||||
// noID can be used for resources that do not have an uuid.
|
||||
// An example is singleton configuration resources.
|
||||
// 51A51C = "Static"
|
||||
var noID = uuid.MustParse("51A51C00-0000-0000-0000-000000000000")
|
||||
|
||||
func ResourceID[T Auditable](tgt T) uuid.UUID {
|
||||
switch typed := any(tgt).(type) {
|
||||
case database.Template:
|
||||
@@ -169,6 +181,12 @@ func ResourceID[T Auditable](tgt T) uuid.UUID {
|
||||
return typed.ID
|
||||
case database.NotificationTemplate:
|
||||
return typed.ID
|
||||
case idpsync.OrganizationSyncSettings:
|
||||
return noID // Deployment all uses the same org sync settings
|
||||
case idpsync.GroupSyncSettings:
|
||||
return noID // Org field on audit log has org id
|
||||
case idpsync.RoleSyncSettings:
|
||||
return noID // Org field on audit log has org id
|
||||
default:
|
||||
panic(fmt.Sprintf("unknown resource %T for ResourceID", tgt))
|
||||
}
|
||||
@@ -214,6 +232,12 @@ func ResourceType[T Auditable](tgt T) database.ResourceType {
|
||||
return database.ResourceTypeOrganization
|
||||
case database.NotificationTemplate:
|
||||
return database.ResourceTypeNotificationTemplate
|
||||
case idpsync.OrganizationSyncSettings:
|
||||
return database.ResourceTypeIdpSyncSettingsOrganization
|
||||
case idpsync.RoleSyncSettings:
|
||||
return database.ResourceTypeIdpSyncSettingsRole
|
||||
case idpsync.GroupSyncSettings:
|
||||
return database.ResourceTypeIdpSyncSettingsGroup
|
||||
default:
|
||||
panic(fmt.Sprintf("unknown resource %T for ResourceType", typed))
|
||||
}
|
||||
@@ -261,6 +285,12 @@ func ResourceRequiresOrgID[T Auditable]() bool {
|
||||
return true
|
||||
case database.NotificationTemplate:
|
||||
return false
|
||||
case idpsync.OrganizationSyncSettings:
|
||||
return false
|
||||
case idpsync.GroupSyncSettings:
|
||||
return true
|
||||
case idpsync.RoleSyncSettings:
|
||||
return true
|
||||
default:
|
||||
panic(fmt.Sprintf("unknown resource %T for ResourceRequiresOrgID", tgt))
|
||||
}
|
||||
|
||||
Generated
+4
-1
@@ -190,7 +190,10 @@ CREATE TYPE resource_type AS ENUM (
|
||||
'custom_role',
|
||||
'organization_member',
|
||||
'notifications_settings',
|
||||
'notification_template'
|
||||
'notification_template',
|
||||
'idp_sync_settings_organization',
|
||||
'idp_sync_settings_group',
|
||||
'idp_sync_settings_role'
|
||||
);
|
||||
|
||||
CREATE TYPE startup_script_behavior AS ENUM (
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
-- Nothing to do
|
||||
@@ -0,0 +1,4 @@
|
||||
-- Allow modifications to notification templates to be audited.
|
||||
ALTER TYPE resource_type ADD VALUE IF NOT EXISTS 'idp_sync_settings_organization';
|
||||
ALTER TYPE resource_type ADD VALUE IF NOT EXISTS 'idp_sync_settings_group';
|
||||
ALTER TYPE resource_type ADD VALUE IF NOT EXISTS 'idp_sync_settings_role';
|
||||
+29
-20
@@ -1524,25 +1524,28 @@ func AllProvisionerTypeValues() []ProvisionerType {
|
||||
type ResourceType string
|
||||
|
||||
const (
|
||||
ResourceTypeOrganization ResourceType = "organization"
|
||||
ResourceTypeTemplate ResourceType = "template"
|
||||
ResourceTypeTemplateVersion ResourceType = "template_version"
|
||||
ResourceTypeUser ResourceType = "user"
|
||||
ResourceTypeWorkspace ResourceType = "workspace"
|
||||
ResourceTypeGitSshKey ResourceType = "git_ssh_key"
|
||||
ResourceTypeApiKey ResourceType = "api_key"
|
||||
ResourceTypeGroup ResourceType = "group"
|
||||
ResourceTypeWorkspaceBuild ResourceType = "workspace_build"
|
||||
ResourceTypeLicense ResourceType = "license"
|
||||
ResourceTypeWorkspaceProxy ResourceType = "workspace_proxy"
|
||||
ResourceTypeConvertLogin ResourceType = "convert_login"
|
||||
ResourceTypeHealthSettings ResourceType = "health_settings"
|
||||
ResourceTypeOauth2ProviderApp ResourceType = "oauth2_provider_app"
|
||||
ResourceTypeOauth2ProviderAppSecret ResourceType = "oauth2_provider_app_secret"
|
||||
ResourceTypeCustomRole ResourceType = "custom_role"
|
||||
ResourceTypeOrganizationMember ResourceType = "organization_member"
|
||||
ResourceTypeNotificationsSettings ResourceType = "notifications_settings"
|
||||
ResourceTypeNotificationTemplate ResourceType = "notification_template"
|
||||
ResourceTypeOrganization ResourceType = "organization"
|
||||
ResourceTypeTemplate ResourceType = "template"
|
||||
ResourceTypeTemplateVersion ResourceType = "template_version"
|
||||
ResourceTypeUser ResourceType = "user"
|
||||
ResourceTypeWorkspace ResourceType = "workspace"
|
||||
ResourceTypeGitSshKey ResourceType = "git_ssh_key"
|
||||
ResourceTypeApiKey ResourceType = "api_key"
|
||||
ResourceTypeGroup ResourceType = "group"
|
||||
ResourceTypeWorkspaceBuild ResourceType = "workspace_build"
|
||||
ResourceTypeLicense ResourceType = "license"
|
||||
ResourceTypeWorkspaceProxy ResourceType = "workspace_proxy"
|
||||
ResourceTypeConvertLogin ResourceType = "convert_login"
|
||||
ResourceTypeHealthSettings ResourceType = "health_settings"
|
||||
ResourceTypeOauth2ProviderApp ResourceType = "oauth2_provider_app"
|
||||
ResourceTypeOauth2ProviderAppSecret ResourceType = "oauth2_provider_app_secret"
|
||||
ResourceTypeCustomRole ResourceType = "custom_role"
|
||||
ResourceTypeOrganizationMember ResourceType = "organization_member"
|
||||
ResourceTypeNotificationsSettings ResourceType = "notifications_settings"
|
||||
ResourceTypeNotificationTemplate ResourceType = "notification_template"
|
||||
ResourceTypeIdpSyncSettingsOrganization ResourceType = "idp_sync_settings_organization"
|
||||
ResourceTypeIdpSyncSettingsGroup ResourceType = "idp_sync_settings_group"
|
||||
ResourceTypeIdpSyncSettingsRole ResourceType = "idp_sync_settings_role"
|
||||
)
|
||||
|
||||
func (e *ResourceType) Scan(src interface{}) error {
|
||||
@@ -1600,7 +1603,10 @@ func (e ResourceType) Valid() bool {
|
||||
ResourceTypeCustomRole,
|
||||
ResourceTypeOrganizationMember,
|
||||
ResourceTypeNotificationsSettings,
|
||||
ResourceTypeNotificationTemplate:
|
||||
ResourceTypeNotificationTemplate,
|
||||
ResourceTypeIdpSyncSettingsOrganization,
|
||||
ResourceTypeIdpSyncSettingsGroup,
|
||||
ResourceTypeIdpSyncSettingsRole:
|
||||
return true
|
||||
}
|
||||
return false
|
||||
@@ -1627,6 +1633,9 @@ func AllResourceTypeValues() []ResourceType {
|
||||
ResourceTypeOrganizationMember,
|
||||
ResourceTypeNotificationsSettings,
|
||||
ResourceTypeNotificationTemplate,
|
||||
ResourceTypeIdpSyncSettingsOrganization,
|
||||
ResourceTypeIdpSyncSettingsGroup,
|
||||
ResourceTypeIdpSyncSettingsRole,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -149,13 +149,13 @@ type OrganizationSyncSettings struct {
|
||||
// Field selects the claim field to be used as the created user's
|
||||
// organizations. If the field is the empty string, then no organization updates
|
||||
// will ever come from the OIDC provider.
|
||||
Field string
|
||||
Field string `json:"field"`
|
||||
// Mapping controls how organizations returned by the OIDC provider get mapped
|
||||
Mapping map[string][]uuid.UUID
|
||||
Mapping map[string][]uuid.UUID `json:"mapping"`
|
||||
// AssignDefault will ensure all users that authenticate will be
|
||||
// placed into the default organization. This is mostly a hack to support
|
||||
// legacy deployments.
|
||||
AssignDefault bool
|
||||
AssignDefault bool `json:"assign_default"`
|
||||
}
|
||||
|
||||
func (s *OrganizationSyncSettings) Set(v string) error {
|
||||
|
||||
+13
-4
@@ -30,10 +30,13 @@ const (
|
||||
ResourceTypeOrganization ResourceType = "organization"
|
||||
ResourceTypeOAuth2ProviderApp ResourceType = "oauth2_provider_app"
|
||||
// nolint:gosec // This is not a secret.
|
||||
ResourceTypeOAuth2ProviderAppSecret ResourceType = "oauth2_provider_app_secret"
|
||||
ResourceTypeCustomRole ResourceType = "custom_role"
|
||||
ResourceTypeOrganizationMember = "organization_member"
|
||||
ResourceTypeNotificationTemplate = "notification_template"
|
||||
ResourceTypeOAuth2ProviderAppSecret ResourceType = "oauth2_provider_app_secret"
|
||||
ResourceTypeCustomRole ResourceType = "custom_role"
|
||||
ResourceTypeOrganizationMember ResourceType = "organization_member"
|
||||
ResourceTypeNotificationTemplate ResourceType = "notification_template"
|
||||
ResourceTypeIdpSyncSettingsOrganization ResourceType = "idp_sync_settings_organization"
|
||||
ResourceTypeIdpSyncSettingsGroup ResourceType = "idp_sync_settings_group"
|
||||
ResourceTypeIdpSyncSettingsRole ResourceType = "idp_sync_settings_role"
|
||||
)
|
||||
|
||||
func (r ResourceType) FriendlyString() string {
|
||||
@@ -78,6 +81,12 @@ func (r ResourceType) FriendlyString() string {
|
||||
return "organization member"
|
||||
case ResourceTypeNotificationTemplate:
|
||||
return "notification template"
|
||||
case ResourceTypeIdpSyncSettingsOrganization:
|
||||
return "settings"
|
||||
case ResourceTypeIdpSyncSettingsGroup:
|
||||
return "settings"
|
||||
case ResourceTypeIdpSyncSettingsRole:
|
||||
return "settings"
|
||||
default:
|
||||
return "unknown"
|
||||
}
|
||||
|
||||
@@ -16,6 +16,7 @@ We track the following resources:
|
||||
| AuditableOrganizationMember<br><i></i> | <table><thead><tr><th>Field</th><th>Tracked</th></tr></thead><tbody><tr><td>created_at</td><td>true</td></tr><tr><td>organization_id</td><td>false</td></tr><tr><td>roles</td><td>true</td></tr><tr><td>updated_at</td><td>true</td></tr><tr><td>user_id</td><td>true</td></tr><tr><td>username</td><td>true</td></tr></tbody></table> |
|
||||
| CustomRole<br><i></i> | <table><thead><tr><th>Field</th><th>Tracked</th></tr></thead><tbody><tr><td>created_at</td><td>false</td></tr><tr><td>display_name</td><td>true</td></tr><tr><td>id</td><td>false</td></tr><tr><td>name</td><td>true</td></tr><tr><td>org_permissions</td><td>true</td></tr><tr><td>organization_id</td><td>false</td></tr><tr><td>site_permissions</td><td>true</td></tr><tr><td>updated_at</td><td>false</td></tr><tr><td>user_permissions</td><td>true</td></tr></tbody></table> |
|
||||
| GitSSHKey<br><i>create</i> | <table><thead><tr><th>Field</th><th>Tracked</th></tr></thead><tbody><tr><td>created_at</td><td>false</td></tr><tr><td>private_key</td><td>true</td></tr><tr><td>public_key</td><td>true</td></tr><tr><td>updated_at</td><td>false</td></tr><tr><td>user_id</td><td>true</td></tr></tbody></table> |
|
||||
| GroupSyncSettings<br><i></i> | <table><thead><tr><th>Field</th><th>Tracked</th></tr></thead><tbody><tr><td>auto_create_missing_groups</td><td>true</td></tr><tr><td>field</td><td>true</td></tr><tr><td>legacy_group_name_mapping</td><td>false</td></tr><tr><td>mapping</td><td>true</td></tr><tr><td>regex_filter</td><td>true</td></tr></tbody></table> |
|
||||
| HealthSettings<br><i></i> | <table><thead><tr><th>Field</th><th>Tracked</th></tr></thead><tbody><tr><td>dismissed_healthchecks</td><td>true</td></tr><tr><td>id</td><td>false</td></tr></tbody></table> |
|
||||
| License<br><i>create, delete</i> | <table><thead><tr><th>Field</th><th>Tracked</th></tr></thead><tbody><tr><td>exp</td><td>true</td></tr><tr><td>id</td><td>false</td></tr><tr><td>jwt</td><td>false</td></tr><tr><td>uploaded_at</td><td>true</td></tr><tr><td>uuid</td><td>true</td></tr></tbody></table> |
|
||||
| NotificationTemplate<br><i></i> | <table><thead><tr><th>Field</th><th>Tracked</th></tr></thead><tbody><tr><td>actions</td><td>true</td></tr><tr><td>body_template</td><td>true</td></tr><tr><td>group</td><td>true</td></tr><tr><td>id</td><td>false</td></tr><tr><td>kind</td><td>true</td></tr><tr><td>method</td><td>true</td></tr><tr><td>name</td><td>true</td></tr><tr><td>title_template</td><td>true</td></tr></tbody></table> |
|
||||
@@ -23,6 +24,8 @@ We track the following resources:
|
||||
| OAuth2ProviderApp<br><i></i> | <table><thead><tr><th>Field</th><th>Tracked</th></tr></thead><tbody><tr><td>callback_url</td><td>true</td></tr><tr><td>created_at</td><td>false</td></tr><tr><td>icon</td><td>true</td></tr><tr><td>id</td><td>false</td></tr><tr><td>name</td><td>true</td></tr><tr><td>updated_at</td><td>false</td></tr></tbody></table> |
|
||||
| OAuth2ProviderAppSecret<br><i></i> | <table><thead><tr><th>Field</th><th>Tracked</th></tr></thead><tbody><tr><td>app_id</td><td>false</td></tr><tr><td>created_at</td><td>false</td></tr><tr><td>display_secret</td><td>false</td></tr><tr><td>hashed_secret</td><td>false</td></tr><tr><td>id</td><td>false</td></tr><tr><td>last_used_at</td><td>false</td></tr><tr><td>secret_prefix</td><td>false</td></tr></tbody></table> |
|
||||
| Organization<br><i></i> | <table><thead><tr><th>Field</th><th>Tracked</th></tr></thead><tbody><tr><td>created_at</td><td>false</td></tr><tr><td>description</td><td>true</td></tr><tr><td>display_name</td><td>true</td></tr><tr><td>icon</td><td>true</td></tr><tr><td>id</td><td>false</td></tr><tr><td>is_default</td><td>true</td></tr><tr><td>name</td><td>true</td></tr><tr><td>updated_at</td><td>true</td></tr></tbody></table> |
|
||||
| OrganizationSyncSettings<br><i></i> | <table><thead><tr><th>Field</th><th>Tracked</th></tr></thead><tbody><tr><td>assign_default</td><td>true</td></tr><tr><td>field</td><td>true</td></tr><tr><td>mapping</td><td>true</td></tr></tbody></table> |
|
||||
| RoleSyncSettings<br><i></i> | <table><thead><tr><th>Field</th><th>Tracked</th></tr></thead><tbody><tr><td>field</td><td>true</td></tr><tr><td>mapping</td><td>true</td></tr></tbody></table> |
|
||||
| Template<br><i>write, delete</i> | <table><thead><tr><th>Field</th><th>Tracked</th></tr></thead><tbody><tr><td>active_version_id</td><td>true</td></tr><tr><td>activity_bump</td><td>true</td></tr><tr><td>allow_user_autostart</td><td>true</td></tr><tr><td>allow_user_autostop</td><td>true</td></tr><tr><td>allow_user_cancel_workspace_jobs</td><td>true</td></tr><tr><td>autostart_block_days_of_week</td><td>true</td></tr><tr><td>autostop_requirement_days_of_week</td><td>true</td></tr><tr><td>autostop_requirement_weeks</td><td>true</td></tr><tr><td>created_at</td><td>false</td></tr><tr><td>created_by</td><td>true</td></tr><tr><td>created_by_avatar_url</td><td>false</td></tr><tr><td>created_by_username</td><td>false</td></tr><tr><td>default_ttl</td><td>true</td></tr><tr><td>deleted</td><td>false</td></tr><tr><td>deprecated</td><td>true</td></tr><tr><td>description</td><td>true</td></tr><tr><td>display_name</td><td>true</td></tr><tr><td>failure_ttl</td><td>true</td></tr><tr><td>group_acl</td><td>true</td></tr><tr><td>icon</td><td>true</td></tr><tr><td>id</td><td>true</td></tr><tr><td>max_port_sharing_level</td><td>true</td></tr><tr><td>name</td><td>true</td></tr><tr><td>organization_display_name</td><td>false</td></tr><tr><td>organization_icon</td><td>false</td></tr><tr><td>organization_id</td><td>false</td></tr><tr><td>organization_name</td><td>false</td></tr><tr><td>provisioner</td><td>true</td></tr><tr><td>require_active_version</td><td>true</td></tr><tr><td>time_til_dormant</td><td>true</td></tr><tr><td>time_til_dormant_autodelete</td><td>true</td></tr><tr><td>updated_at</td><td>false</td></tr><tr><td>user_acl</td><td>true</td></tr></tbody></table> |
|
||||
| TemplateVersion<br><i>create, write</i> | <table><thead><tr><th>Field</th><th>Tracked</th></tr></thead><tbody><tr><td>archived</td><td>true</td></tr><tr><td>created_at</td><td>false</td></tr><tr><td>created_by</td><td>true</td></tr><tr><td>created_by_avatar_url</td><td>false</td></tr><tr><td>created_by_username</td><td>false</td></tr><tr><td>external_auth_providers</td><td>false</td></tr><tr><td>id</td><td>true</td></tr><tr><td>job_id</td><td>false</td></tr><tr><td>message</td><td>false</td></tr><tr><td>name</td><td>true</td></tr><tr><td>organization_id</td><td>false</td></tr><tr><td>readme</td><td>true</td></tr><tr><td>source_example_id</td><td>false</td></tr><tr><td>template_id</td><td>true</td></tr><tr><td>updated_at</td><td>false</td></tr></tbody></table> |
|
||||
| User<br><i>create, write, delete</i> | <table><thead><tr><th>Field</th><th>Tracked</th></tr></thead><tbody><tr><td>avatar_url</td><td>false</td></tr><tr><td>created_at</td><td>false</td></tr><tr><td>deleted</td><td>true</td></tr><tr><td>email</td><td>true</td></tr><tr><td>github_com_user_id</td><td>false</td></tr><tr><td>hashed_one_time_passcode</td><td>false</td></tr><tr><td>hashed_password</td><td>true</td></tr><tr><td>id</td><td>true</td></tr><tr><td>last_seen_at</td><td>false</td></tr><tr><td>login_type</td><td>true</td></tr><tr><td>name</td><td>true</td></tr><tr><td>one_time_passcode_expires_at</td><td>true</td></tr><tr><td>quiet_hours_schedule</td><td>true</td></tr><tr><td>rbac_roles</td><td>true</td></tr><tr><td>status</td><td>true</td></tr><tr><td>theme_preference</td><td>false</td></tr><tr><td>updated_at</td><td>false</td></tr><tr><td>username</td><td>true</td></tr></tbody></table> |
|
||||
|
||||
Generated
+24
-19
@@ -4777,25 +4777,30 @@ CreateWorkspaceRequest provides options for creating a new workspace. Only one o
|
||||
|
||||
#### Enumerated Values
|
||||
|
||||
| Value |
|
||||
| ---------------------------- |
|
||||
| `template` |
|
||||
| `template_version` |
|
||||
| `user` |
|
||||
| `workspace` |
|
||||
| `workspace_build` |
|
||||
| `git_ssh_key` |
|
||||
| `api_key` |
|
||||
| `group` |
|
||||
| `license` |
|
||||
| `convert_login` |
|
||||
| `health_settings` |
|
||||
| `notifications_settings` |
|
||||
| `workspace_proxy` |
|
||||
| `organization` |
|
||||
| `oauth2_provider_app` |
|
||||
| `oauth2_provider_app_secret` |
|
||||
| `custom_role` |
|
||||
| Value |
|
||||
| -------------------------------- |
|
||||
| `template` |
|
||||
| `template_version` |
|
||||
| `user` |
|
||||
| `workspace` |
|
||||
| `workspace_build` |
|
||||
| `git_ssh_key` |
|
||||
| `api_key` |
|
||||
| `group` |
|
||||
| `license` |
|
||||
| `convert_login` |
|
||||
| `health_settings` |
|
||||
| `notifications_settings` |
|
||||
| `workspace_proxy` |
|
||||
| `organization` |
|
||||
| `oauth2_provider_app` |
|
||||
| `oauth2_provider_app_secret` |
|
||||
| `custom_role` |
|
||||
| `organization_member` |
|
||||
| `notification_template` |
|
||||
| `idp_sync_settings_organization` |
|
||||
| `idp_sync_settings_group` |
|
||||
| `idp_sync_settings_role` |
|
||||
|
||||
## codersdk.Response
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@ import (
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"strings"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"golang.org/x/xerrors"
|
||||
@@ -49,6 +50,7 @@ func diffValues(left, right any, table Table) audit.Map {
|
||||
)
|
||||
|
||||
diffName := field.FieldType.Tag.Get("json")
|
||||
diffName = strings.TrimSuffix(diffName, ",omitempty")
|
||||
|
||||
atype, ok := diffKey[diffName]
|
||||
if !ok {
|
||||
|
||||
@@ -5,8 +5,10 @@ import (
|
||||
"os"
|
||||
"reflect"
|
||||
"runtime"
|
||||
"strings"
|
||||
|
||||
"github.com/coder/coder/v2/coderd/database"
|
||||
"github.com/coder/coder/v2/coderd/idpsync"
|
||||
"github.com/coder/coder/v2/codersdk"
|
||||
)
|
||||
|
||||
@@ -286,6 +288,23 @@ var auditableResourcesTypes = map[any]map[string]Action{
|
||||
"method": ActionTrack,
|
||||
"kind": ActionTrack,
|
||||
},
|
||||
&idpsync.OrganizationSyncSettings{}: {
|
||||
"field": ActionTrack,
|
||||
"mapping": ActionTrack,
|
||||
"assign_default": ActionTrack,
|
||||
},
|
||||
&idpsync.GroupSyncSettings{}: {
|
||||
"field": ActionTrack,
|
||||
"mapping": ActionTrack,
|
||||
"regex_filter": ActionTrack,
|
||||
"auto_create_missing_groups": ActionTrack,
|
||||
// Configured in env vars
|
||||
"legacy_group_name_mapping": ActionIgnore,
|
||||
},
|
||||
&idpsync.RoleSyncSettings{}: {
|
||||
"field": ActionTrack,
|
||||
"mapping": ActionTrack,
|
||||
},
|
||||
}
|
||||
|
||||
// auditMap converts a map of struct pointers to a map of struct names as
|
||||
@@ -335,6 +354,7 @@ func entry(v any, f map[string]Action) (string, map[string]Action) {
|
||||
// This field is explicitly ignored.
|
||||
continue
|
||||
}
|
||||
jsonTag = strings.TrimSuffix(jsonTag, ",omitempty")
|
||||
if _, ok := fcpy[jsonTag]; !ok {
|
||||
_, _ = fmt.Fprintf(os.Stderr, "ERROR: Audit table entry missing action for field %q in type %q\nPlease update the auditable resource types in: %s\n", d.FieldType.Name, name, self())
|
||||
//nolint:revive
|
||||
|
||||
@@ -6,6 +6,8 @@ import (
|
||||
|
||||
"github.com/google/uuid"
|
||||
|
||||
"github.com/coder/coder/v2/coderd/audit"
|
||||
"github.com/coder/coder/v2/coderd/database"
|
||||
"github.com/coder/coder/v2/coderd/database/dbauthz"
|
||||
"github.com/coder/coder/v2/coderd/httpapi"
|
||||
"github.com/coder/coder/v2/coderd/httpmw"
|
||||
@@ -56,6 +58,16 @@ func (api *API) groupIDPSyncSettings(rw http.ResponseWriter, r *http.Request) {
|
||||
func (api *API) patchGroupIDPSyncSettings(rw http.ResponseWriter, r *http.Request) {
|
||||
ctx := r.Context()
|
||||
org := httpmw.OrganizationParam(r)
|
||||
auditor := *api.AGPL.Auditor.Load()
|
||||
|
||||
aReq, commitAudit := audit.InitRequest[idpsync.GroupSyncSettings](rw, &audit.RequestParams{
|
||||
Audit: auditor,
|
||||
Log: api.Logger,
|
||||
Request: r,
|
||||
Action: database.AuditActionWrite,
|
||||
OrganizationID: org.ID,
|
||||
})
|
||||
defer commitAudit()
|
||||
|
||||
if !api.Authorize(r, policy.ActionUpdate, rbac.ResourceIdpsyncSettings.InOrg(org.ID)) {
|
||||
httpapi.Forbidden(rw)
|
||||
@@ -83,7 +95,14 @@ func (api *API) patchGroupIDPSyncSettings(rw http.ResponseWriter, r *http.Reques
|
||||
|
||||
//nolint:gocritic // Requires system context to update runtime config
|
||||
sysCtx := dbauthz.AsSystemRestricted(ctx)
|
||||
err := api.IDPSync.UpdateGroupSettings(sysCtx, org.ID, api.Database, idpsync.GroupSyncSettings{
|
||||
existing, err := api.IDPSync.GroupSyncSettings(sysCtx, org.ID, api.Database)
|
||||
if err != nil {
|
||||
httpapi.InternalServerError(rw, err)
|
||||
return
|
||||
}
|
||||
aReq.Old = *existing
|
||||
|
||||
err = api.IDPSync.UpdateGroupSettings(sysCtx, org.ID, api.Database, idpsync.GroupSyncSettings{
|
||||
Field: req.Field,
|
||||
Mapping: req.Mapping,
|
||||
RegexFilter: req.RegexFilter,
|
||||
@@ -101,6 +120,7 @@ func (api *API) patchGroupIDPSyncSettings(rw http.ResponseWriter, r *http.Reques
|
||||
return
|
||||
}
|
||||
|
||||
aReq.New = *settings
|
||||
httpapi.Write(ctx, rw, http.StatusOK, codersdk.GroupSyncSettings{
|
||||
Field: settings.Field,
|
||||
Mapping: settings.Mapping,
|
||||
@@ -151,6 +171,16 @@ func (api *API) roleIDPSyncSettings(rw http.ResponseWriter, r *http.Request) {
|
||||
func (api *API) patchRoleIDPSyncSettings(rw http.ResponseWriter, r *http.Request) {
|
||||
ctx := r.Context()
|
||||
org := httpmw.OrganizationParam(r)
|
||||
auditor := *api.AGPL.Auditor.Load()
|
||||
|
||||
aReq, commitAudit := audit.InitRequest[idpsync.RoleSyncSettings](rw, &audit.RequestParams{
|
||||
Audit: auditor,
|
||||
Log: api.Logger,
|
||||
Request: r,
|
||||
Action: database.AuditActionWrite,
|
||||
OrganizationID: org.ID,
|
||||
})
|
||||
defer commitAudit()
|
||||
|
||||
if !api.Authorize(r, policy.ActionUpdate, rbac.ResourceIdpsyncSettings.InOrg(org.ID)) {
|
||||
httpapi.Forbidden(rw)
|
||||
@@ -164,7 +194,14 @@ func (api *API) patchRoleIDPSyncSettings(rw http.ResponseWriter, r *http.Request
|
||||
|
||||
//nolint:gocritic // Requires system context to update runtime config
|
||||
sysCtx := dbauthz.AsSystemRestricted(ctx)
|
||||
err := api.IDPSync.UpdateRoleSettings(sysCtx, org.ID, api.Database, idpsync.RoleSyncSettings{
|
||||
existing, err := api.IDPSync.RoleSyncSettings(sysCtx, org.ID, api.Database)
|
||||
if err != nil {
|
||||
httpapi.InternalServerError(rw, err)
|
||||
return
|
||||
}
|
||||
aReq.Old = *existing
|
||||
|
||||
err = api.IDPSync.UpdateRoleSettings(sysCtx, org.ID, api.Database, idpsync.RoleSyncSettings{
|
||||
Field: req.Field,
|
||||
Mapping: req.Mapping,
|
||||
})
|
||||
@@ -179,6 +216,7 @@ func (api *API) patchRoleIDPSyncSettings(rw http.ResponseWriter, r *http.Request
|
||||
return
|
||||
}
|
||||
|
||||
aReq.New = *settings
|
||||
httpapi.Write(ctx, rw, http.StatusOK, codersdk.RoleSyncSettings{
|
||||
Field: settings.Field,
|
||||
Mapping: settings.Mapping,
|
||||
@@ -226,6 +264,14 @@ func (api *API) organizationIDPSyncSettings(rw http.ResponseWriter, r *http.Requ
|
||||
// @Router /settings/idpsync/organization [patch]
|
||||
func (api *API) patchOrganizationIDPSyncSettings(rw http.ResponseWriter, r *http.Request) {
|
||||
ctx := r.Context()
|
||||
auditor := *api.AGPL.Auditor.Load()
|
||||
aReq, commitAudit := audit.InitRequest[idpsync.OrganizationSyncSettings](rw, &audit.RequestParams{
|
||||
Audit: auditor,
|
||||
Log: api.Logger,
|
||||
Request: r,
|
||||
Action: database.AuditActionWrite,
|
||||
})
|
||||
defer commitAudit()
|
||||
|
||||
if !api.Authorize(r, policy.ActionUpdate, rbac.ResourceIdpsyncSettings) {
|
||||
httpapi.Forbidden(rw)
|
||||
@@ -239,7 +285,14 @@ func (api *API) patchOrganizationIDPSyncSettings(rw http.ResponseWriter, r *http
|
||||
|
||||
//nolint:gocritic // Requires system context to update runtime config
|
||||
sysCtx := dbauthz.AsSystemRestricted(ctx)
|
||||
err := api.IDPSync.UpdateOrganizationSettings(sysCtx, api.Database, idpsync.OrganizationSyncSettings{
|
||||
existing, err := api.IDPSync.OrganizationSyncSettings(sysCtx, api.Database)
|
||||
if err != nil {
|
||||
httpapi.InternalServerError(rw, err)
|
||||
return
|
||||
}
|
||||
aReq.Old = *existing
|
||||
|
||||
err = api.IDPSync.UpdateOrganizationSettings(sysCtx, api.Database, idpsync.OrganizationSyncSettings{
|
||||
Field: req.Field,
|
||||
// We do not check if the mappings point to actual organizations.
|
||||
Mapping: req.Mapping,
|
||||
@@ -256,6 +309,7 @@ func (api *API) patchOrganizationIDPSyncSettings(rw http.ResponseWriter, r *http
|
||||
return
|
||||
}
|
||||
|
||||
aReq.New = *settings
|
||||
httpapi.Write(ctx, rw, http.StatusOK, codersdk.OrganizationSyncSettings{
|
||||
Field: settings.Field,
|
||||
Mapping: settings.Mapping,
|
||||
|
||||
Generated
+2
-8
@@ -1548,15 +1548,9 @@ export interface ResolveAutostartResponse {
|
||||
}
|
||||
|
||||
// From codersdk/audit.go
|
||||
export type ResourceType = "api_key" | "convert_login" | "custom_role" | "git_ssh_key" | "group" | "health_settings" | "license" | "notifications_settings" | "oauth2_provider_app" | "oauth2_provider_app_secret" | "organization" | "template" | "template_version" | "user" | "workspace" | "workspace_build" | "workspace_proxy";
|
||||
export type ResourceType = "api_key" | "convert_login" | "custom_role" | "git_ssh_key" | "group" | "health_settings" | "idp_sync_settings_group" | "idp_sync_settings_organization" | "idp_sync_settings_role" | "license" | "notification_template" | "notifications_settings" | "oauth2_provider_app" | "oauth2_provider_app_secret" | "organization" | "organization_member" | "template" | "template_version" | "user" | "workspace" | "workspace_build" | "workspace_proxy";
|
||||
|
||||
// From codersdk/audit.go
|
||||
export const ResourceTypeNotificationTemplate = "notification_template";
|
||||
|
||||
// From codersdk/audit.go
|
||||
export const ResourceTypeOrganizationMember = "organization_member";
|
||||
|
||||
export const ResourceTypes: ResourceType[] = ["api_key", "convert_login", "custom_role", "git_ssh_key", "group", "health_settings", "license", "notifications_settings", "oauth2_provider_app", "oauth2_provider_app_secret", "organization", "template", "template_version", "user", "workspace", "workspace_build", "workspace_proxy"];
|
||||
export const ResourceTypes: ResourceType[] = ["api_key", "convert_login", "custom_role", "git_ssh_key", "group", "health_settings", "idp_sync_settings_group", "idp_sync_settings_organization", "idp_sync_settings_role", "license", "notification_template", "notifications_settings", "oauth2_provider_app", "oauth2_provider_app_secret", "organization", "organization_member", "template", "template_version", "user", "workspace", "workspace_build", "workspace_proxy"];
|
||||
|
||||
// From codersdk/client.go
|
||||
export interface Response {
|
||||
|
||||
@@ -23,3 +23,29 @@ export const determineGroupDiff = (auditLogDiff: AuditDiff): AuditDiff => {
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
*
|
||||
* @param auditLogDiff
|
||||
* @returns a diff with the 'mappings' as a JSON string. Otherwise, it is [Object object]
|
||||
*/
|
||||
export const determineIdPSyncMappingDiff = (
|
||||
auditLogDiff: AuditDiff,
|
||||
): AuditDiff => {
|
||||
const old = auditLogDiff.mapping?.old as Record<string, string[]> | undefined;
|
||||
const new_ = auditLogDiff.mapping?.new as
|
||||
| Record<string, string[]>
|
||||
| undefined;
|
||||
if (!old || !new_) {
|
||||
return auditLogDiff;
|
||||
}
|
||||
|
||||
return {
|
||||
...auditLogDiff,
|
||||
mapping: {
|
||||
old: JSON.stringify(old),
|
||||
new: JSON.stringify(new_),
|
||||
secret: auditLogDiff.mapping?.secret,
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
@@ -16,7 +16,10 @@ import type { ThemeRole } from "theme/roles";
|
||||
import userAgentParser from "ua-parser-js";
|
||||
import { AuditLogDescription } from "./AuditLogDescription/AuditLogDescription";
|
||||
import { AuditLogDiff } from "./AuditLogDiff/AuditLogDiff";
|
||||
import { determineGroupDiff } from "./AuditLogDiff/auditUtils";
|
||||
import {
|
||||
determineGroupDiff,
|
||||
determineIdPSyncMappingDiff,
|
||||
} from "./AuditLogDiff/auditUtils";
|
||||
|
||||
const httpStatusColor = (httpStatus: number): ThemeRole => {
|
||||
// Treat server errors (500) as errors
|
||||
@@ -59,6 +62,14 @@ export const AuditLogRow: FC<AuditLogRowProps> = ({
|
||||
auditDiff = determineGroupDiff(auditLog.diff);
|
||||
}
|
||||
|
||||
if (
|
||||
auditLog.resource_type === "idp_sync_settings_organization" ||
|
||||
auditLog.resource_type === "idp_sync_settings_group" ||
|
||||
auditLog.resource_type === "idp_sync_settings_role"
|
||||
) {
|
||||
auditDiff = determineIdPSyncMappingDiff(auditLog.diff);
|
||||
}
|
||||
|
||||
const toggle = () => {
|
||||
if (shouldDisplayDiff) {
|
||||
setIsDiffOpen((v) => !v);
|
||||
|
||||
Reference in New Issue
Block a user