Skip to content

Commit

Permalink
Add the ability to generate sso configuration link (#457)
Browse files Browse the repository at this point in the history
  • Loading branch information
dorsha authored Sep 4, 2024
1 parent 7c79752 commit c560b20
Show file tree
Hide file tree
Showing 7 changed files with 212 additions and 112 deletions.
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -751,6 +751,9 @@ settingsRequest.InactivityTimeUnit = "days"
// update the tenant settings
err := descopeClient.Management.Tenant().ConfigureSettings(context.Background(), "My Tenant", settingsRequest)

// Generate tenant admin self service link for SSO configuration (valid for 24 hours)
link, err := descopeClient.Management.Tenant().GenerateSSOConfigurationLink(context.Background(), "My Tenant", 60 * 60 * 24)

```

### Manage Users
Expand Down
233 changes: 121 additions & 112 deletions descope/api/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -85,118 +85,119 @@ var (
exchangeAccessKey: "auth/accesskey/exchange",
},
mgmt: mgmtEndpoints{
tenantCreate: "mgmt/tenant/create",
tenantUpdate: "mgmt/tenant/update",
tenantDelete: "mgmt/tenant/delete",
tenantLoad: "mgmt/tenant",
tenantLoadAll: "mgmt/tenant/all",
tenantSearchAll: "mgmt/tenant/search",
tenantSettings: "mgmt/tenant/settings",
ssoApplicationOIDCCreate: "mgmt/sso/idp/app/oidc/create",
ssoApplicationSAMLCreate: "mgmt/sso/idp/app/saml/create",
ssoApplicationOIDCUpdate: "mgmt/sso/idp/app/oidc/update",
ssoApplicationSAMLUpdate: "mgmt/sso/idp/app/saml/update",
ssoApplicationDelete: "mgmt/sso/idp/app/delete",
ssoApplicationLoad: "mgmt/sso/idp/app/load",
ssoApplicationLoadAll: "mgmt/sso/idp/apps/load",
userCreate: "mgmt/user/create",
userCreateBatch: "mgmt/user/create/batch",
userUpdate: "mgmt/user/update",
userPatch: "mgmt/user/patch",
userDelete: "mgmt/user/delete",
userDeleteAllTestUsers: "mgmt/user/test/delete/all",
userImport: "mgmt/user/import",
userLoad: "mgmt/user",
userSearchAll: "mgmt/user/search",
userUpdateStatus: "mgmt/user/update/status",
userUpdateLoginID: "mgmt/user/update/loginid",
userUpdateEmail: "mgmt/user/update/email",
userUpdatePhone: "mgmt/user/update/phone",
userUpdateName: "mgmt/user/update/name",
userUpdatePicture: "mgmt/user/update/picture",
userUpdateCustomAttribute: "mgmt/user/update/customAttribute",
userAddTenant: "mgmt/user/update/tenant/add",
userRemoveTenant: "mgmt/user/update/tenant/remove",
userSetRole: "mgmt/user/update/role/set",
userAddRole: "mgmt/user/update/role/add",
userRemoveRole: "mgmt/user/update/role/remove",
userAddSsoApps: "mgmt/user/update/ssoapp/add",
userSetSsoApps: "mgmt/user/update/ssoapp/set",
userRemoveSsoApps: "mgmt/user/update/ssoapp/remove",
userSetPassword: "mgmt/user/password/set",
userSetTemporaryPassword: "mgmt/user/password/set/temporary",
userSetActivePassword: "mgmt/user/password/set/active",
userExpirePassword: "mgmt/user/password/expire",
userRemoveAllPasskeys: "mgmt/user/passkeys/delete",
userGetProviderToken: "mgmt/user/provider/token",
userLogoutAllDevices: "mgmt/user/logout",
userGenerateOTPForTest: "mgmt/tests/generate/otp",
userGenerateMagicLinkForTest: "mgmt/tests/generate/magiclink",
userGenerateEnchantedLinkForTest: "mgmt/tests/generate/enchantedlink",
userCreateEmbeddedLink: "mgmt/user/signin/embeddedlink",
userHistory: "mgmt/user/history",
accessKeyCreate: "mgmt/accesskey/create",
accessKeyLoad: "mgmt/accesskey",
accessKeySearchAll: "mgmt/accesskey/search",
accessKeyUpdate: "mgmt/accesskey/update",
accessKeyDeactivate: "mgmt/accesskey/deactivate",
accessKeyActivate: "mgmt/accesskey/activate",
accessKeyDelete: "mgmt/accesskey/delete",
ssoSettings: "mgmt/sso/settings",
ssoLoadSettings: "mgmt/sso/settings", // v2 only
ssoSAMLSettings: "mgmt/sso/saml",
ssoSAMLSettingsByMetadata: "mgmt/sso/saml/metadata",
ssoOIDCSettings: "mgmt/sso/oidc",
ssoMetadata: "mgmt/sso/metadata",
ssoMapping: "mgmt/sso/mapping",
passwordSettings: "mgmt/password/settings",
updateJWT: "mgmt/jwt/update",
impersonate: "mgmt/impersonate",
permissionCreate: "mgmt/permission/create",
permissionUpdate: "mgmt/permission/update",
permissionDelete: "mgmt/permission/delete",
permissionLoadAll: "mgmt/permission/all",
roleCreate: "mgmt/role/create",
roleUpdate: "mgmt/role/update",
roleDelete: "mgmt/role/delete",
roleLoadAll: "mgmt/role/all",
roleSearch: "mgmt/role/search",
groupLoadAllGroups: "mgmt/group/all",
groupLoadAllGroupsForMember: "mgmt/group/member/all",
groupLoadAllGroupMembers: "mgmt/group/members",
listFlows: "mgmt/flow/list",
deleteFlows: "mgmt/flow/delete",
flowExport: "mgmt/flow/export",
flowImport: "mgmt/flow/import",
themeExport: "mgmt/theme/export",
themeImport: "mgmt/theme/import",
projectsList: "mgmt/projects/list",
projectClone: "mgmt/project/clone",
projectUpdateName: "mgmt/project/update/name",
projectUpdateTags: "mgmt/project/update/tags",
projectDelete: "mgmt/project/delete",
projectExportSnapshot: "mgmt/project/snapshot/export",
projectImportSnapshot: "mgmt/project/snapshot/import",
projectValidateSnapshot: "mgmt/project/snapshot/validate",
auditSearch: "mgmt/audit/search",
auditCreate: "mgmt/audit/event",
authzSchemaSave: "mgmt/authz/schema/save",
authzSchemaDelete: "mgmt/authz/schema/delete",
authzSchemaLoad: "mgmt/authz/schema/load",
authzNSSave: "mgmt/authz/ns/save",
authzNSDelete: "mgmt/authz/ns/delete",
authzRDSave: "mgmt/authz/rd/save",
authzRDDelete: "mgmt/authz/rd/delete",
authzRECreate: "mgmt/authz/re/create",
authzREDelete: "mgmt/authz/re/delete",
authzREDeleteResources: "mgmt/authz/re/deleteresources",
authzREHasRelations: "mgmt/authz/re/has",
authzREWho: "mgmt/authz/re/who",
authzREResource: "mgmt/authz/re/resource",
authzRETargets: "mgmt/authz/re/targets",
authzRETargetAll: "mgmt/authz/re/targetall",
authzRETargetWithRelation: "mgmt/authz/re/targetwithrelation",
authzGetModified: "mgmt/authz/getmodified",
tenantCreate: "mgmt/tenant/create",
tenantUpdate: "mgmt/tenant/update",
tenantDelete: "mgmt/tenant/delete",
tenantLoad: "mgmt/tenant",
tenantLoadAll: "mgmt/tenant/all",
tenantSearchAll: "mgmt/tenant/search",
tenantSettings: "mgmt/tenant/settings",
tenantGenerateSSOConfigurationLink: "mgmt/tenant/adminlinks/sso/generate",
ssoApplicationOIDCCreate: "mgmt/sso/idp/app/oidc/create",
ssoApplicationSAMLCreate: "mgmt/sso/idp/app/saml/create",
ssoApplicationOIDCUpdate: "mgmt/sso/idp/app/oidc/update",
ssoApplicationSAMLUpdate: "mgmt/sso/idp/app/saml/update",
ssoApplicationDelete: "mgmt/sso/idp/app/delete",
ssoApplicationLoad: "mgmt/sso/idp/app/load",
ssoApplicationLoadAll: "mgmt/sso/idp/apps/load",
userCreate: "mgmt/user/create",
userCreateBatch: "mgmt/user/create/batch",
userUpdate: "mgmt/user/update",
userPatch: "mgmt/user/patch",
userDelete: "mgmt/user/delete",
userDeleteAllTestUsers: "mgmt/user/test/delete/all",
userImport: "mgmt/user/import",
userLoad: "mgmt/user",
userSearchAll: "mgmt/user/search",
userUpdateStatus: "mgmt/user/update/status",
userUpdateLoginID: "mgmt/user/update/loginid",
userUpdateEmail: "mgmt/user/update/email",
userUpdatePhone: "mgmt/user/update/phone",
userUpdateName: "mgmt/user/update/name",
userUpdatePicture: "mgmt/user/update/picture",
userUpdateCustomAttribute: "mgmt/user/update/customAttribute",
userAddTenant: "mgmt/user/update/tenant/add",
userRemoveTenant: "mgmt/user/update/tenant/remove",
userSetRole: "mgmt/user/update/role/set",
userAddRole: "mgmt/user/update/role/add",
userRemoveRole: "mgmt/user/update/role/remove",
userAddSsoApps: "mgmt/user/update/ssoapp/add",
userSetSsoApps: "mgmt/user/update/ssoapp/set",
userRemoveSsoApps: "mgmt/user/update/ssoapp/remove",
userSetPassword: "mgmt/user/password/set",
userSetTemporaryPassword: "mgmt/user/password/set/temporary",
userSetActivePassword: "mgmt/user/password/set/active",
userExpirePassword: "mgmt/user/password/expire",
userRemoveAllPasskeys: "mgmt/user/passkeys/delete",
userGetProviderToken: "mgmt/user/provider/token",
userLogoutAllDevices: "mgmt/user/logout",
userGenerateOTPForTest: "mgmt/tests/generate/otp",
userGenerateMagicLinkForTest: "mgmt/tests/generate/magiclink",
userGenerateEnchantedLinkForTest: "mgmt/tests/generate/enchantedlink",
userCreateEmbeddedLink: "mgmt/user/signin/embeddedlink",
userHistory: "mgmt/user/history",
accessKeyCreate: "mgmt/accesskey/create",
accessKeyLoad: "mgmt/accesskey",
accessKeySearchAll: "mgmt/accesskey/search",
accessKeyUpdate: "mgmt/accesskey/update",
accessKeyDeactivate: "mgmt/accesskey/deactivate",
accessKeyActivate: "mgmt/accesskey/activate",
accessKeyDelete: "mgmt/accesskey/delete",
ssoSettings: "mgmt/sso/settings",
ssoLoadSettings: "mgmt/sso/settings", // v2 only
ssoSAMLSettings: "mgmt/sso/saml",
ssoSAMLSettingsByMetadata: "mgmt/sso/saml/metadata",
ssoOIDCSettings: "mgmt/sso/oidc",
ssoMetadata: "mgmt/sso/metadata",
ssoMapping: "mgmt/sso/mapping",
passwordSettings: "mgmt/password/settings",
updateJWT: "mgmt/jwt/update",
impersonate: "mgmt/impersonate",
permissionCreate: "mgmt/permission/create",
permissionUpdate: "mgmt/permission/update",
permissionDelete: "mgmt/permission/delete",
permissionLoadAll: "mgmt/permission/all",
roleCreate: "mgmt/role/create",
roleUpdate: "mgmt/role/update",
roleDelete: "mgmt/role/delete",
roleLoadAll: "mgmt/role/all",
roleSearch: "mgmt/role/search",
groupLoadAllGroups: "mgmt/group/all",
groupLoadAllGroupsForMember: "mgmt/group/member/all",
groupLoadAllGroupMembers: "mgmt/group/members",
listFlows: "mgmt/flow/list",
deleteFlows: "mgmt/flow/delete",
flowExport: "mgmt/flow/export",
flowImport: "mgmt/flow/import",
themeExport: "mgmt/theme/export",
themeImport: "mgmt/theme/import",
projectsList: "mgmt/projects/list",
projectClone: "mgmt/project/clone",
projectUpdateName: "mgmt/project/update/name",
projectUpdateTags: "mgmt/project/update/tags",
projectDelete: "mgmt/project/delete",
projectExportSnapshot: "mgmt/project/snapshot/export",
projectImportSnapshot: "mgmt/project/snapshot/import",
projectValidateSnapshot: "mgmt/project/snapshot/validate",
auditSearch: "mgmt/audit/search",
auditCreate: "mgmt/audit/event",
authzSchemaSave: "mgmt/authz/schema/save",
authzSchemaDelete: "mgmt/authz/schema/delete",
authzSchemaLoad: "mgmt/authz/schema/load",
authzNSSave: "mgmt/authz/ns/save",
authzNSDelete: "mgmt/authz/ns/delete",
authzRDSave: "mgmt/authz/rd/save",
authzRDDelete: "mgmt/authz/rd/delete",
authzRECreate: "mgmt/authz/re/create",
authzREDelete: "mgmt/authz/re/delete",
authzREDeleteResources: "mgmt/authz/re/deleteresources",
authzREHasRelations: "mgmt/authz/re/has",
authzREWho: "mgmt/authz/re/who",
authzREResource: "mgmt/authz/re/resource",
authzRETargets: "mgmt/authz/re/targets",
authzRETargetAll: "mgmt/authz/re/targetall",
authzRETargetWithRelation: "mgmt/authz/re/targetwithrelation",
authzGetModified: "mgmt/authz/getmodified",
},
logout: "auth/logout",
logoutAll: "auth/logoutall",
Expand Down Expand Up @@ -282,6 +283,8 @@ type mgmtEndpoints struct {
tenantSearchAll string
tenantSettings string

tenantGenerateSSOConfigurationLink string

ssoApplicationOIDCCreate string
ssoApplicationSAMLCreate string
ssoApplicationOIDCUpdate string
Expand Down Expand Up @@ -655,6 +658,10 @@ func (e *endpoints) ManagementTenantSettings() string {
return path.Join(e.version, e.mgmt.tenantSettings)
}

func (e *endpoints) ManagementTenantGenerateSSOConfigurationLink() string {
return path.Join(e.version, e.mgmt.tenantGenerateSSOConfigurationLink)
}

func (e *endpoints) ManagementSSOApplicationOIDCCreate() string {
return path.Join(e.version, e.mgmt.ssoApplicationOIDCCreate)
}
Expand Down Expand Up @@ -859,9 +866,11 @@ func (e *endpoints) ManagementSSOLoadSettings() string {
func (e *endpoints) ManagementSSOSAMLSettings() string {
return path.Join(e.version, e.mgmt.ssoSAMLSettings)
}

func (e *endpoints) ManagementSSOSAMLSettingsByMetadata() string {
return path.Join(e.version, e.mgmt.ssoSAMLSettingsByMetadata)
}

func (e *endpoints) ManagementSSOOIDCSettings() string {
return path.Join(e.version, e.mgmt.ssoOIDCSettings)
}
Expand Down
26 changes: 26 additions & 0 deletions descope/internal/mgmt/tenant.go
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,23 @@ func (t *tenant) ConfigureSettings(ctx context.Context, tenantID string, setting
return err
}

func (t *tenant) GenerateSSOConfigurationLink(ctx context.Context, tenantID string, expireDuration int64) (string, error) {
if tenantID == "" {
return "", utils.NewInvalidArgumentError("tenantID")
}

req := map[string]any{
"tenantId": tenantID,
"expireTime": expireDuration,
}

res, err := t.client.DoPostRequest(ctx, api.Routes.ManagementTenantGenerateSSOConfigurationLink(), req, nil, t.conf.ManagementKey)
if err != nil {
return "", err
}
return unmarshalGenerateSSOConfigurationLinkResponse(res)
}

func makeCreateUpdateTenantRequest(id string, tenantRequest *descope.TenantRequest) map[string]any {
return map[string]any{"id": id, "name": tenantRequest.Name, "selfProvisioningDomains": tenantRequest.SelfProvisioningDomains, "customAttributes": tenantRequest.CustomAttributes}
}
Expand Down Expand Up @@ -193,3 +210,12 @@ func unmarshalTenantSettingsResponse(res *api.HTTPResponse) (*descope.TenantSett
tres.TenantSettings.SessionSettingsEnabled = tres.Enabled
return tres.TenantSettings, nil
}

func unmarshalGenerateSSOConfigurationLinkResponse(res *api.HTTPResponse) (string, error) {
var resp *descope.GenerateSSOConfigurationLinkResponse
err := utils.Unmarshal([]byte(res.BodyStr), &resp)
if err != nil {
return "", err
}
return resp.AdminSSOConfigurationLink, err
}
43 changes: 43 additions & 0 deletions descope/internal/mgmt/tenant_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"testing"

"github.com/descope/go-sdk/descope"
"github.com/descope/go-sdk/descope/internal/utils"
"github.com/descope/go-sdk/descope/tests/helpers"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
Expand Down Expand Up @@ -281,3 +282,45 @@ func TestTenantConfigureSettingsError(t *testing.T) {
err := mgmt.Tenant().ConfigureSettings(context.Background(), "test", &descope.TenantSettings{})
require.Error(t, err)
}

func TestTenantGenerateSSOConfigurationLinkSuccess(t *testing.T) {
response := map[string]any{
"adminSSOConfigurationLink": "some link",
}
mgmt := newTestMgmt(nil, helpers.DoOkWithBody(func(r *http.Request) {
require.Equal(t, r.Header.Get("Authorization"), "Bearer a:key")
req := map[string]any{}
require.NoError(t, helpers.ReadBody(r, &req))
require.Equal(t, "tenant", req["tenantId"])
require.Equal(t, float64(60*60*24), req["expireTime"])
}, response))
link, err := mgmt.Tenant().GenerateSSOConfigurationLink(context.Background(), "tenant", 60*60*24)
require.NoError(t, err)
assert.EqualValues(t, "some link", link)
}

func TestTenantGenerateSSOConfigurationLinkError(t *testing.T) {
mgmt := newTestMgmt(nil, helpers.DoBadRequest(func(r *http.Request) {
require.Equal(t, r.Header.Get("Authorization"), "Bearer a:key")
req := map[string]any{}
require.NoError(t, helpers.ReadBody(r, &req))
require.Equal(t, "tenant", req["tenantId"])
require.Equal(t, float64(60*60*24), req["expireTime"])
}))
link, err := mgmt.Tenant().GenerateSSOConfigurationLink(context.Background(), "tenant", 60*60*24)
require.Error(t, err)
assert.Empty(t, link)
}

func TestTenantGenerateSSOConfigurationLinkNoTenantID(t *testing.T) {
mgmt := newTestMgmt(nil, helpers.DoBadRequest(func(r *http.Request) {
require.Equal(t, r.Header.Get("Authorization"), "Bearer a:key")
req := map[string]any{}
require.NoError(t, helpers.ReadBody(r, &req))
require.Equal(t, "tenant", req["tenantId"])
require.Equal(t, float64(60*60*24), req["expireTime"])
}))
link, err := mgmt.Tenant().GenerateSSOConfigurationLink(context.Background(), "", 60*60*24)
require.ErrorIs(t, err, utils.NewInvalidArgumentError("tenantId"))
assert.Empty(t, link)
}
Loading

0 comments on commit c560b20

Please sign in to comment.