@actdim/dynstruct 0.9.0 → 0.9.7
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 +17 -1
- package/dist/appDomain/navigation.es.js.map +1 -1
- package/dist/appDomain/security/securityContracts.es.js.map +1 -1
- package/dist/appDomain/security/securityProvider.es.js +47 -56
- package/dist/appDomain/security/securityProvider.es.js.map +1 -1
- package/dist/componentModel/componentModel.es.js +130 -145
- package/dist/componentModel/componentModel.es.js.map +1 -1
- package/dist/componentModel/scope.es.js +52 -58
- package/dist/componentModel/scope.es.js.map +1 -1
- package/dist/net/apiError.es.js +22 -26
- package/dist/net/apiError.es.js.map +1 -1
- package/dist/net/client.es.js +65 -69
- package/dist/net/client.es.js.map +1 -1
- package/dist/net/request.es.js +3 -3
- package/dist/net/request.es.js.map +1 -1
- package/dist/reactHooks.es.js.map +1 -1
- package/package.json +48 -28
package/README.md
CHANGED
|
@@ -1,2 +1,18 @@
|
|
|
1
1
|
# @actdim/dynstruct
|
|
2
|
-
Build scalable applications with dynamic structured components, explicit wiring, and decoupled message flow. Keep architecture clean and modular.
|
|
2
|
+
Build scalable applications with dynamic structured components, explicit wiring, and decoupled message flow. Keep architecture clean and modular.
|
|
3
|
+
|
|
4
|
+
# use dedupe:
|
|
5
|
+
'rxjs',
|
|
6
|
+
'uuid',
|
|
7
|
+
'http-status',
|
|
8
|
+
'jwt-decode',
|
|
9
|
+
'mobx',
|
|
10
|
+
'mobx-react-lite',
|
|
11
|
+
'mobx-utils',
|
|
12
|
+
'path-to-regexp',
|
|
13
|
+
'react',
|
|
14
|
+
'react-dom',
|
|
15
|
+
'react-router',
|
|
16
|
+
'react-router-dom',
|
|
17
|
+
'@actdim/utico',
|
|
18
|
+
'@actdim/msgmesh'
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"navigation.es.js","sources":["../../src/appDomain/navigation.ts"],"sourcesContent":["import { compile, match } from \"path-to-regexp\";\r\nimport { NavRoute, NavRouteParams } from \"./appContracts\";\r\nimport { ReactNode } from \"react\";\r\n// createAppRoute\r\nexport function createNavigationRoute<TParams extends NavRouteParams = NavRouteParams>(config: {\r\n pattern: string;\r\n element: ReactNode;\r\n defaultParams?: TParams;\r\n}) {\r\n const toPath = compile(config.pattern);\r\n const matcher = match(config.pattern);\r\n return {\r\n path: (params?: object) => {\r\n if (!params) {\r\n return config.pattern;\r\n }\r\n return toPath(params);\r\n },\r\n match: (path: string) => {\r\n const matchResult = matcher(path);\r\n if (matchResult === false) {\r\n return undefined;\r\n }\r\n return matchResult.params;\r\n },\r\n element: config.element,\r\n defaultParams: config.defaultParams\r\n } as NavRoute<TParams>;\r\n}\r\n"],"names":["createNavigationRoute","config","toPath","compile","matcher","match","params","path","matchResult"],"mappings":";AAIO,SAASA,EAAuEC,GAIpF;
|
|
1
|
+
{"version":3,"file":"navigation.es.js","sources":["../../src/appDomain/navigation.ts"],"sourcesContent":["import { compile, match } from \"path-to-regexp\";\r\nimport { NavRoute, NavRouteParams } from \"./appContracts\";\r\nimport { ReactNode } from \"react\";\r\n// createAppRoute\r\nexport function createNavigationRoute<TParams extends NavRouteParams = NavRouteParams>(config: {\r\n pattern: string;\r\n element: ReactNode;\r\n defaultParams?: TParams;\r\n}) {\r\n const toPath = compile(config.pattern);\r\n const matcher = match(config.pattern);\r\n return {\r\n path: (params?: object) => {\r\n if (!params) {\r\n return config.pattern;\r\n }\r\n return toPath(params);\r\n },\r\n match: (path: string) => {\r\n const matchResult = matcher(path);\r\n if (matchResult === false) {\r\n return undefined;\r\n }\r\n return matchResult.params;\r\n },\r\n element: config.element,\r\n defaultParams: config.defaultParams\r\n } as NavRoute<TParams>;\r\n}\r\n"],"names":["createNavigationRoute","config","toPath","compile","matcher","match","params","path","matchResult"],"mappings":";AAIO,SAASA,EAAuEC,GAIpF;AACC,QAAMC,IAASC,EAAQF,EAAO,OAAO,GAC/BG,IAAUC,EAAMJ,EAAO,OAAO;AACpC,SAAO;AAAA,IACH,MAAM,CAACK,MACEA,IAGEJ,EAAOI,CAAM,IAFTL,EAAO;AAAA,IAItB,OAAO,CAACM,MAAiB;AACrB,YAAMC,IAAcJ,EAAQG,CAAI;AAChC,UAAIC,MAAgB;AAGpB,eAAOA,EAAY;AAAA,IACvB;AAAA,IACA,SAASP,EAAO;AAAA,IAChB,eAAeA,EAAO;AAAA,EAAA;AAE9B;"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"securityContracts.es.js","sources":["../../../src/appDomain/security/securityContracts.ts"],"sourcesContent":["import { MsgBus, MsgBusStruct, MsgBusStructBase } from \"@actdim/msgmesh/msgBusCore\";\r\nimport { RequireExtends, Skip } from \"@actdim/utico/typeCore\";\r\n// abstract\r\nexport enum AccessLevel {\r\n None = 0,\r\n Inherited = 1 << 0, // Unset\r\n NoRender = 1 << 1,\r\n Hidden = 1 << 2,\r\n Disabled = 1 << 3,\r\n ReadOnly = 1 << 4, // View\r\n Full = ~(~0 << 5) // Unrestricted\r\n}\r\n\r\n// HTTP RFC 7231\r\nexport type UserCredentials = {\r\n username: string;\r\n password: string;\r\n};\r\n\r\nexport type IAccessDescriptor = {\r\n // deny/grant reason(s)\r\n [P in AccessLevel]?: string;\r\n};\r\n\r\nexport const $SIGNIN = \"APP-SECURITY-AUTH-SIGNIN\";\r\nexport const $REQUEST_SIGNIN = \"APP-SECURITY-REQUEST-AUTH-SIGNIN\";\r\nexport const $SIGNOUT = \"APP-SECURITY-AUTH-SIGNOUT\";\r\nexport const $REQUEST_SIGNOUT = \"APP-SECURITY-REQUEST-AUTH-SIGNOUT\";\r\nexport const $REFRESH = \"APP-SECURITY-AUTH-REFRESH\";\r\nexport const $REQUEST_AUTH = \"APP-SECURITY-REQUEST-AUTH\";\r\nexport const $GET_ACL = \"APP-SECURITY-GET-ACL\";\r\nexport const $GET_CONTEXT = \"APP-SECURITY-GET-CONTEXT\";\r\nexport const $GET_CONFIG = \"APP-SECURITY-GET-CONFIG\";\r\n\r\nexport type SecurityTokens = {\r\n accessToken?: string;\r\n refreshToken?: string;\r\n};\r\n\r\n// Base(App)Security(Msg)BusStruct\r\nexport type BaseSecurityBusStruct = RequireExtends<\r\n {\r\n [$SIGNIN]: {\r\n in: UserCredentials;\r\n out: SecurityContext;\r\n };\r\n [$REQUEST_SIGNIN]: {\r\n in: {\r\n callbackUrl: string;\r\n };\r\n // out: void;\r\n };\r\n [$SIGNOUT]: {\r\n in: void;\r\n out: void;\r\n };\r\n [$REQUEST_SIGNOUT]: {\r\n in: {\r\n callbackUrl: string;\r\n };\r\n // out: void;\r\n };\r\n [$REFRESH]: {\r\n in: Skip<SecurityTokens, \"accessToken\">;\r\n out: SecurityContext;\r\n };\r\n [$REQUEST_AUTH]: {\r\n in: void;\r\n out: void;\r\n };\r\n [$GET_ACL]: {\r\n in: ISecurable;\r\n out: IAccessDescriptor;\r\n };\r\n [$GET_CONTEXT]: {\r\n in: void;\r\n out: SecurityContext;\r\n };\r\n [$GET_CONFIG]: {\r\n in: void;\r\n out: BaseSecurityDomainConfig;\r\n }; \r\n } & MsgBusStructBase,\r\n MsgBusStruct\r\n>;\r\n\r\nexport type ISecurable = {\r\n id: string;\r\n};\r\n\r\n// AppSecurityContext\r\nexport type SecurityContext<TUserInfo = any> = {\r\n // isAuthenticated: boolean;\r\n // isExpired: boolean;\r\n accessToken: string;\r\n refreshToken: string;\r\n // sessionToken: string;\r\n // sid: string; // SID is an acronym for \"security identity\", grant recipient\r\n userInfo: TUserInfo;\r\n // authority: string;\r\n authProvider: string; // authSource\r\n domain: string; // protection space, scope of protection\r\n // authExpiresAt\r\n tokenExpiresAt: string;\r\n // acl: any;\r\n};\r\n\r\n// Base(App)Security(Domain)Config\r\nexport type BaseSecurityDomainConfig = {\r\n id: string;\r\n name?: string;\r\n authType: string;\r\n // endpoints\r\n routes: {\r\n authSignIn: string;\r\n authSignOut: string;\r\n authRefresh: string;\r\n // RFC 8414 (OIDC) — \"OAuth 2.0 Authorization Server Metadata\"\r\n // https://datatracker.ietf.org/doc/html/rfc8414 \r\n authService: string;\r\n };\r\n};\r\n"],"names":["AccessLevel","$SIGNIN","$REQUEST_SIGNIN","$SIGNOUT","$REQUEST_SIGNOUT","$REFRESH","$REQUEST_AUTH","$GET_ACL","$GET_CONTEXT","$GET_CONFIG"],"mappings":"
|
|
1
|
+
{"version":3,"file":"securityContracts.es.js","sources":["../../../src/appDomain/security/securityContracts.ts"],"sourcesContent":["import { MsgBus, MsgBusStruct, MsgBusStructBase } from \"@actdim/msgmesh/msgBusCore\";\r\nimport { RequireExtends, Skip } from \"@actdim/utico/typeCore\";\r\n// abstract\r\nexport enum AccessLevel {\r\n None = 0,\r\n Inherited = 1 << 0, // Unset\r\n NoRender = 1 << 1,\r\n Hidden = 1 << 2,\r\n Disabled = 1 << 3,\r\n ReadOnly = 1 << 4, // View\r\n Full = ~(~0 << 5) // Unrestricted\r\n}\r\n\r\n// HTTP RFC 7231\r\nexport type UserCredentials = {\r\n username: string;\r\n password: string;\r\n};\r\n\r\nexport type IAccessDescriptor = {\r\n // deny/grant reason(s)\r\n [P in AccessLevel]?: string;\r\n};\r\n\r\nexport const $SIGNIN = \"APP-SECURITY-AUTH-SIGNIN\";\r\nexport const $REQUEST_SIGNIN = \"APP-SECURITY-REQUEST-AUTH-SIGNIN\";\r\nexport const $SIGNOUT = \"APP-SECURITY-AUTH-SIGNOUT\";\r\nexport const $REQUEST_SIGNOUT = \"APP-SECURITY-REQUEST-AUTH-SIGNOUT\";\r\nexport const $REFRESH = \"APP-SECURITY-AUTH-REFRESH\";\r\nexport const $REQUEST_AUTH = \"APP-SECURITY-REQUEST-AUTH\";\r\nexport const $GET_ACL = \"APP-SECURITY-GET-ACL\";\r\nexport const $GET_CONTEXT = \"APP-SECURITY-GET-CONTEXT\";\r\nexport const $GET_CONFIG = \"APP-SECURITY-GET-CONFIG\";\r\n\r\nexport type SecurityTokens = {\r\n accessToken?: string;\r\n refreshToken?: string;\r\n};\r\n\r\n// Base(App)Security(Msg)BusStruct\r\nexport type BaseSecurityBusStruct = RequireExtends<\r\n {\r\n [$SIGNIN]: {\r\n in: UserCredentials;\r\n out: SecurityContext;\r\n };\r\n [$REQUEST_SIGNIN]: {\r\n in: {\r\n callbackUrl: string;\r\n };\r\n // out: void;\r\n };\r\n [$SIGNOUT]: {\r\n in: void;\r\n out: void;\r\n };\r\n [$REQUEST_SIGNOUT]: {\r\n in: {\r\n callbackUrl: string;\r\n };\r\n // out: void;\r\n };\r\n [$REFRESH]: {\r\n in: Skip<SecurityTokens, \"accessToken\">;\r\n out: SecurityContext;\r\n };\r\n [$REQUEST_AUTH]: {\r\n in: void;\r\n out: void;\r\n };\r\n [$GET_ACL]: {\r\n in: ISecurable;\r\n out: IAccessDescriptor;\r\n };\r\n [$GET_CONTEXT]: {\r\n in: void;\r\n out: SecurityContext;\r\n };\r\n [$GET_CONFIG]: {\r\n in: void;\r\n out: BaseSecurityDomainConfig;\r\n }; \r\n } & MsgBusStructBase,\r\n MsgBusStruct\r\n>;\r\n\r\nexport type ISecurable = {\r\n id: string;\r\n};\r\n\r\n// AppSecurityContext\r\nexport type SecurityContext<TUserInfo = any> = {\r\n // isAuthenticated: boolean;\r\n // isExpired: boolean;\r\n accessToken: string;\r\n refreshToken: string;\r\n // sessionToken: string;\r\n // sid: string; // SID is an acronym for \"security identity\", grant recipient\r\n userInfo: TUserInfo;\r\n // authority: string;\r\n authProvider: string; // authSource\r\n domain: string; // protection space, scope of protection\r\n // authExpiresAt\r\n tokenExpiresAt: string;\r\n // acl: any;\r\n};\r\n\r\n// Base(App)Security(Domain)Config\r\nexport type BaseSecurityDomainConfig = {\r\n id: string;\r\n name?: string;\r\n authType: string;\r\n // endpoints\r\n routes: {\r\n authSignIn: string;\r\n authSignOut: string;\r\n authRefresh: string;\r\n // RFC 8414 (OIDC) — \"OAuth 2.0 Authorization Server Metadata\"\r\n // https://datatracker.ietf.org/doc/html/rfc8414 \r\n authService: string;\r\n };\r\n};\r\n"],"names":["AccessLevel","$SIGNIN","$REQUEST_SIGNIN","$SIGNOUT","$REQUEST_SIGNOUT","$REFRESH","$REQUEST_AUTH","$GET_ACL","$GET_CONTEXT","$GET_CONFIG"],"mappings":"AAGO,IAAKA,sBAAAA,OACRA,EAAAA,EAAA,OAAO,CAAA,IAAP,QACAA,EAAAA,EAAA,YAAY,CAAA,IAAZ,aACAA,EAAAA,EAAA,WAAW,CAAA,IAAX,YACAA,EAAAA,EAAA,SAAS,CAAA,IAAT,UACAA,EAAAA,EAAA,WAAW,CAAA,IAAX,YACAA,EAAAA,EAAA,WAAW,EAAA,IAAX,YACAA,EAAAA,EAAA,OAAO,EAAA,IAAP,QAPQA,IAAAA,KAAA,CAAA,CAAA;AAqBL,MAAMC,IAAU,4BACVC,IAAkB,oCAClBC,IAAW,6BACXC,IAAmB,qCACnBC,IAAW,6BACXC,IAAgB,6BAChBC,IAAW,wBACXC,IAAe,4BACfC,IAAc;"}
|
|
@@ -1,36 +1,33 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
var t = (r, s, e) => T(r, typeof s != "symbol" ? s + "" : s, e);
|
|
4
|
-
import { AccessLevel as y } from "./securityContracts.es.js";
|
|
5
|
-
import { getValuePrefixer as g } from "@actdim/utico/typeCore";
|
|
1
|
+
import { AccessLevel as h } from "./securityContracts.es.js";
|
|
2
|
+
import { getValuePrefixer as l } from "@actdim/utico/typeCore";
|
|
6
3
|
import "jwt-decode";
|
|
7
|
-
import { getResponseResult as
|
|
8
|
-
import { ApiError as
|
|
9
|
-
const
|
|
4
|
+
import { getResponseResult as c } from "../../net/request.es.js";
|
|
5
|
+
import { ApiError as r } from "../../net/apiError.es.js";
|
|
6
|
+
const u = {
|
|
10
7
|
accessToken: "ACCESS_TOKEN",
|
|
11
8
|
refreshToken: "REFRESH_TOKEN",
|
|
12
9
|
acl: "ACL",
|
|
13
10
|
userCredentials: "USER_CREDENTIALS",
|
|
14
11
|
userInfo: "USER_INFO"
|
|
15
12
|
};
|
|
16
|
-
class
|
|
13
|
+
class E {
|
|
14
|
+
// private isAuthenticated: boolean;
|
|
15
|
+
// private isExpired: boolean;
|
|
16
|
+
msgBus;
|
|
17
|
+
domainConfig;
|
|
18
|
+
storageKeys;
|
|
19
|
+
accessToken;
|
|
20
|
+
refreshToken;
|
|
21
|
+
userCredentials;
|
|
22
|
+
userInfo;
|
|
23
|
+
// private authority: string;
|
|
24
|
+
authProvider;
|
|
25
|
+
tokenExpiresAt;
|
|
26
|
+
// RBAC vs ABAC vs PBAC: https://habr.com/ru/companies/otus/articles/698080/
|
|
27
|
+
acl;
|
|
28
|
+
fetcher = window;
|
|
29
|
+
init;
|
|
17
30
|
constructor(s) {
|
|
18
|
-
// private isAuthenticated: boolean;
|
|
19
|
-
// private isExpired: boolean;
|
|
20
|
-
t(this, "msgBus");
|
|
21
|
-
t(this, "domainConfig");
|
|
22
|
-
t(this, "storageKeys");
|
|
23
|
-
t(this, "accessToken");
|
|
24
|
-
t(this, "refreshToken");
|
|
25
|
-
t(this, "userCredentials");
|
|
26
|
-
t(this, "userInfo");
|
|
27
|
-
// private authority: string;
|
|
28
|
-
t(this, "authProvider");
|
|
29
|
-
t(this, "tokenExpiresAt");
|
|
30
|
-
// RBAC vs ABAC vs PBAC: https://habr.com/ru/companies/otus/articles/698080/
|
|
31
|
-
t(this, "acl");
|
|
32
|
-
t(this, "fetcher", window);
|
|
33
|
-
t(this, "init");
|
|
34
31
|
this.msgBus = s, this.init = this.updateConfigAsync(), this.msgBus.provide({
|
|
35
32
|
channel: "APP-SECURITY-GET-CONTEXT",
|
|
36
33
|
callback: (e) => this.getContext()
|
|
@@ -51,12 +48,9 @@ class f {
|
|
|
51
48
|
callback: (e) => this.requestAuthorize()
|
|
52
49
|
}), this.msgBus.provide({
|
|
53
50
|
channel: "APP-SECURITY-GET-CONFIG",
|
|
54
|
-
callback: async (e) => {
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
channel: "APP-CONFIG-GET"
|
|
58
|
-
})).payload) == null ? void 0 : a.security;
|
|
59
|
-
}
|
|
51
|
+
callback: async (e) => (await this.msgBus.dispatchAsync({
|
|
52
|
+
channel: "APP-CONFIG-GET"
|
|
53
|
+
})).payload?.security
|
|
60
54
|
});
|
|
61
55
|
}
|
|
62
56
|
getContext() {
|
|
@@ -74,8 +68,8 @@ class f {
|
|
|
74
68
|
channel: "APP-CONFIG-GET"
|
|
75
69
|
});
|
|
76
70
|
this.domainConfig = s.payload.security;
|
|
77
|
-
const e =
|
|
78
|
-
this.storageKeys = e(
|
|
71
|
+
const e = l(this.domainConfig.id);
|
|
72
|
+
this.storageKeys = e(u);
|
|
79
73
|
}
|
|
80
74
|
async restoreDataAsync() {
|
|
81
75
|
this.accessToken = (await this.msgBus.dispatchAsync({
|
|
@@ -164,23 +158,22 @@ class f {
|
|
|
164
158
|
}), await Promise.race([s, e]);
|
|
165
159
|
}
|
|
166
160
|
async signInAsync(s) {
|
|
167
|
-
|
|
168
|
-
let e = (u = this.domainConfig.routes) == null ? void 0 : u.authSignIn;
|
|
161
|
+
let e = this.domainConfig.routes?.authSignIn;
|
|
169
162
|
e = e.replace(/[?&]$/, "");
|
|
170
|
-
const
|
|
163
|
+
const n = JSON.stringify(s), t = {
|
|
171
164
|
url: e,
|
|
172
|
-
body:
|
|
165
|
+
body: n,
|
|
173
166
|
method: "POST",
|
|
174
167
|
headers: {
|
|
175
168
|
"Content-Type": "application/json",
|
|
176
169
|
Accept: "text/plain"
|
|
177
170
|
}
|
|
178
171
|
}, i = {
|
|
179
|
-
...
|
|
172
|
+
...t,
|
|
180
173
|
status: "executing"
|
|
181
|
-
},
|
|
182
|
-
await
|
|
183
|
-
let o =
|
|
174
|
+
}, a = await this.fetcher.fetch(e, t);
|
|
175
|
+
await c(a, i), r.assert(a, i);
|
|
176
|
+
let o = a.resolved.json;
|
|
184
177
|
return this.userCredentials = s, this.accessToken = o.accessToken, this.refreshToken = o.refreshToken, this.saveDataAsync(), this.getContext();
|
|
185
178
|
}
|
|
186
179
|
async saveDataAsync() {
|
|
@@ -211,8 +204,7 @@ class f {
|
|
|
211
204
|
});
|
|
212
205
|
}
|
|
213
206
|
async signOutAsync() {
|
|
214
|
-
|
|
215
|
-
let s = (i = this.domainConfig.routes) == null ? void 0 : i.authSignOut;
|
|
207
|
+
let s = this.domainConfig.routes?.authSignOut;
|
|
216
208
|
s && (s = s.replace(/[?&]$/, ""));
|
|
217
209
|
const e = {
|
|
218
210
|
url: s,
|
|
@@ -221,22 +213,21 @@ class f {
|
|
|
221
213
|
"Content-Type": "application/json",
|
|
222
214
|
Accept: "text/plain"
|
|
223
215
|
}
|
|
224
|
-
},
|
|
216
|
+
}, n = {
|
|
225
217
|
...e,
|
|
226
218
|
status: "executing"
|
|
227
|
-
},
|
|
228
|
-
await
|
|
219
|
+
}, t = await this.fetcher.fetch(s, e);
|
|
220
|
+
await c(t, n), r.assert(t, n), this.clearSavedDataAsync();
|
|
229
221
|
}
|
|
230
222
|
async refreshAsync() {
|
|
231
|
-
|
|
232
|
-
let s = (o = this.domainConfig.routes) == null ? void 0 : o.authRefresh;
|
|
223
|
+
let s = this.domainConfig.routes?.authRefresh;
|
|
233
224
|
s && (s = s.replace(/[?&]$/, ""));
|
|
234
225
|
let e = {
|
|
235
226
|
refreshToken: this.refreshToken
|
|
236
227
|
};
|
|
237
|
-
const
|
|
228
|
+
const n = JSON.stringify(e), t = {
|
|
238
229
|
url: s,
|
|
239
|
-
body:
|
|
230
|
+
body: n,
|
|
240
231
|
method: "POST",
|
|
241
232
|
// useAuth: true,
|
|
242
233
|
headers: {
|
|
@@ -244,22 +235,22 @@ class f {
|
|
|
244
235
|
Accept: "text/plain"
|
|
245
236
|
}
|
|
246
237
|
}, i = {
|
|
247
|
-
...
|
|
238
|
+
...t,
|
|
248
239
|
status: "executing"
|
|
249
|
-
},
|
|
250
|
-
return await
|
|
240
|
+
}, a = await this.fetcher.fetch(s, t);
|
|
241
|
+
return await c(a, i), r.assert(a, i), e = a.resolved.json, this.accessToken = e.accessToken, this.refreshToken = e.refreshToken, this.saveDataAsync(), this.getContext();
|
|
251
242
|
}
|
|
252
243
|
async getAcl(s) {
|
|
253
244
|
return {
|
|
254
|
-
[
|
|
245
|
+
[h.Full]: ""
|
|
255
246
|
};
|
|
256
247
|
}
|
|
257
248
|
// authorize
|
|
258
|
-
async verifyAccess(s, e =
|
|
249
|
+
async verifyAccess(s, e = h.Full) {
|
|
259
250
|
return this.getAcl(s), !1;
|
|
260
251
|
}
|
|
261
252
|
}
|
|
262
253
|
export {
|
|
263
|
-
|
|
254
|
+
E as SecurityProvider
|
|
264
255
|
};
|
|
265
256
|
//# sourceMappingURL=securityProvider.es.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"securityProvider.es.js","sources":["../../../src/appDomain/security/securityProvider.ts"],"sourcesContent":["import {\r\n AccessLevel,\r\n IAccessDescriptor,\r\n ISecurable,\r\n BaseSecurityDomainConfig,\r\n UserCredentials,\r\n SecurityTokens,\r\n SecurityContext\r\n} from \"./securityContracts\";\r\nimport { getValuePrefixer } from \"@actdim/utico/typeCore\";\r\nimport { jwtDecode } from \"jwt-decode\";\r\nimport { getResponseResult, IRequestParams, IRequestState, IResponseState } from \"@/net/request\";\r\nimport { ApiError } from \"@/net/apiError\";\r\nimport { MsgBus } from \"@actdim/msgmesh/msgBusCore\";\r\nimport { BaseAppBusStruct } from \"@/appDomain/appContracts\";\r\n\r\nconst userNameClaim = \"http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name\";\r\n\r\n// JwtTokenPayload\r\ntype TokenPayload = {\r\n [userNameClaim]: string;\r\n};\r\n\r\n// Access denied\r\n// Insufficient privileges to perform this operation\r\nconst defaultAccessDeniedReason = \"Insufficient privileges. Contact your system Administrator.\";\r\n\r\nconst storageKeys = {\r\n accessToken: \"ACCESS_TOKEN\",\r\n refreshToken: \"REFRESH_TOKEN\",\r\n acl: \"ACL\",\r\n userCredentials: \"USER_CREDENTIALS\",\r\n userInfo: \"USER_INFO\"\r\n};\r\n\r\nfunction decodeJWTToken<T extends TokenPayload>(token: string): T {\r\n if (!token) {\r\n return null;\r\n }\r\n try {\r\n return jwtDecode<T>(this.accessToken);\r\n } catch {\r\n // something wrong with the token\r\n return null;\r\n }\r\n}\r\n\r\nexport class SecurityProvider<TUserInfo = any> {\r\n // private isAuthenticated: boolean;\r\n\r\n // private isExpired: boolean;\r\n\r\n private msgBus: MsgBus<BaseAppBusStruct>;\r\n\r\n private domainConfig: BaseSecurityDomainConfig;\r\n\r\n private storageKeys: typeof storageKeys;\r\n\r\n private accessToken: string;\r\n\r\n private refreshToken: string;\r\n\r\n private userCredentials: UserCredentials;\r\n\r\n private userInfo: TUserInfo;\r\n\r\n // private authority: string;\r\n private authProvider: string;\r\n\r\n private tokenExpiresAt: string;\r\n\r\n // RBAC vs ABAC vs PBAC: https://habr.com/ru/companies/otus/articles/698080/\r\n private acl: any;\r\n\r\n private fetcher: { fetch(url: RequestInfo, init?: RequestInit): Promise<Response> } = window;\r\n\r\n private init: Promise<any>;\r\n\r\n constructor(msgBus: MsgBus<BaseAppBusStruct>) {\r\n this.msgBus = msgBus;\r\n\r\n this.init = this.updateConfigAsync();\r\n\r\n // TODO: support custom requests\r\n\r\n this.msgBus.provide({\r\n channel: \"APP-SECURITY-GET-CONTEXT\",\r\n callback: (msg) => {\r\n return this.getContext();\r\n }\r\n });\r\n\r\n this.msgBus.provide({\r\n channel: \"APP-SECURITY-GET-ACL\",\r\n callback: (msg) => {\r\n return this.getAcl(msg.payload);\r\n }\r\n });\r\n\r\n this.msgBus.provide({\r\n channel: \"APP-SECURITY-AUTH-SIGNIN\",\r\n callback: (msg) => {\r\n return this.signInAsync(msg.payload);\r\n }\r\n });\r\n\r\n this.msgBus.provide({\r\n channel: \"APP-SECURITY-AUTH-SIGNOUT\",\r\n callback: (msg) => {\r\n return this.signOutAsync();\r\n }\r\n });\r\n\r\n this.msgBus.provide({\r\n channel: \"APP-SECURITY-AUTH-REFRESH\",\r\n callback: (msg) => {\r\n return this.refreshAsync();\r\n }\r\n });\r\n\r\n this.msgBus.provide({\r\n channel: \"APP-SECURITY-REQUEST-AUTH\",\r\n callback: (msg) => {\r\n return this.requestAuthorize();\r\n }\r\n });\r\n\r\n // HELPER\r\n this.msgBus.provide({\r\n channel: \"APP-SECURITY-GET-CONFIG\",\r\n callback: async (msg) => {\r\n return (\r\n await this.msgBus.dispatchAsync({\r\n channel: \"APP-CONFIG-GET\"\r\n })\r\n ).payload?.security;\r\n }\r\n });\r\n }\r\n\r\n public getContext(): SecurityContext {\r\n return {\r\n accessToken: this.accessToken,\r\n refreshToken: this.refreshToken,\r\n userInfo: this.userInfo,\r\n authProvider: this.authProvider,\r\n domain: this.domain,\r\n tokenExpiresAt: this.tokenExpiresAt\r\n };\r\n }\r\n\r\n private async updateConfigAsync() {\r\n const msg = await this.msgBus.dispatchAsync({\r\n channel: \"APP-CONFIG-GET\"\r\n });\r\n this.domainConfig = msg.payload.security;\r\n const prefixer = getValuePrefixer<typeof storageKeys>(this.domainConfig.id);\r\n this.storageKeys = prefixer(storageKeys);\r\n }\r\n\r\n async restoreDataAsync() {\r\n this.accessToken = (\r\n await this.msgBus.dispatchAsync({\r\n channel: \"APP-KV-STORE-GET\",\r\n payload: {\r\n key: this.storageKeys.accessToken\r\n }\r\n })\r\n ).payload;\r\n this.refreshToken = (\r\n await this.msgBus.dispatchAsync({\r\n channel: \"APP-KV-STORE-GET\",\r\n payload: {\r\n key: this.storageKeys.refreshToken\r\n }\r\n })\r\n ).payload;\r\n let value = (\r\n await this.msgBus.dispatchAsync({\r\n channel: \"APP-KV-STORE-GET\",\r\n payload: {\r\n key: this.storageKeys.userCredentials\r\n }\r\n })\r\n ).payload;\r\n\r\n this.userCredentials = value\r\n ? JSON.parse(value)\r\n : {\r\n username: undefined,\r\n password: undefined\r\n };\r\n\r\n value = (\r\n await this.msgBus.dispatchAsync({\r\n channel: \"APP-KV-STORE-GET\",\r\n payload: {\r\n key: this.storageKeys.acl\r\n }\r\n })\r\n ).payload;\r\n\r\n this.acl = value ? JSON.parse(value) : {};\r\n\r\n if (this.accessToken) {\r\n this.msgBus.dispatch({\r\n channel: \"APP-SECURITY-AUTH-SIGNIN\",\r\n group: \"out\",\r\n payload: this.getContext()\r\n });\r\n }\r\n }\r\n\r\n public get domain(): string {\r\n return this.domainConfig.id;\r\n }\r\n\r\n // cleanUserAndActionsStorage = (): void => {\r\n // const obsoleteKeysRegexMatch = /^(user|actions)@.+$/;\r\n // for (let i = localStorage.length - 1; i >= 0; i--) {\r\n // const key = localStorage.key(i);\r\n // if (key && obsoleteKeysRegexMatch.test(key)) {\r\n // localStorage.removeItem(key);\r\n // }\r\n // }\r\n // };\r\n\r\n // removeSavedData\r\n async clearSavedDataAsync() {\r\n this.accessToken = null;\r\n await this.msgBus.dispatchAsync({\r\n channel: \"APP-KV-STORE-REMOVE\",\r\n payload: {\r\n key: this.storageKeys.accessToken\r\n }\r\n });\r\n this.refreshToken = null;\r\n await this.msgBus.dispatchAsync({\r\n channel: \"APP-KV-STORE-REMOVE\",\r\n payload: {\r\n key: this.storageKeys.refreshToken\r\n }\r\n });\r\n this.userCredentials = null;\r\n await this.msgBus.dispatchAsync({\r\n channel: \"APP-KV-STORE-REMOVE\",\r\n payload: {\r\n key: this.storageKeys.userCredentials\r\n }\r\n });\r\n this.acl = null;\r\n await this.msgBus.dispatchAsync({\r\n channel: \"APP-KV-STORE-REMOVE\",\r\n payload: {\r\n key: this.storageKeys.acl\r\n }\r\n });\r\n }\r\n\r\n async requestAuthorize() {\r\n this.accessToken = null;\r\n this.acl = null;\r\n\r\n const signIn = this.msgBus.onceAsync({\r\n channel: \"APP-SECURITY-AUTH-SIGNIN\",\r\n group: \"out\"\r\n });\r\n\r\n const signOut = async () => {\r\n await this.msgBus.onceAsync({\r\n channel: \"APP-SECURITY-AUTH-SIGNOUT\",\r\n group: \"out\"\r\n });\r\n throw new Error(\"Auth failed: login aborted\");\r\n };\r\n\r\n this.msgBus.dispatch({\r\n channel: \"APP-SECURITY-REQUEST-AUTH-SIGNIN\",\r\n payload: {\r\n callbackUrl: window.location.pathname + window.location.search\r\n }\r\n });\r\n await Promise.race([signIn, signOut]);\r\n }\r\n\r\n async signInAsync(credentials: UserCredentials) {\r\n let url = this.domainConfig.routes?.authSignIn;\r\n\r\n url = url.replace(/[?&]$/, \"\");\r\n\r\n const content = JSON.stringify(credentials);\r\n\r\n // application/x-www-form-urlencoded?\r\n // username=&password=\r\n\r\n const requestParams: IRequestParams = {\r\n url: url,\r\n body: content,\r\n method: \"POST\",\r\n headers: {\r\n \"Content-Type\": \"application/json\",\r\n Accept: \"text/plain\"\r\n }\r\n };\r\n\r\n const request: IRequestState = {\r\n ...requestParams,\r\n status: \"executing\"\r\n };\r\n\r\n const response: IResponseState = await this.fetcher.fetch(url, requestParams);\r\n await getResponseResult(response, request);\r\n ApiError.assert(response, request);\r\n\r\n let tokens = response.resolved.json as SecurityTokens;\r\n\r\n this.userCredentials = credentials;\r\n\r\n this.accessToken = tokens.accessToken;\r\n this.refreshToken = tokens.refreshToken;\r\n // this.acl = ...;\r\n\r\n this.saveDataAsync();\r\n\r\n return this.getContext();\r\n }\r\n\r\n async saveDataAsync() {\r\n await this.msgBus.dispatchAsync({\r\n channel: \"APP-KV-STORE-SET\",\r\n payload: {\r\n key: this.storageKeys.accessToken,\r\n value: this.accessToken || null\r\n }\r\n });\r\n await this.msgBus.dispatchAsync({\r\n channel: \"APP-KV-STORE-SET\",\r\n payload: {\r\n key: this.storageKeys.refreshToken,\r\n value: this.refreshToken || null\r\n }\r\n });\r\n await this.msgBus.dispatchAsync({\r\n channel: \"APP-KV-STORE-SET\",\r\n payload: {\r\n key: this.storageKeys.userCredentials,\r\n value: this.userCredentials ? JSON.stringify(this.userCredentials) : \"\"\r\n }\r\n });\r\n await this.msgBus.dispatchAsync({\r\n channel: \"APP-KV-STORE-SET\",\r\n payload: {\r\n key: this.storageKeys.acl,\r\n value: this.acl ? JSON.stringify(this.acl) : \"\"\r\n }\r\n });\r\n }\r\n\r\n async signOutAsync() {\r\n let url = this.domainConfig.routes?.authSignOut;\r\n if (url) {\r\n url = url.replace(/[?&]$/, \"\");\r\n }\r\n\r\n const requestParams: IRequestParams = {\r\n url: url,\r\n method: \"POST\",\r\n headers: {\r\n \"Content-Type\": \"application/json\",\r\n Accept: \"text/plain\"\r\n }\r\n };\r\n\r\n const request: IRequestState = {\r\n ...requestParams,\r\n status: \"executing\"\r\n };\r\n\r\n const response: IResponseState = await this.fetcher.fetch(url, requestParams);\r\n await getResponseResult(response, request);\r\n ApiError.assert(response, request);\r\n\r\n // this.accessToken = null;\r\n // this.refreshToken = null;\r\n // this.userCredentials = null;\r\n // this.acl = null;\r\n // this.saveDataAsync();\r\n this.clearSavedDataAsync();\r\n }\r\n\r\n async refreshAsync() {\r\n let url = this.domainConfig.routes?.authRefresh;\r\n if (url) {\r\n url = url.replace(/[?&]$/, \"\");\r\n }\r\n\r\n let tokens: SecurityTokens = {\r\n refreshToken: this.refreshToken\r\n };\r\n\r\n const content = JSON.stringify(tokens);\r\n\r\n const requestParams: IRequestParams = {\r\n url: url,\r\n body: content,\r\n method: \"POST\",\r\n // useAuth: true,\r\n headers: {\r\n \"Content-Type\": \"application/json\",\r\n Accept: \"text/plain\"\r\n }\r\n };\r\n\r\n const request: IRequestState = {\r\n ...requestParams,\r\n status: \"executing\"\r\n };\r\n\r\n const response: IResponseState = await this.fetcher.fetch(url, requestParams);\r\n await getResponseResult(response, request);\r\n ApiError.assert(response, request);\r\n\r\n tokens = response.resolved.json as SecurityTokens;\r\n\r\n this.accessToken = tokens.accessToken;\r\n this.refreshToken = tokens.refreshToken;\r\n // this.userInfo = ...; // TODO\r\n // this.acl = ...;\r\n\r\n this.saveDataAsync();\r\n\r\n return this.getContext();\r\n }\r\n\r\n async getAcl<T extends ISecurable>(obj: T) {\r\n // TODO: read from this.acl\r\n\r\n return {\r\n [AccessLevel.Full]: \"\"\r\n } as IAccessDescriptor;\r\n }\r\n\r\n // authorize\r\n async verifyAccess<T extends ISecurable>(obj: T, accessLevel = AccessLevel.Full) {\r\n const acl = this.getAcl(obj);\r\n\r\n // TODO: check accessDescriptors\r\n\r\n return false;\r\n }\r\n}\r\n"],"names":["storageKeys","SecurityProvider","msgBus","__publicField","msg","_a","prefixer","getValuePrefixer","value","signIn","signOut","credentials","url","content","requestParams","request","response","getResponseResult","ApiError","tokens","obj","AccessLevel","accessLevel"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"securityProvider.es.js","sources":["../../../src/appDomain/security/securityProvider.ts"],"sourcesContent":["import {\r\n AccessLevel,\r\n IAccessDescriptor,\r\n ISecurable,\r\n BaseSecurityDomainConfig,\r\n UserCredentials,\r\n SecurityTokens,\r\n SecurityContext\r\n} from \"./securityContracts\";\r\nimport { getValuePrefixer } from \"@actdim/utico/typeCore\";\r\nimport { jwtDecode } from \"jwt-decode\";\r\nimport { getResponseResult, IRequestParams, IRequestState, IResponseState } from \"@/net/request\";\r\nimport { ApiError } from \"@/net/apiError\";\r\nimport { MsgBus } from \"@actdim/msgmesh/msgBusCore\";\r\nimport { BaseAppBusStruct } from \"@/appDomain/appContracts\";\r\n\r\nconst userNameClaim = \"http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name\";\r\n\r\n// JwtTokenPayload\r\ntype TokenPayload = {\r\n [userNameClaim]: string;\r\n};\r\n\r\n// Access denied\r\n// Insufficient privileges to perform this operation\r\nconst defaultAccessDeniedReason = \"Insufficient privileges. Contact your system Administrator.\";\r\n\r\nconst storageKeys = {\r\n accessToken: \"ACCESS_TOKEN\",\r\n refreshToken: \"REFRESH_TOKEN\",\r\n acl: \"ACL\",\r\n userCredentials: \"USER_CREDENTIALS\",\r\n userInfo: \"USER_INFO\"\r\n};\r\n\r\nfunction decodeJWTToken<T extends TokenPayload>(token: string): T {\r\n if (!token) {\r\n return null;\r\n }\r\n try {\r\n return jwtDecode<T>(this.accessToken);\r\n } catch {\r\n // something wrong with the token\r\n return null;\r\n }\r\n}\r\n\r\nexport class SecurityProvider<TUserInfo = any> {\r\n // private isAuthenticated: boolean;\r\n\r\n // private isExpired: boolean;\r\n\r\n private msgBus: MsgBus<BaseAppBusStruct>;\r\n\r\n private domainConfig: BaseSecurityDomainConfig;\r\n\r\n private storageKeys: typeof storageKeys;\r\n\r\n private accessToken: string;\r\n\r\n private refreshToken: string;\r\n\r\n private userCredentials: UserCredentials;\r\n\r\n private userInfo: TUserInfo;\r\n\r\n // private authority: string;\r\n private authProvider: string;\r\n\r\n private tokenExpiresAt: string;\r\n\r\n // RBAC vs ABAC vs PBAC: https://habr.com/ru/companies/otus/articles/698080/\r\n private acl: any;\r\n\r\n private fetcher: { fetch(url: RequestInfo, init?: RequestInit): Promise<Response> } = window;\r\n\r\n private init: Promise<any>;\r\n\r\n constructor(msgBus: MsgBus<BaseAppBusStruct>) {\r\n this.msgBus = msgBus;\r\n\r\n this.init = this.updateConfigAsync();\r\n\r\n // TODO: support custom requests\r\n\r\n this.msgBus.provide({\r\n channel: \"APP-SECURITY-GET-CONTEXT\",\r\n callback: (msg) => {\r\n return this.getContext();\r\n }\r\n });\r\n\r\n this.msgBus.provide({\r\n channel: \"APP-SECURITY-GET-ACL\",\r\n callback: (msg) => {\r\n return this.getAcl(msg.payload);\r\n }\r\n });\r\n\r\n this.msgBus.provide({\r\n channel: \"APP-SECURITY-AUTH-SIGNIN\",\r\n callback: (msg) => {\r\n return this.signInAsync(msg.payload);\r\n }\r\n });\r\n\r\n this.msgBus.provide({\r\n channel: \"APP-SECURITY-AUTH-SIGNOUT\",\r\n callback: (msg) => {\r\n return this.signOutAsync();\r\n }\r\n });\r\n\r\n this.msgBus.provide({\r\n channel: \"APP-SECURITY-AUTH-REFRESH\",\r\n callback: (msg) => {\r\n return this.refreshAsync();\r\n }\r\n });\r\n\r\n this.msgBus.provide({\r\n channel: \"APP-SECURITY-REQUEST-AUTH\",\r\n callback: (msg) => {\r\n return this.requestAuthorize();\r\n }\r\n });\r\n\r\n // HELPER\r\n this.msgBus.provide({\r\n channel: \"APP-SECURITY-GET-CONFIG\",\r\n callback: async (msg) => {\r\n return (\r\n await this.msgBus.dispatchAsync({\r\n channel: \"APP-CONFIG-GET\"\r\n })\r\n ).payload?.security;\r\n }\r\n });\r\n }\r\n\r\n public getContext(): SecurityContext {\r\n return {\r\n accessToken: this.accessToken,\r\n refreshToken: this.refreshToken,\r\n userInfo: this.userInfo,\r\n authProvider: this.authProvider,\r\n domain: this.domain,\r\n tokenExpiresAt: this.tokenExpiresAt\r\n };\r\n }\r\n\r\n private async updateConfigAsync() {\r\n const msg = await this.msgBus.dispatchAsync({\r\n channel: \"APP-CONFIG-GET\"\r\n });\r\n this.domainConfig = msg.payload.security;\r\n const prefixer = getValuePrefixer<typeof storageKeys>(this.domainConfig.id);\r\n this.storageKeys = prefixer(storageKeys);\r\n }\r\n\r\n async restoreDataAsync() {\r\n this.accessToken = (\r\n await this.msgBus.dispatchAsync({\r\n channel: \"APP-KV-STORE-GET\",\r\n payload: {\r\n key: this.storageKeys.accessToken\r\n }\r\n })\r\n ).payload;\r\n this.refreshToken = (\r\n await this.msgBus.dispatchAsync({\r\n channel: \"APP-KV-STORE-GET\",\r\n payload: {\r\n key: this.storageKeys.refreshToken\r\n }\r\n })\r\n ).payload;\r\n let value = (\r\n await this.msgBus.dispatchAsync({\r\n channel: \"APP-KV-STORE-GET\",\r\n payload: {\r\n key: this.storageKeys.userCredentials\r\n }\r\n })\r\n ).payload;\r\n\r\n this.userCredentials = value\r\n ? JSON.parse(value)\r\n : {\r\n username: undefined,\r\n password: undefined\r\n };\r\n\r\n value = (\r\n await this.msgBus.dispatchAsync({\r\n channel: \"APP-KV-STORE-GET\",\r\n payload: {\r\n key: this.storageKeys.acl\r\n }\r\n })\r\n ).payload;\r\n\r\n this.acl = value ? JSON.parse(value) : {};\r\n\r\n if (this.accessToken) {\r\n this.msgBus.dispatch({\r\n channel: \"APP-SECURITY-AUTH-SIGNIN\",\r\n group: \"out\",\r\n payload: this.getContext()\r\n });\r\n }\r\n }\r\n\r\n public get domain(): string {\r\n return this.domainConfig.id;\r\n }\r\n\r\n // cleanUserAndActionsStorage = (): void => {\r\n // const obsoleteKeysRegexMatch = /^(user|actions)@.+$/;\r\n // for (let i = localStorage.length - 1; i >= 0; i--) {\r\n // const key = localStorage.key(i);\r\n // if (key && obsoleteKeysRegexMatch.test(key)) {\r\n // localStorage.removeItem(key);\r\n // }\r\n // }\r\n // };\r\n\r\n // removeSavedData\r\n async clearSavedDataAsync() {\r\n this.accessToken = null;\r\n await this.msgBus.dispatchAsync({\r\n channel: \"APP-KV-STORE-REMOVE\",\r\n payload: {\r\n key: this.storageKeys.accessToken\r\n }\r\n });\r\n this.refreshToken = null;\r\n await this.msgBus.dispatchAsync({\r\n channel: \"APP-KV-STORE-REMOVE\",\r\n payload: {\r\n key: this.storageKeys.refreshToken\r\n }\r\n });\r\n this.userCredentials = null;\r\n await this.msgBus.dispatchAsync({\r\n channel: \"APP-KV-STORE-REMOVE\",\r\n payload: {\r\n key: this.storageKeys.userCredentials\r\n }\r\n });\r\n this.acl = null;\r\n await this.msgBus.dispatchAsync({\r\n channel: \"APP-KV-STORE-REMOVE\",\r\n payload: {\r\n key: this.storageKeys.acl\r\n }\r\n });\r\n }\r\n\r\n async requestAuthorize() {\r\n this.accessToken = null;\r\n this.acl = null;\r\n\r\n const signIn = this.msgBus.onceAsync({\r\n channel: \"APP-SECURITY-AUTH-SIGNIN\",\r\n group: \"out\"\r\n });\r\n\r\n const signOut = async () => {\r\n await this.msgBus.onceAsync({\r\n channel: \"APP-SECURITY-AUTH-SIGNOUT\",\r\n group: \"out\"\r\n });\r\n throw new Error(\"Auth failed: login aborted\");\r\n };\r\n\r\n this.msgBus.dispatch({\r\n channel: \"APP-SECURITY-REQUEST-AUTH-SIGNIN\",\r\n payload: {\r\n callbackUrl: window.location.pathname + window.location.search\r\n }\r\n });\r\n await Promise.race([signIn, signOut]);\r\n }\r\n\r\n async signInAsync(credentials: UserCredentials) {\r\n let url = this.domainConfig.routes?.authSignIn;\r\n\r\n url = url.replace(/[?&]$/, \"\");\r\n\r\n const content = JSON.stringify(credentials);\r\n\r\n // application/x-www-form-urlencoded?\r\n // username=&password=\r\n\r\n const requestParams: IRequestParams = {\r\n url: url,\r\n body: content,\r\n method: \"POST\",\r\n headers: {\r\n \"Content-Type\": \"application/json\",\r\n Accept: \"text/plain\"\r\n }\r\n };\r\n\r\n const request: IRequestState = {\r\n ...requestParams,\r\n status: \"executing\"\r\n };\r\n\r\n const response: IResponseState = await this.fetcher.fetch(url, requestParams);\r\n await getResponseResult(response, request);\r\n ApiError.assert(response, request);\r\n\r\n let tokens = response.resolved.json as SecurityTokens;\r\n\r\n this.userCredentials = credentials;\r\n\r\n this.accessToken = tokens.accessToken;\r\n this.refreshToken = tokens.refreshToken;\r\n // this.acl = ...;\r\n\r\n this.saveDataAsync();\r\n\r\n return this.getContext();\r\n }\r\n\r\n async saveDataAsync() {\r\n await this.msgBus.dispatchAsync({\r\n channel: \"APP-KV-STORE-SET\",\r\n payload: {\r\n key: this.storageKeys.accessToken,\r\n value: this.accessToken || null\r\n }\r\n });\r\n await this.msgBus.dispatchAsync({\r\n channel: \"APP-KV-STORE-SET\",\r\n payload: {\r\n key: this.storageKeys.refreshToken,\r\n value: this.refreshToken || null\r\n }\r\n });\r\n await this.msgBus.dispatchAsync({\r\n channel: \"APP-KV-STORE-SET\",\r\n payload: {\r\n key: this.storageKeys.userCredentials,\r\n value: this.userCredentials ? JSON.stringify(this.userCredentials) : \"\"\r\n }\r\n });\r\n await this.msgBus.dispatchAsync({\r\n channel: \"APP-KV-STORE-SET\",\r\n payload: {\r\n key: this.storageKeys.acl,\r\n value: this.acl ? JSON.stringify(this.acl) : \"\"\r\n }\r\n });\r\n }\r\n\r\n async signOutAsync() {\r\n let url = this.domainConfig.routes?.authSignOut;\r\n if (url) {\r\n url = url.replace(/[?&]$/, \"\");\r\n }\r\n\r\n const requestParams: IRequestParams = {\r\n url: url,\r\n method: \"POST\",\r\n headers: {\r\n \"Content-Type\": \"application/json\",\r\n Accept: \"text/plain\"\r\n }\r\n };\r\n\r\n const request: IRequestState = {\r\n ...requestParams,\r\n status: \"executing\"\r\n };\r\n\r\n const response: IResponseState = await this.fetcher.fetch(url, requestParams);\r\n await getResponseResult(response, request);\r\n ApiError.assert(response, request);\r\n\r\n // this.accessToken = null;\r\n // this.refreshToken = null;\r\n // this.userCredentials = null;\r\n // this.acl = null;\r\n // this.saveDataAsync();\r\n this.clearSavedDataAsync();\r\n }\r\n\r\n async refreshAsync() {\r\n let url = this.domainConfig.routes?.authRefresh;\r\n if (url) {\r\n url = url.replace(/[?&]$/, \"\");\r\n }\r\n\r\n let tokens: SecurityTokens = {\r\n refreshToken: this.refreshToken\r\n };\r\n\r\n const content = JSON.stringify(tokens);\r\n\r\n const requestParams: IRequestParams = {\r\n url: url,\r\n body: content,\r\n method: \"POST\",\r\n // useAuth: true,\r\n headers: {\r\n \"Content-Type\": \"application/json\",\r\n Accept: \"text/plain\"\r\n }\r\n };\r\n\r\n const request: IRequestState = {\r\n ...requestParams,\r\n status: \"executing\"\r\n };\r\n\r\n const response: IResponseState = await this.fetcher.fetch(url, requestParams);\r\n await getResponseResult(response, request);\r\n ApiError.assert(response, request);\r\n\r\n tokens = response.resolved.json as SecurityTokens;\r\n\r\n this.accessToken = tokens.accessToken;\r\n this.refreshToken = tokens.refreshToken;\r\n // this.userInfo = ...; // TODO\r\n // this.acl = ...;\r\n\r\n this.saveDataAsync();\r\n\r\n return this.getContext();\r\n }\r\n\r\n async getAcl<T extends ISecurable>(obj: T) {\r\n // TODO: read from this.acl\r\n\r\n return {\r\n [AccessLevel.Full]: \"\"\r\n } as IAccessDescriptor;\r\n }\r\n\r\n // authorize\r\n async verifyAccess<T extends ISecurable>(obj: T, accessLevel = AccessLevel.Full) {\r\n const acl = this.getAcl(obj);\r\n\r\n // TODO: check accessDescriptors\r\n\r\n return false;\r\n }\r\n}\r\n"],"names":["storageKeys","SecurityProvider","msgBus","msg","prefixer","getValuePrefixer","value","signIn","signOut","credentials","url","content","requestParams","request","response","getResponseResult","ApiError","tokens","obj","AccessLevel","accessLevel"],"mappings":"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"}
|