mirror of https://github.com/coder/coder.git
feat: audit oauth2 app management (#12275)
* Audit oauth2 app management * Use 201 for creating secrets
This commit is contained in:
parent
6b866b3f48
commit
f74532ff50
|
@ -11504,7 +11504,9 @@ const docTemplate = `{
|
||||||
"convert_login",
|
"convert_login",
|
||||||
"health_settings",
|
"health_settings",
|
||||||
"workspace_proxy",
|
"workspace_proxy",
|
||||||
"organization"
|
"organization",
|
||||||
|
"oauth2_provider_app",
|
||||||
|
"oauth2_provider_app_secret"
|
||||||
],
|
],
|
||||||
"x-enum-varnames": [
|
"x-enum-varnames": [
|
||||||
"ResourceTypeTemplate",
|
"ResourceTypeTemplate",
|
||||||
|
@ -11519,7 +11521,9 @@ const docTemplate = `{
|
||||||
"ResourceTypeConvertLogin",
|
"ResourceTypeConvertLogin",
|
||||||
"ResourceTypeHealthSettings",
|
"ResourceTypeHealthSettings",
|
||||||
"ResourceTypeWorkspaceProxy",
|
"ResourceTypeWorkspaceProxy",
|
||||||
"ResourceTypeOrganization"
|
"ResourceTypeOrganization",
|
||||||
|
"ResourceTypeOAuth2ProviderApp",
|
||||||
|
"ResourceTypeOAuth2ProviderAppSecret"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"codersdk.Response": {
|
"codersdk.Response": {
|
||||||
|
|
|
@ -10378,7 +10378,9 @@
|
||||||
"convert_login",
|
"convert_login",
|
||||||
"health_settings",
|
"health_settings",
|
||||||
"workspace_proxy",
|
"workspace_proxy",
|
||||||
"organization"
|
"organization",
|
||||||
|
"oauth2_provider_app",
|
||||||
|
"oauth2_provider_app_secret"
|
||||||
],
|
],
|
||||||
"x-enum-varnames": [
|
"x-enum-varnames": [
|
||||||
"ResourceTypeTemplate",
|
"ResourceTypeTemplate",
|
||||||
|
@ -10393,7 +10395,9 @@
|
||||||
"ResourceTypeConvertLogin",
|
"ResourceTypeConvertLogin",
|
||||||
"ResourceTypeHealthSettings",
|
"ResourceTypeHealthSettings",
|
||||||
"ResourceTypeWorkspaceProxy",
|
"ResourceTypeWorkspaceProxy",
|
||||||
"ResourceTypeOrganization"
|
"ResourceTypeOrganization",
|
||||||
|
"ResourceTypeOAuth2ProviderApp",
|
||||||
|
"ResourceTypeOAuth2ProviderAppSecret"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"codersdk.Response": {
|
"codersdk.Response": {
|
||||||
|
|
|
@ -333,6 +333,22 @@ func (api *API) auditLogIsResourceDeleted(ctx context.Context, alog database.Get
|
||||||
api.Logger.Error(ctx, "unable to fetch workspace", slog.Error(err))
|
api.Logger.Error(ctx, "unable to fetch workspace", slog.Error(err))
|
||||||
}
|
}
|
||||||
return workspace.Deleted
|
return workspace.Deleted
|
||||||
|
case database.ResourceTypeOauth2ProviderApp:
|
||||||
|
_, err := api.Database.GetOAuth2ProviderAppByID(ctx, alog.ResourceID)
|
||||||
|
if xerrors.Is(err, sql.ErrNoRows) {
|
||||||
|
return true
|
||||||
|
} else if err != nil {
|
||||||
|
api.Logger.Error(ctx, "unable to fetch oauth2 app", slog.Error(err))
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
case database.ResourceTypeOauth2ProviderAppSecret:
|
||||||
|
_, err := api.Database.GetOAuth2ProviderAppSecretByID(ctx, alog.ResourceID)
|
||||||
|
if xerrors.Is(err, sql.ErrNoRows) {
|
||||||
|
return true
|
||||||
|
} else if err != nil {
|
||||||
|
api.Logger.Error(ctx, "unable to fetch oauth2 app secret", slog.Error(err))
|
||||||
|
}
|
||||||
|
return false
|
||||||
default:
|
default:
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
@ -379,6 +395,16 @@ func (api *API) auditLogResourceLink(ctx context.Context, alog database.GetAudit
|
||||||
return fmt.Sprintf("/@%s/%s/builds/%s",
|
return fmt.Sprintf("/@%s/%s/builds/%s",
|
||||||
workspaceOwner.Username, additionalFields.WorkspaceName, additionalFields.BuildNumber)
|
workspaceOwner.Username, additionalFields.WorkspaceName, additionalFields.BuildNumber)
|
||||||
|
|
||||||
|
case database.ResourceTypeOauth2ProviderApp:
|
||||||
|
return fmt.Sprintf("/deployment/oauth2-provider/apps/%s", alog.ResourceID)
|
||||||
|
|
||||||
|
case database.ResourceTypeOauth2ProviderAppSecret:
|
||||||
|
secret, err := api.Database.GetOAuth2ProviderAppSecretByID(ctx, alog.ResourceID)
|
||||||
|
if err != nil {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
return fmt.Sprintf("/deployment/oauth2-provider/apps/%s", secret.AppID)
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,7 +19,9 @@ type Auditable interface {
|
||||||
database.License |
|
database.License |
|
||||||
database.WorkspaceProxy |
|
database.WorkspaceProxy |
|
||||||
database.AuditOAuthConvertState |
|
database.AuditOAuthConvertState |
|
||||||
database.HealthSettings
|
database.HealthSettings |
|
||||||
|
database.OAuth2ProviderApp |
|
||||||
|
database.OAuth2ProviderAppSecret
|
||||||
}
|
}
|
||||||
|
|
||||||
// Map is a map of changed fields in an audited resource. It maps field names to
|
// Map is a map of changed fields in an audited resource. It maps field names to
|
||||||
|
|
|
@ -99,6 +99,10 @@ func ResourceTarget[T Auditable](tgt T) string {
|
||||||
return string(typed.ToLoginType)
|
return string(typed.ToLoginType)
|
||||||
case database.HealthSettings:
|
case database.HealthSettings:
|
||||||
return "" // no target?
|
return "" // no target?
|
||||||
|
case database.OAuth2ProviderApp:
|
||||||
|
return typed.Name
|
||||||
|
case database.OAuth2ProviderAppSecret:
|
||||||
|
return typed.DisplaySecret
|
||||||
default:
|
default:
|
||||||
panic(fmt.Sprintf("unknown resource %T for ResourceTarget", tgt))
|
panic(fmt.Sprintf("unknown resource %T for ResourceTarget", tgt))
|
||||||
}
|
}
|
||||||
|
@ -132,6 +136,10 @@ func ResourceID[T Auditable](tgt T) uuid.UUID {
|
||||||
case database.HealthSettings:
|
case database.HealthSettings:
|
||||||
// Artificial ID for auditing purposes
|
// Artificial ID for auditing purposes
|
||||||
return typed.ID
|
return typed.ID
|
||||||
|
case database.OAuth2ProviderApp:
|
||||||
|
return typed.ID
|
||||||
|
case database.OAuth2ProviderAppSecret:
|
||||||
|
return typed.ID
|
||||||
default:
|
default:
|
||||||
panic(fmt.Sprintf("unknown resource %T for ResourceID", tgt))
|
panic(fmt.Sprintf("unknown resource %T for ResourceID", tgt))
|
||||||
}
|
}
|
||||||
|
@ -163,6 +171,10 @@ func ResourceType[T Auditable](tgt T) database.ResourceType {
|
||||||
return database.ResourceTypeConvertLogin
|
return database.ResourceTypeConvertLogin
|
||||||
case database.HealthSettings:
|
case database.HealthSettings:
|
||||||
return database.ResourceTypeHealthSettings
|
return database.ResourceTypeHealthSettings
|
||||||
|
case database.OAuth2ProviderApp:
|
||||||
|
return database.ResourceTypeOauth2ProviderApp
|
||||||
|
case database.OAuth2ProviderAppSecret:
|
||||||
|
return database.ResourceTypeOauth2ProviderAppSecret
|
||||||
default:
|
default:
|
||||||
panic(fmt.Sprintf("unknown resource %T for ResourceType", typed))
|
panic(fmt.Sprintf("unknown resource %T for ResourceType", typed))
|
||||||
}
|
}
|
||||||
|
@ -195,6 +207,10 @@ func ResourceRequiresOrgID[T Auditable]() bool {
|
||||||
case database.HealthSettings:
|
case database.HealthSettings:
|
||||||
// Artificial ID for auditing purposes
|
// Artificial ID for auditing purposes
|
||||||
return false
|
return false
|
||||||
|
case database.OAuth2ProviderApp:
|
||||||
|
return false
|
||||||
|
case database.OAuth2ProviderAppSecret:
|
||||||
|
return false
|
||||||
default:
|
default:
|
||||||
panic(fmt.Sprintf("unknown resource %T for ResourceRequiresOrgID", tgt))
|
panic(fmt.Sprintf("unknown resource %T for ResourceRequiresOrgID", tgt))
|
||||||
}
|
}
|
||||||
|
|
|
@ -135,7 +135,9 @@ CREATE TYPE resource_type AS ENUM (
|
||||||
'license',
|
'license',
|
||||||
'workspace_proxy',
|
'workspace_proxy',
|
||||||
'convert_login',
|
'convert_login',
|
||||||
'health_settings'
|
'health_settings',
|
||||||
|
'oauth2_provider_app',
|
||||||
|
'oauth2_provider_app_secret'
|
||||||
);
|
);
|
||||||
|
|
||||||
CREATE TYPE startup_script_behavior AS ENUM (
|
CREATE TYPE startup_script_behavior AS ENUM (
|
||||||
|
|
|
@ -0,0 +1,2 @@
|
||||||
|
-- It is not possible to drop enum values from enum types, so the UPs on
|
||||||
|
-- resource_type have "IF NOT EXISTS".
|
|
@ -0,0 +1,2 @@
|
||||||
|
ALTER TYPE resource_type ADD VALUE IF NOT EXISTS 'oauth2_provider_app';
|
||||||
|
ALTER TYPE resource_type ADD VALUE IF NOT EXISTS 'oauth2_provider_app_secret';
|
|
@ -1149,19 +1149,21 @@ func AllProvisionerTypeValues() []ProvisionerType {
|
||||||
type ResourceType string
|
type ResourceType string
|
||||||
|
|
||||||
const (
|
const (
|
||||||
ResourceTypeOrganization ResourceType = "organization"
|
ResourceTypeOrganization ResourceType = "organization"
|
||||||
ResourceTypeTemplate ResourceType = "template"
|
ResourceTypeTemplate ResourceType = "template"
|
||||||
ResourceTypeTemplateVersion ResourceType = "template_version"
|
ResourceTypeTemplateVersion ResourceType = "template_version"
|
||||||
ResourceTypeUser ResourceType = "user"
|
ResourceTypeUser ResourceType = "user"
|
||||||
ResourceTypeWorkspace ResourceType = "workspace"
|
ResourceTypeWorkspace ResourceType = "workspace"
|
||||||
ResourceTypeGitSshKey ResourceType = "git_ssh_key"
|
ResourceTypeGitSshKey ResourceType = "git_ssh_key"
|
||||||
ResourceTypeApiKey ResourceType = "api_key"
|
ResourceTypeApiKey ResourceType = "api_key"
|
||||||
ResourceTypeGroup ResourceType = "group"
|
ResourceTypeGroup ResourceType = "group"
|
||||||
ResourceTypeWorkspaceBuild ResourceType = "workspace_build"
|
ResourceTypeWorkspaceBuild ResourceType = "workspace_build"
|
||||||
ResourceTypeLicense ResourceType = "license"
|
ResourceTypeLicense ResourceType = "license"
|
||||||
ResourceTypeWorkspaceProxy ResourceType = "workspace_proxy"
|
ResourceTypeWorkspaceProxy ResourceType = "workspace_proxy"
|
||||||
ResourceTypeConvertLogin ResourceType = "convert_login"
|
ResourceTypeConvertLogin ResourceType = "convert_login"
|
||||||
ResourceTypeHealthSettings ResourceType = "health_settings"
|
ResourceTypeHealthSettings ResourceType = "health_settings"
|
||||||
|
ResourceTypeOauth2ProviderApp ResourceType = "oauth2_provider_app"
|
||||||
|
ResourceTypeOauth2ProviderAppSecret ResourceType = "oauth2_provider_app_secret"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (e *ResourceType) Scan(src interface{}) error {
|
func (e *ResourceType) Scan(src interface{}) error {
|
||||||
|
@ -1213,7 +1215,9 @@ func (e ResourceType) Valid() bool {
|
||||||
ResourceTypeLicense,
|
ResourceTypeLicense,
|
||||||
ResourceTypeWorkspaceProxy,
|
ResourceTypeWorkspaceProxy,
|
||||||
ResourceTypeConvertLogin,
|
ResourceTypeConvertLogin,
|
||||||
ResourceTypeHealthSettings:
|
ResourceTypeHealthSettings,
|
||||||
|
ResourceTypeOauth2ProviderApp,
|
||||||
|
ResourceTypeOauth2ProviderAppSecret:
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
|
@ -1234,6 +1238,8 @@ func AllResourceTypeValues() []ResourceType {
|
||||||
ResourceTypeWorkspaceProxy,
|
ResourceTypeWorkspaceProxy,
|
||||||
ResourceTypeConvertLogin,
|
ResourceTypeConvertLogin,
|
||||||
ResourceTypeHealthSettings,
|
ResourceTypeHealthSettings,
|
||||||
|
ResourceTypeOauth2ProviderApp,
|
||||||
|
ResourceTypeOauth2ProviderAppSecret,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -14,19 +14,22 @@ import (
|
||||||
type ResourceType string
|
type ResourceType string
|
||||||
|
|
||||||
const (
|
const (
|
||||||
ResourceTypeTemplate ResourceType = "template"
|
ResourceTypeTemplate ResourceType = "template"
|
||||||
ResourceTypeTemplateVersion ResourceType = "template_version"
|
ResourceTypeTemplateVersion ResourceType = "template_version"
|
||||||
ResourceTypeUser ResourceType = "user"
|
ResourceTypeUser ResourceType = "user"
|
||||||
ResourceTypeWorkspace ResourceType = "workspace"
|
ResourceTypeWorkspace ResourceType = "workspace"
|
||||||
ResourceTypeWorkspaceBuild ResourceType = "workspace_build"
|
ResourceTypeWorkspaceBuild ResourceType = "workspace_build"
|
||||||
ResourceTypeGitSSHKey ResourceType = "git_ssh_key"
|
ResourceTypeGitSSHKey ResourceType = "git_ssh_key"
|
||||||
ResourceTypeAPIKey ResourceType = "api_key"
|
ResourceTypeAPIKey ResourceType = "api_key"
|
||||||
ResourceTypeGroup ResourceType = "group"
|
ResourceTypeGroup ResourceType = "group"
|
||||||
ResourceTypeLicense ResourceType = "license"
|
ResourceTypeLicense ResourceType = "license"
|
||||||
ResourceTypeConvertLogin ResourceType = "convert_login"
|
ResourceTypeConvertLogin ResourceType = "convert_login"
|
||||||
ResourceTypeHealthSettings ResourceType = "health_settings"
|
ResourceTypeHealthSettings ResourceType = "health_settings"
|
||||||
ResourceTypeWorkspaceProxy ResourceType = "workspace_proxy"
|
ResourceTypeWorkspaceProxy ResourceType = "workspace_proxy"
|
||||||
ResourceTypeOrganization ResourceType = "organization"
|
ResourceTypeOrganization ResourceType = "organization"
|
||||||
|
ResourceTypeOAuth2ProviderApp ResourceType = "oauth2_provider_app"
|
||||||
|
// nolint:gosec // This is not a secret.
|
||||||
|
ResourceTypeOAuth2ProviderAppSecret ResourceType = "oauth2_provider_app_secret"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (r ResourceType) FriendlyString() string {
|
func (r ResourceType) FriendlyString() string {
|
||||||
|
@ -59,6 +62,10 @@ func (r ResourceType) FriendlyString() string {
|
||||||
return "organization"
|
return "organization"
|
||||||
case ResourceTypeHealthSettings:
|
case ResourceTypeHealthSettings:
|
||||||
return "health_settings"
|
return "health_settings"
|
||||||
|
case ResourceTypeOAuth2ProviderApp:
|
||||||
|
return "oauth2 app"
|
||||||
|
case ResourceTypeOAuth2ProviderAppSecret:
|
||||||
|
return "oauth2 app secret"
|
||||||
default:
|
default:
|
||||||
return "unknown"
|
return "unknown"
|
||||||
}
|
}
|
||||||
|
|
|
@ -159,7 +159,7 @@ func (c *Client) PostOAuth2ProviderAppSecret(ctx context.Context, appID uuid.UUI
|
||||||
return OAuth2ProviderAppSecretFull{}, err
|
return OAuth2ProviderAppSecretFull{}, err
|
||||||
}
|
}
|
||||||
defer res.Body.Close()
|
defer res.Body.Close()
|
||||||
if res.StatusCode != http.StatusOK {
|
if res.StatusCode != http.StatusCreated {
|
||||||
return OAuth2ProviderAppSecretFull{}, ReadBodyAsError(res)
|
return OAuth2ProviderAppSecretFull{}, ReadBodyAsError(res)
|
||||||
}
|
}
|
||||||
var resp OAuth2ProviderAppSecretFull
|
var resp OAuth2ProviderAppSecretFull
|
||||||
|
|
|
@ -16,6 +16,8 @@ We track the following resources:
|
||||||
| 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> |
|
| 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> |
|
||||||
| 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> |
|
| 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> |
|
| 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> |
|
||||||
|
| 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> |
|
||||||
| 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>max_ttl</td><td>true</td></tr><tr><td>name</td><td>true</td></tr><tr><td>organization_id</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>use_max_ttl</td><td>true</td></tr><tr><td>user_acl</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>max_ttl</td><td>true</td></tr><tr><td>name</td><td>true</td></tr><tr><td>organization_id</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>use_max_ttl</td><td>true</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>template_id</td><td>true</td></tr><tr><td>updated_at</td><td>false</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>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>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>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> |
|
| 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>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>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> |
|
||||||
|
|
|
@ -5367,21 +5367,23 @@ AuthorizationObject can represent a "set" of objects, such as: all workspaces in
|
||||||
|
|
||||||
#### Enumerated Values
|
#### Enumerated Values
|
||||||
|
|
||||||
| Value |
|
| Value |
|
||||||
| ------------------ |
|
| ---------------------------- |
|
||||||
| `template` |
|
| `template` |
|
||||||
| `template_version` |
|
| `template_version` |
|
||||||
| `user` |
|
| `user` |
|
||||||
| `workspace` |
|
| `workspace` |
|
||||||
| `workspace_build` |
|
| `workspace_build` |
|
||||||
| `git_ssh_key` |
|
| `git_ssh_key` |
|
||||||
| `api_key` |
|
| `api_key` |
|
||||||
| `group` |
|
| `group` |
|
||||||
| `license` |
|
| `license` |
|
||||||
| `convert_login` |
|
| `convert_login` |
|
||||||
| `health_settings` |
|
| `health_settings` |
|
||||||
| `workspace_proxy` |
|
| `workspace_proxy` |
|
||||||
| `organization` |
|
| `organization` |
|
||||||
|
| `oauth2_provider_app` |
|
||||||
|
| `oauth2_provider_app_secret` |
|
||||||
|
|
||||||
## codersdk.Response
|
## codersdk.Response
|
||||||
|
|
||||||
|
|
|
@ -219,6 +219,23 @@ var auditableResourcesTypes = map[any]map[string]Action{
|
||||||
"region_id": ActionTrack,
|
"region_id": ActionTrack,
|
||||||
"version": ActionTrack,
|
"version": ActionTrack,
|
||||||
},
|
},
|
||||||
|
&database.OAuth2ProviderApp{}: {
|
||||||
|
"id": ActionIgnore,
|
||||||
|
"created_at": ActionIgnore,
|
||||||
|
"updated_at": ActionIgnore,
|
||||||
|
"name": ActionTrack,
|
||||||
|
"icon": ActionTrack,
|
||||||
|
"callback_url": ActionTrack,
|
||||||
|
},
|
||||||
|
&database.OAuth2ProviderAppSecret{}: {
|
||||||
|
"id": ActionIgnore,
|
||||||
|
"created_at": ActionIgnore,
|
||||||
|
"last_used_at": ActionIgnore,
|
||||||
|
"hashed_secret": ActionIgnore,
|
||||||
|
"display_secret": ActionIgnore,
|
||||||
|
"app_id": ActionIgnore,
|
||||||
|
"secret_prefix": ActionIgnore,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
// auditMap converts a map of struct pointers to a map of struct names as
|
// auditMap converts a map of struct pointers to a map of struct names as
|
||||||
|
|
|
@ -7,6 +7,7 @@ import (
|
||||||
"github.com/google/uuid"
|
"github.com/google/uuid"
|
||||||
|
|
||||||
"github.com/coder/coder/v2/buildinfo"
|
"github.com/coder/coder/v2/buildinfo"
|
||||||
|
"github.com/coder/coder/v2/coderd/audit"
|
||||||
"github.com/coder/coder/v2/coderd/database"
|
"github.com/coder/coder/v2/coderd/database"
|
||||||
"github.com/coder/coder/v2/coderd/database/db2sdk"
|
"github.com/coder/coder/v2/coderd/database/db2sdk"
|
||||||
"github.com/coder/coder/v2/coderd/database/dbtime"
|
"github.com/coder/coder/v2/coderd/database/dbtime"
|
||||||
|
@ -108,7 +109,17 @@ func (api *API) oAuth2ProviderApp(rw http.ResponseWriter, r *http.Request) {
|
||||||
// @Success 200 {object} codersdk.OAuth2ProviderApp
|
// @Success 200 {object} codersdk.OAuth2ProviderApp
|
||||||
// @Router /oauth2-provider/apps [post]
|
// @Router /oauth2-provider/apps [post]
|
||||||
func (api *API) postOAuth2ProviderApp(rw http.ResponseWriter, r *http.Request) {
|
func (api *API) postOAuth2ProviderApp(rw http.ResponseWriter, r *http.Request) {
|
||||||
ctx := r.Context()
|
var (
|
||||||
|
ctx = r.Context()
|
||||||
|
auditor = api.AGPL.Auditor.Load()
|
||||||
|
aReq, commitAudit = audit.InitRequest[database.OAuth2ProviderApp](rw, &audit.RequestParams{
|
||||||
|
Audit: *auditor,
|
||||||
|
Log: api.Logger,
|
||||||
|
Request: r,
|
||||||
|
Action: database.AuditActionCreate,
|
||||||
|
})
|
||||||
|
)
|
||||||
|
defer commitAudit()
|
||||||
var req codersdk.PostOAuth2ProviderAppRequest
|
var req codersdk.PostOAuth2ProviderAppRequest
|
||||||
if !httpapi.Read(ctx, rw, r, &req) {
|
if !httpapi.Read(ctx, rw, r, &req) {
|
||||||
return
|
return
|
||||||
|
@ -128,6 +139,7 @@ func (api *API) postOAuth2ProviderApp(rw http.ResponseWriter, r *http.Request) {
|
||||||
})
|
})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
aReq.New = app
|
||||||
httpapi.Write(ctx, rw, http.StatusCreated, db2sdk.OAuth2ProviderApp(api.AccessURL, app))
|
httpapi.Write(ctx, rw, http.StatusCreated, db2sdk.OAuth2ProviderApp(api.AccessURL, app))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -142,8 +154,19 @@ func (api *API) postOAuth2ProviderApp(rw http.ResponseWriter, r *http.Request) {
|
||||||
// @Success 200 {object} codersdk.OAuth2ProviderApp
|
// @Success 200 {object} codersdk.OAuth2ProviderApp
|
||||||
// @Router /oauth2-provider/apps/{app} [put]
|
// @Router /oauth2-provider/apps/{app} [put]
|
||||||
func (api *API) putOAuth2ProviderApp(rw http.ResponseWriter, r *http.Request) {
|
func (api *API) putOAuth2ProviderApp(rw http.ResponseWriter, r *http.Request) {
|
||||||
ctx := r.Context()
|
var (
|
||||||
app := httpmw.OAuth2ProviderApp(r)
|
ctx = r.Context()
|
||||||
|
app = httpmw.OAuth2ProviderApp(r)
|
||||||
|
auditor = api.AGPL.Auditor.Load()
|
||||||
|
aReq, commitAudit = audit.InitRequest[database.OAuth2ProviderApp](rw, &audit.RequestParams{
|
||||||
|
Audit: *auditor,
|
||||||
|
Log: api.Logger,
|
||||||
|
Request: r,
|
||||||
|
Action: database.AuditActionWrite,
|
||||||
|
})
|
||||||
|
)
|
||||||
|
aReq.Old = app
|
||||||
|
defer commitAudit()
|
||||||
var req codersdk.PutOAuth2ProviderAppRequest
|
var req codersdk.PutOAuth2ProviderAppRequest
|
||||||
if !httpapi.Read(ctx, rw, r, &req) {
|
if !httpapi.Read(ctx, rw, r, &req) {
|
||||||
return
|
return
|
||||||
|
@ -162,6 +185,7 @@ func (api *API) putOAuth2ProviderApp(rw http.ResponseWriter, r *http.Request) {
|
||||||
})
|
})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
aReq.New = app
|
||||||
httpapi.Write(ctx, rw, http.StatusOK, db2sdk.OAuth2ProviderApp(api.AccessURL, app))
|
httpapi.Write(ctx, rw, http.StatusOK, db2sdk.OAuth2ProviderApp(api.AccessURL, app))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -173,8 +197,19 @@ func (api *API) putOAuth2ProviderApp(rw http.ResponseWriter, r *http.Request) {
|
||||||
// @Success 204
|
// @Success 204
|
||||||
// @Router /oauth2-provider/apps/{app} [delete]
|
// @Router /oauth2-provider/apps/{app} [delete]
|
||||||
func (api *API) deleteOAuth2ProviderApp(rw http.ResponseWriter, r *http.Request) {
|
func (api *API) deleteOAuth2ProviderApp(rw http.ResponseWriter, r *http.Request) {
|
||||||
ctx := r.Context()
|
var (
|
||||||
app := httpmw.OAuth2ProviderApp(r)
|
ctx = r.Context()
|
||||||
|
app = httpmw.OAuth2ProviderApp(r)
|
||||||
|
auditor = api.AGPL.Auditor.Load()
|
||||||
|
aReq, commitAudit = audit.InitRequest[database.OAuth2ProviderApp](rw, &audit.RequestParams{
|
||||||
|
Audit: *auditor,
|
||||||
|
Log: api.Logger,
|
||||||
|
Request: r,
|
||||||
|
Action: database.AuditActionDelete,
|
||||||
|
})
|
||||||
|
)
|
||||||
|
aReq.Old = app
|
||||||
|
defer commitAudit()
|
||||||
err := api.Database.DeleteOAuth2ProviderAppByID(ctx, app.ID)
|
err := api.Database.DeleteOAuth2ProviderAppByID(ctx, app.ID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
httpapi.Write(ctx, rw, http.StatusInternalServerError, codersdk.Response{
|
httpapi.Write(ctx, rw, http.StatusInternalServerError, codersdk.Response{
|
||||||
|
@ -225,8 +260,18 @@ func (api *API) oAuth2ProviderAppSecrets(rw http.ResponseWriter, r *http.Request
|
||||||
// @Success 200 {array} codersdk.OAuth2ProviderAppSecretFull
|
// @Success 200 {array} codersdk.OAuth2ProviderAppSecretFull
|
||||||
// @Router /oauth2-provider/apps/{app}/secrets [post]
|
// @Router /oauth2-provider/apps/{app}/secrets [post]
|
||||||
func (api *API) postOAuth2ProviderAppSecret(rw http.ResponseWriter, r *http.Request) {
|
func (api *API) postOAuth2ProviderAppSecret(rw http.ResponseWriter, r *http.Request) {
|
||||||
ctx := r.Context()
|
var (
|
||||||
app := httpmw.OAuth2ProviderApp(r)
|
ctx = r.Context()
|
||||||
|
app = httpmw.OAuth2ProviderApp(r)
|
||||||
|
auditor = api.AGPL.Auditor.Load()
|
||||||
|
aReq, commitAudit = audit.InitRequest[database.OAuth2ProviderAppSecret](rw, &audit.RequestParams{
|
||||||
|
Audit: *auditor,
|
||||||
|
Log: api.Logger,
|
||||||
|
Request: r,
|
||||||
|
Action: database.AuditActionCreate,
|
||||||
|
})
|
||||||
|
)
|
||||||
|
defer commitAudit()
|
||||||
secret, err := identityprovider.GenerateSecret()
|
secret, err := identityprovider.GenerateSecret()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
httpapi.Write(ctx, rw, http.StatusInternalServerError, codersdk.Response{
|
httpapi.Write(ctx, rw, http.StatusInternalServerError, codersdk.Response{
|
||||||
|
@ -253,7 +298,8 @@ func (api *API) postOAuth2ProviderAppSecret(rw http.ResponseWriter, r *http.Requ
|
||||||
})
|
})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
httpapi.Write(ctx, rw, http.StatusOK, codersdk.OAuth2ProviderAppSecretFull{
|
aReq.New = dbSecret
|
||||||
|
httpapi.Write(ctx, rw, http.StatusCreated, codersdk.OAuth2ProviderAppSecretFull{
|
||||||
ID: dbSecret.ID,
|
ID: dbSecret.ID,
|
||||||
ClientSecretFull: secret.Formatted,
|
ClientSecretFull: secret.Formatted,
|
||||||
})
|
})
|
||||||
|
@ -268,8 +314,19 @@ func (api *API) postOAuth2ProviderAppSecret(rw http.ResponseWriter, r *http.Requ
|
||||||
// @Success 204
|
// @Success 204
|
||||||
// @Router /oauth2-provider/apps/{app}/secrets/{secretID} [delete]
|
// @Router /oauth2-provider/apps/{app}/secrets/{secretID} [delete]
|
||||||
func (api *API) deleteOAuth2ProviderAppSecret(rw http.ResponseWriter, r *http.Request) {
|
func (api *API) deleteOAuth2ProviderAppSecret(rw http.ResponseWriter, r *http.Request) {
|
||||||
ctx := r.Context()
|
var (
|
||||||
secret := httpmw.OAuth2ProviderAppSecret(r)
|
ctx = r.Context()
|
||||||
|
secret = httpmw.OAuth2ProviderAppSecret(r)
|
||||||
|
auditor = api.AGPL.Auditor.Load()
|
||||||
|
aReq, commitAudit = audit.InitRequest[database.OAuth2ProviderAppSecret](rw, &audit.RequestParams{
|
||||||
|
Audit: *auditor,
|
||||||
|
Log: api.Logger,
|
||||||
|
Request: r,
|
||||||
|
Action: database.AuditActionDelete,
|
||||||
|
})
|
||||||
|
)
|
||||||
|
aReq.Old = secret
|
||||||
|
defer commitAudit()
|
||||||
err := api.Database.DeleteOAuth2ProviderAppSecretByID(ctx, secret.ID)
|
err := api.Database.DeleteOAuth2ProviderAppSecretByID(ctx, secret.ID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
httpapi.Write(ctx, rw, http.StatusInternalServerError, codersdk.Response{
|
httpapi.Write(ctx, rw, http.StatusInternalServerError, codersdk.Response{
|
||||||
|
|
|
@ -2251,6 +2251,8 @@ export type ResourceType =
|
||||||
| "group"
|
| "group"
|
||||||
| "health_settings"
|
| "health_settings"
|
||||||
| "license"
|
| "license"
|
||||||
|
| "oauth2_provider_app"
|
||||||
|
| "oauth2_provider_app_secret"
|
||||||
| "organization"
|
| "organization"
|
||||||
| "template"
|
| "template"
|
||||||
| "template_version"
|
| "template_version"
|
||||||
|
@ -2265,6 +2267,8 @@ export const ResourceTypes: ResourceType[] = [
|
||||||
"group",
|
"group",
|
||||||
"health_settings",
|
"health_settings",
|
||||||
"license",
|
"license",
|
||||||
|
"oauth2_provider_app",
|
||||||
|
"oauth2_provider_app_secret",
|
||||||
"organization",
|
"organization",
|
||||||
"template",
|
"template",
|
||||||
"template_version",
|
"template_version",
|
||||||
|
|
Loading…
Reference in New Issue