@clemax/nest-keycloak-connect 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (43) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +298 -0
  3. package/dist/constants.d.ts +69 -0
  4. package/dist/constants.js +76 -0
  5. package/dist/decorators/access-token.decorator.d.ts +5 -0
  6. package/dist/decorators/access-token.decorator.js +13 -0
  7. package/dist/decorators/enforcer-options.decorator.d.ts +8 -0
  8. package/dist/decorators/enforcer-options.decorator.js +12 -0
  9. package/dist/decorators/keycloak-user.decorator.d.ts +5 -0
  10. package/dist/decorators/keycloak-user.decorator.js +13 -0
  11. package/dist/decorators/public.decorator.d.ts +7 -0
  12. package/dist/decorators/public.decorator.js +12 -0
  13. package/dist/decorators/resource.decorator.d.ts +6 -0
  14. package/dist/decorators/resource.decorator.js +11 -0
  15. package/dist/decorators/roles.decorator.d.ts +10 -0
  16. package/dist/decorators/roles.decorator.js +15 -0
  17. package/dist/decorators/scopes.decorator.d.ts +19 -0
  18. package/dist/decorators/scopes.decorator.js +28 -0
  19. package/dist/guards/auth.guard.d.ts +19 -0
  20. package/dist/guards/auth.guard.js +172 -0
  21. package/dist/guards/resource.guard.d.ts +18 -0
  22. package/dist/guards/resource.guard.js +175 -0
  23. package/dist/guards/role.guard.d.ts +17 -0
  24. package/dist/guards/role.guard.js +139 -0
  25. package/dist/index.d.ts +1 -0
  26. package/dist/index.js +18 -0
  27. package/dist/interface/keycloak-connect-module-async-options.interface.d.ts +9 -0
  28. package/dist/interface/keycloak-connect-module-async-options.interface.js +2 -0
  29. package/dist/interface/keycloak-connect-options-factory.interface.d.ts +4 -0
  30. package/dist/interface/keycloak-connect-options-factory.interface.js +2 -0
  31. package/dist/interface/keycloak-connect-options.interface.d.ts +166 -0
  32. package/dist/interface/keycloak-connect-options.interface.js +3 -0
  33. package/dist/internal.util.d.ts +8 -0
  34. package/dist/internal.util.js +69 -0
  35. package/dist/keycloak-connect.module.d.ts +32 -0
  36. package/dist/keycloak-connect.module.js +126 -0
  37. package/dist/keycloak-connect.providers.d.ts +7 -0
  38. package/dist/keycloak-connect.providers.js +81 -0
  39. package/dist/services/keycloak-multitenant.service.d.ts +24 -0
  40. package/dist/services/keycloak-multitenant.service.js +174 -0
  41. package/dist/util.d.ts +1 -0
  42. package/dist/util.js +8 -0
  43. package/package.json +100 -0
