feat: add additional patch routes for group and role idp sync (#16351)
This commit is contained in:
Generated
+284
@@ -3438,6 +3438,100 @@ const docTemplate = `{
|
||||
}
|
||||
}
|
||||
},
|
||||
"/organizations/{organization}/settings/idpsync/groups/config": {
|
||||
"patch": {
|
||||
"security": [
|
||||
{
|
||||
"CoderSessionToken": []
|
||||
}
|
||||
],
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"Enterprise"
|
||||
],
|
||||
"summary": "Update group IdP Sync config",
|
||||
"operationId": "update-group-idp-sync-config",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
"format": "uuid",
|
||||
"description": "Organization ID or name",
|
||||
"name": "organization",
|
||||
"in": "path",
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"description": "New config values",
|
||||
"name": "request",
|
||||
"in": "body",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/definitions/codersdk.PatchGroupIDPSyncConfigRequest"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/codersdk.GroupSyncSettings"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/organizations/{organization}/settings/idpsync/groups/mapping": {
|
||||
"patch": {
|
||||
"security": [
|
||||
{
|
||||
"CoderSessionToken": []
|
||||
}
|
||||
],
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"Enterprise"
|
||||
],
|
||||
"summary": "Update group IdP Sync mapping",
|
||||
"operationId": "update-group-idp-sync-mapping",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
"format": "uuid",
|
||||
"description": "Organization ID or name",
|
||||
"name": "organization",
|
||||
"in": "path",
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"description": "Description of the mappings to add and remove",
|
||||
"name": "request",
|
||||
"in": "body",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/definitions/codersdk.PatchGroupIDPSyncMappingRequest"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/codersdk.GroupSyncSettings"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/organizations/{organization}/settings/idpsync/roles": {
|
||||
"get": {
|
||||
"security": [
|
||||
@@ -3518,6 +3612,100 @@ const docTemplate = `{
|
||||
}
|
||||
}
|
||||
},
|
||||
"/organizations/{organization}/settings/idpsync/roles/config": {
|
||||
"patch": {
|
||||
"security": [
|
||||
{
|
||||
"CoderSessionToken": []
|
||||
}
|
||||
],
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"Enterprise"
|
||||
],
|
||||
"summary": "Update role IdP Sync config",
|
||||
"operationId": "update-role-idp-sync-config",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
"format": "uuid",
|
||||
"description": "Organization ID or name",
|
||||
"name": "organization",
|
||||
"in": "path",
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"description": "New config values",
|
||||
"name": "request",
|
||||
"in": "body",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/definitions/codersdk.PatchRoleIDPSyncConfigRequest"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/codersdk.RoleSyncSettings"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/organizations/{organization}/settings/idpsync/roles/mapping": {
|
||||
"patch": {
|
||||
"security": [
|
||||
{
|
||||
"CoderSessionToken": []
|
||||
}
|
||||
],
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"Enterprise"
|
||||
],
|
||||
"summary": "Update role IdP Sync mapping",
|
||||
"operationId": "update-role-idp-sync-mapping",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
"format": "uuid",
|
||||
"description": "Organization ID or name",
|
||||
"name": "organization",
|
||||
"in": "path",
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"description": "Description of the mappings to add and remove",
|
||||
"name": "request",
|
||||
"in": "body",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/definitions/codersdk.PatchRoleIDPSyncMappingRequest"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/codersdk.RoleSyncSettings"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/organizations/{organization}/templates": {
|
||||
"get": {
|
||||
"security": [
|
||||
@@ -12469,6 +12657,57 @@ const docTemplate = `{
|
||||
}
|
||||
}
|
||||
},
|
||||
"codersdk.PatchGroupIDPSyncConfigRequest": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"auto_create_missing_groups": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"field": {
|
||||
"type": "string"
|
||||
},
|
||||
"regex_filter": {
|
||||
"$ref": "#/definitions/regexp.Regexp"
|
||||
}
|
||||
}
|
||||
},
|
||||
"codersdk.PatchGroupIDPSyncMappingRequest": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"add": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"gets": {
|
||||
"description": "The ID of the Coder resource the user should be added to",
|
||||
"type": "string"
|
||||
},
|
||||
"given": {
|
||||
"description": "The IdP claim the user has",
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"remove": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"gets": {
|
||||
"description": "The ID of the Coder resource the user should be added to",
|
||||
"type": "string"
|
||||
},
|
||||
"given": {
|
||||
"description": "The IdP claim the user has",
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"codersdk.PatchGroupRequest": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
@@ -12546,6 +12785,51 @@ const docTemplate = `{
|
||||
}
|
||||
}
|
||||
},
|
||||
"codersdk.PatchRoleIDPSyncConfigRequest": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"field": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"codersdk.PatchRoleIDPSyncMappingRequest": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"add": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"gets": {
|
||||
"description": "The ID of the Coder resource the user should be added to",
|
||||
"type": "string"
|
||||
},
|
||||
"given": {
|
||||
"description": "The IdP claim the user has",
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"remove": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"gets": {
|
||||
"description": "The ID of the Coder resource the user should be added to",
|
||||
"type": "string"
|
||||
},
|
||||
"given": {
|
||||
"description": "The IdP claim the user has",
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"codersdk.PatchTemplateVersionRequest": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
|
||||
Generated
+260
@@ -3030,6 +3030,88 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"/organizations/{organization}/settings/idpsync/groups/config": {
|
||||
"patch": {
|
||||
"security": [
|
||||
{
|
||||
"CoderSessionToken": []
|
||||
}
|
||||
],
|
||||
"consumes": ["application/json"],
|
||||
"produces": ["application/json"],
|
||||
"tags": ["Enterprise"],
|
||||
"summary": "Update group IdP Sync config",
|
||||
"operationId": "update-group-idp-sync-config",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
"format": "uuid",
|
||||
"description": "Organization ID or name",
|
||||
"name": "organization",
|
||||
"in": "path",
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"description": "New config values",
|
||||
"name": "request",
|
||||
"in": "body",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/definitions/codersdk.PatchGroupIDPSyncConfigRequest"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/codersdk.GroupSyncSettings"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/organizations/{organization}/settings/idpsync/groups/mapping": {
|
||||
"patch": {
|
||||
"security": [
|
||||
{
|
||||
"CoderSessionToken": []
|
||||
}
|
||||
],
|
||||
"consumes": ["application/json"],
|
||||
"produces": ["application/json"],
|
||||
"tags": ["Enterprise"],
|
||||
"summary": "Update group IdP Sync mapping",
|
||||
"operationId": "update-group-idp-sync-mapping",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
"format": "uuid",
|
||||
"description": "Organization ID or name",
|
||||
"name": "organization",
|
||||
"in": "path",
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"description": "Description of the mappings to add and remove",
|
||||
"name": "request",
|
||||
"in": "body",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/definitions/codersdk.PatchGroupIDPSyncMappingRequest"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/codersdk.GroupSyncSettings"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/organizations/{organization}/settings/idpsync/roles": {
|
||||
"get": {
|
||||
"security": [
|
||||
@@ -3100,6 +3182,88 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"/organizations/{organization}/settings/idpsync/roles/config": {
|
||||
"patch": {
|
||||
"security": [
|
||||
{
|
||||
"CoderSessionToken": []
|
||||
}
|
||||
],
|
||||
"consumes": ["application/json"],
|
||||
"produces": ["application/json"],
|
||||
"tags": ["Enterprise"],
|
||||
"summary": "Update role IdP Sync config",
|
||||
"operationId": "update-role-idp-sync-config",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
"format": "uuid",
|
||||
"description": "Organization ID or name",
|
||||
"name": "organization",
|
||||
"in": "path",
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"description": "New config values",
|
||||
"name": "request",
|
||||
"in": "body",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/definitions/codersdk.PatchRoleIDPSyncConfigRequest"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/codersdk.RoleSyncSettings"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/organizations/{organization}/settings/idpsync/roles/mapping": {
|
||||
"patch": {
|
||||
"security": [
|
||||
{
|
||||
"CoderSessionToken": []
|
||||
}
|
||||
],
|
||||
"consumes": ["application/json"],
|
||||
"produces": ["application/json"],
|
||||
"tags": ["Enterprise"],
|
||||
"summary": "Update role IdP Sync mapping",
|
||||
"operationId": "update-role-idp-sync-mapping",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
"format": "uuid",
|
||||
"description": "Organization ID or name",
|
||||
"name": "organization",
|
||||
"in": "path",
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"description": "Description of the mappings to add and remove",
|
||||
"name": "request",
|
||||
"in": "body",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/definitions/codersdk.PatchRoleIDPSyncMappingRequest"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/codersdk.RoleSyncSettings"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/organizations/{organization}/templates": {
|
||||
"get": {
|
||||
"security": [
|
||||
@@ -11238,6 +11402,57 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"codersdk.PatchGroupIDPSyncConfigRequest": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"auto_create_missing_groups": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"field": {
|
||||
"type": "string"
|
||||
},
|
||||
"regex_filter": {
|
||||
"$ref": "#/definitions/regexp.Regexp"
|
||||
}
|
||||
}
|
||||
},
|
||||
"codersdk.PatchGroupIDPSyncMappingRequest": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"add": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"gets": {
|
||||
"description": "The ID of the Coder resource the user should be added to",
|
||||
"type": "string"
|
||||
},
|
||||
"given": {
|
||||
"description": "The IdP claim the user has",
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"remove": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"gets": {
|
||||
"description": "The ID of the Coder resource the user should be added to",
|
||||
"type": "string"
|
||||
},
|
||||
"given": {
|
||||
"description": "The IdP claim the user has",
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"codersdk.PatchGroupRequest": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
@@ -11315,6 +11530,51 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"codersdk.PatchRoleIDPSyncConfigRequest": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"field": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"codersdk.PatchRoleIDPSyncMappingRequest": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"add": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"gets": {
|
||||
"description": "The ID of the Coder resource the user should be added to",
|
||||
"type": "string"
|
||||
},
|
||||
"given": {
|
||||
"description": "The IdP claim the user has",
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"remove": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"gets": {
|
||||
"description": "The ID of the Coder resource the user should be added to",
|
||||
"type": "string"
|
||||
},
|
||||
"given": {
|
||||
"description": "The IdP claim the user has",
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"codersdk.PatchTemplateVersionRequest": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
|
||||
@@ -30,7 +30,7 @@ func (AGPLIDPSync) GroupSyncEntitled() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (s AGPLIDPSync) UpdateGroupSettings(ctx context.Context, orgID uuid.UUID, db database.Store, settings GroupSyncSettings) error {
|
||||
func (s AGPLIDPSync) UpdateGroupSyncSettings(ctx context.Context, orgID uuid.UUID, db database.Store, settings GroupSyncSettings) error {
|
||||
orgResolver := s.Manager.OrganizationResolver(db, orgID)
|
||||
err := s.SyncSettings.Group.SetRuntimeValue(ctx, orgResolver, &settings)
|
||||
if err != nil {
|
||||
|
||||
@@ -48,7 +48,7 @@ type IDPSync interface {
|
||||
// on the settings used by IDPSync. This entry is thread safe and can be
|
||||
// accessed concurrently. The settings are stored in the database.
|
||||
GroupSyncSettings(ctx context.Context, orgID uuid.UUID, db database.Store) (*GroupSyncSettings, error)
|
||||
UpdateGroupSettings(ctx context.Context, orgID uuid.UUID, db database.Store, settings GroupSyncSettings) error
|
||||
UpdateGroupSyncSettings(ctx context.Context, orgID uuid.UUID, db database.Store, settings GroupSyncSettings) error
|
||||
|
||||
// RoleSyncEntitled returns true if the deployment is entitled to role syncing.
|
||||
RoleSyncEntitled() bool
|
||||
@@ -61,7 +61,7 @@ type IDPSync interface {
|
||||
// RoleSyncSettings is similar to GroupSyncSettings. See GroupSyncSettings for
|
||||
// rational.
|
||||
RoleSyncSettings(ctx context.Context, orgID uuid.UUID, db database.Store) (*RoleSyncSettings, error)
|
||||
UpdateRoleSettings(ctx context.Context, orgID uuid.UUID, db database.Store, settings RoleSyncSettings) error
|
||||
UpdateRoleSyncSettings(ctx context.Context, orgID uuid.UUID, db database.Store, settings RoleSyncSettings) error
|
||||
// ParseRoleClaims takes claims from an OIDC provider, and returns the params
|
||||
// for role syncing. Most of the logic happens in SyncRoles.
|
||||
ParseRoleClaims(ctx context.Context, mergedClaims jwt.MapClaims) (RoleParams, *HTTPError)
|
||||
|
||||
@@ -42,7 +42,7 @@ func (AGPLIDPSync) SiteRoleSyncEnabled() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (s AGPLIDPSync) UpdateRoleSettings(ctx context.Context, orgID uuid.UUID, db database.Store, settings RoleSyncSettings) error {
|
||||
func (s AGPLIDPSync) UpdateRoleSyncSettings(ctx context.Context, orgID uuid.UUID, db database.Store, settings RoleSyncSettings) error {
|
||||
orgResolver := s.Manager.OrganizationResolver(db, orgID)
|
||||
err := s.SyncSettings.Role.SetRuntimeValue(ctx, orgResolver, &settings)
|
||||
if err != nil {
|
||||
|
||||
@@ -68,6 +68,46 @@ func (c *Client) PatchGroupIDPSyncSettings(ctx context.Context, orgID string, re
|
||||
return resp, json.NewDecoder(res.Body).Decode(&resp)
|
||||
}
|
||||
|
||||
type PatchGroupIDPSyncConfigRequest struct {
|
||||
Field string `json:"field"`
|
||||
RegexFilter *regexp.Regexp `json:"regex_filter"`
|
||||
AutoCreateMissing bool `json:"auto_create_missing_groups"`
|
||||
}
|
||||
|
||||
func (c *Client) PatchGroupIDPSyncConfig(ctx context.Context, orgID string, req PatchGroupIDPSyncConfigRequest) (GroupSyncSettings, error) {
|
||||
res, err := c.Request(ctx, http.MethodPatch, fmt.Sprintf("/api/v2/organizations/%s/settings/idpsync/groups/config", orgID), req)
|
||||
if err != nil {
|
||||
return GroupSyncSettings{}, xerrors.Errorf("make request: %w", err)
|
||||
}
|
||||
defer res.Body.Close()
|
||||
|
||||
if res.StatusCode != http.StatusOK {
|
||||
return GroupSyncSettings{}, ReadBodyAsError(res)
|
||||
}
|
||||
var resp GroupSyncSettings
|
||||
return resp, json.NewDecoder(res.Body).Decode(&resp)
|
||||
}
|
||||
|
||||
// If the same mapping is present in both Add and Remove, Remove will take presidence.
|
||||
type PatchGroupIDPSyncMappingRequest struct {
|
||||
Add []IDPSyncMapping[uuid.UUID]
|
||||
Remove []IDPSyncMapping[uuid.UUID]
|
||||
}
|
||||
|
||||
func (c *Client) PatchGroupIDPSyncMapping(ctx context.Context, orgID string, req PatchGroupIDPSyncMappingRequest) (GroupSyncSettings, error) {
|
||||
res, err := c.Request(ctx, http.MethodPatch, fmt.Sprintf("/api/v2/organizations/%s/settings/idpsync/groups/mapping", orgID), req)
|
||||
if err != nil {
|
||||
return GroupSyncSettings{}, xerrors.Errorf("make request: %w", err)
|
||||
}
|
||||
defer res.Body.Close()
|
||||
|
||||
if res.StatusCode != http.StatusOK {
|
||||
return GroupSyncSettings{}, ReadBodyAsError(res)
|
||||
}
|
||||
var resp GroupSyncSettings
|
||||
return resp, json.NewDecoder(res.Body).Decode(&resp)
|
||||
}
|
||||
|
||||
type RoleSyncSettings struct {
|
||||
// Field is the name of the claim field that specifies what organization roles
|
||||
// a user should be given. If empty, no roles will be synced.
|
||||
@@ -104,6 +144,44 @@ func (c *Client) PatchRoleIDPSyncSettings(ctx context.Context, orgID string, req
|
||||
return resp, json.NewDecoder(res.Body).Decode(&resp)
|
||||
}
|
||||
|
||||
type PatchRoleIDPSyncConfigRequest struct {
|
||||
Field string `json:"field"`
|
||||
}
|
||||
|
||||
func (c *Client) PatchRoleIDPSyncConfig(ctx context.Context, orgID string, req PatchRoleIDPSyncConfigRequest) (RoleSyncSettings, error) {
|
||||
res, err := c.Request(ctx, http.MethodPatch, fmt.Sprintf("/api/v2/organizations/%s/settings/idpsync/roles/config", orgID), req)
|
||||
if err != nil {
|
||||
return RoleSyncSettings{}, xerrors.Errorf("make request: %w", err)
|
||||
}
|
||||
defer res.Body.Close()
|
||||
|
||||
if res.StatusCode != http.StatusOK {
|
||||
return RoleSyncSettings{}, ReadBodyAsError(res)
|
||||
}
|
||||
var resp RoleSyncSettings
|
||||
return resp, json.NewDecoder(res.Body).Decode(&resp)
|
||||
}
|
||||
|
||||
// If the same mapping is present in both Add and Remove, Remove will take presidence.
|
||||
type PatchRoleIDPSyncMappingRequest struct {
|
||||
Add []IDPSyncMapping[string]
|
||||
Remove []IDPSyncMapping[string]
|
||||
}
|
||||
|
||||
func (c *Client) PatchRoleIDPSyncMapping(ctx context.Context, orgID string, req PatchRoleIDPSyncMappingRequest) (RoleSyncSettings, error) {
|
||||
res, err := c.Request(ctx, http.MethodPatch, fmt.Sprintf("/api/v2/organizations/%s/settings/idpsync/roles/mapping", orgID), req)
|
||||
if err != nil {
|
||||
return RoleSyncSettings{}, xerrors.Errorf("make request: %w", err)
|
||||
}
|
||||
defer res.Body.Close()
|
||||
|
||||
if res.StatusCode != http.StatusOK {
|
||||
return RoleSyncSettings{}, ReadBodyAsError(res)
|
||||
}
|
||||
var resp RoleSyncSettings
|
||||
return resp, json.NewDecoder(res.Body).Decode(&resp)
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
Generated
+256
@@ -1953,6 +1953,141 @@ curl -X PATCH http://coder-server:8080/api/v2/organizations/{organization}/setti
|
||||
|
||||
To perform this operation, you must be authenticated. [Learn more](authentication.md).
|
||||
|
||||
## Update group IdP Sync config
|
||||
|
||||
### Code samples
|
||||
|
||||
```shell
|
||||
# Example request using curl
|
||||
curl -X PATCH http://coder-server:8080/api/v2/organizations/{organization}/settings/idpsync/groups/config \
|
||||
-H 'Content-Type: application/json' \
|
||||
-H 'Accept: application/json' \
|
||||
-H 'Coder-Session-Token: API_KEY'
|
||||
```
|
||||
|
||||
`PATCH /organizations/{organization}/settings/idpsync/groups/config`
|
||||
|
||||
> Body parameter
|
||||
|
||||
```json
|
||||
{
|
||||
"auto_create_missing_groups": true,
|
||||
"field": "string",
|
||||
"regex_filter": {}
|
||||
}
|
||||
```
|
||||
|
||||
### Parameters
|
||||
|
||||
| Name | In | Type | Required | Description |
|
||||
|----------------|------|----------------------------------------------------------------------------------------------|----------|-------------------------|
|
||||
| `organization` | path | string(uuid) | true | Organization ID or name |
|
||||
| `body` | body | [codersdk.PatchGroupIDPSyncConfigRequest](schemas.md#codersdkpatchgroupidpsyncconfigrequest) | true | New config values |
|
||||
|
||||
### Example responses
|
||||
|
||||
> 200 Response
|
||||
|
||||
```json
|
||||
{
|
||||
"auto_create_missing_groups": true,
|
||||
"field": "string",
|
||||
"legacy_group_name_mapping": {
|
||||
"property1": "string",
|
||||
"property2": "string"
|
||||
},
|
||||
"mapping": {
|
||||
"property1": [
|
||||
"string"
|
||||
],
|
||||
"property2": [
|
||||
"string"
|
||||
]
|
||||
},
|
||||
"regex_filter": {}
|
||||
}
|
||||
```
|
||||
|
||||
### Responses
|
||||
|
||||
| Status | Meaning | Description | Schema |
|
||||
|--------|---------------------------------------------------------|-------------|--------------------------------------------------------------------|
|
||||
| 200 | [OK](https://tools.ietf.org/html/rfc7231#section-6.3.1) | OK | [codersdk.GroupSyncSettings](schemas.md#codersdkgroupsyncsettings) |
|
||||
|
||||
To perform this operation, you must be authenticated. [Learn more](authentication.md).
|
||||
|
||||
## Update group IdP Sync mapping
|
||||
|
||||
### Code samples
|
||||
|
||||
```shell
|
||||
# Example request using curl
|
||||
curl -X PATCH http://coder-server:8080/api/v2/organizations/{organization}/settings/idpsync/groups/mapping \
|
||||
-H 'Content-Type: application/json' \
|
||||
-H 'Accept: application/json' \
|
||||
-H 'Coder-Session-Token: API_KEY'
|
||||
```
|
||||
|
||||
`PATCH /organizations/{organization}/settings/idpsync/groups/mapping`
|
||||
|
||||
> Body parameter
|
||||
|
||||
```json
|
||||
{
|
||||
"add": [
|
||||
{
|
||||
"gets": "string",
|
||||
"given": "string"
|
||||
}
|
||||
],
|
||||
"remove": [
|
||||
{
|
||||
"gets": "string",
|
||||
"given": "string"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### Parameters
|
||||
|
||||
| Name | In | Type | Required | Description |
|
||||
|----------------|------|------------------------------------------------------------------------------------------------|----------|-----------------------------------------------|
|
||||
| `organization` | path | string(uuid) | true | Organization ID or name |
|
||||
| `body` | body | [codersdk.PatchGroupIDPSyncMappingRequest](schemas.md#codersdkpatchgroupidpsyncmappingrequest) | true | Description of the mappings to add and remove |
|
||||
|
||||
### Example responses
|
||||
|
||||
> 200 Response
|
||||
|
||||
```json
|
||||
{
|
||||
"auto_create_missing_groups": true,
|
||||
"field": "string",
|
||||
"legacy_group_name_mapping": {
|
||||
"property1": "string",
|
||||
"property2": "string"
|
||||
},
|
||||
"mapping": {
|
||||
"property1": [
|
||||
"string"
|
||||
],
|
||||
"property2": [
|
||||
"string"
|
||||
]
|
||||
},
|
||||
"regex_filter": {}
|
||||
}
|
||||
```
|
||||
|
||||
### Responses
|
||||
|
||||
| Status | Meaning | Description | Schema |
|
||||
|--------|---------------------------------------------------------|-------------|--------------------------------------------------------------------|
|
||||
| 200 | [OK](https://tools.ietf.org/html/rfc7231#section-6.3.1) | OK | [codersdk.GroupSyncSettings](schemas.md#codersdkgroupsyncsettings) |
|
||||
|
||||
To perform this operation, you must be authenticated. [Learn more](authentication.md).
|
||||
|
||||
## Get role IdP Sync settings by organization
|
||||
|
||||
### Code samples
|
||||
@@ -2061,6 +2196,127 @@ curl -X PATCH http://coder-server:8080/api/v2/organizations/{organization}/setti
|
||||
|
||||
To perform this operation, you must be authenticated. [Learn more](authentication.md).
|
||||
|
||||
## Update role IdP Sync config
|
||||
|
||||
### Code samples
|
||||
|
||||
```shell
|
||||
# Example request using curl
|
||||
curl -X PATCH http://coder-server:8080/api/v2/organizations/{organization}/settings/idpsync/roles/config \
|
||||
-H 'Content-Type: application/json' \
|
||||
-H 'Accept: application/json' \
|
||||
-H 'Coder-Session-Token: API_KEY'
|
||||
```
|
||||
|
||||
`PATCH /organizations/{organization}/settings/idpsync/roles/config`
|
||||
|
||||
> Body parameter
|
||||
|
||||
```json
|
||||
{
|
||||
"field": "string"
|
||||
}
|
||||
```
|
||||
|
||||
### Parameters
|
||||
|
||||
| Name | In | Type | Required | Description |
|
||||
|----------------|------|--------------------------------------------------------------------------------------------|----------|-------------------------|
|
||||
| `organization` | path | string(uuid) | true | Organization ID or name |
|
||||
| `body` | body | [codersdk.PatchRoleIDPSyncConfigRequest](schemas.md#codersdkpatchroleidpsyncconfigrequest) | true | New config values |
|
||||
|
||||
### Example responses
|
||||
|
||||
> 200 Response
|
||||
|
||||
```json
|
||||
{
|
||||
"field": "string",
|
||||
"mapping": {
|
||||
"property1": [
|
||||
"string"
|
||||
],
|
||||
"property2": [
|
||||
"string"
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Responses
|
||||
|
||||
| Status | Meaning | Description | Schema |
|
||||
|--------|---------------------------------------------------------|-------------|------------------------------------------------------------------|
|
||||
| 200 | [OK](https://tools.ietf.org/html/rfc7231#section-6.3.1) | OK | [codersdk.RoleSyncSettings](schemas.md#codersdkrolesyncsettings) |
|
||||
|
||||
To perform this operation, you must be authenticated. [Learn more](authentication.md).
|
||||
|
||||
## Update role IdP Sync mapping
|
||||
|
||||
### Code samples
|
||||
|
||||
```shell
|
||||
# Example request using curl
|
||||
curl -X PATCH http://coder-server:8080/api/v2/organizations/{organization}/settings/idpsync/roles/mapping \
|
||||
-H 'Content-Type: application/json' \
|
||||
-H 'Accept: application/json' \
|
||||
-H 'Coder-Session-Token: API_KEY'
|
||||
```
|
||||
|
||||
`PATCH /organizations/{organization}/settings/idpsync/roles/mapping`
|
||||
|
||||
> Body parameter
|
||||
|
||||
```json
|
||||
{
|
||||
"add": [
|
||||
{
|
||||
"gets": "string",
|
||||
"given": "string"
|
||||
}
|
||||
],
|
||||
"remove": [
|
||||
{
|
||||
"gets": "string",
|
||||
"given": "string"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### Parameters
|
||||
|
||||
| Name | In | Type | Required | Description |
|
||||
|----------------|------|----------------------------------------------------------------------------------------------|----------|-----------------------------------------------|
|
||||
| `organization` | path | string(uuid) | true | Organization ID or name |
|
||||
| `body` | body | [codersdk.PatchRoleIDPSyncMappingRequest](schemas.md#codersdkpatchroleidpsyncmappingrequest) | true | Description of the mappings to add and remove |
|
||||
|
||||
### Example responses
|
||||
|
||||
> 200 Response
|
||||
|
||||
```json
|
||||
{
|
||||
"field": "string",
|
||||
"mapping": {
|
||||
"property1": [
|
||||
"string"
|
||||
],
|
||||
"property2": [
|
||||
"string"
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Responses
|
||||
|
||||
| Status | Meaning | Description | Schema |
|
||||
|--------|---------------------------------------------------------|-------------|------------------------------------------------------------------|
|
||||
| 200 | [OK](https://tools.ietf.org/html/rfc7231#section-6.3.1) | OK | [codersdk.RoleSyncSettings](schemas.md#codersdkrolesyncsettings) |
|
||||
|
||||
To perform this operation, you must be authenticated. [Learn more](authentication.md).
|
||||
|
||||
## Fetch provisioner key details
|
||||
|
||||
### Code samples
|
||||
|
||||
Generated
+92
@@ -4152,6 +4152,54 @@ Git clone makes use of this by parsing the URL from: 'Username for "https://gith
|
||||
| » `[any property]` | array of string | false | | |
|
||||
| `organization_assign_default` | boolean | false | | Organization assign default will ensure the default org is always included for every user, regardless of their claims. This preserves legacy behavior. |
|
||||
|
||||
## codersdk.PatchGroupIDPSyncConfigRequest
|
||||
|
||||
```json
|
||||
{
|
||||
"auto_create_missing_groups": true,
|
||||
"field": "string",
|
||||
"regex_filter": {}
|
||||
}
|
||||
```
|
||||
|
||||
### Properties
|
||||
|
||||
| Name | Type | Required | Restrictions | Description |
|
||||
|------------------------------|--------------------------------|----------|--------------|-------------|
|
||||
| `auto_create_missing_groups` | boolean | false | | |
|
||||
| `field` | string | false | | |
|
||||
| `regex_filter` | [regexp.Regexp](#regexpregexp) | false | | |
|
||||
|
||||
## codersdk.PatchGroupIDPSyncMappingRequest
|
||||
|
||||
```json
|
||||
{
|
||||
"add": [
|
||||
{
|
||||
"gets": "string",
|
||||
"given": "string"
|
||||
}
|
||||
],
|
||||
"remove": [
|
||||
{
|
||||
"gets": "string",
|
||||
"given": "string"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### Properties
|
||||
|
||||
| Name | Type | Required | Restrictions | Description |
|
||||
|-----------|-----------------|----------|--------------|----------------------------------------------------------|
|
||||
| `add` | array of object | false | | |
|
||||
| `» gets` | string | false | | The ID of the Coder resource the user should be added to |
|
||||
| `» given` | string | false | | The IdP claim the user has |
|
||||
| `remove` | array of object | false | | |
|
||||
| `» gets` | string | false | | The ID of the Coder resource the user should be added to |
|
||||
| `» given` | string | false | | The IdP claim the user has |
|
||||
|
||||
## codersdk.PatchGroupRequest
|
||||
|
||||
```json
|
||||
@@ -4226,6 +4274,50 @@ Git clone makes use of this by parsing the URL from: 'Username for "https://gith
|
||||
| `» gets` | string | false | | The ID of the Coder resource the user should be added to |
|
||||
| `» given` | string | false | | The IdP claim the user has |
|
||||
|
||||
## codersdk.PatchRoleIDPSyncConfigRequest
|
||||
|
||||
```json
|
||||
{
|
||||
"field": "string"
|
||||
}
|
||||
```
|
||||
|
||||
### Properties
|
||||
|
||||
| Name | Type | Required | Restrictions | Description |
|
||||
|---------|--------|----------|--------------|-------------|
|
||||
| `field` | string | false | | |
|
||||
|
||||
## codersdk.PatchRoleIDPSyncMappingRequest
|
||||
|
||||
```json
|
||||
{
|
||||
"add": [
|
||||
{
|
||||
"gets": "string",
|
||||
"given": "string"
|
||||
}
|
||||
],
|
||||
"remove": [
|
||||
{
|
||||
"gets": "string",
|
||||
"given": "string"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### Properties
|
||||
|
||||
| Name | Type | Required | Restrictions | Description |
|
||||
|-----------|-----------------|----------|--------------|----------------------------------------------------------|
|
||||
| `add` | array of object | false | | |
|
||||
| `» gets` | string | false | | The ID of the Coder resource the user should be added to |
|
||||
| `» given` | string | false | | The IdP claim the user has |
|
||||
| `remove` | array of object | false | | |
|
||||
| `» gets` | string | false | | The ID of the Coder resource the user should be added to |
|
||||
| `» given` | string | false | | The IdP claim the user has |
|
||||
|
||||
## codersdk.PatchTemplateVersionRequest
|
||||
|
||||
```json
|
||||
|
||||
@@ -312,8 +312,13 @@ func New(ctx context.Context, options *Options) (_ *API, err error) {
|
||||
r.Route("/organizations/{organization}/settings", func(r chi.Router) {
|
||||
r.Get("/idpsync/groups", api.groupIDPSyncSettings)
|
||||
r.Patch("/idpsync/groups", api.patchGroupIDPSyncSettings)
|
||||
r.Patch("/idpsync/groups/config", api.patchGroupIDPSyncConfig)
|
||||
r.Patch("/idpsync/groups/mapping", api.patchGroupIDPSyncMapping)
|
||||
|
||||
r.Get("/idpsync/roles", api.roleIDPSyncSettings)
|
||||
r.Patch("/idpsync/roles", api.patchRoleIDPSyncSettings)
|
||||
r.Patch("/idpsync/roles/config", api.patchRoleIDPSyncConfig)
|
||||
r.Patch("/idpsync/roles/mapping", api.patchRoleIDPSyncMapping)
|
||||
|
||||
r.Get("/idpsync/available-fields", api.organizationIDPSyncClaimFields)
|
||||
r.Get("/idpsync/field-values", api.organizationIDPSyncClaimFieldValues)
|
||||
|
||||
+317
-31
@@ -61,7 +61,6 @@ func (api *API) patchGroupIDPSyncSettings(rw http.ResponseWriter, r *http.Reques
|
||||
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,
|
||||
@@ -104,7 +103,7 @@ func (api *API) patchGroupIDPSyncSettings(rw http.ResponseWriter, r *http.Reques
|
||||
}
|
||||
aReq.Old = *existing
|
||||
|
||||
err = api.IDPSync.UpdateGroupSettings(sysCtx, org.ID, api.Database, idpsync.GroupSyncSettings{
|
||||
err = api.IDPSync.UpdateGroupSyncSettings(sysCtx, org.ID, api.Database, idpsync.GroupSyncSettings{
|
||||
Field: req.Field,
|
||||
Mapping: req.Mapping,
|
||||
RegexFilter: req.RegexFilter,
|
||||
@@ -132,6 +131,153 @@ func (api *API) patchGroupIDPSyncSettings(rw http.ResponseWriter, r *http.Reques
|
||||
})
|
||||
}
|
||||
|
||||
// @Summary Update group IdP Sync config
|
||||
// @ID update-group-idp-sync-config
|
||||
// @Security CoderSessionToken
|
||||
// @Produce json
|
||||
// @Accept json
|
||||
// @Tags Enterprise
|
||||
// @Success 200 {object} codersdk.GroupSyncSettings
|
||||
// @Param organization path string true "Organization ID or name" format(uuid)
|
||||
// @Param request body codersdk.PatchGroupIDPSyncConfigRequest true "New config values"
|
||||
// @Router /organizations/{organization}/settings/idpsync/groups/config [patch]
|
||||
func (api *API) patchGroupIDPSyncConfig(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)
|
||||
return
|
||||
}
|
||||
|
||||
var req codersdk.PatchGroupIDPSyncConfigRequest
|
||||
if !httpapi.Read(ctx, rw, r, &req) {
|
||||
return
|
||||
}
|
||||
|
||||
var settings idpsync.GroupSyncSettings
|
||||
//nolint:gocritic // Requires system context to update runtime config
|
||||
sysCtx := dbauthz.AsSystemRestricted(ctx)
|
||||
err := database.ReadModifyUpdate(api.Database, func(tx database.Store) error {
|
||||
existing, err := api.IDPSync.GroupSyncSettings(sysCtx, org.ID, tx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
aReq.Old = *existing
|
||||
|
||||
settings = idpsync.GroupSyncSettings{
|
||||
Field: req.Field,
|
||||
RegexFilter: req.RegexFilter,
|
||||
AutoCreateMissing: req.AutoCreateMissing,
|
||||
LegacyNameMapping: existing.LegacyNameMapping,
|
||||
Mapping: existing.Mapping,
|
||||
}
|
||||
|
||||
err = api.IDPSync.UpdateGroupSyncSettings(sysCtx, org.ID, tx, settings)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
httpapi.InternalServerError(rw, err)
|
||||
return
|
||||
}
|
||||
|
||||
aReq.New = settings
|
||||
httpapi.Write(ctx, rw, http.StatusOK, codersdk.GroupSyncSettings{
|
||||
Field: settings.Field,
|
||||
RegexFilter: settings.RegexFilter,
|
||||
AutoCreateMissing: settings.AutoCreateMissing,
|
||||
LegacyNameMapping: settings.LegacyNameMapping,
|
||||
Mapping: settings.Mapping,
|
||||
})
|
||||
}
|
||||
|
||||
// @Summary Update group IdP Sync mapping
|
||||
// @ID update-group-idp-sync-mapping
|
||||
// @Security CoderSessionToken
|
||||
// @Produce json
|
||||
// @Accept json
|
||||
// @Tags Enterprise
|
||||
// @Success 200 {object} codersdk.GroupSyncSettings
|
||||
// @Param organization path string true "Organization ID or name" format(uuid)
|
||||
// @Param request body codersdk.PatchGroupIDPSyncMappingRequest true "Description of the mappings to add and remove"
|
||||
// @Router /organizations/{organization}/settings/idpsync/groups/mapping [patch]
|
||||
func (api *API) patchGroupIDPSyncMapping(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)
|
||||
return
|
||||
}
|
||||
|
||||
var req codersdk.PatchGroupIDPSyncMappingRequest
|
||||
if !httpapi.Read(ctx, rw, r, &req) {
|
||||
return
|
||||
}
|
||||
|
||||
var settings idpsync.GroupSyncSettings
|
||||
//nolint:gocritic // Requires system context to update runtime config
|
||||
sysCtx := dbauthz.AsSystemRestricted(ctx)
|
||||
err := database.ReadModifyUpdate(api.Database, func(tx database.Store) error {
|
||||
existing, err := api.IDPSync.GroupSyncSettings(sysCtx, org.ID, tx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
aReq.Old = *existing
|
||||
|
||||
newMapping := applyIDPSyncMappingDiff(existing.Mapping, req.Add, req.Remove)
|
||||
settings = idpsync.GroupSyncSettings{
|
||||
Field: existing.Field,
|
||||
RegexFilter: existing.RegexFilter,
|
||||
AutoCreateMissing: existing.AutoCreateMissing,
|
||||
LegacyNameMapping: existing.LegacyNameMapping,
|
||||
Mapping: newMapping,
|
||||
}
|
||||
|
||||
err = api.IDPSync.UpdateGroupSyncSettings(sysCtx, org.ID, tx, settings)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
httpapi.InternalServerError(rw, err)
|
||||
return
|
||||
}
|
||||
|
||||
aReq.New = settings
|
||||
httpapi.Write(ctx, rw, http.StatusOK, codersdk.GroupSyncSettings{
|
||||
Field: settings.Field,
|
||||
RegexFilter: settings.RegexFilter,
|
||||
AutoCreateMissing: settings.AutoCreateMissing,
|
||||
LegacyNameMapping: settings.LegacyNameMapping,
|
||||
Mapping: settings.Mapping,
|
||||
})
|
||||
}
|
||||
|
||||
// @Summary Get role IdP Sync settings by organization
|
||||
// @ID get-role-idp-sync-settings-by-organization
|
||||
// @Security CoderSessionToken
|
||||
@@ -203,7 +349,7 @@ func (api *API) patchRoleIDPSyncSettings(rw http.ResponseWriter, r *http.Request
|
||||
}
|
||||
aReq.Old = *existing
|
||||
|
||||
err = api.IDPSync.UpdateRoleSettings(sysCtx, org.ID, api.Database, idpsync.RoleSyncSettings{
|
||||
err = api.IDPSync.UpdateRoleSyncSettings(sysCtx, org.ID, api.Database, idpsync.RoleSyncSettings{
|
||||
Field: req.Field,
|
||||
Mapping: req.Mapping,
|
||||
})
|
||||
@@ -225,6 +371,141 @@ func (api *API) patchRoleIDPSyncSettings(rw http.ResponseWriter, r *http.Request
|
||||
})
|
||||
}
|
||||
|
||||
// @Summary Update role IdP Sync config
|
||||
// @ID update-role-idp-sync-config
|
||||
// @Security CoderSessionToken
|
||||
// @Produce json
|
||||
// @Accept json
|
||||
// @Tags Enterprise
|
||||
// @Success 200 {object} codersdk.RoleSyncSettings
|
||||
// @Param organization path string true "Organization ID or name" format(uuid)
|
||||
// @Param request body codersdk.PatchRoleIDPSyncConfigRequest true "New config values"
|
||||
// @Router /organizations/{organization}/settings/idpsync/roles/config [patch]
|
||||
func (api *API) patchRoleIDPSyncConfig(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)
|
||||
return
|
||||
}
|
||||
|
||||
var req codersdk.PatchRoleIDPSyncConfigRequest
|
||||
if !httpapi.Read(ctx, rw, r, &req) {
|
||||
return
|
||||
}
|
||||
|
||||
var settings idpsync.RoleSyncSettings
|
||||
//nolint:gocritic // Requires system context to update runtime config
|
||||
sysCtx := dbauthz.AsSystemRestricted(ctx)
|
||||
err := database.ReadModifyUpdate(api.Database, func(tx database.Store) error {
|
||||
existing, err := api.IDPSync.RoleSyncSettings(sysCtx, org.ID, tx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
aReq.Old = *existing
|
||||
|
||||
settings = idpsync.RoleSyncSettings{
|
||||
Field: req.Field,
|
||||
Mapping: existing.Mapping,
|
||||
}
|
||||
|
||||
err = api.IDPSync.UpdateRoleSyncSettings(sysCtx, org.ID, tx, settings)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
httpapi.InternalServerError(rw, err)
|
||||
return
|
||||
}
|
||||
|
||||
aReq.New = settings
|
||||
httpapi.Write(ctx, rw, http.StatusOK, codersdk.RoleSyncSettings{
|
||||
Field: settings.Field,
|
||||
Mapping: settings.Mapping,
|
||||
})
|
||||
}
|
||||
|
||||
// @Summary Update role IdP Sync mapping
|
||||
// @ID update-role-idp-sync-mapping
|
||||
// @Security CoderSessionToken
|
||||
// @Produce json
|
||||
// @Accept json
|
||||
// @Tags Enterprise
|
||||
// @Success 200 {object} codersdk.RoleSyncSettings
|
||||
// @Param organization path string true "Organization ID or name" format(uuid)
|
||||
// @Param request body codersdk.PatchRoleIDPSyncMappingRequest true "Description of the mappings to add and remove"
|
||||
// @Router /organizations/{organization}/settings/idpsync/roles/mapping [patch]
|
||||
func (api *API) patchRoleIDPSyncMapping(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)
|
||||
return
|
||||
}
|
||||
|
||||
var req codersdk.PatchRoleIDPSyncMappingRequest
|
||||
if !httpapi.Read(ctx, rw, r, &req) {
|
||||
return
|
||||
}
|
||||
|
||||
var settings idpsync.RoleSyncSettings
|
||||
//nolint:gocritic // Requires system context to update runtime config
|
||||
sysCtx := dbauthz.AsSystemRestricted(ctx)
|
||||
err := database.ReadModifyUpdate(api.Database, func(tx database.Store) error {
|
||||
existing, err := api.IDPSync.RoleSyncSettings(sysCtx, org.ID, tx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
aReq.Old = *existing
|
||||
|
||||
newMapping := applyIDPSyncMappingDiff(existing.Mapping, req.Add, req.Remove)
|
||||
settings = idpsync.RoleSyncSettings{
|
||||
Field: existing.Field,
|
||||
Mapping: newMapping,
|
||||
}
|
||||
|
||||
err = api.IDPSync.UpdateRoleSyncSettings(sysCtx, org.ID, tx, settings)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
httpapi.InternalServerError(rw, err)
|
||||
return
|
||||
}
|
||||
|
||||
aReq.New = settings
|
||||
httpapi.Write(ctx, rw, http.StatusOK, codersdk.RoleSyncSettings{
|
||||
Field: settings.Field,
|
||||
Mapping: settings.Mapping,
|
||||
})
|
||||
}
|
||||
|
||||
// @Summary Get organization IdP Sync settings
|
||||
// @ID get-organization-idp-sync-settings
|
||||
// @Security CoderSessionToken
|
||||
@@ -349,7 +630,7 @@ func (api *API) patchOrganizationIDPSyncConfig(rw http.ResponseWriter, r *http.R
|
||||
return
|
||||
}
|
||||
|
||||
var settings *idpsync.OrganizationSyncSettings
|
||||
var settings idpsync.OrganizationSyncSettings
|
||||
//nolint:gocritic // Requires system context to update runtime config
|
||||
sysCtx := dbauthz.AsSystemRestricted(ctx)
|
||||
err := database.ReadModifyUpdate(api.Database, func(tx database.Store) error {
|
||||
@@ -359,16 +640,13 @@ func (api *API) patchOrganizationIDPSyncConfig(rw http.ResponseWriter, r *http.R
|
||||
}
|
||||
aReq.Old = *existing
|
||||
|
||||
err = api.IDPSync.UpdateOrganizationSyncSettings(sysCtx, tx, idpsync.OrganizationSyncSettings{
|
||||
settings = idpsync.OrganizationSyncSettings{
|
||||
Field: req.Field,
|
||||
AssignDefault: req.AssignDefault,
|
||||
Mapping: existing.Mapping,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
settings, err = api.IDPSync.OrganizationSyncSettings(sysCtx, tx)
|
||||
err = api.IDPSync.UpdateOrganizationSyncSettings(sysCtx, tx, settings)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -380,7 +658,7 @@ func (api *API) patchOrganizationIDPSyncConfig(rw http.ResponseWriter, r *http.R
|
||||
return
|
||||
}
|
||||
|
||||
aReq.New = *settings
|
||||
aReq.New = settings
|
||||
httpapi.Write(ctx, rw, http.StatusOK, codersdk.OrganizationSyncSettings{
|
||||
Field: settings.Field,
|
||||
Mapping: settings.Mapping,
|
||||
@@ -428,27 +706,7 @@ func (api *API) patchOrganizationIDPSyncMapping(rw http.ResponseWriter, r *http.
|
||||
}
|
||||
aReq.Old = *existing
|
||||
|
||||
newMapping := make(map[string][]uuid.UUID)
|
||||
|
||||
// Copy existing mapping
|
||||
for key, ids := range existing.Mapping {
|
||||
newMapping[key] = append(newMapping[key], ids...)
|
||||
}
|
||||
|
||||
// Add unique entries
|
||||
for _, mapping := range req.Add {
|
||||
if !slice.Contains(newMapping[mapping.Given], mapping.Gets) {
|
||||
newMapping[mapping.Given] = append(newMapping[mapping.Given], mapping.Gets)
|
||||
}
|
||||
}
|
||||
|
||||
// Remove entries
|
||||
for _, mapping := range req.Remove {
|
||||
newMapping[mapping.Given] = slices.DeleteFunc(newMapping[mapping.Given], func(u uuid.UUID) bool {
|
||||
return u == mapping.Gets
|
||||
})
|
||||
}
|
||||
|
||||
newMapping := applyIDPSyncMappingDiff(existing.Mapping, req.Add, req.Remove)
|
||||
settings = idpsync.OrganizationSyncSettings{
|
||||
Field: existing.Field,
|
||||
Mapping: newMapping,
|
||||
@@ -581,3 +839,31 @@ func (api *API) idpSyncClaimFieldValues(orgID uuid.UUID, rw http.ResponseWriter,
|
||||
|
||||
httpapi.Write(ctx, rw, http.StatusOK, fieldValues)
|
||||
}
|
||||
|
||||
func applyIDPSyncMappingDiff[IDType uuid.UUID | string](
|
||||
previous map[string][]IDType,
|
||||
add, remove []codersdk.IDPSyncMapping[IDType],
|
||||
) map[string][]IDType {
|
||||
next := make(map[string][]IDType)
|
||||
|
||||
// Copy existing mapping
|
||||
for key, ids := range previous {
|
||||
next[key] = append(next[key], ids...)
|
||||
}
|
||||
|
||||
// Add unique entries
|
||||
for _, mapping := range add {
|
||||
if !slice.Contains(next[mapping.Given], mapping.Gets) {
|
||||
next[mapping.Given] = append(next[mapping.Given], mapping.Gets)
|
||||
}
|
||||
}
|
||||
|
||||
// Remove entries
|
||||
for _, mapping := range remove {
|
||||
next[mapping.Given] = slices.DeleteFunc(next[mapping.Given], func(u IDType) bool {
|
||||
return u == mapping.Gets
|
||||
})
|
||||
}
|
||||
|
||||
return next
|
||||
}
|
||||
|
||||
@@ -0,0 +1,117 @@
|
||||
package coderd
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/coder/coder/v2/codersdk"
|
||||
)
|
||||
|
||||
func TestApplyIDPSyncMappingDiff(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
t.Run("with UUIDs", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
id := []uuid.UUID{
|
||||
uuid.MustParse("00000000-b8bd-46bb-bb6c-6c2b2c0dd2ea"),
|
||||
uuid.MustParse("01000000-fbe8-464c-9429-fe01a03f3644"),
|
||||
uuid.MustParse("02000000-0926-407b-9998-39af62e3d0c5"),
|
||||
uuid.MustParse("03000000-92f6-4bfd-bba6-0f54667b131c"),
|
||||
}
|
||||
|
||||
mapping := applyIDPSyncMappingDiff(map[string][]uuid.UUID{},
|
||||
[]codersdk.IDPSyncMapping[uuid.UUID]{
|
||||
{Given: "wibble", Gets: id[0]},
|
||||
{Given: "wibble", Gets: id[1]},
|
||||
{Given: "wobble", Gets: id[0]},
|
||||
{Given: "wobble", Gets: id[1]},
|
||||
{Given: "wobble", Gets: id[2]},
|
||||
{Given: "wobble", Gets: id[3]},
|
||||
{Given: "wooble", Gets: id[0]},
|
||||
},
|
||||
// Remove takes priority over Add, so `3` should not actually be added.
|
||||
[]codersdk.IDPSyncMapping[uuid.UUID]{
|
||||
{Given: "wobble", Gets: id[3]},
|
||||
},
|
||||
)
|
||||
|
||||
expected := map[string][]uuid.UUID{
|
||||
"wibble": {id[0], id[1]},
|
||||
"wobble": {id[0], id[1], id[2]},
|
||||
"wooble": {id[0]},
|
||||
}
|
||||
|
||||
require.Equal(t, expected, mapping)
|
||||
|
||||
mapping = applyIDPSyncMappingDiff(mapping,
|
||||
[]codersdk.IDPSyncMapping[uuid.UUID]{
|
||||
{Given: "wibble", Gets: id[2]},
|
||||
{Given: "wobble", Gets: id[3]},
|
||||
{Given: "wooble", Gets: id[0]},
|
||||
},
|
||||
[]codersdk.IDPSyncMapping[uuid.UUID]{
|
||||
{Given: "wibble", Gets: id[0]},
|
||||
{Given: "wobble", Gets: id[1]},
|
||||
},
|
||||
)
|
||||
|
||||
expected = map[string][]uuid.UUID{
|
||||
"wibble": {id[1], id[2]},
|
||||
"wobble": {id[0], id[2], id[3]},
|
||||
"wooble": {id[0]},
|
||||
}
|
||||
|
||||
require.Equal(t, expected, mapping)
|
||||
})
|
||||
|
||||
t.Run("with strings", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
mapping := applyIDPSyncMappingDiff(map[string][]string{},
|
||||
[]codersdk.IDPSyncMapping[string]{
|
||||
{Given: "wibble", Gets: "group-00"},
|
||||
{Given: "wibble", Gets: "group-01"},
|
||||
{Given: "wobble", Gets: "group-00"},
|
||||
{Given: "wobble", Gets: "group-01"},
|
||||
{Given: "wobble", Gets: "group-02"},
|
||||
{Given: "wobble", Gets: "group-03"},
|
||||
{Given: "wooble", Gets: "group-00"},
|
||||
},
|
||||
// Remove takes priority over Add, so `3` should not actually be added.
|
||||
[]codersdk.IDPSyncMapping[string]{
|
||||
{Given: "wobble", Gets: "group-03"},
|
||||
},
|
||||
)
|
||||
|
||||
expected := map[string][]string{
|
||||
"wibble": {"group-00", "group-01"},
|
||||
"wobble": {"group-00", "group-01", "group-02"},
|
||||
"wooble": {"group-00"},
|
||||
}
|
||||
|
||||
require.Equal(t, expected, mapping)
|
||||
|
||||
mapping = applyIDPSyncMappingDiff(mapping,
|
||||
[]codersdk.IDPSyncMapping[string]{
|
||||
{Given: "wibble", Gets: "group-02"},
|
||||
{Given: "wobble", Gets: "group-03"},
|
||||
{Given: "wooble", Gets: "group-00"},
|
||||
},
|
||||
[]codersdk.IDPSyncMapping[string]{
|
||||
{Given: "wibble", Gets: "group-00"},
|
||||
{Given: "wobble", Gets: "group-01"},
|
||||
},
|
||||
)
|
||||
|
||||
expected = map[string][]string{
|
||||
"wibble": {"group-01", "group-02"},
|
||||
"wobble": {"group-00", "group-02", "group-03"},
|
||||
"wooble": {"group-00"},
|
||||
}
|
||||
|
||||
require.Equal(t, expected, mapping)
|
||||
})
|
||||
}
|
||||
@@ -141,6 +141,171 @@ func TestPatchGroupSyncSettings(t *testing.T) {
|
||||
})
|
||||
}
|
||||
|
||||
func TestPatchGroupSyncConfig(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
t.Run("OK", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
owner, user := coderdenttest.New(t, &coderdenttest.Options{
|
||||
LicenseOptions: &coderdenttest.LicenseOptions{
|
||||
Features: license.Features{
|
||||
codersdk.FeatureCustomRoles: 1,
|
||||
codersdk.FeatureMultipleOrganizations: 1,
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
orgID := user.OrganizationID
|
||||
orgAdmin, _ := coderdtest.CreateAnotherUser(t, owner, orgID, rbac.ScopedRoleOrgAdmin(user.OrganizationID))
|
||||
|
||||
mapping := map[string][]uuid.UUID{"wibble": {uuid.New()}}
|
||||
|
||||
ctx := testutil.Context(t, testutil.WaitShort)
|
||||
_, err := orgAdmin.PatchGroupIDPSyncSettings(ctx, orgID.String(), codersdk.GroupSyncSettings{
|
||||
Field: "wibble",
|
||||
RegexFilter: regexp.MustCompile("wib{2,}le"),
|
||||
AutoCreateMissing: false,
|
||||
Mapping: mapping,
|
||||
})
|
||||
|
||||
require.NoError(t, err)
|
||||
|
||||
fetchedSettings, err := orgAdmin.GroupIDPSyncSettings(ctx, orgID.String())
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, "wibble", fetchedSettings.Field)
|
||||
require.Equal(t, "wib{2,}le", fetchedSettings.RegexFilter.String())
|
||||
require.Equal(t, false, fetchedSettings.AutoCreateMissing)
|
||||
require.Equal(t, mapping, fetchedSettings.Mapping)
|
||||
|
||||
ctx = testutil.Context(t, testutil.WaitShort)
|
||||
settings, err := orgAdmin.PatchGroupIDPSyncConfig(ctx, orgID.String(), codersdk.PatchGroupIDPSyncConfigRequest{
|
||||
Field: "wobble",
|
||||
RegexFilter: regexp.MustCompile("wob{2,}le"),
|
||||
AutoCreateMissing: true,
|
||||
})
|
||||
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, "wobble", settings.Field)
|
||||
require.Equal(t, "wob{2,}le", settings.RegexFilter.String())
|
||||
require.Equal(t, true, settings.AutoCreateMissing)
|
||||
require.Equal(t, mapping, settings.Mapping)
|
||||
|
||||
fetchedSettings, err = orgAdmin.GroupIDPSyncSettings(ctx, orgID.String())
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, "wobble", fetchedSettings.Field)
|
||||
require.Equal(t, "wob{2,}le", fetchedSettings.RegexFilter.String())
|
||||
require.Equal(t, true, fetchedSettings.AutoCreateMissing)
|
||||
require.Equal(t, mapping, fetchedSettings.Mapping)
|
||||
})
|
||||
|
||||
t.Run("NotAuthorized", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
owner, user := coderdenttest.New(t, &coderdenttest.Options{
|
||||
LicenseOptions: &coderdenttest.LicenseOptions{
|
||||
Features: license.Features{
|
||||
codersdk.FeatureCustomRoles: 1,
|
||||
codersdk.FeatureMultipleOrganizations: 1,
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
member, _ := coderdtest.CreateAnotherUser(t, owner, user.OrganizationID)
|
||||
|
||||
ctx := testutil.Context(t, testutil.WaitShort)
|
||||
_, err := member.PatchGroupIDPSyncConfig(ctx, user.OrganizationID.String(), codersdk.PatchGroupIDPSyncConfigRequest{})
|
||||
var apiError *codersdk.Error
|
||||
require.ErrorAs(t, err, &apiError)
|
||||
require.Equal(t, http.StatusForbidden, apiError.StatusCode())
|
||||
})
|
||||
}
|
||||
|
||||
func TestPatchGroupSyncMapping(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
t.Run("OK", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
owner, user := coderdenttest.New(t, &coderdenttest.Options{
|
||||
LicenseOptions: &coderdenttest.LicenseOptions{
|
||||
Features: license.Features{
|
||||
codersdk.FeatureCustomRoles: 1,
|
||||
codersdk.FeatureMultipleOrganizations: 1,
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
orgID := user.OrganizationID
|
||||
orgAdmin, _ := coderdtest.CreateAnotherUser(t, owner, orgID, rbac.ScopedRoleOrgAdmin(user.OrganizationID))
|
||||
// These IDs are easier to visually diff if the test fails than truly random
|
||||
// ones.
|
||||
orgs := []uuid.UUID{
|
||||
uuid.MustParse("00000000-b8bd-46bb-bb6c-6c2b2c0dd2ea"),
|
||||
uuid.MustParse("01000000-fbe8-464c-9429-fe01a03f3644"),
|
||||
uuid.MustParse("02000000-0926-407b-9998-39af62e3d0c5"),
|
||||
}
|
||||
|
||||
ctx := testutil.Context(t, testutil.WaitShort)
|
||||
_, err := orgAdmin.PatchGroupIDPSyncSettings(ctx, orgID.String(), codersdk.GroupSyncSettings{
|
||||
Field: "wibble",
|
||||
RegexFilter: regexp.MustCompile("wib{2,}le"),
|
||||
AutoCreateMissing: true,
|
||||
Mapping: map[string][]uuid.UUID{"wobble": {orgs[0]}},
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
ctx = testutil.Context(t, testutil.WaitShort)
|
||||
settings, err := orgAdmin.PatchGroupIDPSyncMapping(ctx, orgID.String(), codersdk.PatchGroupIDPSyncMappingRequest{
|
||||
Add: []codersdk.IDPSyncMapping[uuid.UUID]{
|
||||
{Given: "wibble", Gets: orgs[0]},
|
||||
{Given: "wobble", Gets: orgs[1]},
|
||||
{Given: "wobble", Gets: orgs[2]},
|
||||
},
|
||||
// Remove takes priority over Add, so "3" should not actually be added to wooble.
|
||||
Remove: []codersdk.IDPSyncMapping[uuid.UUID]{
|
||||
{Given: "wobble", Gets: orgs[1]},
|
||||
},
|
||||
})
|
||||
|
||||
expected := map[string][]uuid.UUID{
|
||||
"wibble": {orgs[0]},
|
||||
"wobble": {orgs[0], orgs[2]},
|
||||
}
|
||||
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, expected, settings.Mapping)
|
||||
|
||||
fetchedSettings, err := orgAdmin.GroupIDPSyncSettings(ctx, orgID.String())
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, "wibble", fetchedSettings.Field)
|
||||
require.Equal(t, "wib{2,}le", fetchedSettings.RegexFilter.String())
|
||||
require.Equal(t, true, fetchedSettings.AutoCreateMissing)
|
||||
require.Equal(t, expected, fetchedSettings.Mapping)
|
||||
})
|
||||
|
||||
t.Run("NotAuthorized", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
owner, user := coderdenttest.New(t, &coderdenttest.Options{
|
||||
LicenseOptions: &coderdenttest.LicenseOptions{
|
||||
Features: license.Features{
|
||||
codersdk.FeatureCustomRoles: 1,
|
||||
codersdk.FeatureMultipleOrganizations: 1,
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
member, _ := coderdtest.CreateAnotherUser(t, owner, user.OrganizationID)
|
||||
|
||||
ctx := testutil.Context(t, testutil.WaitShort)
|
||||
_, err := member.PatchGroupIDPSyncMapping(ctx, user.OrganizationID.String(), codersdk.PatchGroupIDPSyncMappingRequest{})
|
||||
var apiError *codersdk.Error
|
||||
require.ErrorAs(t, err, &apiError)
|
||||
require.Equal(t, http.StatusForbidden, apiError.StatusCode())
|
||||
})
|
||||
}
|
||||
|
||||
func TestGetRoleSyncSettings(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
@@ -233,6 +398,150 @@ func TestPatchRoleSyncSettings(t *testing.T) {
|
||||
})
|
||||
}
|
||||
|
||||
func TestPatchRoleSyncConfig(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
t.Run("OK", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
owner, user := coderdenttest.New(t, &coderdenttest.Options{
|
||||
LicenseOptions: &coderdenttest.LicenseOptions{
|
||||
Features: license.Features{
|
||||
codersdk.FeatureCustomRoles: 1,
|
||||
codersdk.FeatureMultipleOrganizations: 1,
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
orgID := user.OrganizationID
|
||||
orgAdmin, _ := coderdtest.CreateAnotherUser(t, owner, orgID, rbac.ScopedRoleOrgAdmin(user.OrganizationID))
|
||||
|
||||
mapping := map[string][]string{"wibble": {"group-01"}}
|
||||
|
||||
ctx := testutil.Context(t, testutil.WaitShort)
|
||||
_, err := orgAdmin.PatchRoleIDPSyncSettings(ctx, orgID.String(), codersdk.RoleSyncSettings{
|
||||
Field: "wibble",
|
||||
Mapping: mapping,
|
||||
})
|
||||
|
||||
require.NoError(t, err)
|
||||
|
||||
fetchedSettings, err := orgAdmin.RoleIDPSyncSettings(ctx, orgID.String())
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, "wibble", fetchedSettings.Field)
|
||||
require.Equal(t, mapping, fetchedSettings.Mapping)
|
||||
|
||||
ctx = testutil.Context(t, testutil.WaitShort)
|
||||
settings, err := orgAdmin.PatchRoleIDPSyncConfig(ctx, orgID.String(), codersdk.PatchRoleIDPSyncConfigRequest{
|
||||
Field: "wobble",
|
||||
})
|
||||
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, "wobble", settings.Field)
|
||||
require.Equal(t, mapping, settings.Mapping)
|
||||
|
||||
fetchedSettings, err = orgAdmin.RoleIDPSyncSettings(ctx, orgID.String())
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, "wobble", fetchedSettings.Field)
|
||||
require.Equal(t, mapping, fetchedSettings.Mapping)
|
||||
})
|
||||
|
||||
t.Run("NotAuthorized", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
owner, user := coderdenttest.New(t, &coderdenttest.Options{
|
||||
LicenseOptions: &coderdenttest.LicenseOptions{
|
||||
Features: license.Features{
|
||||
codersdk.FeatureCustomRoles: 1,
|
||||
codersdk.FeatureMultipleOrganizations: 1,
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
member, _ := coderdtest.CreateAnotherUser(t, owner, user.OrganizationID)
|
||||
|
||||
ctx := testutil.Context(t, testutil.WaitShort)
|
||||
_, err := member.PatchGroupIDPSyncConfig(ctx, user.OrganizationID.String(), codersdk.PatchGroupIDPSyncConfigRequest{})
|
||||
var apiError *codersdk.Error
|
||||
require.ErrorAs(t, err, &apiError)
|
||||
require.Equal(t, http.StatusForbidden, apiError.StatusCode())
|
||||
})
|
||||
}
|
||||
|
||||
func TestPatchRoleSyncMapping(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
t.Run("OK", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
owner, user := coderdenttest.New(t, &coderdenttest.Options{
|
||||
LicenseOptions: &coderdenttest.LicenseOptions{
|
||||
Features: license.Features{
|
||||
codersdk.FeatureCustomRoles: 1,
|
||||
codersdk.FeatureMultipleOrganizations: 1,
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
orgID := user.OrganizationID
|
||||
orgAdmin, _ := coderdtest.CreateAnotherUser(t, owner, orgID, rbac.ScopedRoleOrgAdmin(user.OrganizationID))
|
||||
|
||||
ctx := testutil.Context(t, testutil.WaitShort)
|
||||
_, err := orgAdmin.PatchRoleIDPSyncSettings(ctx, orgID.String(), codersdk.RoleSyncSettings{
|
||||
Field: "wibble",
|
||||
Mapping: map[string][]string{"wobble": {"group-00"}},
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
ctx = testutil.Context(t, testutil.WaitShort)
|
||||
settings, err := orgAdmin.PatchRoleIDPSyncMapping(ctx, orgID.String(), codersdk.PatchRoleIDPSyncMappingRequest{
|
||||
Add: []codersdk.IDPSyncMapping[string]{
|
||||
{Given: "wibble", Gets: "group-00"},
|
||||
{Given: "wobble", Gets: "group-01"},
|
||||
{Given: "wobble", Gets: "group-02"},
|
||||
},
|
||||
// Remove takes priority over Add, so "3" should not actually be added to wooble.
|
||||
Remove: []codersdk.IDPSyncMapping[string]{
|
||||
{Given: "wobble", Gets: "group-01"},
|
||||
},
|
||||
})
|
||||
|
||||
expected := map[string][]string{
|
||||
"wibble": {"group-00"},
|
||||
"wobble": {"group-00", "group-02"},
|
||||
}
|
||||
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, expected, settings.Mapping)
|
||||
|
||||
fetchedSettings, err := orgAdmin.RoleIDPSyncSettings(ctx, orgID.String())
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, "wibble", fetchedSettings.Field)
|
||||
require.Equal(t, expected, fetchedSettings.Mapping)
|
||||
})
|
||||
|
||||
t.Run("NotAuthorized", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
owner, user := coderdenttest.New(t, &coderdenttest.Options{
|
||||
LicenseOptions: &coderdenttest.LicenseOptions{
|
||||
Features: license.Features{
|
||||
codersdk.FeatureCustomRoles: 1,
|
||||
codersdk.FeatureMultipleOrganizations: 1,
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
member, _ := coderdtest.CreateAnotherUser(t, owner, user.OrganizationID)
|
||||
|
||||
ctx := testutil.Context(t, testutil.WaitShort)
|
||||
_, err := member.PatchGroupIDPSyncMapping(ctx, user.OrganizationID.String(), codersdk.PatchGroupIDPSyncMappingRequest{})
|
||||
var apiError *codersdk.Error
|
||||
require.ErrorAs(t, err, &apiError)
|
||||
require.Equal(t, http.StatusForbidden, apiError.StatusCode())
|
||||
})
|
||||
}
|
||||
|
||||
func TestGetOrganizationSyncSettings(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
@@ -416,11 +725,6 @@ func TestPatchOrganizationSyncMapping(t *testing.T) {
|
||||
uuid.MustParse("00000000-b8bd-46bb-bb6c-6c2b2c0dd2ea"),
|
||||
uuid.MustParse("01000000-fbe8-464c-9429-fe01a03f3644"),
|
||||
uuid.MustParse("02000000-0926-407b-9998-39af62e3d0c5"),
|
||||
uuid.MustParse("03000000-92f6-4bfd-bba6-0f54667b131c"),
|
||||
uuid.MustParse("04000000-b9d0-46fe-910f-6e2ea0c62caa"),
|
||||
uuid.MustParse("05000000-67c0-4c19-a52d-0dc3f65abee0"),
|
||||
uuid.MustParse("06000000-a8a8-4a2c-bdd0-b59aa6882b55"),
|
||||
uuid.MustParse("07000000-5390-4cc7-a9c8-e4330a683ae7"),
|
||||
}
|
||||
|
||||
ctx := testutil.Context(t, testutil.WaitShort)
|
||||
@@ -428,23 +732,18 @@ func TestPatchOrganizationSyncMapping(t *testing.T) {
|
||||
settings, err := owner.PatchOrganizationIDPSyncMapping(ctx, codersdk.PatchOrganizationIDPSyncMappingRequest{
|
||||
Add: []codersdk.IDPSyncMapping[uuid.UUID]{
|
||||
{Given: "wibble", Gets: orgs[0]},
|
||||
{Given: "wibble", Gets: orgs[1]},
|
||||
{Given: "wobble", Gets: orgs[0]},
|
||||
{Given: "wobble", Gets: orgs[1]},
|
||||
{Given: "wobble", Gets: orgs[2]},
|
||||
{Given: "wobble", Gets: orgs[3]},
|
||||
{Given: "wooble", Gets: orgs[0]},
|
||||
},
|
||||
// Remove takes priority over Add, so "3" should not actually be added to wooble.
|
||||
Remove: []codersdk.IDPSyncMapping[uuid.UUID]{
|
||||
{Given: "wobble", Gets: orgs[3]},
|
||||
{Given: "wobble", Gets: orgs[1]},
|
||||
},
|
||||
})
|
||||
|
||||
expected := map[string][]uuid.UUID{
|
||||
"wibble": {orgs[0], orgs[1]},
|
||||
"wobble": {orgs[0], orgs[1], orgs[2]},
|
||||
"wooble": {orgs[0]},
|
||||
"wibble": {orgs[0]},
|
||||
"wobble": {orgs[0], orgs[2]},
|
||||
}
|
||||
|
||||
require.NoError(t, err)
|
||||
@@ -453,33 +752,6 @@ func TestPatchOrganizationSyncMapping(t *testing.T) {
|
||||
fetchedSettings, err := owner.OrganizationIDPSyncSettings(ctx)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, expected, fetchedSettings.Mapping)
|
||||
|
||||
ctx = testutil.Context(t, testutil.WaitShort)
|
||||
settings, err = owner.PatchOrganizationIDPSyncMapping(ctx, codersdk.PatchOrganizationIDPSyncMappingRequest{
|
||||
Add: []codersdk.IDPSyncMapping[uuid.UUID]{
|
||||
{Given: "wibble", Gets: orgs[2]},
|
||||
{Given: "wobble", Gets: orgs[3]},
|
||||
{Given: "wooble", Gets: orgs[0]},
|
||||
},
|
||||
// Remove takes priority over Add, so `f` should not actually be added.
|
||||
Remove: []codersdk.IDPSyncMapping[uuid.UUID]{
|
||||
{Given: "wibble", Gets: orgs[0]},
|
||||
{Given: "wobble", Gets: orgs[1]},
|
||||
},
|
||||
})
|
||||
|
||||
expected = map[string][]uuid.UUID{
|
||||
"wibble": {orgs[1], orgs[2]},
|
||||
"wobble": {orgs[0], orgs[2], orgs[3]},
|
||||
"wooble": {orgs[0]},
|
||||
}
|
||||
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, expected, settings.Mapping)
|
||||
|
||||
fetchedSettings, err = owner.OrganizationIDPSyncSettings(ctx)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, expected, fetchedSettings.Mapping)
|
||||
})
|
||||
|
||||
t.Run("NotAuthorized", func(t *testing.T) {
|
||||
|
||||
Generated
+24
@@ -1455,6 +1455,19 @@ export interface Pagination {
|
||||
readonly offset?: number;
|
||||
}
|
||||
|
||||
// From codersdk/idpsync.go
|
||||
export interface PatchGroupIDPSyncConfigRequest {
|
||||
readonly field: string;
|
||||
readonly regex_filter: string | null;
|
||||
readonly auto_create_missing_groups: boolean;
|
||||
}
|
||||
|
||||
// From codersdk/idpsync.go
|
||||
export interface PatchGroupIDPSyncMappingRequest {
|
||||
readonly Add: readonly IDPSyncMapping<string>[];
|
||||
readonly Remove: readonly IDPSyncMapping<string>[];
|
||||
}
|
||||
|
||||
// From codersdk/groups.go
|
||||
export interface PatchGroupRequest {
|
||||
readonly add_users: readonly string[];
|
||||
@@ -1477,6 +1490,17 @@ export interface PatchOrganizationIDPSyncMappingRequest {
|
||||
readonly Remove: readonly IDPSyncMapping<string>[];
|
||||
}
|
||||
|
||||
// From codersdk/idpsync.go
|
||||
export interface PatchRoleIDPSyncConfigRequest {
|
||||
readonly field: string;
|
||||
}
|
||||
|
||||
// From codersdk/idpsync.go
|
||||
export interface PatchRoleIDPSyncMappingRequest {
|
||||
readonly Add: readonly IDPSyncMapping<string>[];
|
||||
readonly Remove: readonly IDPSyncMapping<string>[];
|
||||
}
|
||||
|
||||
// From codersdk/templateversions.go
|
||||
export interface PatchTemplateVersionRequest {
|
||||
readonly name: string;
|
||||
|
||||
Reference in New Issue
Block a user