Skip to content

Commit

Permalink
feat: added ignore to AuthRoleClaimsFactoryConfig
Browse files Browse the repository at this point in the history
  • Loading branch information
dereekb committed Jun 8, 2022
1 parent 92bfd84 commit 71e3cac
Show file tree
Hide file tree
Showing 2 changed files with 60 additions and 41 deletions.
23 changes: 19 additions & 4 deletions packages/util/src/lib/auth/auth.role.claims.spec.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,18 @@
import { AUTH_USER_ROLE, Maybe } from '@dereekb/util';
import { AUTH_USER_ROLE, Maybe, objectHasKey } from '@dereekb/util';
import { containsAllValues, hasDifferentValues } from '../set';
import { AuthRoleSet, AUTH_ADMIN_ROLE } from './auth.role';
import { AuthClaimsObject, AuthRoleClaimsService, authRoleClaimsService, AUTH_ROLE_CLAIMS_DEFAULT_CLAIM_VALUE, AUTH_ROLE_CLAIMS_DEFAULT_EMPTY_VALUE } from './auth.role.claims';
import { AuthClaimsObject, AuthRoleClaimsFactoryConfig, AuthRoleClaimsService, authRoleClaimsService, AUTH_ROLE_CLAIMS_DEFAULT_CLAIM_VALUE, AUTH_ROLE_CLAIMS_DEFAULT_EMPTY_VALUE } from './auth.role.claims';

type TestClaims = {
test: string;
u: number;
m: string;
ignoredValue: boolean;
};

type TestComplexClaims = {
type: number;
ignoredValue: boolean;
};

describe('authRoleClaimsFactory()', () => {
Expand Down Expand Up @@ -40,7 +42,8 @@ describe('authRoleClaimsFactory()', () => {
},
m: {
roles: ['a', 'b', 'c'] // multiple roles applied when m exists.
}
},
ignoredValue: null // set ignored
};

const service = authRoleClaimsService<TestClaims>(claimsConfig);
Expand All @@ -55,6 +58,8 @@ describe('authRoleClaimsFactory()', () => {
expect(result['test']).toBe(AUTH_ROLE_CLAIMS_DEFAULT_EMPTY_VALUE);
expect(result['u']).toBe(claimsConfig['u'].value);
expect(result['m']).toBe(AUTH_ROLE_CLAIMS_DEFAULT_CLAIM_VALUE);
expect(result['ignoredValue']).not.toBeDefined();
expect(objectHasKey(result, 'ignoredValue')).toBe(false);
});

it(`should apply the default value for every key in the config that doesn't exist in the roles set.`, () => {
Expand Down Expand Up @@ -101,7 +106,8 @@ describe('authRoleClaimsFactory()', () => {
return [AUTH_USER_ROLE];
}
}
}
},
ignoredValue: null // set ignored
};

const service = authRoleClaimsService<TestComplexClaims>(claimsConfig);
Expand All @@ -116,6 +122,15 @@ describe('authRoleClaimsFactory()', () => {
expect(Object.keys(result).length).toBe(1);
expect(result.type).toBe(AUTH_ROLE_CLAIMS_DEFAULT_EMPTY_VALUE);
});

it('should have ignored the ignoredValue.', () => {
const roles = new Set([]);
const result = service.toClaims(roles);

expect(Object.keys(result).length).toBe(1);
expect(result.ignoredValue).not.toBeDefined();
expect(objectHasKey(result, 'ignoredValue')).toBe(false);
});
});
});
});
78 changes: 41 additions & 37 deletions packages/util/src/lib/auth/auth.role.claims.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ export type ClearAuthClaimValue = null;
/**
* Value in claims.
*/
export type SimpleAuthClaimValue = string | number;
export type SimpleAuthClaimValue = string | number | boolean;