@@ -0,0 +1,172 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
19
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
20
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
21
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
22
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
23
+ };
24
+ var __importStar = (this && this.__importStar) || function (mod) {
25
+ if (mod && mod.__esModule) return mod;
26
+ var result = {};
27
+ if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
28
+ __setModuleDefault(result, mod);
29
+ return result;
30
+ };
31
+ var __metadata = (this && this.__metadata) || function (k, v) {
32
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
33
+ };
34
+ var __param = (this && this.__param) || function (paramIndex, decorator) {
35
+ return function (target, key) { decorator(target, key, paramIndex); }
36
+ };
37
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
38
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
39
+ return new (P || (P = Promise))(function (resolve, reject) {
40
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
41
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
42
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
43
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
44
+ });
45
+ };
46
+ var AuthGuard_1;
47
+ Object.defineProperty(exports, "__esModule", { value: true });
48
+ exports.AuthGuard = void 0;
49
+ const common_1 = require("@nestjs/common");
50
+ const core_1 = require("@nestjs/core");
51
+ const KeycloakConnect = __importStar(require("keycloak-connect"));
52
+ const constants_1 = require("../constants");
53
+ const public_decorator_1 = require("../decorators/public.decorator");
54
+ const internal_util_1 = require("../internal.util");
55
+ const keycloak_multitenant_service_1 = require("../services/keycloak-multitenant.service");
56
+ const util_1 = require("../util");
57
+ /**
58
+ * An authentication guard. Will return a 401 unauthorized when it is unable to
59
+ * verify the JWT token or Bearer header is missing.
60
+ */
61
+ let AuthGuard = AuthGuard_1 = class AuthGuard {
62
+ constructor(singleTenant, keycloakOpts, multiTenant) {
63
+ this.singleTenant = singleTenant;
64
+ this.keycloakOpts = keycloakOpts;
65
+ this.multiTenant = multiTenant;
66
+ this.logger = new common_1.Logger(AuthGuard_1.name);
67
+ this.reflector = new core_1.Reflector();
68
+ }
69
+ canActivate(context) {
70
+ return __awaiter(this, void 0, void 0, function* () {
71
+ const isPublic = this.reflector.getAllAndOverride(public_decorator_1.META_PUBLIC, [
72
+ context.getClass(),
73
+ context.getHandler(),
74
+ ]);
75
+ // Extract request/response
76
+ const cookieKey = this.keycloakOpts.cookieKey || constants_1.KEYCLOAK_COOKIE_DEFAULT;
77
+ const [request] = (0, internal_util_1.extractRequestAndAttachCookie)(context, cookieKey);
78
+ // if is not an HTTP request ignore this guard
79
+ if (!request) {
80
+ return true;
81
+ }
82
+ const jwt = this.extractJwt(request.headers);
83
+ const isJwtEmpty = jwt === null || jwt === undefined;
84
+ // Not a public route, require jwt
85
+ if (!isPublic && isJwtEmpty) {
86
+ this.logger.verbose('Empty jwt, unauthorized');
87
+ throw new common_1.UnauthorizedException();
88
+ }
89
+ // Public route, no jwt sent
90
+ if (isPublic && isJwtEmpty) {
91
+ return true;
92
+ }
93
+ this.logger.verbose(`Validating jwt`, { jwt });
94
+ const keycloak = yield (0, internal_util_1.useKeycloak)(request, jwt, this.singleTenant, this.multiTenant, this.keycloakOpts);
95
+ const isValidToken = yield this.validateToken(keycloak, jwt);
96
+ if (isValidToken) {
97
+ // Attach user info object
98
+ request.user = (0, util_1.parseToken)(jwt);
99
+ // Attach raw access token JWT extracted from bearer/cookie
100
+ request.accessToken = jwt;
101
+ this.logger.verbose(`User authenticated`, { user: request.user });
102
+ return true;
103
+ }
104
+ // Valid token should return, this time we warn
105
+ if (isPublic) {
106
+ this.logger.warn(`A jwt token was retrieved but failed validation.`, {
107
+ jwt,
108
+ });
109
+ return true;
110
+ }
111
+ throw new common_1.UnauthorizedException();
112
+ });
113
+ }
114
+ validateToken(keycloak, jwt) {
115
+ return __awaiter(this, void 0, void 0, function* () {
116
+ const tokenValidation = this.keycloakOpts.tokenValidation || constants_1.TokenValidation.ONLINE;
117
+ const gm = keycloak.grantManager;
118
+ let grant;
119
+ try {
120
+ grant = yield gm.createGrant({ access_token: jwt });
121
+ }
122
+ catch (ex) {
123
+ this.logger.warn(`Cannot validate access token: ${ex}`);
124
+ // It will fail to create grants on invalid access token (i.e expired or wrong domain)
125
+ return false;
126
+ }
127
+ const token = grant.access_token;
128
+ this.logger.verbose(`Using token validation method: ${tokenValidation.toUpperCase()}`);
129
+ try {
130
+ let result;
131
+ switch (tokenValidation) {
132
+ case constants_1.TokenValidation.ONLINE:
133
+ result = yield gm.validateAccessToken(token);
134
+ return result === token;
135
+ case constants_1.TokenValidation.OFFLINE:
136
+ result = yield gm.validateToken(token, 'Bearer');
137
+ return result === token;
138
+ case constants_1.TokenValidation.NONE:
139
+ return true;
140
+ default:
141
+ this.logger.warn(`Unknown validation method: ${tokenValidation}`);
142
+ return false;
143
+ }
144
+ }
145
+ catch (ex) {
146
+ this.logger.warn(`Cannot validate access token: ${ex}`);
147
+ }
148
+ return false;
149
+ });
150
+ }
151
+ extractJwt(headers) {
152
+ if (headers && !headers.authorization) {
153
+ this.logger.verbose(`No authorization header`);
154
+ return null;
155
+ }
156
+ const auth = headers.authorization.split(' ');
157
+ // We only allow bearer
158
+ if (auth[0].toLowerCase() !== 'bearer') {
159
+ this.logger.verbose(`No bearer header`);
160
+ return null;
161
+ }
162
+ return auth[1];
163
+ }
164
+ };
165
+ exports.AuthGuard = AuthGuard;
166
+ exports.AuthGuard = AuthGuard = AuthGuard_1 = __decorate([
167
+ (0, common_1.Injectable)(),
168
+ __param(0, (0, common_1.Inject)(constants_1.KEYCLOAK_INSTANCE)),
169
+ __param(1, (0, common_1.Inject)(constants_1.KEYCLOAK_CONNECT_OPTIONS)),
170
+ __param(2, (0, common_1.Inject)(constants_1.KEYCLOAK_MULTITENANT_SERVICE)),
171
+ __metadata("design:paramtypes", [Object, Object, keycloak_multitenant_service_1.KeycloakMultiTenantService])
172
+ ], AuthGuard);
@@ -0,0 +1,18 @@
1
+ import { CanActivate, ExecutionContext } from '@nestjs/common';
2
+ import * as KeycloakConnect from 'keycloak-connect';
3
+ import { KeycloakConnectConfig } from '../interface/keycloak-connect-options.interface';
4
+ import { KeycloakMultiTenantService } from '../services/keycloak-multitenant.service';
5
+ /**
6
+ * This adds a resource guard, which is policy enforcement by default is permissive.
7
+ * Only controllers annotated with `@Resource` and methods with `@Scopes`
8
+ * are handled by this guard.
9
+ */
10
+ export declare class ResourceGuard implements CanActivate {
11
+ private singleTenant;
12
+ private keycloakOpts;
13
+ private multiTenant;
14
+ private readonly logger;
15
+ private readonly reflector;
16
+ constructor(singleTenant: KeycloakConnect.Keycloak, keycloakOpts: KeycloakConnectConfig, multiTenant: KeycloakMultiTenantService);
17
+ canActivate(context: ExecutionContext): Promise<boolean>;
18
+ }
@@ -0,0 +1,175 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
19
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
20
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
21
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
22
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
23
+ };
24
+ var __importStar = (this && this.__importStar) || function (mod) {
25
+ if (mod && mod.__esModule) return mod;
26
+ var result = {};
27
+ if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
28
+ __setModuleDefault(result, mod);
29
+ return result;
30
+ };
31
+ var __metadata = (this && this.__metadata) || function (k, v) {
32
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
33
+ };
34
+ var __param = (this && this.__param) || function (paramIndex, decorator) {
35
+ return function (target, key) { decorator(target, key, paramIndex); }
36
+ };
37
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
38
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
39
+ return new (P || (P = Promise))(function (resolve, reject) {
40
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
41
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
42
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
43
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
44
+ });
45
+ };
46
+ var ResourceGuard_1;
47
+ Object.defineProperty(exports, "__esModule", { value: true });
48
+ exports.ResourceGuard = void 0;
49
+ const common_1 = require("@nestjs/common");
50
+ const core_1 = require("@nestjs/core");
51
+ const KeycloakConnect = __importStar(require("keycloak-connect"));
52
+ const constants_1 = require("../constants");
53
+ const enforcer_options_decorator_1 = require("../decorators/enforcer-options.decorator");
54
+ const public_decorator_1 = require("../decorators/public.decorator");
55
+ const resource_decorator_1 = require("../decorators/resource.decorator");
56
+ const scopes_decorator_1 = require("../decorators/scopes.decorator");
57
+ const internal_util_1 = require("../internal.util");
58
+ const keycloak_multitenant_service_1 = require("../services/keycloak-multitenant.service");
59
+ /**
60
+ * This adds a resource guard, which is policy enforcement by default is permissive.
61
+ * Only controllers annotated with `@Resource` and methods with `@Scopes`
62
+ * are handled by this guard.
63
+ */
64
+ let ResourceGuard = ResourceGuard_1 = class ResourceGuard {
65
+ constructor(singleTenant, keycloakOpts, multiTenant) {
66
+ this.singleTenant = singleTenant;
67
+ this.keycloakOpts = keycloakOpts;
68
+ this.multiTenant = multiTenant;
69
+ this.logger = new common_1.Logger(ResourceGuard_1.name);
70
+ this.reflector = new core_1.Reflector();
71
+ }
72
+ canActivate(context) {
73
+ return __awaiter(this, void 0, void 0, function* () {
74
+ var _a, _b, _c, _d;
75
+ const defaultEnforcerOpts = {
76
+ claims: (request) => {
77
+ const httpUri = request.url;
78
+ const userAgent = request.headers['user-agent'];
79
+ this.logger.verbose(`Enforcing claims, http.uri: ${httpUri}, user.agent: ${userAgent}`);
80
+ return {
81
+ 'http.uri': [httpUri],
82
+ 'user.agent': [userAgent],
83
+ };
84
+ },
85
+ };
86
+ const resource = this.reflector.get(resource_decorator_1.META_RESOURCE, context.getClass());
87
+ const explicitScopes = (_a = this.reflector.get(scopes_decorator_1.META_SCOPES, context.getHandler())) !== null && _a !== void 0 ? _a : [];
88
+ const conditionalScopes = this.reflector.get(scopes_decorator_1.META_CONDITIONAL_SCOPES, context.getHandler());
89
+ const isPublic = this.reflector.getAllAndOverride(public_decorator_1.META_PUBLIC, [
90
+ context.getClass(),
91
+ context.getHandler(),
92
+ ]);
93
+ const enforcerOpts = (_b = this.reflector.getAllAndOverride(enforcer_options_decorator_1.META_ENFORCER_OPTIONS, [context.getClass(), context.getHandler()])) !== null && _b !== void 0 ? _b : defaultEnforcerOpts;
94
+ // Default to permissive
95
+ const policyEnforcementMode = this.keycloakOpts.policyEnforcement || constants_1.PolicyEnforcementMode.PERMISSIVE;
96
+ const shouldAllow = policyEnforcementMode === constants_1.PolicyEnforcementMode.PERMISSIVE;
97
+ // Extract request/response
98
+ const cookieKey = this.keycloakOpts.cookieKey || constants_1.KEYCLOAK_COOKIE_DEFAULT;
99
+ const [request, response] = (0, internal_util_1.extractRequestAndAttachCookie)(context, cookieKey);
100
+ // if is not an HTTP request ignore this guard
101
+ if (!request) {
102
+ return true;
103
+ }
104
+ if (!request.user && isPublic) {
105
+ this.logger.verbose(`Route has no user, and is public, allowed`);
106
+ return true;
107
+ }
108
+ const keycloak = yield (0, internal_util_1.useKeycloak)(request, request.accessToken, this.singleTenant, this.multiTenant, this.keycloakOpts);
109
+ const grant = yield keycloak.grantManager.createGrant({
110
+ access_token: request.accessToken,
111
+ });
112
+ // No resource given, check policy enforcement mode
113
+ if (!resource) {
114
+ if (shouldAllow) {
115
+ this.logger.verbose(`Controller has no @Resource defined, request allowed due to policy enforcement`);
116
+ }
117
+ else {
118
+ this.logger.verbose(`Controller has no @Resource defined, request denied due to policy enforcement`);
119
+ }
120
+ return shouldAllow;
121
+ }
122
+ // Build the required scopes
123
+ const conditionalScopesResult = conditionalScopes != null || conditionalScopes != undefined
124
+ ? conditionalScopes(request, grant.access_token)
125
+ : [];
126
+ const scopes = [...explicitScopes, ...conditionalScopesResult];
127
+ // Attach resolved scopes
128
+ request.scopes = scopes;
129
+ // No scopes given, check policy enforcement mode
130
+ if (!scopes || scopes.length === 0) {
131
+ if (shouldAllow) {
132
+ this.logger.verbose(`Route has no @Scope/@ConditionalScopes defined, request allowed due to policy enforcement`);
133
+ }
134
+ else {
135
+ this.logger.verbose(`Route has no @Scope/@ConditionalScopes defined, request denied due to policy enforcement`);
136
+ }
137
+ return shouldAllow;
138
+ }
139
+ this.logger.verbose(`Protecting resource [ ${resource} ] with scopes: [ ${scopes} ]`);
140
+ const user = (_d = (_c = request.user) === null || _c === void 0 ? void 0 : _c.preferred_username) !== null && _d !== void 0 ? _d : 'user';
141
+ const enforcerFn = createEnforcerContext(request, response, enforcerOpts);
142
+ // Build permissions
143
+ const permissions = scopes.map((scope) => `${resource}:${scope}`);
144
+ const isAllowed = yield enforcerFn(keycloak, permissions);
145
+ // If statement for verbose logging only
146
+ if (!isAllowed) {
147
+ this.logger.verbose(`Resource [ ${resource} ] denied to [ ${user} ]`);
148
+ }
149
+ else {
150
+ this.logger.verbose(`Resource [ ${resource} ] granted to [ ${user} ]`);
151
+ }
152
+ return isAllowed;
153
+ });
154
+ }
155
+ };
156
+ exports.ResourceGuard = ResourceGuard;
157
+ exports.ResourceGuard = ResourceGuard = ResourceGuard_1 = __decorate([
158
+ (0, common_1.Injectable)(),
159
+ __param(0, (0, common_1.Inject)(constants_1.KEYCLOAK_INSTANCE)),
160
+ __param(1, (0, common_1.Inject)(constants_1.KEYCLOAK_CONNECT_OPTIONS)),
161
+ __param(2, (0, common_1.Inject)(constants_1.KEYCLOAK_MULTITENANT_SERVICE)),
162
+ __metadata("design:paramtypes", [Object, Object, keycloak_multitenant_service_1.KeycloakMultiTenantService])
163
+ ], ResourceGuard);
164
+ const createEnforcerContext = (request, response, options) => (keycloak, permissions) =>
165
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
166
+ new Promise((resolve, _) =>
167
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
168
+ keycloak.enforcer(permissions, options)(request, response, (_) => {
169
+ if (request.resourceDenied) {
170
+ resolve(false);
171
+ }
172
+ else {
173
+ resolve(true);
174
+ }
175
+ }));
@@ -0,0 +1,17 @@
1
+ import { CanActivate, ExecutionContext } from '@nestjs/common';
2
+ import * as KeycloakConnect from 'keycloak-connect';
3
+ import { KeycloakConnectConfig } from '../interface/keycloak-connect-options.interface';
4
+ import { KeycloakMultiTenantService } from '../services/keycloak-multitenant.service';
5
+ /**
6
+ * A permissive type of role guard. Roles are set via `@Roles` decorator.
7
+ * @since 1.1.0
8
+ */
9
+ export declare class RoleGuard implements CanActivate {
10
+ private singleTenant;
11
+ private keycloakOpts;
12
+ private multiTenant;
13
+ private readonly logger;
14
+ private readonly reflector;
15
+ constructor(singleTenant: KeycloakConnect.Keycloak, keycloakOpts: KeycloakConnectConfig, multiTenant: KeycloakMultiTenantService);
16
+ canActivate(context: ExecutionContext): Promise<boolean>;
17
+ }
@@ -0,0 +1,139 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
19
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
20
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
21
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
22
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
23
+ };
24
+ var __importStar = (this && this.__importStar) || function (mod) {
25
+ if (mod && mod.__esModule) return mod;
26
+ var result = {};
27
+ if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
28
+ __setModuleDefault(result, mod);
29
+ return result;
30
+ };
31
+ var __metadata = (this && this.__metadata) || function (k, v) {
32
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
33
+ };
34
+ var __param = (this && this.__param) || function (paramIndex, decorator) {
35
+ return function (target, key) { decorator(target, key, paramIndex); }
36
+ };
37
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
38
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
39
+ return new (P || (P = Promise))(function (resolve, reject) {
40
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
41
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
42
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
43
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
44
+ });
45
+ };
46
+ var RoleGuard_1;
47
+ Object.defineProperty(exports, "__esModule", { value: true });
48
+ exports.RoleGuard = void 0;
49
+ const common_1 = require("@nestjs/common");
50
+ const core_1 = require("@nestjs/core");
51
+ const KeycloakConnect = __importStar(require("keycloak-connect"));
52
+ const constants_1 = require("../constants");
53
+ const roles_decorator_1 = require("../decorators/roles.decorator");
54
+ const internal_util_1 = require("../internal.util");
55
+ const keycloak_multitenant_service_1 = require("../services/keycloak-multitenant.service");
56
+ /**
57
+ * A permissive type of role guard. Roles are set via `@Roles` decorator.
58
+ * @since 1.1.0
59
+ */
60
+ let RoleGuard = RoleGuard_1 = class RoleGuard {
61
+ constructor(singleTenant, keycloakOpts, multiTenant) {
62
+ this.singleTenant = singleTenant;
63
+ this.keycloakOpts = keycloakOpts;
64
+ this.multiTenant = multiTenant;
65
+ this.logger = new common_1.Logger(RoleGuard_1.name);
66
+ this.reflector = new core_1.Reflector();
67
+ }
68
+ canActivate(context) {
69
+ return __awaiter(this, void 0, void 0, function* () {
70
+ const roleMerge = this.keycloakOpts.roleMerge
71
+ ? this.keycloakOpts.roleMerge
72
+ : constants_1.RoleMerge.OVERRIDE;
73
+ const roles = [];
74
+ const matchingMode = this.reflector.getAllAndOverride(roles_decorator_1.META_ROLE_MATCHING_MODE, [context.getClass(), context.getHandler()]);
75
+ if (roleMerge == constants_1.RoleMerge.ALL) {
76
+ const mergedRoles = this.reflector.getAllAndMerge(roles_decorator_1.META_ROLES, [
77
+ context.getClass(),
78
+ context.getHandler(),
79
+ ]);
80
+ if (mergedRoles) {
81
+ roles.push(...mergedRoles);
82
+ }
83
+ }
84
+ else if (roleMerge == constants_1.RoleMerge.OVERRIDE) {
85
+ const resultRoles = this.reflector.getAllAndOverride(roles_decorator_1.META_ROLES, [context.getClass(), context.getHandler()]);
86
+ if (resultRoles) {
87
+ roles.push(...resultRoles);
88
+ }
89
+ }
90
+ else {
91
+ throw Error(`Unknown role merge: ${roleMerge}`);
92
+ }
93
+ if (roles.length === 0) {
94
+ return true;
95
+ }
96
+ const roleMatchingMode = matchingMode !== null && matchingMode !== void 0 ? matchingMode : constants_1.RoleMatch.ANY;
97
+ this.logger.verbose(`Using matching mode: ${roleMatchingMode}`, { roles });
98
+ // Extract request
99
+ const cookieKey = this.keycloakOpts.cookieKey || constants_1.KEYCLOAK_COOKIE_DEFAULT;
100
+ const [request] = (0, internal_util_1.extractRequestAndAttachCookie)(context, cookieKey);
101
+ const { accessToken } = request;
102
+ // if is not an HTTP request ignore this guard
103
+ if (!request) {
104
+ return true;
105
+ }
106
+ if (!accessToken) {
107
+ // No access token attached, auth guard should have attached the necessary token
108
+ this.logger.warn('No access token found in request, are you sure AuthGuard is first in the chain?');
109
+ return false;
110
+ }
111
+ // Create grant
112
+ const keycloak = yield (0, internal_util_1.useKeycloak)(request, request.accessToken, this.singleTenant, this.multiTenant, this.keycloakOpts);
113
+ const grant = yield keycloak.grantManager.createGrant({
114
+ access_token: accessToken,
115
+ });
116
+ // Grab access token from grant
117
+ const grantAccessToken = grant.access_token;
118
+ // For verbose logging, we store it instead of returning it immediately
119
+ const granted = roleMatchingMode === constants_1.RoleMatch.ANY
120
+ ? roles.some((r) => grantAccessToken.hasRole(r))
121
+ : roles.every((r) => grantAccessToken.hasRole(r));
122
+ if (granted) {
123
+ this.logger.verbose(`Resource granted due to role(s)`);
124
+ }
125
+ else {
126
+ this.logger.verbose(`Resource denied due to mismatched role(s)`);
127
+ }
128
+ return granted;
129
+ });
130
+ }
131
+ };
132
+ exports.RoleGuard = RoleGuard;
133
+ exports.RoleGuard = RoleGuard = RoleGuard_1 = __decorate([
134
+ (0, common_1.Injectable)(),
135
+ __param(0, (0, common_1.Inject)(constants_1.KEYCLOAK_INSTANCE)),
136
+ __param(1, (0, common_1.Inject)(constants_1.KEYCLOAK_CONNECT_OPTIONS)),
137
+ __param(2, (0, common_1.Inject)(constants_1.KEYCLOAK_MULTITENANT_SERVICE)),
138
+ __metadata("design:paramtypes", [Object, Object, keycloak_multitenant_service_1.KeycloakMultiTenantService])
139
+ ], RoleGuard);
@@ -0,0 +1 @@
1
+ export * from './keycloak-connect.module';
package/dist/index.js ADDED
@@ -0,0 +1,18 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
+ };
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ // public-api
18
+ __exportStar(require("./keycloak-connect.module"), exports);
@@ -0,0 +1,9 @@
1
+ import { ModuleMetadata, Type } from '@nestjs/common/interfaces';
2
+ import { KeycloakConnectOptionsFactory } from './keycloak-connect-options-factory.interface';
3
+ import { KeycloakConnectOptions } from './keycloak-connect-options.interface';
4
+ export interface KeycloakConnectModuleAsyncOptions extends Pick<ModuleMetadata, 'imports'> {
5
+ inject?: any[];
6
+ useExisting?: Type<KeycloakConnectOptionsFactory>;
7
+ useClass?: Type<KeycloakConnectOptionsFactory>;
8
+ useFactory?: (...args: any[]) => Promise<KeycloakConnectOptions> | KeycloakConnectOptions;
9
+ }
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -0,0 +1,4 @@
1
+ import { KeycloakConnectOptions } from './keycloak-connect-options.interface';
2
+ export interface KeycloakConnectOptionsFactory {
3
+ createKeycloakConnectOptions(): Promise<KeycloakConnectOptions> | KeycloakConnectOptions;
4
+ }
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });