mirror of
https://github.com/coder/coder.git
synced 2025-07-09 11:45:56 +00:00
feat: add endpoint for partial updates to org sync field and assign_default (#16337)
This commit is contained in:
@ -295,6 +295,7 @@ func New(ctx context.Context, options *Options) (_ *API, err error) {
|
||||
r.Route("/organization", func(r chi.Router) {
|
||||
r.Get("/", api.organizationIDPSyncSettings)
|
||||
r.Patch("/", api.patchOrganizationIDPSyncSettings)
|
||||
r.Patch("/config", api.patchOrganizationIDPSyncConfig)
|
||||
r.Patch("/mapping", api.patchOrganizationIDPSyncMapping)
|
||||
})
|
||||
|
||||
|
@ -319,6 +319,75 @@ func (api *API) patchOrganizationIDPSyncSettings(rw http.ResponseWriter, r *http
|
||||
})
|
||||
}
|
||||
|
||||
// @Summary Update organization IdP Sync config
|
||||
// @ID update-organization-idp-sync-config
|
||||
// @Security CoderSessionToken
|
||||
// @Produce json
|
||||
// @Accept json
|
||||
// @Tags Enterprise
|
||||
// @Success 200 {object} codersdk.OrganizationSyncSettings
|
||||
// @Param request body codersdk.PatchOrganizationIDPSyncConfigRequest true "New config values"
|
||||
// @Router /settings/idpsync/organization/config [patch]
|
||||
func (api *API) patchOrganizationIDPSyncConfig(rw http.ResponseWriter, r *http.Request) {
|
||||
ctx := r.Context()
|
||||
auditor := *api.AGPL.Auditor.Load()
|
||||
aReq, commitAudit := audit.InitRequest[idpsync.OrganizationSyncSettings](rw, &audit.RequestParams{
|
||||
Audit: auditor,
|
||||
Log: api.Logger,
|
||||
Request: r,
|
||||
Action: database.AuditActionWrite,
|
||||
})
|
||||
defer commitAudit()
|
||||
|
||||
if !api.Authorize(r, policy.ActionUpdate, rbac.ResourceIdpsyncSettings) {
|
||||
httpapi.Forbidden(rw)
|
||||
return
|
||||
}
|
||||
|
||||
var req codersdk.PatchOrganizationIDPSyncConfigRequest
|
||||
if !httpapi.Read(ctx, rw, r, &req) {
|
||||
return
|
||||
}
|
||||
|
||||
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 {
|
||||
existing, err := api.IDPSync.OrganizationSyncSettings(sysCtx, tx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
aReq.Old = *existing
|
||||
|
||||
err = api.IDPSync.UpdateOrganizationSyncSettings(sysCtx, tx, idpsync.OrganizationSyncSettings{
|
||||
Field: req.Field,
|
||||
AssignDefault: req.AssignDefault,
|
||||
Mapping: existing.Mapping,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
settings, err = api.IDPSync.OrganizationSyncSettings(sysCtx, tx)
|
||||
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.OrganizationSyncSettings{
|
||||
Field: settings.Field,
|
||||
Mapping: settings.Mapping,
|
||||
AssignDefault: settings.AssignDefault,
|
||||
})
|
||||
}
|
||||
|
||||
// @Summary Update organization IdP Sync mapping
|
||||
// @ID update-organization-idp-sync-mapping
|
||||
// @Security CoderSessionToken
|
||||
|
@ -20,7 +20,7 @@ import (
|
||||
"github.com/coder/serpent"
|
||||
)
|
||||
|
||||
func TestGetGroupSyncConfig(t *testing.T) {
|
||||
func TestGetGroupSyncSettings(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
t.Run("OK", func(t *testing.T) {
|
||||
@ -83,7 +83,7 @@ func TestGetGroupSyncConfig(t *testing.T) {
|
||||
})
|
||||
}
|
||||
|
||||
func TestPatchGroupSyncConfig(t *testing.T) {
|
||||
func TestPatchGroupSyncSettings(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
t.Run("OK", func(t *testing.T) {
|
||||
@ -141,7 +141,7 @@ func TestPatchGroupSyncConfig(t *testing.T) {
|
||||
})
|
||||
}
|
||||
|
||||
func TestGetRoleSyncConfig(t *testing.T) {
|
||||
func TestGetRoleSyncSettings(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
t.Run("OK", func(t *testing.T) {
|
||||
@ -175,7 +175,7 @@ func TestGetRoleSyncConfig(t *testing.T) {
|
||||
})
|
||||
}
|
||||
|
||||
func TestPatchRoleSyncConfig(t *testing.T) {
|
||||
func TestPatchRoleSyncSettings(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
t.Run("OK", func(t *testing.T) {
|
||||
@ -323,6 +323,78 @@ func TestPatchOrganizationSyncSettings(t *testing.T) {
|
||||
})
|
||||
}
|
||||
|
||||
func TestPatchOrganizationSyncConfig(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,
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
mapping := map[string][]uuid.UUID{"wibble": {user.OrganizationID}}
|
||||
|
||||
ctx := testutil.Context(t, testutil.WaitShort)
|
||||
//nolint:gocritic // Only owners can change Organization IdP sync settings
|
||||
_, err := owner.PatchOrganizationIDPSyncSettings(ctx, codersdk.OrganizationSyncSettings{
|
||||
Field: "wibble",
|
||||
AssignDefault: true,
|
||||
Mapping: mapping,
|
||||
})
|
||||
|
||||
require.NoError(t, err)
|
||||
|
||||
fetchedSettings, err := owner.OrganizationIDPSyncSettings(ctx)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, "wibble", fetchedSettings.Field)
|
||||
require.Equal(t, true, fetchedSettings.AssignDefault)
|
||||
require.Equal(t, mapping, fetchedSettings.Mapping)
|
||||
|
||||
ctx = testutil.Context(t, testutil.WaitShort)
|
||||
settings, err := owner.PatchOrganizationIDPSyncConfig(ctx, codersdk.PatchOrganizationIDPSyncConfigRequest{
|
||||
Field: "wobble",
|
||||
})
|
||||
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, "wobble", settings.Field)
|
||||
require.Equal(t, false, settings.AssignDefault)
|
||||
require.Equal(t, mapping, settings.Mapping)
|
||||
|
||||
fetchedSettings, err = owner.OrganizationIDPSyncSettings(ctx)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, "wobble", fetchedSettings.Field)
|
||||
require.Equal(t, false, fetchedSettings.AssignDefault)
|
||||
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.PatchOrganizationIDPSyncConfig(ctx, codersdk.PatchOrganizationIDPSyncConfigRequest{})
|
||||
var apiError *codersdk.Error
|
||||
require.ErrorAs(t, err, &apiError)
|
||||
require.Equal(t, http.StatusForbidden, apiError.StatusCode())
|
||||
})
|
||||
}
|
||||
|
||||
func TestPatchOrganizationSyncMapping(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
|
Reference in New Issue
Block a user