@tilde-nlp/ngx-user-access 8.0.1
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.
- package/README.md +63 -0
- package/fesm2022/tilde-nlp-ngx-user-access.mjs +513 -0
- package/fesm2022/tilde-nlp-ngx-user-access.mjs.map +1 -0
- package/index.d.ts +233 -0
- package/package.json +28 -0
package/README.md
ADDED
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
# NgxUserAccess
|
|
2
|
+
|
|
3
|
+
This project was generated using [Angular CLI](https://github.com/angular/angular-cli) version 20.2.0.
|
|
4
|
+
|
|
5
|
+
## Code scaffolding
|
|
6
|
+
|
|
7
|
+
Angular CLI includes powerful code scaffolding tools. To generate a new component, run:
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
ng generate component component-name
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
For a complete list of available schematics (such as `components`, `directives`, or `pipes`), run:
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
ng generate --help
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
## Building
|
|
20
|
+
|
|
21
|
+
To build the library, run:
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
ng build ngx-user-access
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
This command will compile your project, and the build artifacts will be placed in the `dist/` directory.
|
|
28
|
+
|
|
29
|
+
### Publishing the Library
|
|
30
|
+
|
|
31
|
+
Once the project is built, you can publish your library by following these steps:
|
|
32
|
+
|
|
33
|
+
1. Navigate to the `dist` directory:
|
|
34
|
+
```bash
|
|
35
|
+
cd dist/ngx-user-access
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
2. Run the `npm publish` command to publish your library to the npm registry:
|
|
39
|
+
```bash
|
|
40
|
+
npm publish
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
## Running unit tests
|
|
44
|
+
|
|
45
|
+
To execute unit tests with the [Karma](https://karma-runner.github.io) test runner, use the following command:
|
|
46
|
+
|
|
47
|
+
```bash
|
|
48
|
+
ng test
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
## Running end-to-end tests
|
|
52
|
+
|
|
53
|
+
For end-to-end (e2e) testing, run:
|
|
54
|
+
|
|
55
|
+
```bash
|
|
56
|
+
ng e2e
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
Angular CLI does not come with an end-to-end testing framework by default. You can choose one that suits your needs.
|
|
60
|
+
|
|
61
|
+
## Additional Resources
|
|
62
|
+
|
|
63
|
+
For more information on using the Angular CLI, including detailed command references, visit the [Angular CLI Overview and Command Reference](https://angular.dev/tools/cli) page.
|
|
@@ -0,0 +1,513 @@
|
|
|
1
|
+
import * as i0 from '@angular/core';
|
|
2
|
+
import { InjectionToken, inject, Injectable, Inject } from '@angular/core';
|
|
3
|
+
import * as i2 from 'keycloak-angular';
|
|
4
|
+
import { KeycloakService, KeycloakEventTypeLegacy, KeycloakBearerInterceptor } from 'keycloak-angular';
|
|
5
|
+
import { BehaviorSubject, Subject } from 'rxjs';
|
|
6
|
+
import * as i1 from '@angular/router';
|
|
7
|
+
import { HTTP_INTERCEPTORS } from '@angular/common/http';
|
|
8
|
+
|
|
9
|
+
const USER_ACCESS_CONFIG_TOKEN = new InjectionToken('USER_ACCESS_CONFIG_TOKEN');
|
|
10
|
+
|
|
11
|
+
class UserAccessConfigService {
|
|
12
|
+
#config = inject(USER_ACCESS_CONFIG_TOKEN);
|
|
13
|
+
getConfiguration() {
|
|
14
|
+
return this.#config;
|
|
15
|
+
}
|
|
16
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.2.1", ngImport: i0, type: UserAccessConfigService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
17
|
+
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.2.1", ngImport: i0, type: UserAccessConfigService, providedIn: 'root' }); }
|
|
18
|
+
}
|
|
19
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.2.1", ngImport: i0, type: UserAccessConfigService, decorators: [{
|
|
20
|
+
type: Injectable,
|
|
21
|
+
args: [{
|
|
22
|
+
providedIn: 'root',
|
|
23
|
+
}]
|
|
24
|
+
}] });
|
|
25
|
+
|
|
26
|
+
var PermissionStatus;
|
|
27
|
+
(function (PermissionStatus) {
|
|
28
|
+
PermissionStatus["OK"] = "OK";
|
|
29
|
+
PermissionStatus["EXPIRED_SUBSCRIPTION"] = "EXPIRED_SUBSCRIPTION";
|
|
30
|
+
PermissionStatus["NO_SUBSCRIPTION"] = "NO_SUBSCRIPTION";
|
|
31
|
+
})(PermissionStatus || (PermissionStatus = {}));
|
|
32
|
+
|
|
33
|
+
var DefaultPermissions;
|
|
34
|
+
(function (DefaultPermissions) {
|
|
35
|
+
DefaultPermissions["ALL"] = "ALL";
|
|
36
|
+
})(DefaultPermissions || (DefaultPermissions = {}));
|
|
37
|
+
|
|
38
|
+
const USER_CUSTOM_CONFIGURATION_SERVICE_TOKEN = new InjectionToken('USER_CUSTOM_CONFIGURATION_SERVICE_TOKEN');
|
|
39
|
+
|
|
40
|
+
class AuthService {
|
|
41
|
+
get token() {
|
|
42
|
+
return this._token;
|
|
43
|
+
}
|
|
44
|
+
get parsedKeyCloakToken() {
|
|
45
|
+
return this._parsedKeycloakToken;
|
|
46
|
+
}
|
|
47
|
+
constructor(config, keyCloak) {
|
|
48
|
+
this.config = config;
|
|
49
|
+
this.keyCloak = keyCloak;
|
|
50
|
+
this._isAuthenticated = false;
|
|
51
|
+
this.username = new BehaviorSubject('');
|
|
52
|
+
// make jwt available in window object, so that it can be accessed everywhere (e.g. from webtranslate iframe)
|
|
53
|
+
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
54
|
+
window.keycloakToken = () => {
|
|
55
|
+
return this._token;
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
userSubscriptionPlan() {
|
|
59
|
+
return this.parsedKeyCloakToken?.subscription?.find((plan) => Object.values(this.config.getConfiguration().plans).includes(plan.PlanId));
|
|
60
|
+
}
|
|
61
|
+
isAuthenticated() {
|
|
62
|
+
return this._isAuthenticated;
|
|
63
|
+
}
|
|
64
|
+
getUserName() {
|
|
65
|
+
return this.isAuthenticated() ? this.keyCloak?.getUsername() : '';
|
|
66
|
+
}
|
|
67
|
+
getUserDisplayName() {
|
|
68
|
+
if (!this.authEnabled || !this._isAuthenticated) {
|
|
69
|
+
return '';
|
|
70
|
+
}
|
|
71
|
+
let username = this.getUserName();
|
|
72
|
+
if (this._parsedKeycloakToken?.name) {
|
|
73
|
+
username = this.parsedKeyCloakToken.name;
|
|
74
|
+
}
|
|
75
|
+
return username;
|
|
76
|
+
}
|
|
77
|
+
getToken() {
|
|
78
|
+
return this.keyCloak.getToken();
|
|
79
|
+
}
|
|
80
|
+
logout() {
|
|
81
|
+
if (!this.authEnabled) {
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
84
|
+
this.keyCloak.logout(window.location.origin);
|
|
85
|
+
}
|
|
86
|
+
async checkLogin() {
|
|
87
|
+
if (!this.authEnabled) {
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
90
|
+
this._isAuthenticated = await this.keyCloak.isLoggedIn();
|
|
91
|
+
this.refreshKeycloakToken();
|
|
92
|
+
const username = this.getUserName();
|
|
93
|
+
this.username.next(username);
|
|
94
|
+
}
|
|
95
|
+
login() {
|
|
96
|
+
if (!this.authEnabled) {
|
|
97
|
+
return;
|
|
98
|
+
}
|
|
99
|
+
this.keyCloak.login();
|
|
100
|
+
}
|
|
101
|
+
enableAuth() {
|
|
102
|
+
this.authEnabled = true;
|
|
103
|
+
}
|
|
104
|
+
refreshKeycloakToken() {
|
|
105
|
+
if (!this.authEnabled) {
|
|
106
|
+
return;
|
|
107
|
+
}
|
|
108
|
+
const keyCloakInstance = this.keyCloak.getKeycloakInstance();
|
|
109
|
+
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
110
|
+
const tokenParsed = keyCloakInstance.tokenParsed;
|
|
111
|
+
this._token = keyCloakInstance.token;
|
|
112
|
+
tokenParsed?.subscription?.forEach((subscription) => {
|
|
113
|
+
// Removes "-Yearly", "-Monthly" from end of string or -Eur- and everything that follows.
|
|
114
|
+
subscription.Label = subscription.PlanId.replace(/-Monthly$|-Yearly$|-EUR-(.*)/, '');
|
|
115
|
+
});
|
|
116
|
+
this._parsedKeycloakToken = tokenParsed;
|
|
117
|
+
}
|
|
118
|
+
userIsGroupMember() {
|
|
119
|
+
return this.parsedKeyCloakToken?.membership?.length > 0;
|
|
120
|
+
}
|
|
121
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.2.1", ngImport: i0, type: AuthService, deps: [{ token: UserAccessConfigService }, { token: i2.KeycloakService }], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
122
|
+
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.2.1", ngImport: i0, type: AuthService, providedIn: 'root' }); }
|
|
123
|
+
}
|
|
124
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.2.1", ngImport: i0, type: AuthService, decorators: [{
|
|
125
|
+
type: Injectable,
|
|
126
|
+
args: [{
|
|
127
|
+
providedIn: 'root',
|
|
128
|
+
}]
|
|
129
|
+
}], ctorParameters: () => [{ type: UserAccessConfigService }, { type: i2.KeycloakService }] });
|
|
130
|
+
|
|
131
|
+
var AuthGuardParamKeys;
|
|
132
|
+
(function (AuthGuardParamKeys) {
|
|
133
|
+
AuthGuardParamKeys["REDIRECT_ROUTE"] = "redirectRoute";
|
|
134
|
+
})(AuthGuardParamKeys || (AuthGuardParamKeys = {}));
|
|
135
|
+
|
|
136
|
+
class AuthGuard {
|
|
137
|
+
constructor(router, keycloak, auth) {
|
|
138
|
+
this.router = router;
|
|
139
|
+
this.keycloak = keycloak;
|
|
140
|
+
this.auth = auth;
|
|
141
|
+
}
|
|
142
|
+
async canActivate(route, state) {
|
|
143
|
+
if (!this.auth.isAuthenticated()) {
|
|
144
|
+
// redirectToLogin query param is more important than redirectRoute
|
|
145
|
+
if (!route.queryParams?.redirectToLogin && route.data?.[AuthGuardParamKeys.REDIRECT_ROUTE]) {
|
|
146
|
+
this.router.navigate([route.data[AuthGuardParamKeys.REDIRECT_ROUTE]]);
|
|
147
|
+
}
|
|
148
|
+
else {
|
|
149
|
+
await this.keycloak.login({
|
|
150
|
+
redirectUri: window.location.origin + state.url,
|
|
151
|
+
});
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
// Clear unnecessary query param
|
|
155
|
+
if (route.queryParams?.redirectToLogin) {
|
|
156
|
+
const queryParams = { ...route.queryParams };
|
|
157
|
+
delete queryParams.redirectToLogin;
|
|
158
|
+
this.router.navigate([state.url.split('?')[0]], { queryParams });
|
|
159
|
+
}
|
|
160
|
+
return this.auth.isAuthenticated();
|
|
161
|
+
}
|
|
162
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.2.1", ngImport: i0, type: AuthGuard, deps: [{ token: i1.Router }, { token: i2.KeycloakService }, { token: AuthService }], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
163
|
+
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.2.1", ngImport: i0, type: AuthGuard, providedIn: 'root' }); }
|
|
164
|
+
}
|
|
165
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.2.1", ngImport: i0, type: AuthGuard, decorators: [{
|
|
166
|
+
type: Injectable,
|
|
167
|
+
args: [{
|
|
168
|
+
providedIn: 'root',
|
|
169
|
+
}]
|
|
170
|
+
}], ctorParameters: () => [{ type: i1.Router }, { type: i2.KeycloakService }, { type: AuthService }] });
|
|
171
|
+
|
|
172
|
+
class PermissionsService {
|
|
173
|
+
get statusLocalStorageKey() {
|
|
174
|
+
return this.userSpecificLocalstorageKey(this.status);
|
|
175
|
+
}
|
|
176
|
+
get subscriptionConfig() {
|
|
177
|
+
return this.config.getConfiguration().subscriptions;
|
|
178
|
+
}
|
|
179
|
+
get token() {
|
|
180
|
+
return this.auth.parsedKeyCloakToken;
|
|
181
|
+
}
|
|
182
|
+
get status() {
|
|
183
|
+
return this._status;
|
|
184
|
+
}
|
|
185
|
+
get permissions() {
|
|
186
|
+
return this._permissions;
|
|
187
|
+
}
|
|
188
|
+
get expiredSubscriptions() {
|
|
189
|
+
return this._expiredSubscriptions;
|
|
190
|
+
}
|
|
191
|
+
get expiredNotConfirmedSubscriptions() {
|
|
192
|
+
return this._expiredNotConfirmedSubscriptions;
|
|
193
|
+
}
|
|
194
|
+
get hasOnlyUnauthorizedPermissions() {
|
|
195
|
+
return this._hasOnlyUnauthorizedPermissions;
|
|
196
|
+
}
|
|
197
|
+
get onPermissionRefresh() {
|
|
198
|
+
return this._onPermissionRefresh.asObservable();
|
|
199
|
+
}
|
|
200
|
+
constructor(config, auth, userConfigurationService) {
|
|
201
|
+
this.config = config;
|
|
202
|
+
this.auth = auth;
|
|
203
|
+
this.userConfigurationService = userConfigurationService;
|
|
204
|
+
this._expiredSubscriptions = [];
|
|
205
|
+
this._expiredNotConfirmedSubscriptions = [];
|
|
206
|
+
this._onPermissionRefresh = new Subject();
|
|
207
|
+
this.SUPPORT_ROLE_KEY = 'support';
|
|
208
|
+
}
|
|
209
|
+
/**
|
|
210
|
+
* Checks wether user has necessary permission/permissions. By default, if user has at least one of given permissions, method returns true. To check all permissions, set needsAll to true.
|
|
211
|
+
* @param permissions Permissions to be checked.
|
|
212
|
+
* @param needsAll If user should have all of given permissions
|
|
213
|
+
* @returns
|
|
214
|
+
*/
|
|
215
|
+
hasPermission(permissions, needsAll = false) {
|
|
216
|
+
// if user is not any group member, access should not be provided
|
|
217
|
+
if (!this.auth.userIsGroupMember() && this.auth.userSubscriptionPlan()) {
|
|
218
|
+
return false;
|
|
219
|
+
}
|
|
220
|
+
if (!permissions || permissions.length === 0 || this.permissions.includes(DefaultPermissions.ALL)) {
|
|
221
|
+
return true;
|
|
222
|
+
}
|
|
223
|
+
for (const permission of permissions) {
|
|
224
|
+
const hasPermission = this.permissions.includes(permission);
|
|
225
|
+
if (needsAll && !hasPermission) {
|
|
226
|
+
return false;
|
|
227
|
+
}
|
|
228
|
+
else if (!needsAll && hasPermission) {
|
|
229
|
+
return true;
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
return needsAll ? true : false;
|
|
233
|
+
}
|
|
234
|
+
configurePermissions() {
|
|
235
|
+
this.isDebug = this.config.getConfiguration()?.isDebug ?? false;
|
|
236
|
+
this.userConfiguration = {};
|
|
237
|
+
this._status = PermissionStatus.OK;
|
|
238
|
+
let allPermissions = [];
|
|
239
|
+
const expiredSubscriptions = [];
|
|
240
|
+
allPermissions = this.refreshAllPermissionsForUser();
|
|
241
|
+
if (new Date() > new Date(this.auth.userSubscriptionPlan()?.EndDate)) {
|
|
242
|
+
expiredSubscriptions.push(this.auth.userSubscriptionPlan());
|
|
243
|
+
}
|
|
244
|
+
this.setPermissions(allPermissions);
|
|
245
|
+
this._expiredSubscriptions = expiredSubscriptions;
|
|
246
|
+
if (this.isDebug) {
|
|
247
|
+
// eslint-disable-next-line no-console
|
|
248
|
+
console.log('permissions', this.permissions);
|
|
249
|
+
// eslint-disable-next-line no-console
|
|
250
|
+
console.log('userConfig', this.userConfiguration);
|
|
251
|
+
}
|
|
252
|
+
this.shouldShowMessage();
|
|
253
|
+
}
|
|
254
|
+
confirmMessage() {
|
|
255
|
+
const localStorageKey = this.statusLocalStorageKey;
|
|
256
|
+
if (this.status === PermissionStatus.NO_SUBSCRIPTION) {
|
|
257
|
+
localStorage.setItem(localStorageKey, `true`);
|
|
258
|
+
}
|
|
259
|
+
else if (this.status === PermissionStatus.EXPIRED_SUBSCRIPTION) {
|
|
260
|
+
const confirmed = JSON.parse(localStorage.getItem(localStorageKey)) ?? {};
|
|
261
|
+
this.expiredNotConfirmedSubscriptions.forEach((subscription) => {
|
|
262
|
+
confirmed[subscription.PlanId] = new Date().toString();
|
|
263
|
+
});
|
|
264
|
+
localStorage.setItem(localStorageKey, JSON.stringify(confirmed));
|
|
265
|
+
}
|
|
266
|
+
this.showSubscriptionMessage = false;
|
|
267
|
+
}
|
|
268
|
+
userSpecificLocalstorageKey(key) {
|
|
269
|
+
return this.hashString(`${key}_${this.auth.getUserName().toUpperCase()}`);
|
|
270
|
+
}
|
|
271
|
+
refreshAllPermissionsForUser() {
|
|
272
|
+
// We should ignore subscription permissions for Supports.
|
|
273
|
+
return this.isSupport()
|
|
274
|
+
? this.checkRolePermissions()
|
|
275
|
+
: this.checkSubscriptionPermissions().concat(this.checkRolePermissions());
|
|
276
|
+
}
|
|
277
|
+
checkSubscriptionPermissions() {
|
|
278
|
+
const subscriptionConfig = this.subscriptionConfig;
|
|
279
|
+
const allPermissions = subscriptionConfig.plans;
|
|
280
|
+
const token = this.token;
|
|
281
|
+
const isAuthenticated = this.auth.isAuthenticated();
|
|
282
|
+
let permissions = [];
|
|
283
|
+
if (isAuthenticated && !subscriptionConfig.ignoreSubscriptions) {
|
|
284
|
+
let hasAnySubscription = false;
|
|
285
|
+
if (token?.subscription) {
|
|
286
|
+
for (const plan of token.subscription) {
|
|
287
|
+
const permissionConfig = this.findPermissionConfig(allPermissions, plan.Label);
|
|
288
|
+
if (!this.isPlanActive(plan) && this.isSupportedPlan(plan)) {
|
|
289
|
+
if (permissionConfig) {
|
|
290
|
+
hasAnySubscription = true;
|
|
291
|
+
this._status = PermissionStatus.EXPIRED_SUBSCRIPTION;
|
|
292
|
+
}
|
|
293
|
+
continue;
|
|
294
|
+
}
|
|
295
|
+
if (this.isPlanActive(plan) && this.isSupportedPlan(plan) && permissionConfig) {
|
|
296
|
+
hasAnySubscription = true;
|
|
297
|
+
}
|
|
298
|
+
if (permissionConfig?.permissions) {
|
|
299
|
+
permissionConfig.permissions.forEach((permission) => {
|
|
300
|
+
if (!permissions.includes(permission)) {
|
|
301
|
+
permissions.push(permission);
|
|
302
|
+
}
|
|
303
|
+
});
|
|
304
|
+
}
|
|
305
|
+
if (permissionConfig?.userConfiguration) {
|
|
306
|
+
this.updateUserConfiguration(permissionConfig.userConfiguration);
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
if (!hasAnySubscription) {
|
|
311
|
+
this._status = PermissionStatus.NO_SUBSCRIPTION;
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
else if (subscriptionConfig.ignoreSubscriptions && this.isDebug) {
|
|
315
|
+
console.warn('You are ignoring subscriptions, which means that your permissions might not work as expected. This option might be set in config.local.debug.json for easier testing.');
|
|
316
|
+
}
|
|
317
|
+
// if has no permissions, use default permissions
|
|
318
|
+
if (permissions.length === 0) {
|
|
319
|
+
const defaultPlanName = this.findDefaultPlanName(subscriptionConfig, isAuthenticated);
|
|
320
|
+
const permissionConfig = this.findPermissionConfig(allPermissions, defaultPlanName);
|
|
321
|
+
permissions = permissionConfig?.permissions ?? [];
|
|
322
|
+
this.updateUserConfiguration(permissionConfig?.userConfiguration);
|
|
323
|
+
if (!subscriptionConfig.ignoreSubscriptions) {
|
|
324
|
+
this._hasOnlyUnauthorizedPermissions = true;
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
return permissions;
|
|
328
|
+
}
|
|
329
|
+
checkRolePermissions() {
|
|
330
|
+
const subscriptionConfig = this.subscriptionConfig;
|
|
331
|
+
const token = this.token;
|
|
332
|
+
let permissions = [];
|
|
333
|
+
const roles = token?.realm_access?.roles;
|
|
334
|
+
// if no roles for user or no permissions defined for roles, return empty array.
|
|
335
|
+
if (!roles?.length || !subscriptionConfig.roles?.length) {
|
|
336
|
+
return [];
|
|
337
|
+
}
|
|
338
|
+
if (this.isSupport()) {
|
|
339
|
+
return this.findPermissionConfig(subscriptionConfig.roles, this.SUPPORT_ROLE_KEY)?.permissions;
|
|
340
|
+
}
|
|
341
|
+
roles.forEach((role) => {
|
|
342
|
+
const rolePermissionConfig = this.findPermissionConfig(subscriptionConfig.roles, role);
|
|
343
|
+
if (rolePermissionConfig) {
|
|
344
|
+
permissions = permissions.concat(rolePermissionConfig.permissions);
|
|
345
|
+
this.updateUserConfiguration(rolePermissionConfig.userConfiguration);
|
|
346
|
+
}
|
|
347
|
+
});
|
|
348
|
+
return permissions;
|
|
349
|
+
}
|
|
350
|
+
/**
|
|
351
|
+
* Updates current user configuration object with values that gives more permissions (bigger limits) to the user.
|
|
352
|
+
* @param userConfiguration User configuration object to be compared with current userconfig object
|
|
353
|
+
*/
|
|
354
|
+
updateUserConfiguration(userConfiguration) {
|
|
355
|
+
if (!userConfiguration) {
|
|
356
|
+
return;
|
|
357
|
+
}
|
|
358
|
+
this.userConfigurationService.updateUserConfiguration(this.userConfiguration, userConfiguration);
|
|
359
|
+
}
|
|
360
|
+
shouldShowMessage() {
|
|
361
|
+
const localStorageValue = localStorage.getItem(this.statusLocalStorageKey);
|
|
362
|
+
if (this.status === PermissionStatus.NO_SUBSCRIPTION) {
|
|
363
|
+
this.showSubscriptionMessage = localStorageValue ? false : true;
|
|
364
|
+
}
|
|
365
|
+
else if (this.status === PermissionStatus.EXPIRED_SUBSCRIPTION) {
|
|
366
|
+
const confirmed = JSON.parse(localStorageValue);
|
|
367
|
+
if (!confirmed) {
|
|
368
|
+
this._expiredNotConfirmedSubscriptions = this.expiredSubscriptions;
|
|
369
|
+
this.showSubscriptionMessage = true;
|
|
370
|
+
return;
|
|
371
|
+
}
|
|
372
|
+
const notConfirmedExpired = [];
|
|
373
|
+
for (const subscription of this.expiredSubscriptions) {
|
|
374
|
+
if (confirmed[subscription.PlanId] && new Date() > new Date(subscription.EndDate)) {
|
|
375
|
+
continue;
|
|
376
|
+
}
|
|
377
|
+
notConfirmedExpired.push(subscription);
|
|
378
|
+
this.showSubscriptionMessage = true;
|
|
379
|
+
}
|
|
380
|
+
this._expiredNotConfirmedSubscriptions = notConfirmedExpired;
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
isPlanActive(plan) {
|
|
384
|
+
if (!plan.EndDate || new Date() < new Date(plan.EndDate)) {
|
|
385
|
+
return true;
|
|
386
|
+
}
|
|
387
|
+
return false;
|
|
388
|
+
}
|
|
389
|
+
isSupportedPlan(plan) {
|
|
390
|
+
const planLabels = Object.values(this.config.getConfiguration().plans);
|
|
391
|
+
if (planLabels.includes(plan.Label)) {
|
|
392
|
+
return true;
|
|
393
|
+
}
|
|
394
|
+
return false;
|
|
395
|
+
}
|
|
396
|
+
/**
|
|
397
|
+
* Finds permission config in array based on given key
|
|
398
|
+
* @param permissions permission array where to find config from
|
|
399
|
+
* @param key object identification key
|
|
400
|
+
* @returns permission config
|
|
401
|
+
*/
|
|
402
|
+
findPermissionConfig(permissions, key) {
|
|
403
|
+
for (const permission of permissions) {
|
|
404
|
+
if (permission.keys.includes(key)) {
|
|
405
|
+
return permission;
|
|
406
|
+
}
|
|
407
|
+
}
|
|
408
|
+
return null;
|
|
409
|
+
}
|
|
410
|
+
// for storing user specific info in localstorage;
|
|
411
|
+
hashString(username) {
|
|
412
|
+
let hash = 0;
|
|
413
|
+
if (username.length > 0) {
|
|
414
|
+
for (let i = 0; i < username.length; i++) {
|
|
415
|
+
const char = username.charCodeAt(i);
|
|
416
|
+
hash = (hash << 5) - hash + char;
|
|
417
|
+
hash = hash & hash; // Convert to 32bit integer
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
return hash.toString();
|
|
421
|
+
}
|
|
422
|
+
findDefaultPlanName(config, isAuthenticated) {
|
|
423
|
+
let defaultPlanName;
|
|
424
|
+
if (isAuthenticated) {
|
|
425
|
+
if (this.status === PermissionStatus.EXPIRED_SUBSCRIPTION) {
|
|
426
|
+
defaultPlanName = config.expiredPermissionsPlanName || config.authorizedDefaultPermissionPlan;
|
|
427
|
+
}
|
|
428
|
+
else {
|
|
429
|
+
defaultPlanName = config.authorizedDefaultPermissionPlan;
|
|
430
|
+
}
|
|
431
|
+
}
|
|
432
|
+
return defaultPlanName || config.unauthorizedDefaultPermissionPlan;
|
|
433
|
+
}
|
|
434
|
+
setPermissions(permissions) {
|
|
435
|
+
this._permissions = permissions;
|
|
436
|
+
this._onPermissionRefresh.next(this._permissions);
|
|
437
|
+
}
|
|
438
|
+
isSupport() {
|
|
439
|
+
return this.token?.realm_access?.roles?.includes(this.SUPPORT_ROLE_KEY);
|
|
440
|
+
}
|
|
441
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.2.1", ngImport: i0, type: PermissionsService, deps: [{ token: UserAccessConfigService }, { token: AuthService }, { token: USER_CUSTOM_CONFIGURATION_SERVICE_TOKEN }], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
442
|
+
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.2.1", ngImport: i0, type: PermissionsService, providedIn: 'root' }); }
|
|
443
|
+
}
|
|
444
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.2.1", ngImport: i0, type: PermissionsService, decorators: [{
|
|
445
|
+
type: Injectable,
|
|
446
|
+
args: [{
|
|
447
|
+
providedIn: 'root',
|
|
448
|
+
}]
|
|
449
|
+
}], ctorParameters: () => [{ type: UserAccessConfigService }, { type: AuthService }, { type: undefined, decorators: [{
|
|
450
|
+
type: Inject,
|
|
451
|
+
args: [USER_CUSTOM_CONFIGURATION_SERVICE_TOKEN]
|
|
452
|
+
}] }] });
|
|
453
|
+
|
|
454
|
+
async function KEYCLOAK_INITIALIZER() {
|
|
455
|
+
const keycloak = inject(KeycloakService);
|
|
456
|
+
const config = inject(UserAccessConfigService);
|
|
457
|
+
const permissions = inject(PermissionsService);
|
|
458
|
+
const auth = inject(AuthService);
|
|
459
|
+
await keycloak
|
|
460
|
+
.init({
|
|
461
|
+
config: config.getConfiguration().keycloak,
|
|
462
|
+
enableBearerInterceptor: false,
|
|
463
|
+
loadUserProfileAtStartUp: true,
|
|
464
|
+
initOptions: {
|
|
465
|
+
onLoad: 'check-sso',
|
|
466
|
+
silentCheckSsoRedirectUri: window.location.origin + '/assets/auth/silent-check-sso.html',
|
|
467
|
+
redirectUri: window.location.origin,
|
|
468
|
+
},
|
|
469
|
+
bearerExcludedUrls: config.getConfiguration().keycloak.bearerExcludedUrls,
|
|
470
|
+
})
|
|
471
|
+
.catch((error) => {
|
|
472
|
+
console.error(error);
|
|
473
|
+
permissions.configurePermissions();
|
|
474
|
+
return;
|
|
475
|
+
});
|
|
476
|
+
auth.enableAuth();
|
|
477
|
+
await auth.checkLogin();
|
|
478
|
+
permissions.configurePermissions();
|
|
479
|
+
keycloak.keycloakEvents$.subscribe((ev) => {
|
|
480
|
+
if (ev.type == KeycloakEventTypeLegacy.OnTokenExpired) {
|
|
481
|
+
const minValidity = 5;
|
|
482
|
+
if (keycloak.isTokenExpired(minValidity)) {
|
|
483
|
+
keycloak.updateToken(minValidity).then(() => {
|
|
484
|
+
auth.refreshKeycloakToken();
|
|
485
|
+
permissions.configurePermissions();
|
|
486
|
+
}, (err) => {
|
|
487
|
+
console.error('Token refresh failed', err);
|
|
488
|
+
keycloak.login();
|
|
489
|
+
});
|
|
490
|
+
}
|
|
491
|
+
}
|
|
492
|
+
});
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
async function KEYCLOAK_PROVIDER() {
|
|
496
|
+
return [
|
|
497
|
+
KeycloakService,
|
|
498
|
+
{
|
|
499
|
+
provide: HTTP_INTERCEPTORS,
|
|
500
|
+
useClass: KeycloakBearerInterceptor,
|
|
501
|
+
deps: [KeycloakService],
|
|
502
|
+
multi: true,
|
|
503
|
+
},
|
|
504
|
+
await KEYCLOAK_INITIALIZER(),
|
|
505
|
+
];
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
/**
|
|
509
|
+
* Generated bundle index. Do not edit.
|
|
510
|
+
*/
|
|
511
|
+
|
|
512
|
+
export { AuthGuard, AuthGuardParamKeys, AuthService, DefaultPermissions, KEYCLOAK_PROVIDER, PermissionStatus, PermissionsService, USER_ACCESS_CONFIG_TOKEN, USER_CUSTOM_CONFIGURATION_SERVICE_TOKEN, UserAccessConfigService };
|
|
513
|
+
//# sourceMappingURL=tilde-nlp-ngx-user-access.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tilde-nlp-ngx-user-access.mjs","sources":["../../../projects/ngx-user-access/src/lib/user-access-config/tokens/user-access-config,.token.ts","../../../projects/ngx-user-access/src/lib/user-access-config/user-access-config.service.ts","../../../projects/ngx-user-access/src/lib/permissions/models/permission-status.model.ts","../../../projects/ngx-user-access/src/lib/permissions/models/default-permissions.enum.ts","../../../projects/ngx-user-access/src/lib/permissions/models/user-custom-configuration-service.token.ts","../../../projects/ngx-user-access/src/lib/auth/auth.service.ts","../../../projects/ngx-user-access/src/lib/auth/models/auth-guard-param-keys.enum.ts","../../../projects/ngx-user-access/src/lib/auth/auth.guard.ts","../../../projects/ngx-user-access/src/lib/permissions/permissions.service.ts","../../../projects/ngx-user-access/src/lib/keycloak/keycloak.initializer.ts","../../../projects/ngx-user-access/src/lib/keycloak/keycloak.provider.ts","../../../projects/ngx-user-access/src/tilde-nlp-ngx-user-access.ts"],"sourcesContent":["import { InjectionToken } from '@angular/core';\r\nimport { UserAccessConfig } from '../models/user-access-config.model';\r\n\r\nexport const USER_ACCESS_CONFIG_TOKEN = new InjectionToken<UserAccessConfig>('USER_ACCESS_CONFIG_TOKEN');\r\n","import { inject, Injectable } from '@angular/core';\r\nimport { USER_ACCESS_CONFIG_TOKEN } from './tokens/user-access-config,.token';\r\nimport { UserAccessConfig } from './models/user-access-config.model';\r\n\r\n@Injectable({\r\n\tprovidedIn: 'root',\r\n})\r\nexport class UserAccessConfigService {\r\n\t#config = inject(USER_ACCESS_CONFIG_TOKEN);\r\n\r\n\tgetConfiguration(): UserAccessConfig {\r\n\t\treturn this.#config;\r\n\t}\r\n}\r\n","export enum PermissionStatus {\r\n\tOK = 'OK',\r\n\tEXPIRED_SUBSCRIPTION = 'EXPIRED_SUBSCRIPTION',\r\n\tNO_SUBSCRIPTION = 'NO_SUBSCRIPTION',\r\n}\r\n","export enum DefaultPermissions {\r\n ALL = 'ALL'\r\n}","import { InjectionToken } from '@angular/core';\r\n\r\nexport const USER_CUSTOM_CONFIGURATION_SERVICE_TOKEN = new InjectionToken('USER_CUSTOM_CONFIGURATION_SERVICE_TOKEN');\r\n","import { Injectable } from '@angular/core';\r\nimport { KeycloakService } from 'keycloak-angular';\r\nimport { BehaviorSubject } from 'rxjs';\r\nimport { UserAccessConfigService } from '../user-access-config';\r\nimport { AuthTokenParsed } from './models/auth-token-parsed.model';\r\nimport { AuthSubscription } from '@tilde-nlp/ngx-common';\r\n\r\n@Injectable({\r\n\tprovidedIn: 'root',\r\n})\r\nexport class AuthService {\r\n\tauthEnabled!: boolean;\r\n\tusername: BehaviorSubject<string>;\r\n\r\n\tprivate _isAuthenticated = false;\r\n\tprivate _parsedKeycloakToken!: AuthTokenParsed;\r\n\tprivate _token!: string | undefined;\r\n\tget token() {\r\n\t\treturn this._token;\r\n\t}\r\n\r\n\tget parsedKeyCloakToken() {\r\n\t\treturn this._parsedKeycloakToken;\r\n\t}\r\n\r\n\tconstructor(private readonly config: UserAccessConfigService, private readonly keyCloak: KeycloakService) {\r\n\t\tthis.username = new BehaviorSubject<string>('');\r\n\r\n\t\t// make jwt available in window object, so that it can be accessed everywhere (e.g. from webtranslate iframe)\r\n\t\t/* eslint-disable @typescript-eslint/no-explicit-any */\r\n\t\t(window as any).keycloakToken = () => {\r\n\t\t\treturn this._token;\r\n\t\t};\r\n\t}\r\n\r\n\tuserSubscriptionPlan() {\r\n\t\treturn this.parsedKeyCloakToken?.subscription?.find((plan: { PlanId: string }) =>\r\n\t\t\tObject.values(this.config.getConfiguration().plans).includes(plan.PlanId)\r\n\t\t);\r\n\t}\r\n\r\n\tisAuthenticated(): boolean {\r\n\t\treturn this._isAuthenticated;\r\n\t}\r\n\r\n\tgetUserName(): string {\r\n\t\treturn this.isAuthenticated() ? this.keyCloak?.getUsername() : '';\r\n\t}\r\n\r\n\tgetUserDisplayName(): string {\r\n\t\tif (!this.authEnabled || !this._isAuthenticated) {\r\n\t\t\treturn '';\r\n\t\t}\r\n\t\tlet username = this.getUserName();\r\n\r\n\t\tif (this._parsedKeycloakToken?.name) {\r\n\t\t\tusername = this.parsedKeyCloakToken.name;\r\n\t\t}\r\n\t\treturn username;\r\n\t}\r\n\r\n\tgetToken() {\r\n\t\treturn this.keyCloak.getToken();\r\n\t}\r\n\r\n\tlogout() {\r\n\t\tif (!this.authEnabled) {\r\n\t\t\treturn;\r\n\t\t}\r\n\r\n\t\tthis.keyCloak.logout(window.location.origin);\r\n\t}\r\n\r\n\tasync checkLogin() {\r\n\t\tif (!this.authEnabled) {\r\n\t\t\treturn;\r\n\t\t}\r\n\r\n\t\tthis._isAuthenticated = await this.keyCloak.isLoggedIn();\r\n\t\tthis.refreshKeycloakToken();\r\n\t\tconst username = this.getUserName();\r\n\t\tthis.username.next(username);\r\n\t}\r\n\r\n\tlogin() {\r\n\t\tif (!this.authEnabled) {\r\n\t\t\treturn;\r\n\t\t}\r\n\r\n\t\tthis.keyCloak.login();\r\n\t}\r\n\r\n\tenableAuth() {\r\n\t\tthis.authEnabled = true;\r\n\t}\r\n\r\n\trefreshKeycloakToken() {\r\n\t\tif (!this.authEnabled) {\r\n\t\t\treturn;\r\n\t\t}\r\n\r\n\t\tconst keyCloakInstance = this.keyCloak.getKeycloakInstance();\r\n\t\t/* eslint-disable @typescript-eslint/no-explicit-any */\r\n\t\tconst tokenParsed: AuthTokenParsed = keyCloakInstance.tokenParsed as any;\r\n\t\tthis._token = keyCloakInstance.token;\r\n\t\ttokenParsed?.subscription?.forEach((subscription: AuthSubscription) => {\r\n\t\t\t// Removes \"-Yearly\", \"-Monthly\" from end of string or -Eur- and everything that follows.\r\n\t\t\tsubscription.Label = subscription.PlanId.replace(/-Monthly$|-Yearly$|-EUR-(.*)/, '');\r\n\t\t});\r\n\t\tthis._parsedKeycloakToken = tokenParsed;\r\n\t}\r\n\r\n\tuserIsGroupMember(): boolean {\r\n\t\treturn this.parsedKeyCloakToken?.membership?.length > 0;\r\n\t}\r\n}\r\n","export enum AuthGuardParamKeys {\r\n\tREDIRECT_ROUTE = 'redirectRoute',\r\n}\r\n","import { Injectable } from '@angular/core';\r\nimport { ActivatedRouteSnapshot, Router, RouterStateSnapshot, CanActivate } from '@angular/router';\r\nimport { KeycloakService } from 'keycloak-angular';\r\n\r\nimport { AuthService } from './auth.service';\r\nimport { AuthGuardParamKeys } from './models/auth-guard-param-keys.enum';\r\n\r\n@Injectable({\r\n\tprovidedIn: 'root',\r\n})\r\nexport class AuthGuard implements CanActivate {\r\n\tconstructor(private readonly router: Router, private readonly keycloak: KeycloakService, private readonly auth: AuthService) {}\r\n\r\n\tasync canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Promise<boolean> {\r\n\t\tif (!this.auth.isAuthenticated()) {\r\n\t\t\t// redirectToLogin query param is more important than redirectRoute\r\n\t\t\tif (!route.queryParams?.redirectToLogin && route.data?.[AuthGuardParamKeys.REDIRECT_ROUTE]) {\r\n\t\t\t\tthis.router.navigate([route.data[AuthGuardParamKeys.REDIRECT_ROUTE]]);\r\n\t\t\t} else {\r\n\t\t\t\tawait this.keycloak.login({\r\n\t\t\t\t\tredirectUri: window.location.origin + state.url,\r\n\t\t\t\t});\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\t// Clear unnecessary query param\r\n\t\tif (route.queryParams?.redirectToLogin) {\r\n\t\t\tconst queryParams = { ...route.queryParams };\r\n\t\t\tdelete queryParams.redirectToLogin;\r\n\t\t\tthis.router.navigate([state.url.split('?')[0]], { queryParams });\r\n\t\t}\r\n\r\n\t\treturn this.auth.isAuthenticated();\r\n\t}\r\n}\r\n","import { Inject, Injectable } from '@angular/core';\r\nimport { PermissionStatus } from './models/permission-status.model';\r\nimport { Observable, Subject } from 'rxjs';\r\nimport { UserAccessConfigService } from '../user-access-config';\r\nimport { AuthService } from '../auth';\r\nimport { DefaultPermissions } from './models/default-permissions.enum';\r\nimport { PermissionsConfig } from './models/permissions-config.model';\r\nimport { USER_CUSTOM_CONFIGURATION_SERVICE_TOKEN } from './models/user-custom-configuration-service.token';\r\nimport { UserCustomConfigurationService } from './models/user-custom-configuration-service.model';\r\nimport { DefaultSubscriptionConfig } from './models/default-subscription-config.model';\r\nimport { AuthSubscription } from '@tilde-nlp/ngx-common';\r\n\r\n@Injectable({\r\n\tprovidedIn: 'root',\r\n})\r\nexport class PermissionsService<Permission, UserConfiguration> {\r\n\tuserConfiguration!: UserConfiguration;\r\n\tshowSubscriptionMessage!: boolean;\r\n\r\n\tprivate _hasOnlyUnauthorizedPermissions!: boolean;\r\n\r\n\tprivate _status!: PermissionStatus;\r\n\r\n\tprivate _permissions!: Permission[];\r\n\r\n\tprivate _expiredSubscriptions: AuthSubscription[] = [];\r\n\r\n\tprivate _expiredNotConfirmedSubscriptions: AuthSubscription[] = [];\r\n\r\n\tprivate _onPermissionRefresh = new Subject<Permission[]>();\r\n\r\n\tprivate isDebug!: boolean;\r\n\r\n\tprivate get statusLocalStorageKey() {\r\n\t\treturn this.userSpecificLocalstorageKey(this.status);\r\n\t}\r\n\r\n\tprivate get subscriptionConfig() {\r\n\t\treturn this.config.getConfiguration().subscriptions;\r\n\t}\r\n\r\n\tprivate get token() {\r\n\t\treturn this.auth.parsedKeyCloakToken;\r\n\t}\r\n\r\n\tget status() {\r\n\t\treturn this._status;\r\n\t}\r\n\tget permissions() {\r\n\t\treturn this._permissions;\r\n\t}\r\n\tget expiredSubscriptions() {\r\n\t\treturn this._expiredSubscriptions;\r\n\t}\r\n\tget expiredNotConfirmedSubscriptions() {\r\n\t\treturn this._expiredNotConfirmedSubscriptions;\r\n\t}\r\n\tget hasOnlyUnauthorizedPermissions() {\r\n\t\treturn this._hasOnlyUnauthorizedPermissions;\r\n\t}\r\n\r\n\tget onPermissionRefresh(): Observable<Permission[]> {\r\n\t\treturn this._onPermissionRefresh.asObservable();\r\n\t}\r\n\r\n\tprivate readonly SUPPORT_ROLE_KEY = 'support';\r\n\r\n\tconstructor(\r\n\t\tprivate readonly config: UserAccessConfigService,\r\n\t\tprivate readonly auth: AuthService,\r\n\t\t@Inject(USER_CUSTOM_CONFIGURATION_SERVICE_TOKEN)\r\n\t\tprivate readonly userConfigurationService: UserCustomConfigurationService<UserConfiguration>\r\n\t) {}\r\n\r\n\t/**\r\n\t * Checks wether user has necessary permission/permissions. By default, if user has at least one of given permissions, method returns true. To check all permissions, set needsAll to true.\r\n\t * @param permissions Permissions to be checked.\r\n\t * @param needsAll If user should have all of given permissions\r\n\t * @returns\r\n\t */\r\n\thasPermission(permissions: Permission[], needsAll = false): boolean {\r\n\t\t// if user is not any group member, access should not be provided\r\n\t\tif (!this.auth.userIsGroupMember() && this.auth.userSubscriptionPlan()) {\r\n\t\t\treturn false;\r\n\t\t}\r\n\r\n\t\tif (!permissions || permissions.length === 0 || this.permissions.includes(DefaultPermissions.ALL as Permission)) {\r\n\t\t\treturn true;\r\n\t\t}\r\n\r\n\t\tfor (const permission of permissions) {\r\n\t\t\tconst hasPermission = this.permissions.includes(permission);\r\n\t\t\tif (needsAll && !hasPermission) {\r\n\t\t\t\treturn false;\r\n\t\t\t} else if (!needsAll && hasPermission) {\r\n\t\t\t\treturn true;\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\treturn needsAll ? true : false;\r\n\t}\r\n\r\n\tconfigurePermissions(): void {\r\n\t\tthis.isDebug = this.config.getConfiguration()?.isDebug ?? false;\r\n\r\n\t\tthis.userConfiguration = {} as UserConfiguration;\r\n\t\tthis._status = PermissionStatus.OK;\r\n\r\n\t\tlet allPermissions: Permission[] | undefined = [];\r\n\t\tconst expiredSubscriptions: AuthSubscription[] = [];\r\n\r\n\t\tallPermissions = this.refreshAllPermissionsForUser();\r\n\r\n\t\tif (new Date() > new Date(this.auth.userSubscriptionPlan()?.EndDate as string)) {\r\n\t\t\texpiredSubscriptions.push(this.auth.userSubscriptionPlan() as AuthSubscription);\r\n\t\t}\r\n\r\n\t\tthis.setPermissions(allPermissions as Permission[]);\r\n\t\tthis._expiredSubscriptions = expiredSubscriptions;\r\n\t\tif (this.isDebug) {\r\n\t\t\t// eslint-disable-next-line no-console\r\n\t\t\tconsole.log('permissions', this.permissions);\r\n\t\t\t// eslint-disable-next-line no-console\r\n\t\t\tconsole.log('userConfig', this.userConfiguration);\r\n\t\t}\r\n\t\tthis.shouldShowMessage();\r\n\t}\r\n\r\n\tconfirmMessage() {\r\n\t\tconst localStorageKey = this.statusLocalStorageKey;\r\n\t\tif (this.status === PermissionStatus.NO_SUBSCRIPTION) {\r\n\t\t\tlocalStorage.setItem(localStorageKey, `true`);\r\n\t\t} else if (this.status === PermissionStatus.EXPIRED_SUBSCRIPTION) {\r\n\t\t\tconst confirmed = JSON.parse(localStorage.getItem(localStorageKey) as string) ?? {};\r\n\t\t\tthis.expiredNotConfirmedSubscriptions.forEach((subscription) => {\r\n\t\t\t\tconfirmed[subscription.PlanId] = new Date().toString();\r\n\t\t\t});\r\n\t\t\tlocalStorage.setItem(localStorageKey, JSON.stringify(confirmed));\r\n\t\t}\r\n\t\tthis.showSubscriptionMessage = false;\r\n\t}\r\n\r\n\tuserSpecificLocalstorageKey(key: string) {\r\n\t\treturn this.hashString(`${key}_${this.auth.getUserName().toUpperCase()}`);\r\n\t}\r\n\r\n\tprivate refreshAllPermissionsForUser() {\r\n\t\t// We should ignore subscription permissions for Supports.\r\n\t\treturn this.isSupport()\r\n\t\t\t? this.checkRolePermissions()\r\n\t\t\t: this.checkSubscriptionPermissions().concat(this.checkRolePermissions() as Permission[]);\r\n\t}\r\n\r\n\tprivate checkSubscriptionPermissions(): Permission[] {\r\n\t\tconst subscriptionConfig = this.subscriptionConfig;\r\n\t\tconst allPermissions = subscriptionConfig.plans;\r\n\t\tconst token = this.token;\r\n\t\tconst isAuthenticated = this.auth.isAuthenticated();\r\n\r\n\t\tlet permissions: Permission[] = [];\r\n\r\n\t\tif (isAuthenticated && !subscriptionConfig.ignoreSubscriptions) {\r\n\t\t\tlet hasAnySubscription = false;\r\n\t\t\tif (token?.subscription) {\r\n\t\t\t\tfor (const plan of token.subscription) {\r\n\t\t\t\t\tconst permissionConfig = this.findPermissionConfig(allPermissions, plan.Label as string);\r\n\t\t\t\t\tif (!this.isPlanActive(plan) && this.isSupportedPlan(plan)) {\r\n\t\t\t\t\t\tif (permissionConfig) {\r\n\t\t\t\t\t\t\thasAnySubscription = true;\r\n\t\t\t\t\t\t\tthis._status = PermissionStatus.EXPIRED_SUBSCRIPTION;\r\n\t\t\t\t\t\t}\r\n\t\t\t\t\t\tcontinue;\r\n\t\t\t\t\t}\r\n\r\n\t\t\t\t\tif (this.isPlanActive(plan) && this.isSupportedPlan(plan) && permissionConfig) {\r\n\t\t\t\t\t\thasAnySubscription = true;\r\n\t\t\t\t\t}\r\n\r\n\t\t\t\t\tif (permissionConfig?.permissions) {\r\n\t\t\t\t\t\tpermissionConfig.permissions.forEach((permission) => {\r\n\t\t\t\t\t\t\tif (!permissions.includes(permission)) {\r\n\t\t\t\t\t\t\t\tpermissions.push(permission);\r\n\t\t\t\t\t\t\t}\r\n\t\t\t\t\t\t});\r\n\t\t\t\t\t}\r\n\r\n\t\t\t\t\tif (permissionConfig?.userConfiguration) {\r\n\t\t\t\t\t\tthis.updateUserConfiguration(permissionConfig.userConfiguration);\r\n\t\t\t\t\t}\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t\tif (!hasAnySubscription) {\r\n\t\t\t\tthis._status = PermissionStatus.NO_SUBSCRIPTION;\r\n\t\t\t}\r\n\t\t} else if (subscriptionConfig.ignoreSubscriptions && this.isDebug) {\r\n\t\t\tconsole.warn(\r\n\t\t\t\t'You are ignoring subscriptions, which means that your permissions might not work as expected. This option might be set in config.local.debug.json for easier testing.'\r\n\t\t\t);\r\n\t\t}\r\n\r\n\t\t// if has no permissions, use default permissions\r\n\t\tif (permissions.length === 0) {\r\n\t\t\tconst defaultPlanName = this.findDefaultPlanName(subscriptionConfig, isAuthenticated);\r\n\t\t\tconst permissionConfig = this.findPermissionConfig(allPermissions, defaultPlanName);\r\n\r\n\t\t\tpermissions = permissionConfig?.permissions ?? [];\r\n\t\t\tthis.updateUserConfiguration(permissionConfig?.userConfiguration as UserConfiguration);\r\n\t\t\tif (!subscriptionConfig.ignoreSubscriptions) {\r\n\t\t\t\tthis._hasOnlyUnauthorizedPermissions = true;\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\treturn permissions;\r\n\t}\r\n\r\n\tprivate checkRolePermissions(): Permission[] | undefined {\r\n\t\tconst subscriptionConfig = this.subscriptionConfig;\r\n\t\tconst token = this.token;\r\n\t\tlet permissions: Permission[] = [];\r\n\r\n\t\tconst roles = token?.realm_access?.roles;\r\n\r\n\t\t// if no roles for user or no permissions defined for roles, return empty array.\r\n\t\tif (!roles?.length || !subscriptionConfig.roles?.length) {\r\n\t\t\treturn [];\r\n\t\t}\r\n\r\n\t\tif (this.isSupport()) {\r\n\t\t\treturn this.findPermissionConfig(subscriptionConfig.roles, this.SUPPORT_ROLE_KEY)?.permissions;\r\n\t\t}\r\n\r\n\t\troles.forEach((role) => {\r\n\t\t\tconst rolePermissionConfig = this.findPermissionConfig(subscriptionConfig.roles, role);\r\n\t\t\tif (rolePermissionConfig) {\r\n\t\t\t\tpermissions = permissions.concat(rolePermissionConfig.permissions as Permission[]);\r\n\t\t\t\tthis.updateUserConfiguration(rolePermissionConfig.userConfiguration);\r\n\t\t\t}\r\n\t\t});\r\n\r\n\t\treturn permissions;\r\n\t}\r\n\r\n\t/**\r\n\t * Updates current user configuration object with values that gives more permissions (bigger limits) to the user.\r\n\t * @param userConfiguration User configuration object to be compared with current userconfig object\r\n\t */\r\n\tprivate updateUserConfiguration(userConfiguration: UserConfiguration) {\r\n\t\tif (!userConfiguration) {\r\n\t\t\treturn;\r\n\t\t}\r\n\r\n\t\tthis.userConfigurationService.updateUserConfiguration(this.userConfiguration, userConfiguration);\r\n\t}\r\n\r\n\tprivate shouldShowMessage() {\r\n\t\tconst localStorageValue = localStorage.getItem(this.statusLocalStorageKey);\r\n\t\tif (this.status === PermissionStatus.NO_SUBSCRIPTION) {\r\n\t\t\tthis.showSubscriptionMessage = localStorageValue ? false : true;\r\n\t\t} else if (this.status === PermissionStatus.EXPIRED_SUBSCRIPTION) {\r\n\t\t\tconst confirmed = JSON.parse(localStorageValue as string);\r\n\t\t\tif (!confirmed) {\r\n\t\t\t\tthis._expiredNotConfirmedSubscriptions = this.expiredSubscriptions;\r\n\t\t\t\tthis.showSubscriptionMessage = true;\r\n\t\t\t\treturn;\r\n\t\t\t}\r\n\t\t\tconst notConfirmedExpired = [];\r\n\t\t\tfor (const subscription of this.expiredSubscriptions) {\r\n\t\t\t\tif (confirmed[subscription.PlanId] && new Date() > new Date(subscription.EndDate as string)) {\r\n\t\t\t\t\tcontinue;\r\n\t\t\t\t}\r\n\t\t\t\tnotConfirmedExpired.push(subscription);\r\n\t\t\t\tthis.showSubscriptionMessage = true;\r\n\t\t\t}\r\n\t\t\tthis._expiredNotConfirmedSubscriptions = notConfirmedExpired;\r\n\t\t}\r\n\t}\r\n\r\n\tprivate isPlanActive(plan: AuthSubscription) {\r\n\t\tif (!plan.EndDate || new Date() < new Date(plan.EndDate)) {\r\n\t\t\treturn true;\r\n\t\t}\r\n\r\n\t\treturn false;\r\n\t}\r\n\r\n\tprivate isSupportedPlan(plan: AuthSubscription) {\r\n\t\tconst planLabels = Object.values(this.config.getConfiguration().plans) as string[];\r\n\r\n\t\tif (planLabels.includes(plan.Label as string)) {\r\n\t\t\treturn true;\r\n\t\t}\r\n\r\n\t\treturn false;\r\n\t}\r\n\r\n\t/**\r\n\t * Finds permission config in array based on given key\r\n\t * @param permissions permission array where to find config from\r\n\t * @param key object identification key\r\n\t * @returns permission config\r\n\t */\r\n\tprivate findPermissionConfig(\r\n\t\tpermissions: PermissionsConfig<Permission, UserConfiguration>[],\r\n\t\tkey: string\r\n\t): PermissionsConfig<Permission, UserConfiguration> | null {\r\n\t\tfor (const permission of permissions) {\r\n\t\t\tif (permission.keys.includes(key)) {\r\n\t\t\t\treturn permission;\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\treturn null;\r\n\t}\r\n\r\n\t// for storing user specific info in localstorage;\r\n\tprivate hashString(username: string) {\r\n\t\tlet hash = 0;\r\n\t\tif (username.length > 0) {\r\n\t\t\tfor (let i = 0; i < username.length; i++) {\r\n\t\t\t\tconst char = username.charCodeAt(i);\r\n\t\t\t\thash = (hash << 5) - hash + char;\r\n\t\t\t\thash = hash & hash; // Convert to 32bit integer\r\n\t\t\t}\r\n\t\t}\r\n\t\treturn hash.toString();\r\n\t}\r\n\r\n\tprivate findDefaultPlanName(config: DefaultSubscriptionConfig, isAuthenticated: boolean): string {\r\n\t\tlet defaultPlanName;\r\n\t\tif (isAuthenticated) {\r\n\t\t\tif (this.status === PermissionStatus.EXPIRED_SUBSCRIPTION) {\r\n\t\t\t\tdefaultPlanName = config.expiredPermissionsPlanName || config.authorizedDefaultPermissionPlan;\r\n\t\t\t} else {\r\n\t\t\t\tdefaultPlanName = config.authorizedDefaultPermissionPlan;\r\n\t\t\t}\r\n\t\t}\r\n\t\treturn defaultPlanName || (config.unauthorizedDefaultPermissionPlan as string);\r\n\t}\r\n\r\n\tprivate setPermissions(permissions: Permission[]) {\r\n\t\tthis._permissions = permissions;\r\n\t\tthis._onPermissionRefresh.next(this._permissions);\r\n\t}\r\n\r\n\tprivate isSupport(): boolean {\r\n\t\treturn this.token?.realm_access?.roles?.includes(this.SUPPORT_ROLE_KEY);\r\n\t}\r\n}\r\n","import { inject } from '@angular/core';\r\nimport { KeycloakEventTypeLegacy, KeycloakService } from 'keycloak-angular';\r\nimport { UserAccessConfigService } from '../user-access-config';\r\nimport { PermissionsService } from '../permissions';\r\nimport { AuthService } from '../auth';\r\n\r\nexport async function KEYCLOAK_INITIALIZER() {\r\n\tconst keycloak = inject(KeycloakService);\r\n\tconst config = inject(UserAccessConfigService);\r\n\tconst permissions = inject(PermissionsService);\r\n\tconst auth = inject(AuthService);\r\n\r\n\tawait keycloak\r\n\t\t.init({\r\n\t\t\tconfig: config.getConfiguration().keycloak,\r\n\t\t\tenableBearerInterceptor: false,\r\n\t\t\tloadUserProfileAtStartUp: true,\r\n\t\t\tinitOptions: {\r\n\t\t\t\tonLoad: 'check-sso',\r\n\t\t\t\tsilentCheckSsoRedirectUri: window.location.origin + '/assets/auth/silent-check-sso.html',\r\n\t\t\t\tredirectUri: window.location.origin,\r\n\t\t\t},\r\n\t\t\tbearerExcludedUrls: config.getConfiguration().keycloak.bearerExcludedUrls,\r\n\t\t})\r\n\t\t.catch((error) => {\r\n\t\t\tconsole.error(error);\r\n\t\t\tpermissions.configurePermissions();\r\n\t\t\treturn;\r\n\t\t});\r\n\r\n\tauth.enableAuth();\r\n\tawait auth.checkLogin();\r\n\t\r\n\tpermissions.configurePermissions();\r\n\r\n\tkeycloak.keycloakEvents$.subscribe((ev) => {\r\n\t\tif (ev.type == KeycloakEventTypeLegacy.OnTokenExpired) {\r\n\t\t\tconst minValidity = 5;\r\n\t\t\tif (keycloak.isTokenExpired(minValidity)) {\r\n\t\t\t\tkeycloak.updateToken(minValidity).then(\r\n\t\t\t\t\t() => {\r\n\t\t\t\t\t\tauth.refreshKeycloakToken();\r\n\t\t\t\t\t\tpermissions.configurePermissions();\r\n\t\t\t\t\t},\r\n\t\t\t\t\t(err) => {\r\n\t\t\t\t\t\tconsole.error('Token refresh failed', err);\r\n\t\t\t\t\t\tkeycloak.login();\r\n\t\t\t\t\t}\r\n\t\t\t\t);\r\n\t\t\t}\r\n\t\t}\r\n\t});\r\n}\r\n","import { KeycloakBearerInterceptor, KeycloakService } from 'keycloak-angular';\r\nimport { KEYCLOAK_INITIALIZER } from './keycloak.initializer';\r\nimport { HTTP_INTERCEPTORS } from '@angular/common/http';\r\n\r\nexport async function KEYCLOAK_PROVIDER() {\r\n\treturn [\r\n\t\tKeycloakService,\r\n\t\t{\r\n\t\t\tprovide: HTTP_INTERCEPTORS,\r\n\t\t\tuseClass: KeycloakBearerInterceptor,\r\n\t\t\tdeps: [KeycloakService],\r\n\t\t\tmulti: true,\r\n\t\t},\r\n\t\tawait KEYCLOAK_INITIALIZER(),\r\n\t];\r\n}\r\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":["i1.UserAccessConfigService","i3.AuthService"],"mappings":"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"}
|
package/index.d.ts
ADDED
|
@@ -0,0 +1,233 @@
|
|
|
1
|
+
import * as i0 from '@angular/core';
|
|
2
|
+
import { InjectionToken } from '@angular/core';
|
|
3
|
+
import * as _angular_common_http from '@angular/common/http';
|
|
4
|
+
import { KeycloakService, KeycloakBearerInterceptor } from 'keycloak-angular';
|
|
5
|
+
import { BehaviorSubject, Observable } from 'rxjs';
|
|
6
|
+
import { AuthSubscription } from '@tilde-nlp/ngx-common';
|
|
7
|
+
import { CanActivate, Router, ActivatedRouteSnapshot, RouterStateSnapshot } from '@angular/router';
|
|
8
|
+
|
|
9
|
+
interface UserAccessConfig {
|
|
10
|
+
keycloak: {
|
|
11
|
+
url: string;
|
|
12
|
+
realm: string;
|
|
13
|
+
clientId: string;
|
|
14
|
+
bearerExcludedUrls: string[];
|
|
15
|
+
};
|
|
16
|
+
plans: string[];
|
|
17
|
+
subscriptions: any;
|
|
18
|
+
/** Use this to keep useful console logs in dev environment */
|
|
19
|
+
isDebug?: boolean;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
declare class UserAccessConfigService {
|
|
23
|
+
#private;
|
|
24
|
+
getConfiguration(): UserAccessConfig;
|
|
25
|
+
static ɵfac: i0.ɵɵFactoryDeclaration<UserAccessConfigService, never>;
|
|
26
|
+
static ɵprov: i0.ɵɵInjectableDeclaration<UserAccessConfigService>;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
declare const USER_ACCESS_CONFIG_TOKEN: InjectionToken<UserAccessConfig>;
|
|
30
|
+
|
|
31
|
+
declare function KEYCLOAK_PROVIDER(): Promise<(void | typeof KeycloakService | {
|
|
32
|
+
provide: i0.InjectionToken<readonly _angular_common_http.HttpInterceptor[]>;
|
|
33
|
+
useClass: typeof KeycloakBearerInterceptor;
|
|
34
|
+
deps: (typeof KeycloakService)[];
|
|
35
|
+
multi: boolean;
|
|
36
|
+
})[]>;
|
|
37
|
+
|
|
38
|
+
interface RolesCollection<Type> {
|
|
39
|
+
roles: Type[];
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
interface ResourceAccess {
|
|
43
|
+
account: RolesCollection<string>;
|
|
44
|
+
scope: string;
|
|
45
|
+
session_state: string;
|
|
46
|
+
sub: string;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
interface AuthTokenParsed {
|
|
50
|
+
MT: RolesCollection<string>;
|
|
51
|
+
acr: string;
|
|
52
|
+
at_hash: string;
|
|
53
|
+
aud: string[];
|
|
54
|
+
authTime: number;
|
|
55
|
+
azp: string;
|
|
56
|
+
email: string;
|
|
57
|
+
email_verified: boolean;
|
|
58
|
+
exp: number;
|
|
59
|
+
family_name: string;
|
|
60
|
+
given_name: string;
|
|
61
|
+
iat: number;
|
|
62
|
+
iss: string;
|
|
63
|
+
jti: string;
|
|
64
|
+
membership: string[];
|
|
65
|
+
name: string;
|
|
66
|
+
nonce: string;
|
|
67
|
+
plan: string;
|
|
68
|
+
preferred_username: string;
|
|
69
|
+
session_state: string;
|
|
70
|
+
sub: string;
|
|
71
|
+
subscription?: AuthSubscription[];
|
|
72
|
+
typ: string;
|
|
73
|
+
realm_access: RolesCollection<string>;
|
|
74
|
+
resource_access: ResourceAccess;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
declare class AuthService {
|
|
78
|
+
private readonly config;
|
|
79
|
+
private readonly keyCloak;
|
|
80
|
+
authEnabled: boolean;
|
|
81
|
+
username: BehaviorSubject<string>;
|
|
82
|
+
private _isAuthenticated;
|
|
83
|
+
private _parsedKeycloakToken;
|
|
84
|
+
private _token;
|
|
85
|
+
get token(): string | undefined;
|
|
86
|
+
get parsedKeyCloakToken(): AuthTokenParsed;
|
|
87
|
+
constructor(config: UserAccessConfigService, keyCloak: KeycloakService);
|
|
88
|
+
userSubscriptionPlan(): AuthSubscription | undefined;
|
|
89
|
+
isAuthenticated(): boolean;
|
|
90
|
+
getUserName(): string;
|
|
91
|
+
getUserDisplayName(): string;
|
|
92
|
+
getToken(): Promise<string>;
|
|
93
|
+
logout(): void;
|
|
94
|
+
checkLogin(): Promise<void>;
|
|
95
|
+
login(): void;
|
|
96
|
+
enableAuth(): void;
|
|
97
|
+
refreshKeycloakToken(): void;
|
|
98
|
+
userIsGroupMember(): boolean;
|
|
99
|
+
static ɵfac: i0.ɵɵFactoryDeclaration<AuthService, never>;
|
|
100
|
+
static ɵprov: i0.ɵɵInjectableDeclaration<AuthService>;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
declare class AuthGuard implements CanActivate {
|
|
104
|
+
private readonly router;
|
|
105
|
+
private readonly keycloak;
|
|
106
|
+
private readonly auth;
|
|
107
|
+
constructor(router: Router, keycloak: KeycloakService, auth: AuthService);
|
|
108
|
+
canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Promise<boolean>;
|
|
109
|
+
static ɵfac: i0.ɵɵFactoryDeclaration<AuthGuard, never>;
|
|
110
|
+
static ɵprov: i0.ɵɵInjectableDeclaration<AuthGuard>;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
declare enum AuthGuardParamKeys {
|
|
114
|
+
REDIRECT_ROUTE = "redirectRoute"
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
declare enum PermissionStatus {
|
|
118
|
+
OK = "OK",
|
|
119
|
+
EXPIRED_SUBSCRIPTION = "EXPIRED_SUBSCRIPTION",
|
|
120
|
+
NO_SUBSCRIPTION = "NO_SUBSCRIPTION"
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
interface UserCustomConfigurationService<T> {
|
|
124
|
+
updateUserConfiguration: (currentConfiguration: T, newConfiguration: T) => T;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
declare class PermissionsService<Permission, UserConfiguration> {
|
|
128
|
+
private readonly config;
|
|
129
|
+
private readonly auth;
|
|
130
|
+
private readonly userConfigurationService;
|
|
131
|
+
userConfiguration: UserConfiguration;
|
|
132
|
+
showSubscriptionMessage: boolean;
|
|
133
|
+
private _hasOnlyUnauthorizedPermissions;
|
|
134
|
+
private _status;
|
|
135
|
+
private _permissions;
|
|
136
|
+
private _expiredSubscriptions;
|
|
137
|
+
private _expiredNotConfirmedSubscriptions;
|
|
138
|
+
private _onPermissionRefresh;
|
|
139
|
+
private isDebug;
|
|
140
|
+
private get statusLocalStorageKey();
|
|
141
|
+
private get subscriptionConfig();
|
|
142
|
+
private get token();
|
|
143
|
+
get status(): PermissionStatus;
|
|
144
|
+
get permissions(): Permission[];
|
|
145
|
+
get expiredSubscriptions(): AuthSubscription[];
|
|
146
|
+
get expiredNotConfirmedSubscriptions(): AuthSubscription[];
|
|
147
|
+
get hasOnlyUnauthorizedPermissions(): boolean;
|
|
148
|
+
get onPermissionRefresh(): Observable<Permission[]>;
|
|
149
|
+
private readonly SUPPORT_ROLE_KEY;
|
|
150
|
+
constructor(config: UserAccessConfigService, auth: AuthService, userConfigurationService: UserCustomConfigurationService<UserConfiguration>);
|
|
151
|
+
/**
|
|
152
|
+
* Checks wether user has necessary permission/permissions. By default, if user has at least one of given permissions, method returns true. To check all permissions, set needsAll to true.
|
|
153
|
+
* @param permissions Permissions to be checked.
|
|
154
|
+
* @param needsAll If user should have all of given permissions
|
|
155
|
+
* @returns
|
|
156
|
+
*/
|
|
157
|
+
hasPermission(permissions: Permission[], needsAll?: boolean): boolean;
|
|
158
|
+
configurePermissions(): void;
|
|
159
|
+
confirmMessage(): void;
|
|
160
|
+
userSpecificLocalstorageKey(key: string): string;
|
|
161
|
+
private refreshAllPermissionsForUser;
|
|
162
|
+
private checkSubscriptionPermissions;
|
|
163
|
+
private checkRolePermissions;
|
|
164
|
+
/**
|
|
165
|
+
* Updates current user configuration object with values that gives more permissions (bigger limits) to the user.
|
|
166
|
+
* @param userConfiguration User configuration object to be compared with current userconfig object
|
|
167
|
+
*/
|
|
168
|
+
private updateUserConfiguration;
|
|
169
|
+
private shouldShowMessage;
|
|
170
|
+
private isPlanActive;
|
|
171
|
+
private isSupportedPlan;
|
|
172
|
+
/**
|
|
173
|
+
* Finds permission config in array based on given key
|
|
174
|
+
* @param permissions permission array where to find config from
|
|
175
|
+
* @param key object identification key
|
|
176
|
+
* @returns permission config
|
|
177
|
+
*/
|
|
178
|
+
private findPermissionConfig;
|
|
179
|
+
private hashString;
|
|
180
|
+
private findDefaultPlanName;
|
|
181
|
+
private setPermissions;
|
|
182
|
+
private isSupport;
|
|
183
|
+
static ɵfac: i0.ɵɵFactoryDeclaration<PermissionsService<any, any>, never>;
|
|
184
|
+
static ɵprov: i0.ɵɵInjectableDeclaration<PermissionsService<any, any>>;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
declare enum DefaultPermissions {
|
|
188
|
+
ALL = "ALL"
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
interface PermissionsConfig<Permission, UserConfiguration> {
|
|
192
|
+
/** Depends on where this is used (either for plans or roles), keys are object identification string names. */
|
|
193
|
+
keys: string[];
|
|
194
|
+
permissions: Permission[];
|
|
195
|
+
userConfiguration: UserConfiguration;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
interface DefaultSubscriptionConfig {
|
|
199
|
+
/**
|
|
200
|
+
* Plan name that is used if user is not authenticated or is authenticated but no default plan for that case is provided.
|
|
201
|
+
* If this is not defined - it searches for "unauthorized".
|
|
202
|
+
*/
|
|
203
|
+
unauthorizedDefaultPermissionPlan?: string;
|
|
204
|
+
/**
|
|
205
|
+
* Plan name that is used only if user is authenticated. It is used when no active subscriptions are found or if "ignoreSubscriptions" is set to true.
|
|
206
|
+
* */
|
|
207
|
+
authorizedDefaultPermissionPlan?: string;
|
|
208
|
+
/**
|
|
209
|
+
* Even if user is authenticated, when this is set to true, all subscriptions are ignored and default plans are used.
|
|
210
|
+
*/
|
|
211
|
+
ignoreSubscriptions?: boolean;
|
|
212
|
+
/**
|
|
213
|
+
* If any subscriptions are expired, use speicific plan.
|
|
214
|
+
*/
|
|
215
|
+
expiredPermissionsPlanName?: string;
|
|
216
|
+
/**
|
|
217
|
+
* If user has no subscriptions but has MT roles.
|
|
218
|
+
*/
|
|
219
|
+
supportedRolesPermissionPlanName?: string;
|
|
220
|
+
/**
|
|
221
|
+
* Permission configs for subscription plans.
|
|
222
|
+
*/
|
|
223
|
+
plans: PermissionsConfig<unknown, unknown>[];
|
|
224
|
+
/**
|
|
225
|
+
* Permission configs for roles.
|
|
226
|
+
*/
|
|
227
|
+
roles?: PermissionsConfig<unknown, unknown>[];
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
declare const USER_CUSTOM_CONFIGURATION_SERVICE_TOKEN: InjectionToken<unknown>;
|
|
231
|
+
|
|
232
|
+
export { AuthGuard, AuthGuardParamKeys, AuthService, DefaultPermissions, KEYCLOAK_PROVIDER, PermissionStatus, PermissionsService, USER_ACCESS_CONFIG_TOKEN, USER_CUSTOM_CONFIGURATION_SERVICE_TOKEN, UserAccessConfigService };
|
|
233
|
+
export type { AuthTokenParsed, DefaultSubscriptionConfig, PermissionsConfig, UserAccessConfig, UserCustomConfigurationService };
|
package/package.json
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@tilde-nlp/ngx-user-access",
|
|
3
|
+
"version": "8.0.1",
|
|
4
|
+
"peerDependencies": {
|
|
5
|
+
"@angular/common": "^20.2.0",
|
|
6
|
+
"@angular/core": "^20.2.0",
|
|
7
|
+
"@tilde-nlp/ngx-common": "^8.0.0",
|
|
8
|
+
"keycloak-angular": "^20.0.0",
|
|
9
|
+
"keycloak-js": "^26.2.0"
|
|
10
|
+
},
|
|
11
|
+
"dependencies": {
|
|
12
|
+
"tslib": "^2.3.0"
|
|
13
|
+
},
|
|
14
|
+
"author": "Tilde",
|
|
15
|
+
"license": "Apache-2.0",
|
|
16
|
+
"module": "fesm2022/tilde-nlp-ngx-user-access.mjs",
|
|
17
|
+
"typings": "index.d.ts",
|
|
18
|
+
"exports": {
|
|
19
|
+
"./package.json": {
|
|
20
|
+
"default": "./package.json"
|
|
21
|
+
},
|
|
22
|
+
".": {
|
|
23
|
+
"types": "./index.d.ts",
|
|
24
|
+
"default": "./fesm2022/tilde-nlp-ngx-user-access.mjs"
|
|
25
|
+
}
|
|
26
|
+
},
|
|
27
|
+
"sideEffects": false
|
|
28
|
+
}
|