mirror of
https://github.com/logto-io/logto.git
synced 2025-03-03 22:15:32 -05:00
feat(core,schemas): add CRUD for consent organization resource scopes (#5804)
feat(core,schemas): add crud for user consent organization resource scopes
This commit is contained in:
parent
2e96eea60c
commit
5adf3dfad7
10 changed files with 176 additions and 26 deletions
packages
console/src/pages/ApplicationDetails/ApplicationDetailsContent/Permissions/ApplicationScopesAssignmentModal
core/src
libraries
queries
routes/applications
integration-tests/src
schemas/src/types
|
@ -10,6 +10,11 @@ export const permissionTabs = Object.freeze({
|
||||||
title: 'application_details.permissions.api_resource',
|
title: 'application_details.permissions.api_resource',
|
||||||
key: ApplicationUserConsentScopeType.ResourceScopes,
|
key: ApplicationUserConsentScopeType.ResourceScopes,
|
||||||
},
|
},
|
||||||
|
[ApplicationUserConsentScopeType.OrganizationResourceScopes]: {
|
||||||
|
// TODO @xiaoyijun: update the title
|
||||||
|
title: 'application_details.permissions.api_resource',
|
||||||
|
key: ApplicationUserConsentScopeType.OrganizationResourceScopes,
|
||||||
|
},
|
||||||
[ApplicationUserConsentScopeType.OrganizationScopes]: {
|
[ApplicationUserConsentScopeType.OrganizationScopes]: {
|
||||||
title: 'application_details.permissions.organization',
|
title: 'application_details.permissions.organization',
|
||||||
key: ApplicationUserConsentScopeType.OrganizationScopes,
|
key: ApplicationUserConsentScopeType.OrganizationScopes,
|
||||||
|
|
|
@ -72,6 +72,8 @@ const useApplicationScopesAssignment = (applicationId: string) => {
|
||||||
[ApplicationUserConsentScopeType.UserScopes]: userScopesAssignment,
|
[ApplicationUserConsentScopeType.UserScopes]: userScopesAssignment,
|
||||||
[ApplicationUserConsentScopeType.OrganizationScopes]: organizationScopesAssignment,
|
[ApplicationUserConsentScopeType.OrganizationScopes]: organizationScopesAssignment,
|
||||||
[ApplicationUserConsentScopeType.ResourceScopes]: resourceScopesAssignment,
|
[ApplicationUserConsentScopeType.ResourceScopes]: resourceScopesAssignment,
|
||||||
|
// TODO @xiaoyijun: Replace with correct scopes
|
||||||
|
[ApplicationUserConsentScopeType.OrganizationResourceScopes]: resourceScopesAssignment,
|
||||||
}),
|
}),
|
||||||
[organizationScopesAssignment, resourceScopesAssignment, userScopesAssignment]
|
[organizationScopesAssignment, resourceScopesAssignment, userScopesAssignment]
|
||||||
);
|
);
|
||||||
|
|
|
@ -32,6 +32,7 @@ export const createApplicationLibrary = (queries: Queries) => {
|
||||||
applications: {
|
applications: {
|
||||||
findApplicationById,
|
findApplicationById,
|
||||||
userConsentOrganizationScopes,
|
userConsentOrganizationScopes,
|
||||||
|
userConsentOrganizationResourceScopes,
|
||||||
userConsentResourceScopes,
|
userConsentResourceScopes,
|
||||||
userConsentUserScopes,
|
userConsentUserScopes,
|
||||||
},
|
},
|
||||||
|
@ -76,16 +77,20 @@ export const createApplicationLibrary = (queries: Queries) => {
|
||||||
{
|
{
|
||||||
organizationScopes = [],
|
organizationScopes = [],
|
||||||
resourceScopes = [],
|
resourceScopes = [],
|
||||||
|
organizationResourceScopes = [],
|
||||||
}: {
|
}: {
|
||||||
organizationScopes?: string[];
|
organizationScopes?: string[];
|
||||||
resourceScopes?: string[];
|
resourceScopes?: string[];
|
||||||
|
organizationResourceScopes?: string[];
|
||||||
},
|
},
|
||||||
tenantId: string
|
tenantId: string
|
||||||
) => {
|
) => {
|
||||||
const [organizationScopesData, resourceScopesData] = await Promise.all([
|
const [organizationScopesData, resourceScopesData, organizationResourceScopesData] =
|
||||||
organizationScopesQuery.findByIds(organizationScopes),
|
await Promise.all([
|
||||||
findScopesByIds(resourceScopes),
|
organizationScopesQuery.findByIds(organizationScopes),
|
||||||
]);
|
findScopesByIds(resourceScopes),
|
||||||
|
findScopesByIds(organizationResourceScopes),
|
||||||
|
]);
|
||||||
|
|
||||||
const invalidOrganizationScopes = organizationScopes.filter(
|
const invalidOrganizationScopes = organizationScopes.filter(
|
||||||
(scope) => !organizationScopesData.some(({ id }) => id === scope)
|
(scope) => !organizationScopesData.some(({ id }) => id === scope)
|
||||||
|
@ -95,22 +100,28 @@ export const createApplicationLibrary = (queries: Queries) => {
|
||||||
(scope) => !resourceScopesData.some(({ id }) => id === scope)
|
(scope) => !resourceScopesData.some(({ id }) => id === scope)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const invalidOrganizationResourceScopes = organizationResourceScopes.filter(
|
||||||
|
(scope) => !organizationResourceScopesData.some(({ id }) => id === scope)
|
||||||
|
);
|
||||||
|
|
||||||
// Assert that all scopes exist, return the missing ones
|
// Assert that all scopes exist, return the missing ones
|
||||||
assertThat(
|
assertThat(
|
||||||
invalidOrganizationScopes.length === 0 && invalidResourceScopes.length === 0,
|
invalidOrganizationScopes.length === 0 &&
|
||||||
|
invalidResourceScopes.length === 0 &&
|
||||||
|
invalidOrganizationResourceScopes.length === 0,
|
||||||
new RequestError(
|
new RequestError(
|
||||||
{
|
{
|
||||||
code: 'application.user_consent_scopes_not_found',
|
code: 'application.user_consent_scopes_not_found',
|
||||||
status: 422,
|
status: 422,
|
||||||
},
|
},
|
||||||
{ invalidOrganizationScopes, invalidResourceScopes }
|
{ invalidOrganizationScopes, invalidResourceScopes, invalidOrganizationResourceScopes }
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
const managementApiResourceIndicator = getManagementApiResourceIndicator(tenantId);
|
const managementApiResourceIndicator = getManagementApiResourceIndicator(tenantId);
|
||||||
|
|
||||||
const managementApiScopes = await findScopesByIdsAndResourceIndicator(
|
const managementApiScopes = await findScopesByIdsAndResourceIndicator(
|
||||||
resourceScopes,
|
[...resourceScopes, ...organizationResourceScopes],
|
||||||
managementApiResourceIndicator
|
managementApiResourceIndicator
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -129,10 +140,12 @@ export const createApplicationLibrary = (queries: Queries) => {
|
||||||
{
|
{
|
||||||
organizationScopes,
|
organizationScopes,
|
||||||
resourceScopes,
|
resourceScopes,
|
||||||
|
organizationResourceScopes,
|
||||||
userScopes,
|
userScopes,
|
||||||
}: {
|
}: {
|
||||||
organizationScopes?: string[];
|
organizationScopes?: string[];
|
||||||
resourceScopes?: string[];
|
resourceScopes?: string[];
|
||||||
|
organizationResourceScopes?: string[];
|
||||||
userScopes?: string[];
|
userScopes?: string[];
|
||||||
}
|
}
|
||||||
) => {
|
) => {
|
||||||
|
@ -148,6 +161,12 @@ export const createApplicationLibrary = (queries: Queries) => {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (organizationResourceScopes) {
|
||||||
|
await userConsentOrganizationResourceScopes.insert(
|
||||||
|
...organizationResourceScopes.map<[string, string]>((scope) => [applicationId, scope])
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
if (userScopes) {
|
if (userScopes) {
|
||||||
await Promise.all(
|
await Promise.all(
|
||||||
userScopes.map(async (userScope) =>
|
userScopes.map(async (userScope) =>
|
||||||
|
@ -181,6 +200,21 @@ export const createApplicationLibrary = (queries: Queries) => {
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const getApplicationUserConsentOrganizationResourceScopes = async (applicationId: string) => {
|
||||||
|
const [, scopes] = await userConsentOrganizationResourceScopes.getEntities(Scopes, {
|
||||||
|
applicationId,
|
||||||
|
});
|
||||||
|
|
||||||
|
const groupedScopes = groupResourceScopesByResourceId(scopes);
|
||||||
|
|
||||||
|
return Promise.all(
|
||||||
|
groupedScopes.map(async ({ resourceId, scopes }) => ({
|
||||||
|
resource: await findResourceById(resourceId),
|
||||||
|
scopes,
|
||||||
|
}))
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
const getApplicationUserConsentScopes = async (applicationId: string) =>
|
const getApplicationUserConsentScopes = async (applicationId: string) =>
|
||||||
userConsentUserScopes.findAllByApplicationId(applicationId);
|
userConsentUserScopes.findAllByApplicationId(applicationId);
|
||||||
|
|
||||||
|
@ -198,6 +232,10 @@ export const createApplicationLibrary = (queries: Queries) => {
|
||||||
await userConsentResourceScopes.delete({ applicationId, scopeId });
|
await userConsentResourceScopes.delete({ applicationId, scopeId });
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case ApplicationUserConsentScopeType.OrganizationResourceScopes: {
|
||||||
|
await userConsentOrganizationResourceScopes.delete({ applicationId, scopeId });
|
||||||
|
break;
|
||||||
|
}
|
||||||
case ApplicationUserConsentScopeType.UserScopes: {
|
case ApplicationUserConsentScopeType.UserScopes: {
|
||||||
await userConsentUserScopes.deleteByApplicationIdAndScopeId(applicationId, scopeId);
|
await userConsentUserScopes.deleteByApplicationIdAndScopeId(applicationId, scopeId);
|
||||||
break;
|
break;
|
||||||
|
@ -250,6 +288,7 @@ export const createApplicationLibrary = (queries: Queries) => {
|
||||||
assignApplicationUserConsentScopes,
|
assignApplicationUserConsentScopes,
|
||||||
getApplicationUserConsentOrganizationScopes,
|
getApplicationUserConsentOrganizationScopes,
|
||||||
getApplicationUserConsentResourceScopes,
|
getApplicationUserConsentResourceScopes,
|
||||||
|
getApplicationUserConsentOrganizationResourceScopes,
|
||||||
getApplicationUserConsentScopes,
|
getApplicationUserConsentScopes,
|
||||||
deleteApplicationUserConsentScopesByTypeAndScopeId,
|
deleteApplicationUserConsentScopesByTypeAndScopeId,
|
||||||
validateUserConsentOrganizationMembership,
|
validateUserConsentOrganizationMembership,
|
||||||
|
|
|
@ -2,6 +2,7 @@ import { type UserScope } from '@logto/core-kit';
|
||||||
import {
|
import {
|
||||||
ApplicationUserConsentOrganizationScopes,
|
ApplicationUserConsentOrganizationScopes,
|
||||||
ApplicationUserConsentResourceScopes,
|
ApplicationUserConsentResourceScopes,
|
||||||
|
ApplicationUserConsentOrganizationResourceScopes,
|
||||||
ApplicationUserConsentUserScopes,
|
ApplicationUserConsentUserScopes,
|
||||||
Applications,
|
Applications,
|
||||||
OrganizationScopes,
|
OrganizationScopes,
|
||||||
|
@ -32,6 +33,15 @@ export class ApplicationUserConsentResourceScopeQueries extends TwoRelationsQuer
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export class ApplicationUserConsentOrganizationResourceScopeQueries extends TwoRelationsQueries<
|
||||||
|
typeof Applications,
|
||||||
|
typeof Scopes
|
||||||
|
> {
|
||||||
|
constructor(pool: CommonQueryMethods) {
|
||||||
|
super(pool, ApplicationUserConsentOrganizationResourceScopes.table, Applications, Scopes);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export const createApplicationUserConsentUserScopeQueries = (pool: CommonQueryMethods) => {
|
export const createApplicationUserConsentUserScopeQueries = (pool: CommonQueryMethods) => {
|
||||||
const insert = buildInsertIntoWithPool(pool)(ApplicationUserConsentUserScopes, {
|
const insert = buildInsertIntoWithPool(pool)(ApplicationUserConsentUserScopes, {
|
||||||
onConflict: { ignore: true },
|
onConflict: { ignore: true },
|
||||||
|
|
|
@ -15,6 +15,7 @@ import type { OmitAutoSetFields } from '#src/utils/sql.js';
|
||||||
|
|
||||||
import ApplicationUserConsentOrganizationsQuery from './application-user-consent-organizations.js';
|
import ApplicationUserConsentOrganizationsQuery from './application-user-consent-organizations.js';
|
||||||
import {
|
import {
|
||||||
|
ApplicationUserConsentOrganizationResourceScopeQueries,
|
||||||
ApplicationUserConsentOrganizationScopeQueries,
|
ApplicationUserConsentOrganizationScopeQueries,
|
||||||
ApplicationUserConsentResourceScopeQueries,
|
ApplicationUserConsentResourceScopeQueries,
|
||||||
createApplicationUserConsentUserScopeQueries,
|
createApplicationUserConsentUserScopeQueries,
|
||||||
|
@ -253,6 +254,8 @@ export const createApplicationQueries = (pool: CommonQueryMethods) => {
|
||||||
deleteApplicationById,
|
deleteApplicationById,
|
||||||
userConsentOrganizationScopes: new ApplicationUserConsentOrganizationScopeQueries(pool),
|
userConsentOrganizationScopes: new ApplicationUserConsentOrganizationScopeQueries(pool),
|
||||||
userConsentResourceScopes: new ApplicationUserConsentResourceScopeQueries(pool),
|
userConsentResourceScopes: new ApplicationUserConsentResourceScopeQueries(pool),
|
||||||
|
userConsentOrganizationResourceScopes:
|
||||||
|
new ApplicationUserConsentOrganizationResourceScopeQueries(pool),
|
||||||
userConsentUserScopes: createApplicationUserConsentUserScopeQueries(pool),
|
userConsentUserScopes: createApplicationUserConsentUserScopeQueries(pool),
|
||||||
userConsentOrganizations: new ApplicationUserConsentOrganizationsQuery(pool),
|
userConsentOrganizations: new ApplicationUserConsentOrganizationsQuery(pool),
|
||||||
};
|
};
|
||||||
|
|
|
@ -15,6 +15,9 @@
|
||||||
"resourceScopes": {
|
"resourceScopes": {
|
||||||
"description": "A list of resource scope id to assign to the application. Throws error if any given resource scope is not found."
|
"description": "A list of resource scope id to assign to the application. Throws error if any given resource scope is not found."
|
||||||
},
|
},
|
||||||
|
"organizationResourceScopes": {
|
||||||
|
"description": "A list of organization resource scope id to assign to the application. Throws error if any given resource scope is not found."
|
||||||
|
},
|
||||||
"userScopes": {
|
"userScopes": {
|
||||||
"description": "A list of user scope enum value to assign to the application."
|
"description": "A list of user scope enum value to assign to the application."
|
||||||
}
|
}
|
||||||
|
@ -51,6 +54,9 @@
|
||||||
"resourceScopes": {
|
"resourceScopes": {
|
||||||
"description": "A list of resource scope details grouped by resource id assigned to the application."
|
"description": "A list of resource scope details grouped by resource id assigned to the application."
|
||||||
},
|
},
|
||||||
|
"organizationResourceScopes": {
|
||||||
|
"description": "A list of organization resource scope details grouped by resource id assigned to the application."
|
||||||
|
},
|
||||||
"userScopes": {
|
"userScopes": {
|
||||||
"description": "A list of user scope enum value assigned to the application."
|
"description": "A list of user scope enum value assigned to the application."
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,6 +5,7 @@ import {
|
||||||
} from '@logto/schemas';
|
} from '@logto/schemas';
|
||||||
import { object, string, nativeEnum } from 'zod';
|
import { object, string, nativeEnum } from 'zod';
|
||||||
|
|
||||||
|
import { EnvSet } from '#src/env-set/index.js';
|
||||||
import koaGuard from '#src/middleware/koa-guard.js';
|
import koaGuard from '#src/middleware/koa-guard.js';
|
||||||
|
|
||||||
import type { ManagementApiRouter, RouterInitArgs } from '../types.js';
|
import type { ManagementApiRouter, RouterInitArgs } from '../types.js';
|
||||||
|
@ -24,6 +25,7 @@ export default function applicationUserConsentScopeRoutes<T extends ManagementAp
|
||||||
assignApplicationUserConsentScopes,
|
assignApplicationUserConsentScopes,
|
||||||
getApplicationUserConsentOrganizationScopes,
|
getApplicationUserConsentOrganizationScopes,
|
||||||
getApplicationUserConsentResourceScopes,
|
getApplicationUserConsentResourceScopes,
|
||||||
|
getApplicationUserConsentOrganizationResourceScopes,
|
||||||
getApplicationUserConsentScopes,
|
getApplicationUserConsentScopes,
|
||||||
deleteApplicationUserConsentScopesByTypeAndScopeId,
|
deleteApplicationUserConsentScopesByTypeAndScopeId,
|
||||||
},
|
},
|
||||||
|
@ -40,6 +42,7 @@ export default function applicationUserConsentScopeRoutes<T extends ManagementAp
|
||||||
body: object({
|
body: object({
|
||||||
organizationScopes: string().array().optional(),
|
organizationScopes: string().array().optional(),
|
||||||
resourceScopes: string().array().optional(),
|
resourceScopes: string().array().optional(),
|
||||||
|
organizationResourceScopes: string().array().optional(),
|
||||||
userScopes: nativeEnum(UserScope).array().optional(),
|
userScopes: nativeEnum(UserScope).array().optional(),
|
||||||
}),
|
}),
|
||||||
status: [201, 404, 422],
|
status: [201, 404, 422],
|
||||||
|
@ -50,11 +53,15 @@ export default function applicationUserConsentScopeRoutes<T extends ManagementAp
|
||||||
body,
|
body,
|
||||||
} = ctx.guard;
|
} = ctx.guard;
|
||||||
|
|
||||||
|
// TODO @wangsijie: Remove this when feature is enabled in production
|
||||||
|
const { organizationResourceScopes, ...rest } = body;
|
||||||
|
const theBody = EnvSet.values.isDevFeaturesEnabled ? body : rest;
|
||||||
|
|
||||||
await validateThirdPartyApplicationById(applicationId);
|
await validateThirdPartyApplicationById(applicationId);
|
||||||
|
|
||||||
await validateApplicationUserConsentScopes(body, tenantId);
|
await validateApplicationUserConsentScopes(theBody, tenantId);
|
||||||
|
|
||||||
await assignApplicationUserConsentScopes(applicationId, body);
|
await assignApplicationUserConsentScopes(applicationId, theBody);
|
||||||
|
|
||||||
ctx.status = 201;
|
ctx.status = 201;
|
||||||
|
|
||||||
|
@ -68,7 +75,9 @@ export default function applicationUserConsentScopeRoutes<T extends ManagementAp
|
||||||
params: object({
|
params: object({
|
||||||
applicationId: string(),
|
applicationId: string(),
|
||||||
}),
|
}),
|
||||||
response: applicationUserConsentScopesResponseGuard,
|
response: EnvSet.values.isDevFeaturesEnabled
|
||||||
|
? applicationUserConsentScopesResponseGuard
|
||||||
|
: applicationUserConsentScopesResponseGuard.omit({ organizationResourceScopes: true }),
|
||||||
status: [200, 404],
|
status: [200, 404],
|
||||||
}),
|
}),
|
||||||
async (ctx, next) => {
|
async (ctx, next) => {
|
||||||
|
@ -77,17 +86,26 @@ export default function applicationUserConsentScopeRoutes<T extends ManagementAp
|
||||||
await findApplicationById(applicationId);
|
await findApplicationById(applicationId);
|
||||||
|
|
||||||
// Note: The following queries will return full data schema, we rely on the response guard to filter out the fields we don't need.
|
// Note: The following queries will return full data schema, we rely on the response guard to filter out the fields we don't need.
|
||||||
const [organizationScopes, resourceScopes, userScopes] = await Promise.all([
|
const [organizationScopes, resourceScopes, organizationResourceScopes, userScopes] =
|
||||||
getApplicationUserConsentOrganizationScopes(applicationId),
|
await Promise.all([
|
||||||
getApplicationUserConsentResourceScopes(applicationId),
|
getApplicationUserConsentOrganizationScopes(applicationId),
|
||||||
getApplicationUserConsentScopes(applicationId),
|
getApplicationUserConsentResourceScopes(applicationId),
|
||||||
]);
|
getApplicationUserConsentOrganizationResourceScopes(applicationId),
|
||||||
|
getApplicationUserConsentScopes(applicationId),
|
||||||
|
]);
|
||||||
|
|
||||||
ctx.body = {
|
ctx.body = EnvSet.values.isDevFeaturesEnabled
|
||||||
organizationScopes,
|
? {
|
||||||
resourceScopes,
|
organizationScopes,
|
||||||
userScopes,
|
resourceScopes,
|
||||||
};
|
organizationResourceScopes,
|
||||||
|
userScopes,
|
||||||
|
}
|
||||||
|
: {
|
||||||
|
organizationScopes,
|
||||||
|
resourceScopes,
|
||||||
|
userScopes,
|
||||||
|
};
|
||||||
|
|
||||||
return next();
|
return next();
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,6 +11,7 @@ export const assignUserConsentScopes = async (
|
||||||
payload: {
|
payload: {
|
||||||
organizationScopes?: string[];
|
organizationScopes?: string[];
|
||||||
resourceScopes?: string[];
|
resourceScopes?: string[];
|
||||||
|
organizationResourceScopes?: string[];
|
||||||
userScopes?: UserScope[];
|
userScopes?: UserScope[];
|
||||||
}
|
}
|
||||||
) => authedAdminApi.post(`applications/${applicationId}/user-consent-scopes`, { json: payload });
|
) => authedAdminApi.post(`applications/${applicationId}/user-consent-scopes`, { json: payload });
|
||||||
|
|
|
@ -16,6 +16,7 @@ describe('assign user consent scopes to application', () => {
|
||||||
const applicationIds = new Map<string, string>();
|
const applicationIds = new Map<string, string>();
|
||||||
const organizationScopes = new Map<string, string>();
|
const organizationScopes = new Map<string, string>();
|
||||||
const resourceScopes = new Map<string, string>();
|
const resourceScopes = new Map<string, string>();
|
||||||
|
const organizationResourceScopes = new Map<string, string>();
|
||||||
const resourceIds = new Set<string>();
|
const resourceIds = new Set<string>();
|
||||||
|
|
||||||
const organizationScopeApi = new OrganizationScopeApi();
|
const organizationScopeApi = new OrganizationScopeApi();
|
||||||
|
@ -52,6 +53,12 @@ describe('assign user consent scopes to application', () => {
|
||||||
|
|
||||||
resourceScopes.set('resourceScope1', resourceScope1.id);
|
resourceScopes.set('resourceScope1', resourceScope1.id);
|
||||||
resourceScopes.set('resourceScope2', resourceScope2.id);
|
resourceScopes.set('resourceScope2', resourceScope2.id);
|
||||||
|
|
||||||
|
const resourceScope3 = await createScope(resource.id);
|
||||||
|
const resourceScope4 = await createScope(resource.id);
|
||||||
|
|
||||||
|
organizationResourceScopes.set('resourceScope1', resourceScope3.id);
|
||||||
|
organizationResourceScopes.set('resourceScope2', resourceScope4.id);
|
||||||
});
|
});
|
||||||
|
|
||||||
afterAll(async () => {
|
afterAll(async () => {
|
||||||
|
@ -75,6 +82,7 @@ describe('assign user consent scopes to application', () => {
|
||||||
assignUserConsentScopes(applicationIds.get('firstPartyApp')!, {
|
assignUserConsentScopes(applicationIds.get('firstPartyApp')!, {
|
||||||
organizationScopes: Array.from(organizationScopes.values()),
|
organizationScopes: Array.from(organizationScopes.values()),
|
||||||
resourceScopes: Array.from(resourceScopes.values()),
|
resourceScopes: Array.from(resourceScopes.values()),
|
||||||
|
organizationResourceScopes: Array.from(organizationResourceScopes.values()),
|
||||||
}),
|
}),
|
||||||
{
|
{
|
||||||
code: 'application.third_party_application_only',
|
code: 'application.third_party_application_only',
|
||||||
|
@ -107,11 +115,24 @@ describe('assign user consent scopes to application', () => {
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should throw error when trying to assign a non-existing organization resource scope', async () => {
|
||||||
|
await expectRejects(
|
||||||
|
assignUserConsentScopes(applicationIds.get('thirdPartyApp')!, {
|
||||||
|
organizationResourceScopes: ['non-existing-resource-scope'],
|
||||||
|
}),
|
||||||
|
{
|
||||||
|
code: 'application.user_consent_scopes_not_found',
|
||||||
|
status: 422,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
it('should assign scopes to third-party application successfully', async () => {
|
it('should assign scopes to third-party application successfully', async () => {
|
||||||
await expect(
|
await expect(
|
||||||
assignUserConsentScopes(applicationIds.get('thirdPartyApp')!, {
|
assignUserConsentScopes(applicationIds.get('thirdPartyApp')!, {
|
||||||
organizationScopes: Array.from(organizationScopes.values()),
|
organizationScopes: Array.from(organizationScopes.values()),
|
||||||
resourceScopes: Array.from(resourceScopes.values()),
|
resourceScopes: Array.from(resourceScopes.values()),
|
||||||
|
organizationResourceScopes: Array.from(organizationResourceScopes.values()),
|
||||||
userScopes: [UserScope.Profile, UserScope.Email, UserScope.OrganizationRoles],
|
userScopes: [UserScope.Profile, UserScope.Email, UserScope.OrganizationRoles],
|
||||||
})
|
})
|
||||||
).resolves.not.toThrow();
|
).resolves.not.toThrow();
|
||||||
|
@ -122,6 +143,7 @@ describe('assign user consent scopes to application', () => {
|
||||||
assignUserConsentScopes(applicationIds.get('thirdPartyApp')!, {
|
assignUserConsentScopes(applicationIds.get('thirdPartyApp')!, {
|
||||||
organizationScopes: [organizationScopes.get('organizationScope1')!],
|
organizationScopes: [organizationScopes.get('organizationScope1')!],
|
||||||
resourceScopes: [resourceScopes.get('resourceScope1')!],
|
resourceScopes: [resourceScopes.get('resourceScope1')!],
|
||||||
|
organizationResourceScopes: [organizationResourceScopes.get('resourceScope1')!],
|
||||||
userScopes: [UserScope.Profile],
|
userScopes: [UserScope.Profile],
|
||||||
})
|
})
|
||||||
).resolves.not.toThrow();
|
).resolves.not.toThrow();
|
||||||
|
@ -154,6 +176,18 @@ describe('assign user consent scopes to application', () => {
|
||||||
).toBeTruthy();
|
).toBeTruthy();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
expect(result.organizationResourceScopes.length).toBe(1);
|
||||||
|
expect(result.organizationResourceScopes[0]!.resource.id).toBe(Array.from(resourceIds)[0]);
|
||||||
|
expect(result.organizationResourceScopes[0]!.scopes.length).toBe(
|
||||||
|
organizationResourceScopes.size
|
||||||
|
);
|
||||||
|
|
||||||
|
for (const resourceScopeId of organizationResourceScopes.values()) {
|
||||||
|
expect(
|
||||||
|
result.organizationResourceScopes[0]!.scopes.some(({ id }) => id === resourceScopeId)
|
||||||
|
).toBeTruthy();
|
||||||
|
}
|
||||||
|
|
||||||
expect(result.userScopes.length).toBe(3);
|
expect(result.userScopes.length).toBe(3);
|
||||||
|
|
||||||
for (const userScope of [UserScope.Profile, UserScope.Email, UserScope.OrganizationRoles]) {
|
for (const userScope of [UserScope.Profile, UserScope.Email, UserScope.OrganizationRoles]) {
|
||||||
|
@ -170,6 +204,7 @@ describe('assign user consent scopes to application', () => {
|
||||||
|
|
||||||
expect(result.organizationScopes.length).toBe(0);
|
expect(result.organizationScopes.length).toBe(0);
|
||||||
expect(result.resourceScopes.length).toBe(0);
|
expect(result.resourceScopes.length).toBe(0);
|
||||||
|
expect(result.organizationResourceScopes.length).toBe(0);
|
||||||
expect(result.userScopes.length).toBe(0);
|
expect(result.userScopes.length).toBe(0);
|
||||||
|
|
||||||
await deleteApplication(newApp.id);
|
await deleteApplication(newApp.id);
|
||||||
|
@ -214,6 +249,18 @@ describe('assign user consent scopes to application', () => {
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
await expectRejects(
|
||||||
|
deleteUserConsentScopes(
|
||||||
|
applicationIds.get('thirdPartyApp')!,
|
||||||
|
ApplicationUserConsentScopeType.OrganizationResourceScopes,
|
||||||
|
'non-existing-resource-scope'
|
||||||
|
),
|
||||||
|
{
|
||||||
|
code: 'entity.not_found',
|
||||||
|
status: 404,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
await expectRejects(
|
await expectRejects(
|
||||||
deleteUserConsentScopes(
|
deleteUserConsentScopes(
|
||||||
applicationIds.get('thirdPartyApp')!,
|
applicationIds.get('thirdPartyApp')!,
|
||||||
|
@ -232,6 +279,7 @@ describe('assign user consent scopes to application', () => {
|
||||||
assignUserConsentScopes(applicationIds.get('thirdPartyApp')!, {
|
assignUserConsentScopes(applicationIds.get('thirdPartyApp')!, {
|
||||||
organizationScopes: Array.from(organizationScopes.values()),
|
organizationScopes: Array.from(organizationScopes.values()),
|
||||||
resourceScopes: Array.from(resourceScopes.values()),
|
resourceScopes: Array.from(resourceScopes.values()),
|
||||||
|
organizationResourceScopes: Array.from(organizationResourceScopes.values()),
|
||||||
userScopes: [UserScope.Profile, UserScope.Email, UserScope.OrganizationRoles],
|
userScopes: [UserScope.Profile, UserScope.Email, UserScope.OrganizationRoles],
|
||||||
})
|
})
|
||||||
).resolves.not.toThrow();
|
).resolves.not.toThrow();
|
||||||
|
@ -252,6 +300,14 @@ describe('assign user consent scopes to application', () => {
|
||||||
)
|
)
|
||||||
).resolves.not.toThrow();
|
).resolves.not.toThrow();
|
||||||
|
|
||||||
|
await expect(
|
||||||
|
deleteUserConsentScopes(
|
||||||
|
applicationIds.get('thirdPartyApp')!,
|
||||||
|
ApplicationUserConsentScopeType.OrganizationResourceScopes,
|
||||||
|
organizationResourceScopes.get('resourceScope1')!
|
||||||
|
)
|
||||||
|
).resolves.not.toThrow();
|
||||||
|
|
||||||
await expect(
|
await expect(
|
||||||
deleteUserConsentScopes(
|
deleteUserConsentScopes(
|
||||||
applicationIds.get('thirdPartyApp')!,
|
applicationIds.get('thirdPartyApp')!,
|
||||||
|
@ -274,6 +330,12 @@ describe('assign user consent scopes to application', () => {
|
||||||
)
|
)
|
||||||
).toBeUndefined();
|
).toBeUndefined();
|
||||||
|
|
||||||
|
expect(
|
||||||
|
result.organizationResourceScopes[0]!.scopes.find(
|
||||||
|
({ id }) => id === organizationResourceScopes.get('resourceScope1')!
|
||||||
|
)
|
||||||
|
).toBeUndefined();
|
||||||
|
|
||||||
expect(result.userScopes.includes(UserScope.OrganizationRoles)).toBeFalsy();
|
expect(result.userScopes.includes(UserScope.OrganizationRoles)).toBeFalsy();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -40,22 +40,26 @@ export const applicationPatchGuard = applicationCreateGuard.partial().omit({
|
||||||
isThirdParty: true,
|
isThirdParty: true,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const resourceScopesGuard = z.array(
|
||||||
|
z.object({
|
||||||
|
resource: Resources.guard.pick({ id: true, name: true, indicator: true }),
|
||||||
|
scopes: z.array(Scopes.guard.pick({ id: true, name: true, description: true })),
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
export const applicationUserConsentScopesResponseGuard = z.object({
|
export const applicationUserConsentScopesResponseGuard = z.object({
|
||||||
organizationScopes: z.array(
|
organizationScopes: z.array(
|
||||||
OrganizationScopes.guard.pick({ id: true, name: true, description: true })
|
OrganizationScopes.guard.pick({ id: true, name: true, description: true })
|
||||||
),
|
),
|
||||||
resourceScopes: z.array(
|
resourceScopes: resourceScopesGuard,
|
||||||
z.object({
|
organizationResourceScopes: resourceScopesGuard,
|
||||||
resource: Resources.guard.pick({ id: true, name: true, indicator: true }),
|
|
||||||
scopes: z.array(Scopes.guard.pick({ id: true, name: true, description: true })),
|
|
||||||
})
|
|
||||||
),
|
|
||||||
userScopes: z.array(z.nativeEnum(UserScope)),
|
userScopes: z.array(z.nativeEnum(UserScope)),
|
||||||
});
|
});
|
||||||
|
|
||||||
export enum ApplicationUserConsentScopeType {
|
export enum ApplicationUserConsentScopeType {
|
||||||
OrganizationScopes = 'organization-scopes',
|
OrganizationScopes = 'organization-scopes',
|
||||||
ResourceScopes = 'resource-scopes',
|
ResourceScopes = 'resource-scopes',
|
||||||
|
OrganizationResourceScopes = 'organization-resource-scopes',
|
||||||
UserScopes = 'user-scopes',
|
UserScopes = 'user-scopes',
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue