Skip to content

Commit

Permalink
Get matched roles and permissions (#302)
Browse files Browse the repository at this point in the history
* Get matched roles and permissions

* remove dots
  • Loading branch information
asafshen authored Dec 14, 2023
1 parent 4991481 commit 7f19cae
Show file tree
Hide file tree
Showing 3 changed files with 120 additions and 8 deletions.
33 changes: 27 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -400,7 +400,7 @@ For multi-tenant uses:

```typescript
// You can validate specific permissions
const validTenantPermissions = await descopeClient.validateTenantPermissions(
const validTenantPermissions = descopeClient.validateTenantPermissions(
authInfo,
'my-tenant-ID',
['Permission to validate'],
Expand All @@ -410,30 +410,51 @@ if (!validTenantPermissions) {
}

// Or validate roles directly
const validTenantRoles = await descopeClient.validateTenantRoles(authInfo, 'my-tenant-ID', [
const validTenantRoles = descopeClient.validateTenantRoles(authInfo, 'my-tenant-ID', [
'Role to validate',
]);
if (!validTenantRoles) {
// Deny access
}

// Or get the matched roles/permissions
const matchedTenantRoles = descopeClient.getMatchedTenantRoles(authInfo, 'my-tenant-ID', [
'Role to validate',
'Another role to validate'
]);

const matchedTenantPermissions = descopeClient.getMatchedTenantPermissions(
authInfo,
'my-tenant-ID',
['Permission to validate', 'Another permission to validate']],
);
```

When not using tenants use:

```typescript
// You can validate specific permissions
const validPermissions = await descopeClient.validatePermissions(authInfo, [
'Permission to validate',
]);
const validPermissions = descopeClient.validatePermissions(authInfo, ['Permission to validate']);
if (!validPermissions) {
// Deny access
}

// Or validate roles directly
const validRoles = await descopeClient.validateRoles(authInfo, ['Role to validate']);
const validRoles = descopeClient.validateRoles(authInfo, ['Role to validate']);
if (!validRoles) {
// Deny access
}

// Or get the matched roles/permissions
const matchedRoles = descopeClient.getMatchedRoles(authInfo, [
'Role to validate',
'Another role to validate',
]);

const matchedPermissions = descopeClient.getMatchedPermissions(authInfo, [
'Permission to validate',
'Another permission to validate',
]);
```

### Logging Out
Expand Down
37 changes: 37 additions & 0 deletions lib/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -331,6 +331,43 @@ describe('sdk', () => {
});
});

describe('getMatchedPermissionsRoles', () => {
it('should always succeed with empty requirements', () => {
expect(sdk.getMatchedPermissions(permAuthInfo, [])).toStrictEqual([]);
expect(sdk.getMatchedTenantPermissions(permTenantAuthInfo, 'kuku', [])).toStrictEqual([]);
expect(sdk.getMatchedRoles(permAuthInfo, [])).toStrictEqual([]);
expect(sdk.getMatchedTenantPermissions(permTenantAuthInfo, 'kuku', [])).toStrictEqual([]);
});
it('should return the matched permissions or roles', () => {
// all permissions matched
expect(sdk.getMatchedPermissions(permAuthInfo, ['foo'])).toStrictEqual(['foo']);
// some permissions are matched
expect(
sdk.getMatchedTenantPermissions(permTenantAuthInfo, 'kuku', ['foo', 'bar', 'qux']),
).toStrictEqual(['foo', 'bar']);
// all roles matched
expect(sdk.getMatchedRoles(permAuthInfo, ['abc'])).toStrictEqual(['abc']);
// some roles are matched
expect(
sdk.getMatchedTenantRoles(permTenantAuthInfo, 'kuku', ['abc', 'xyz', 'tuv']),
).toStrictEqual(['abc', 'xyz']);
});

it('should return empty list when there are no matched permissions or roles', () => {
// no permissions matched
expect(sdk.getMatchedPermissions(permAuthInfo, ['qux'])).toStrictEqual([]);
expect(
sdk.getMatchedTenantPermissions(permTenantAuthInfo, 'kuku', ['qux', 'zuk']),
).toStrictEqual([]);
// no roles matched
expect(sdk.getMatchedRoles(permAuthInfo, ['tuv'])).toStrictEqual([]);
// some roles are matched
expect(sdk.getMatchedTenantRoles(permTenantAuthInfo, 'kuku', ['tuv', 'rum'])).toStrictEqual(
[],
);
});
});

describe('withCookies', () => {
describe('when no cookie', () => {
const paths = [
Expand Down
58 changes: 56 additions & 2 deletions lib/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -219,12 +219,23 @@ const nodeSdk = ({ managementKey, publicKey, ...config }: NodeSdkArgs) => {
* @returns true if all permissions exist, false otherwise
*/
validatePermissions(authInfo: AuthenticationInfo, permissions: string[]): boolean {
return sdk.validateTenantPermissions(authInfo, null, permissions);
return sdk.validateTenantPermissions(authInfo, '', permissions);
},

/**
* Retrieves the permissions from JWT top level claims that match the specified permissions list
* @param authInfo JWT parsed info containing the permissions
* @param permissions List of permissions to match against the JWT claims
* @returns An array of permissions that are both in the JWT claims and the specified list. Returns an empty array if no matches are found
*/
getMatchedPermissions(authInfo: AuthenticationInfo, permissions: string[]): string[] {
return sdk.getMatchedTenantPermissions(authInfo, '', permissions);
},

/**
* Make sure that all given permissions exist on the parsed JWT tenant claims
* @param authInfo JWT parsed info
* @param tenant tenant to validate the permissions for
* @param permissions list of permissions to make sure they exist on te JWT claims
* @returns true if all permissions exist, false otherwise
*/
Expand All @@ -240,19 +251,48 @@ const nodeSdk = ({ managementKey, publicKey, ...config }: NodeSdkArgs) => {
return permissions.every((perm) => granted.includes(perm));
},

/**
* Retrieves the permissions from JWT tenant claims that match the specified permissions list
* @param authInfo JWT parsed info containing the permissions
* @param tenant tenant to match the permissions for
* @param permissions List of permissions to match against the JWT claims
* @returns An array of permissions that are both in the JWT claims and the specified list. Returns an empty array if no matches are found
* */
getMatchedTenantPermissions(
authInfo: AuthenticationInfo,
tenant: string,
permissions: string[],
): string[] {
if (tenant && !isUserAssociatedWithTenant(authInfo, tenant)) return [];

const granted = getAuthorizationClaimItems(authInfo, permissionsClaimName, tenant);
return permissions.filter((perm) => granted.includes(perm));
},

/**
* Make sure that all given roles exist on the parsed JWT top level claims
* @param authInfo JWT parsed info
* @param roles list of roles to make sure they exist on te JWT claims
* @returns true if all roles exist, false otherwise
*/
validateRoles(authInfo: AuthenticationInfo, roles: string[]): boolean {
return sdk.validateTenantRoles(authInfo, null, roles);
return sdk.validateTenantRoles(authInfo, '', roles);
},

/**
* Retrieves the roles from JWT top level claims that match the specified roles list
* @param authInfo JWT parsed info containing the roles
* @param roles List of roles to match against the JWT claims
* @returns An array of roles that are both in the JWT claims and the specified list. Returns an empty array if no matches are found
*/
getMatchedRoles(authInfo: AuthenticationInfo, roles: string[]): string[] {
return sdk.getMatchedTenantRoles(authInfo, '', roles);
},

/**
* Make sure that all given roles exist on the parsed JWT tenant claims
* @param authInfo JWT parsed info
* @param tenant tenant to validate the roles for
* @param roles list of roles to make sure they exist on te JWT claims
* @returns true if all roles exist, false otherwise
*/
Expand All @@ -263,6 +303,20 @@ const nodeSdk = ({ managementKey, publicKey, ...config }: NodeSdkArgs) => {
const membership = getAuthorizationClaimItems(authInfo, rolesClaimName, tenant);
return roles.every((role) => membership.includes(role));
},

/**
* Retrieves the roles from JWT tenant claims that match the specified roles list
* @param authInfo JWT parsed info containing the roles
* @param tenant tenant to match the roles for
* @param roles List of roles to match against the JWT claims
* @returns An array of roles that are both in the JWT claims and the specified list. Returns an empty array if no matches are found
*/
getMatchedTenantRoles(authInfo: AuthenticationInfo, tenant: string, roles: string[]): string[] {
if (tenant && !isUserAssociatedWithTenant(authInfo, tenant)) return [];

const membership = getAuthorizationClaimItems(authInfo, rolesClaimName, tenant);
return roles.filter((role) => membership.includes(role));
},
};

return wrapWith(
Expand Down

0 comments on commit 7f19cae

Please sign in to comment.