mirror of
https://github.com/coder/coder.git
synced 2025-07-09 11:45:56 +00:00
chore: add query to fetch top level idp claim fields (#15525)
Adds an api endpoint to grab all available sync field options for IDP sync. This is for autocomplete on idp sync forms. This is required for organization admins to have some insight into the claim fields available when configuring group/role sync.
This commit is contained in:
@ -291,9 +291,12 @@ func New(ctx context.Context, options *Options) (_ *API, err error) {
|
||||
r.Use(
|
||||
apiKeyMiddleware,
|
||||
)
|
||||
r.Route("/settings/idpsync/organization", func(r chi.Router) {
|
||||
r.Get("/", api.organizationIDPSyncSettings)
|
||||
r.Patch("/", api.patchOrganizationIDPSyncSettings)
|
||||
r.Route("/settings/idpsync", func(r chi.Router) {
|
||||
r.Route("/organization", func(r chi.Router) {
|
||||
r.Get("/", api.organizationIDPSyncSettings)
|
||||
r.Patch("/", api.patchOrganizationIDPSyncSettings)
|
||||
})
|
||||
r.Get("/available-fields", api.deploymentIDPSyncClaimFields)
|
||||
})
|
||||
})
|
||||
|
||||
@ -303,6 +306,7 @@ func New(ctx context.Context, options *Options) (_ *API, err error) {
|
||||
httpmw.ExtractOrganizationParam(api.Database),
|
||||
)
|
||||
r.Route("/organizations/{organization}/settings", func(r chi.Router) {
|
||||
r.Get("/idpsync/available-fields", api.organizationIDPSyncClaimFields)
|
||||
r.Get("/idpsync/groups", api.groupIDPSyncSettings)
|
||||
r.Patch("/idpsync/groups", api.patchGroupIDPSyncSettings)
|
||||
r.Get("/idpsync/roles", api.roleIDPSyncSettings)
|
||||
|
@ -1,8 +1,11 @@
|
||||
package coderd
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
||||
"github.com/google/uuid"
|
||||
|
||||
"github.com/coder/coder/v2/coderd/database/dbauthz"
|
||||
"github.com/coder/coder/v2/coderd/httpapi"
|
||||
"github.com/coder/coder/v2/coderd/httpmw"
|
||||
@ -259,3 +262,50 @@ func (api *API) patchOrganizationIDPSyncSettings(rw http.ResponseWriter, r *http
|
||||
AssignDefault: settings.AssignDefault,
|
||||
})
|
||||
}
|
||||
|
||||
// @Summary Get the available organization idp sync claim fields
|
||||
// @ID get-the-available-organization-idp-sync-claim-fields
|
||||
// @Security CoderSessionToken
|
||||
// @Produce json
|
||||
// @Tags Enterprise
|
||||
// @Param organization path string true "Organization ID" format(uuid)
|
||||
// @Success 200 {array} string
|
||||
// @Router /organizations/{organization}/settings/idpsync/available-fields [get]
|
||||
func (api *API) organizationIDPSyncClaimFields(rw http.ResponseWriter, r *http.Request) {
|
||||
org := httpmw.OrganizationParam(r)
|
||||
api.idpSyncClaimFields(org.ID, rw, r)
|
||||
}
|
||||
|
||||
// @Summary Get the available idp sync claim fields
|
||||
// @ID get-the-available-idp-sync-claim-fields
|
||||
// @Security CoderSessionToken
|
||||
// @Produce json
|
||||
// @Tags Enterprise
|
||||
// @Param organization path string true "Organization ID" format(uuid)
|
||||
// @Success 200 {array} string
|
||||
// @Router /settings/idpsync/available-fields [get]
|
||||
func (api *API) deploymentIDPSyncClaimFields(rw http.ResponseWriter, r *http.Request) {
|
||||
// nil uuid implies all organizations
|
||||
api.idpSyncClaimFields(uuid.Nil, rw, r)
|
||||
}
|
||||
|
||||
func (api *API) idpSyncClaimFields(orgID uuid.UUID, rw http.ResponseWriter, r *http.Request) {
|
||||
ctx := r.Context()
|
||||
|
||||
fields, err := api.Database.OIDCClaimFields(ctx, orgID)
|
||||
if httpapi.IsUnauthorizedError(err) {
|
||||
// Give a helpful error. The user could read the org, so this does not
|
||||
// leak anything.
|
||||
httpapi.Write(ctx, rw, http.StatusForbidden, codersdk.Response{
|
||||
Message: "You do not have permission to view the available IDP fields",
|
||||
Detail: fmt.Sprintf("%s.read permission is required", rbac.ResourceIdpsyncSettings.Type),
|
||||
})
|
||||
return
|
||||
}
|
||||
if err != nil {
|
||||
httpapi.InternalServerError(rw, err)
|
||||
return
|
||||
}
|
||||
|
||||
httpapi.Write(ctx, rw, http.StatusOK, fields)
|
||||
}
|
||||
|
@ -165,6 +165,19 @@ func TestUserOIDC(t *testing.T) {
|
||||
user, err := userClient.User(ctx, codersdk.Me)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Then: the available sync fields should be "email" and "organization"
|
||||
fields, err := runner.AdminClient.GetAvailableIDPSyncFields(ctx)
|
||||
require.NoError(t, err)
|
||||
require.ElementsMatch(t, []string{
|
||||
"aud", "exp", "iss", // Always included from jwt
|
||||
"email", "organization",
|
||||
}, fields)
|
||||
|
||||
// This should be the same as above
|
||||
orgFields, err := runner.AdminClient.GetOrganizationAvailableIDPSyncFields(ctx, orgOne.ID.String())
|
||||
require.NoError(t, err)
|
||||
require.ElementsMatch(t, fields, orgFields)
|
||||
|
||||
// When: they are manually added to the fourth organization, a new sync
|
||||
// should remove them.
|
||||
_, err = runner.AdminClient.PostOrganizationMember(ctx, orgThree.ID, "alice")
|
||||
|
Reference in New Issue
Block a user