Skip to content

Commit 7421135

Browse files
committed
feat(keto-client-wrapper): create OryAuthorizationGuard
1 parent 33a4e07 commit 7421135

File tree

4 files changed

+117
-0
lines changed

4 files changed

+117
-0
lines changed

packages/keto-client-wrapper/src/index.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,8 @@
1+
export { OryAuthorizationGuard } from './lib/ory-authorization.guard';
2+
export {
3+
OryPermissionChecks,
4+
getOryPermissionChecks,
5+
} from './lib/ory-permission-checks.decorator';
16
export {
27
OryPermissionsModule,
38
OryPermissionsModuleAsyncOptions,
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
import {
2+
CanActivate,
3+
ExecutionContext,
4+
Injectable,
5+
mixin,
6+
} from '@nestjs/common';
7+
import { Reflector } from '@nestjs/core';
8+
import {
9+
RelationTuple,
10+
createPermissionCheckQuery,
11+
} from '@getlarge/keto-relations-parser';
12+
13+
import { OryPermissionsService } from './ory-permissions';
14+
import { getOryPermissionChecks } from './ory-permission-checks.decorator';
15+
16+
export const OryAuthorizationGuard = (
17+
options: {
18+
errorFactory?: (error: Error) => Error;
19+
postCheck?: (relationTuple: RelationTuple, isPermitted: boolean) => void;
20+
} = {}
21+
) => {
22+
@Injectable()
23+
class AuthorizationGuard implements CanActivate {
24+
constructor(
25+
readonly reflector: Reflector,
26+
readonly oryService: OryPermissionsService
27+
) {}
28+
29+
async canActivate(context: ExecutionContext): Promise<boolean> {
30+
const factories =
31+
getOryPermissionChecks(this.reflector, context.getHandler()) ?? [];
32+
if (!factories?.length) {
33+
return true;
34+
}
35+
for (const { relationTupleFactory } of factories) {
36+
const relationTuple = relationTupleFactory(context);
37+
const result = createPermissionCheckQuery(relationTuple);
38+
if (result.hasError()) {
39+
if (options.errorFactory) {
40+
throw options.errorFactory(result.error);
41+
}
42+
return false;
43+
}
44+
const { data } = await this.oryService.checkPermission(result.value);
45+
const isPermitted = data.allowed;
46+
if (options.postCheck) {
47+
options.postCheck(relationTuple, isPermitted);
48+
}
49+
if (!isPermitted) {
50+
return false;
51+
}
52+
}
53+
return true;
54+
}
55+
}
56+
return mixin(AuthorizationGuard);
57+
};
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
import { CustomDecorator, ExecutionContext, SetMetadata } from '@nestjs/common';
2+
import { Reflector } from '@nestjs/core';
3+
import {
4+
parseRelationTuple,
5+
RelationTuple,
6+
} from '@getlarge/keto-relations-parser';
7+
8+
type OryPermissionCheckMetadataType = {
9+
relationTupleFactory: (ctx: ExecutionContext) => RelationTuple;
10+
};
11+
12+
const ORY_PERMISSION_CHECKS_METADATA_KEY = Symbol('OryPermissionChecksKey');
13+
14+
export const OryPermissionChecks = (
15+
...relationTupleFactories: (string | ((ctx: ExecutionContext) => string))[]
16+
): CustomDecorator<typeof ORY_PERMISSION_CHECKS_METADATA_KEY> => {
17+
const valueToSet: OryPermissionCheckMetadataType[] = [];
18+
19+
for (const relationTupleFactory of relationTupleFactories) {
20+
if (typeof relationTupleFactory === 'string') {
21+
valueToSet.push({
22+
relationTupleFactory: () =>
23+
parseRelationTuple(relationTupleFactory).unwrapOrThrow(),
24+
});
25+
} else {
26+
valueToSet.push({
27+
relationTupleFactory: (ctx) =>
28+
parseRelationTuple(relationTupleFactory(ctx)).unwrapOrThrow(),
29+
});
30+
}
31+
}
32+
return SetMetadata(ORY_PERMISSION_CHECKS_METADATA_KEY, valueToSet);
33+
};
34+
35+
export const getOryPermissionChecks = (
36+
reflector: Reflector,
37+
handler: Parameters<Reflector['get']>[1]
38+
): OryPermissionCheckMetadataType[] | null => {
39+
const oryPermissions =
40+
reflector.get<
41+
OryPermissionCheckMetadataType[],
42+
typeof ORY_PERMISSION_CHECKS_METADATA_KEY
43+
>(ORY_PERMISSION_CHECKS_METADATA_KEY, handler) ?? [];
44+
return oryPermissions.length > 0 ? oryPermissions : null;
45+
};

packages/keto-relations-parser/src/index.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,3 +21,13 @@ export {
2121
export type { RelationTupleStringGenerator } from './lib/with-replacements/relation-tuple-with-replacements-parser';
2222
export { parseRelationTupleWithReplacements } from './lib/with-replacements/relation-tuple-with-replacements-parser';
2323
export type { ReplacementValues } from './lib/with-replacements/replacement-values';
24+
25+
export {
26+
createFlattenRelationQuery,
27+
createRelationQuery,
28+
createRelationship,
29+
} from './lib/keto-converters/tuple-to-relationships-parameters';
30+
export {
31+
createExpandPermissionQuery,
32+
createPermissionCheckQuery,
33+
} from './lib/keto-converters/tuple-to-permissions-parameters';

0 commit comments

Comments
 (0)