/**
* Value in claims.
Expand Down Expand Up @@ -89,8 +89,10 @@ export interface AuthRoleClaimsFactoryConfigEntryEncodeOptions<V extends AuthCla
decodeRolesFromValue: (value: Maybe<V>) => AuthRole[] | undefined;
}

export type IgnoreAuthRoleClaimsEntry = null;

export type AuthRoleClaimsFactoryConfig<T extends AuthClaimsObject = AuthClaimsObject> = {
[K in keyof T]: AuthRoleClaimsFactoryConfigEntry<T[K]>;
[K in keyof T]: AuthRoleClaimsFactoryConfigEntry<T[K]> | IgnoreAuthRoleClaimsEntry;
};

export interface AuthRoleClaimsFactoryDefaults {
Expand Down Expand Up @@ -144,46 +146,48 @@ export function authRoleClaimsService<T extends AuthClaimsObject>(config: AuthRo
return (entry as AuthRoleClaimsFactoryConfigEntrySimpleOptions).roles != null;
}

const tuples: [AuthClaimKey, AuthRoleClaimsServiceConfigMapEntry][] = objectToTuples<AuthRoleClaimsFactoryConfigEntryEncodeOptions>(config as any).map((x) => {
const inputEntry = x[1];
let entry: AuthRoleClaimsFactoryConfigEntryEncodeOptions;

if (isSimpleOptions(inputEntry)) {
const expectedValue = inputEntry.value ?? defaultClaimValue;
const claimRoles = asArray(inputEntry.roles);

// since checking uses equivalence, the objects will never match equivalence via the === properly.
// AuthRoleClaimsFactoryConfigEntryEncodeOptions is likely to be used for these cases unknownways, but this will help avoid unexpected errors.
if (typeof expectedValue === 'object') {
throw new Error(`failed decoding claims. Expected value to be a string or number. Object isn't supported with simple claims.`);
}
const tuples: [AuthClaimKey, AuthRoleClaimsServiceConfigMapEntry][] = objectToTuples<AuthRoleClaimsFactoryConfigEntry>(config as any)
.filter(([, entry]) => entry != null) // skip any ignored/null values
.map((x) => {
const inputEntry = x[1];
let entry: AuthRoleClaimsFactoryConfigEntryEncodeOptions;

entry = {
encodeValueFromRoles: (roles: AuthRoleSet) => {
let claimsValue;
if (isSimpleOptions(inputEntry)) {
const expectedValue = inputEntry.value ?? defaultClaimValue;
const claimRoles = asArray(inputEntry.roles);

// only set the claims value if all values are present in the claims.
if (setContainsAllValues(roles, claimRoles)) {
claimsValue = inputEntry.value ?? defaultClaimValue;
}
// since checking uses equivalence, the objects will never match equivalence via the === properly.
// AuthRoleClaimsFactoryConfigEntryEncodeOptions is likely to be used for these cases unknownways, but this will help avoid unexpected errors.
if (typeof expectedValue === 'object') {
throw new Error(`failed decoding claims. Expected value to be a string or number. Object isn't supported with simple claims.`);
}

return claimsValue;
},
decodeRolesFromValue: (value: Maybe<AuthClaimValue>) => {
if (value === expectedValue) {
return claimRoles;
} else {
return [];
entry = {
encodeValueFromRoles: (roles: AuthRoleSet) => {
let claimsValue;

// only set the claims value if all values are present in the claims.
if (setContainsAllValues(roles, claimRoles)) {
claimsValue = inputEntry.value ?? defaultClaimValue;
}

return claimsValue;
},
decodeRolesFromValue: (value: Maybe<AuthClaimValue>) => {
if (value === expectedValue) {
return claimRoles;
} else {
return [];
}
}
}
};
} else {
entry = inputEntry as AuthRoleClaimsFactoryConfigEntryEncodeOptions;
}
};
} else {
entry = inputEntry as AuthRoleClaimsFactoryConfigEntryEncodeOptions;
}

(entry as AuthRoleClaimsServiceConfigMapEntry).claimKey = x[0];
return [x[0], entry as AuthRoleClaimsServiceConfigMapEntry];
});
(entry as AuthRoleClaimsServiceConfigMapEntry).claimKey = x[0];
return [x[0], entry as AuthRoleClaimsServiceConfigMapEntry];
});

const authRoleMap = new Map<AuthClaimKey, AuthRoleClaimsServiceConfigMapEntry>(tuples.map((x) => [x[0].toLowerCase(), x[1]]));

Expand Down

0 comments on commit 71e3cac

Please sign in to comment.