@bitblit/ratchet-warden-common 6.0.146-alpha → 6.0.147-alpha

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (54) hide show
  1. package/package.json +4 -3
  2. package/src/build/ratchet-warden-common-info.ts +19 -0
  3. package/src/client/provider/warden-client-abstract-recent-login-provider.ts +53 -0
  4. package/src/client/provider/warden-client-current-logged-in-jwt-token-provider.ts +3 -0
  5. package/src/client/provider/warden-client-recent-login-provider.ts +11 -0
  6. package/src/client/provider/warden-client-storage-based-logged-in-user-provider.ts +54 -0
  7. package/src/client/provider/warden-client-storage-based-recent-login-provider.ts +46 -0
  8. package/src/client/provider/warden-client-transient-memory-logged-in-user-provider.ts +19 -0
  9. package/src/client/provider/warden-client-transient-memory-recent-login-provider.ts +15 -0
  10. package/src/client/provider/warden-command-exchange-provider.ts +8 -0
  11. package/src/client/provider/warden-logged-in-user-provider.ts +7 -0
  12. package/src/client/provider/warden-logged-in-user-wrapper.ts +7 -0
  13. package/src/client/provider/warden-recent-login-descriptor.ts +6 -0
  14. package/src/client/provider/warden-user-service-event-processing-provider.ts +14 -0
  15. package/src/client/provider/warden-user-service-options.ts +17 -0
  16. package/src/client/warden-client.spec.ts +32 -0
  17. package/src/client/warden-client.ts +239 -0
  18. package/src/client/warden-delegating-current-user-providing-user-service-event-processing-provider.ts +76 -0
  19. package/src/client/warden-user-service.spec.ts +29 -0
  20. package/src/client/warden-user-service.ts +434 -0
  21. package/src/common/command/add-web-authn-registration-to-logged-in-user.ts +7 -0
  22. package/src/common/command/create-account.ts +8 -0
  23. package/src/common/command/remove-web-authn-registration.ts +4 -0
  24. package/src/common/command/send-magic-link.ts +15 -0
  25. package/src/common/command/warden-command-response.ts +24 -0
  26. package/src/common/command/warden-command.ts +29 -0
  27. package/src/common/command/warden-contact-lookup.ts +7 -0
  28. package/src/common/command/warden-custom-template-descriptor.ts +7 -0
  29. package/src/common/command/web-authn-object-wrapper.ts +3 -0
  30. package/src/common/error/warden-error-codes.ts +9 -0
  31. package/src/common/error/warden-error.ts +122 -0
  32. package/src/common/model/warden-contact-type.ts +4 -0
  33. package/src/common/model/warden-contact.ts +6 -0
  34. package/src/common/model/warden-customer-message-type.ts +5 -0
  35. package/src/common/model/warden-entry-summary.ts +9 -0
  36. package/src/common/model/warden-entry.ts +14 -0
  37. package/src/common/model/warden-jwt-token.ts +15 -0
  38. package/src/common/model/warden-login-request-type.ts +6 -0
  39. package/src/common/model/warden-login-request.ts +15 -0
  40. package/src/common/model/warden-login-results.ts +8 -0
  41. package/src/common/model/warden-login-third-party-token.ts +5 -0
  42. package/src/common/model/warden-permission.ts +4 -0
  43. package/src/common/model/warden-role.ts +8 -0
  44. package/src/common/model/warden-store-registration-response-type.ts +5 -0
  45. package/src/common/model/warden-store-registration-response.ts +9 -0
  46. package/src/common/model/warden-team-role-mapping.ts +4 -0
  47. package/src/common/model/warden-team.ts +5 -0
  48. package/src/common/model/warden-third-party-authentication.ts +6 -0
  49. package/src/common/model/warden-user-decoration.ts +10 -0
  50. package/src/common/model/warden-web-authn-entry-summary.ts +6 -0
  51. package/src/common/model/warden-web-authn-entry.ts +14 -0
  52. package/src/common/model/warden-web-authn-transport-future-type.ts +8 -0
  53. package/src/common/util/warden-utils.spec.ts +15 -0
  54. package/src/common/util/warden-utils.ts +284 -0
@@ -0,0 +1,122 @@
1
+ import { StringRatchet } from "@bitblit/ratchet-common/lang/string-ratchet";
2
+ import { WardenErrorCode } from "./warden-error-codes";
3
+
4
+ /**
5
+ * This is the superclass for errors that can be thrown in the Warden system -
6
+ * most aren't user-recoverable but some are or hold more data.
7
+ *
8
+ * Big users of ratchet will notice similarities to WardenError which
9
+ * are not accidental
10
+ */
11
+ export class WardenError<T = void> extends Error {
12
+ private static readonly WARDEN_ERROR_FLAG_KEY: string = '__wardenErrorFlag';
13
+ private _errorCode: number;
14
+ private _internalMessage: string;
15
+ private _publicMessage: string;
16
+ private _details: T;
17
+ private _wrappedError: Error;
18
+
19
+ constructor(code: number, internalMessage?: string, publicMessage?: string, wrapped?: Error, details?: T) {
20
+ super(StringRatchet.trimToNull(internalMessage)??'Warden Security Error');
21
+ this._errorCode = code;
22
+ this._internalMessage = StringRatchet.trimToNull(internalMessage)??'Warden Security Error'
23
+ this._publicMessage = StringRatchet.trimToNull(publicMessage) ?? this._internalMessage;
24
+ this._details = details;
25
+ this._wrappedError = wrapped;
26
+ this[WardenError.WARDEN_ERROR_FLAG_KEY] = true; // Just used to tell if one has been wrapped
27
+ }
28
+
29
+ public withFormattedInternalErrorMessage(format: string, ...input: any[]): WardenError<T> {
30
+ this.setFormattedInternalErrorMessage(format, ...input);
31
+ return this;
32
+ }
33
+
34
+ public setFormattedInternalErrorMessage(format: string, ...input: any[]): void {
35
+ const msg: string = StringRatchet.format(format, ...input);
36
+ this._internalMessage = msg;
37
+ }
38
+
39
+ public withFormattedPublicErrorMessage(format: string, ...input: any[]): WardenError<T> {
40
+ this.setFormattedPublicErrorMessage(format, ...input);
41
+ return this;
42
+ }
43
+
44
+ public setFormattedPublicErrorMessage(format: string, ...input: any[]): void {
45
+ const msg: string = StringRatchet.format(format, ...input);
46
+ this._publicMessage = msg;
47
+ }
48
+
49
+ public withErrorCode(code: number): WardenError<T> {
50
+ this.errorCode = code; // Call setter
51
+ return this;
52
+ }
53
+
54
+ public withDetails(details: T): WardenError<T> {
55
+ this._details = details; // Call setter
56
+ return this;
57
+ }
58
+
59
+ public withWrappedError(err: Error): WardenError<T> {
60
+ this._wrappedError = err; // Call setter
61
+ return this;
62
+ }
63
+
64
+ public isWrappedError(): boolean {
65
+ return !!this._wrappedError;
66
+ }
67
+
68
+ public static wrapError<T = void>(err: Error): WardenError<T> {
69
+ let rval: WardenError<T> = null;
70
+ if (WardenError.objectIsWardenError(err)) {
71
+ rval = err as WardenError<T>;
72
+ } else {
73
+ rval = new WardenError<T>(WardenErrorCode.Unspecified).withWrappedError(err);
74
+ }
75
+ return rval;
76
+ }
77
+
78
+ public static objectIsWardenError(obj: any): boolean {
79
+ return obj && obj[WardenError.WARDEN_ERROR_FLAG_KEY] === true;
80
+ }
81
+
82
+ get errorCode(): number {
83
+ return this._errorCode;
84
+ }
85
+
86
+ set errorCode(value: number) {
87
+ this._errorCode = value;
88
+ }
89
+
90
+ get publicMessage(): string {
91
+ return this._publicMessage;
92
+ }
93
+
94
+ set publicMessage(value: string) {
95
+ this._publicMessage = value;
96
+ }
97
+
98
+ get internalMessage(): string {
99
+ return this._internalMessage;
100
+ }
101
+
102
+ set internalMessage(value: string) {
103
+ this._internalMessage = value;
104
+ }
105
+
106
+ get details(): T {
107
+ return this._details;
108
+ }
109
+
110
+ set details(value: T) {
111
+ this._details = value;
112
+ }
113
+
114
+ get wrappedError(): Error {
115
+ return this._wrappedError;
116
+ }
117
+
118
+ set wrappedError(value: Error) {
119
+ this._wrappedError = value;
120
+ }
121
+
122
+ }
@@ -0,0 +1,4 @@
1
+ export enum WardenContactType {
2
+ TextCapablePhoneNumber = 'TextCapablePhoneNumber',
3
+ EmailAddress = 'EmailAddress',
4
+ }
@@ -0,0 +1,6 @@
1
+ import { WardenContactType } from './warden-contact-type.js';
2
+
3
+ export interface WardenContact {
4
+ value: string;
5
+ type: WardenContactType;
6
+ }
@@ -0,0 +1,5 @@
1
+ export enum WardenCustomerMessageType {
2
+ ExpiringCode = 'ExpiringCode',
3
+ MagicLink = 'MagicLink',
4
+ Custom = 'Custom',
5
+ }
@@ -0,0 +1,9 @@
1
+ import { WardenContact } from './warden-contact.js';
2
+ import { WardenWebAuthnEntrySummary } from './warden-web-authn-entry-summary.js';
3
+
4
+ export interface WardenEntrySummary {
5
+ userId: string;
6
+ userLabel: string; // Usually full name, could be something else
7
+ contactMethods: WardenContact[];
8
+ webAuthnAuthenticatorSummaries: WardenWebAuthnEntrySummary[];
9
+ }
@@ -0,0 +1,14 @@
1
+ import { WardenWebAuthnEntry } from './warden-web-authn-entry.js';
2
+ import { WardenContact } from './warden-contact.js';
3
+ import { WardenThirdPartyAuthentication } from "./warden-third-party-authentication.js";
4
+
5
+ export interface WardenEntry {
6
+ userId: string;
7
+ userLabel: string; // Usually full name, could be something else
8
+ contactMethods: WardenContact[];
9
+ tags: string[];
10
+ webAuthnAuthenticators: WardenWebAuthnEntry[];
11
+ thirdPartyAuthenticators: WardenThirdPartyAuthentication[];
12
+ createdEpochMS: number;
13
+ updatedEpochMS: number;
14
+ }
@@ -0,0 +1,15 @@
1
+ import { CommonJwtToken } from '@bitblit/ratchet-common/jwt/common-jwt-token';
2
+ import { WardenEntrySummary } from './warden-entry-summary.js';
3
+ import { WardenTeamRoleMapping } from "./warden-team-role-mapping.ts";
4
+
5
+ export interface WardenJwtToken<T> extends CommonJwtToken<T> {
6
+ wardenData: WardenEntrySummary;
7
+
8
+ // This is built using a combo of the user data that warden tracks, and that the decorator tracks
9
+ // Decorator output goes into the user and proxy fields from commonJwtToken, plus the
10
+ // fields below
11
+
12
+ globalRoleIds: string[];
13
+ teamRoleMappings: WardenTeamRoleMapping[];
14
+
15
+ }
@@ -0,0 +1,6 @@
1
+ export enum WardenLoginRequestType {
2
+ WebAuthn='WebAuthn',
3
+ ExpiringToken='ExpiringToken',
4
+ JwtTokenToRefresh='JwtTokenToRefresh',
5
+ ThirdParty='ThirdParty',
6
+ }
@@ -0,0 +1,15 @@
1
+ import { WardenContact } from './warden-contact.js';
2
+ import { AuthenticationResponseJSON } from '@simplewebauthn/browser';
3
+ import { WardenLoginRequestType } from "./warden-login-request-type.js";
4
+ import { WardenLoginThirdPartyToken } from "./warden-login-third-party-token";
5
+
6
+ export interface WardenLoginRequest {
7
+ type: WardenLoginRequestType;
8
+ userId?: string;
9
+ contact?: WardenContact;
10
+ webAuthn?: AuthenticationResponseJSON;
11
+ thirdPartyToken?: WardenLoginThirdPartyToken;
12
+ expiringToken?: string;
13
+ jwtTokenToRefresh?: string;
14
+ createUserIfMissing?: boolean;
15
+ }
@@ -0,0 +1,8 @@
1
+ import { WardenLoginRequest } from './warden-login-request.js';
2
+
3
+ export interface WardenLoginResults {
4
+ request: WardenLoginRequest;
5
+ userId?: string;
6
+ jwtToken?: string;
7
+ error?: string;
8
+ }
@@ -0,0 +1,5 @@
1
+
2
+ export interface WardenLoginThirdPartyToken {
3
+ thirdParty: string;
4
+ token: string;
5
+ }
@@ -0,0 +1,4 @@
1
+ export interface WardenPermission {
2
+ actions: string[];
3
+ resourceSuffixes: string[];
4
+ }
@@ -0,0 +1,8 @@
1
+ import { WardenPermission } from "./warden-permission.ts";
2
+
3
+ export interface WardenRole {
4
+ roleId: string;
5
+ label: string;
6
+ description?: string;
7
+ permissions: WardenPermission[];
8
+ }
@@ -0,0 +1,5 @@
1
+ export enum WardenStoreRegistrationResponseType {
2
+ Verified = 'Verified',
3
+ Failed = 'Failed',
4
+ Error = 'Error',
5
+ }
@@ -0,0 +1,9 @@
1
+ import { WardenStoreRegistrationResponseType } from './warden-store-registration-response-type.js';
2
+ import { WardenEntry } from './warden-entry.js';
3
+
4
+ export interface WardenStoreRegistrationResponse {
5
+ updatedEntry?: WardenEntry;
6
+ registrationResponseId: string;
7
+ result: WardenStoreRegistrationResponseType;
8
+ error?: string;
9
+ }
@@ -0,0 +1,4 @@
1
+ export interface WardenTeamRoleMapping {
2
+ teamId: string;
3
+ roleId: string;
4
+ }
@@ -0,0 +1,5 @@
1
+ export interface WardenTeam {
2
+ teamId: string;
3
+ label: string;
4
+ description?: string;
5
+ }
@@ -0,0 +1,6 @@
1
+
2
+ export interface WardenThirdPartyAuthentication{
3
+ thirdParty: string;
4
+ thirdPartyId: string;
5
+ meta?: Record<string,string>;
6
+ }
@@ -0,0 +1,10 @@
1
+ import { WardenTeamRoleMapping } from './warden-team-role-mapping.ts';
2
+
3
+ export interface WardenUserDecoration<T> {
4
+ userTokenData: T;
5
+ proxyUserTokenData: T;
6
+ userTokenExpirationSeconds: number;
7
+
8
+ globalRoleIds: string[];
9
+ teamRoleMappings: WardenTeamRoleMapping[];
10
+ }
@@ -0,0 +1,6 @@
1
+ export interface WardenWebAuthnEntrySummary {
2
+ origin: string;
3
+ applicationName: string;
4
+ deviceLabel: string;
5
+ credentialIdBase64: string;
6
+ }
@@ -0,0 +1,14 @@
1
+ import { WardenWebAuthnTransportFutureType } from './warden-web-authn-transport-future-type.js';
2
+
3
+ export interface WardenWebAuthnEntry {
4
+ origin: string;
5
+ applicationName: string;
6
+ deviceLabel: string;
7
+
8
+ credentialIdBase64: string;
9
+ credentialPublicKeyBase64: string;
10
+ counter: number;
11
+ credentialBackedUp: boolean;
12
+ credentialDeviceType: string;
13
+ transports?: WardenWebAuthnTransportFutureType[];
14
+ }
@@ -0,0 +1,8 @@
1
+ export enum WardenWebAuthnTransportFutureType {
2
+ ble = 'ble',
3
+ internal = 'internal',
4
+ nfc = 'nfc',
5
+ usb = 'usb',
6
+ cable = 'cable',
7
+ hybrid = 'hybrid',
8
+ }
@@ -0,0 +1,15 @@
1
+ import { WardenContactType } from '../model/warden-contact-type.js';
2
+ import { WardenContact } from '../model/warden-contact.js';
3
+ import { WardenUtils } from './warden-utils.js';
4
+ import { describe, expect, test } from 'vitest';
5
+
6
+ describe('#WardenUtils', () => {
7
+ //beforeEach(() => {});
8
+
9
+ test('Should convert a string to a contact type', async () => {
10
+ const output: WardenContact = WardenUtils.stringToWardenContact('test@test.com');
11
+ expect(output).not.toBeNull();
12
+ expect(output.value).toEqual('test@test.com');
13
+ expect(output.type).toEqual(WardenContactType.EmailAddress);
14
+ });
15
+ });
@@ -0,0 +1,284 @@
1
+ import { WardenContact } from "../model/warden-contact.js";
2
+ import { WardenContactType } from "../model/warden-contact-type.js";
3
+
4
+ import { Logger } from "@bitblit/ratchet-common/logger/logger";
5
+ import { StringRatchet } from "@bitblit/ratchet-common/lang/string-ratchet";
6
+ import { WardenEntrySummary } from "../model/warden-entry-summary.js";
7
+ import { WardenEntry } from "../model/warden-entry.js";
8
+ import { WardenLoginRequest } from "../model/warden-login-request.js";
9
+ import { WardenTeamRoleMapping } from "../model/warden-team-role-mapping.ts";
10
+ import { WardenWebAuthnEntry } from "../model/warden-web-authn-entry.js";
11
+ import { WardenWebAuthnEntrySummary } from "../model/warden-web-authn-entry-summary.js";
12
+ import { WardenLoggedInUserWrapper } from "../../client/provider/warden-logged-in-user-wrapper.js";
13
+ import { WardenLoginRequestType } from "../model/warden-login-request-type.ts";
14
+ import { WardenUserDecoration } from "../model/warden-user-decoration.ts";
15
+ import { BooleanRatchet } from "@bitblit/ratchet-common/lang/boolean-ratchet";
16
+ import { WardenJwtToken } from "../model/warden-jwt-token.ts";
17
+
18
+ export class WardenUtils {
19
+ // Prevent instantiation
20
+ // eslint-disable-next-line @typescript-eslint/no-useless-constructor
21
+ constructor() {
22
+ // Empty
23
+ }
24
+
25
+ public static extractContactsOfType(req: WardenEntry | WardenEntrySummary, type: WardenContactType): string[] {
26
+ let rval: string[] = null;
27
+ if (req?.contactMethods) {
28
+ rval = req.contactMethods.filter((s) => s.type === type).map((s) => s.value);
29
+ }
30
+ return rval;
31
+ }
32
+
33
+ public static loginRequestErrors(req: WardenLoginRequest): string[] {
34
+ const rval: string[] = [];
35
+
36
+ if (req) {
37
+ if (req.type) {
38
+ switch(req.type) {
39
+ case WardenLoginRequestType.ExpiringToken:
40
+ if (!req.userId && !WardenUtils.validContact(req.contact)) {
41
+ rval.push('User ID or contact is required');
42
+ }
43
+ if (!req.expiringToken) {
44
+ rval.push('Expiring token is required');
45
+ }
46
+ break;
47
+ case WardenLoginRequestType.ThirdParty:
48
+ if (req.thirdPartyToken) {
49
+ if (!req.thirdPartyToken.thirdParty) {
50
+ rval.push('Third party is required');
51
+ }
52
+ if (!req.thirdPartyToken.token) {
53
+ rval.push('Third party token is required');
54
+ }
55
+ } else {
56
+ rval.push('Third party auth is required');
57
+ }
58
+ break;
59
+ case WardenLoginRequestType.WebAuthn:
60
+ if (!req.userId && !WardenUtils.validContact(req.contact)) {
61
+ rval.push('User ID or contact is required');
62
+ }
63
+ if (!req.webAuthn) {
64
+ rval.push('Web authn is required');
65
+ }
66
+ break;
67
+ case WardenLoginRequestType.JwtTokenToRefresh:
68
+ if (!req.jwtTokenToRefresh) {
69
+ rval.push('JwtToken is required');
70
+ }
71
+ break;
72
+ default: rval.push('Unknown request type');
73
+ }
74
+ } else {
75
+ rval.push('Request type is null');
76
+ }
77
+ } else {
78
+ rval.push('Request is null');
79
+ }
80
+ return rval;
81
+ }
82
+
83
+
84
+ public static validLoginRequest(req: WardenLoginRequest): boolean {
85
+ return WardenUtils.loginRequestErrors(req).length === 0;
86
+ }
87
+
88
+ public static stringToWardenContact(input: string): WardenContact {
89
+ let rval: WardenContact = null;
90
+ const type: WardenContactType = WardenUtils.stringToContactType(input);
91
+ if (type) {
92
+ rval = {
93
+ type: type,
94
+ value: input,
95
+ };
96
+ } else {
97
+ Logger.error('Failed to convert a string to a contact type', input);
98
+ }
99
+ return rval;
100
+ }
101
+
102
+ public static teamRolesToRoles(teamRoles: WardenTeamRoleMapping[]): string[] {
103
+ const rval: string[] = teamRoles?.length ? teamRoles.map((t) => WardenUtils.teamRoleToRoleString(t)) : [];
104
+ return rval;
105
+ }
106
+
107
+ public static roleStringsToTeamRoles(roles: string[]): WardenTeamRoleMapping[] {
108
+ const rval: WardenTeamRoleMapping[] = roles?.length ? roles.map((t) => WardenUtils.roleStringToTeamRole(t)) : [];
109
+ return rval;
110
+ }
111
+
112
+ public static roleStringToTeamRole(role: string): WardenTeamRoleMapping {
113
+ let rval: WardenTeamRoleMapping = null;
114
+ if (role && role.indexOf('_/_') >= 0) {
115
+ const sp: string[] = role.split('_/_');
116
+ rval = {
117
+ teamId: sp[0],
118
+ roleId: sp[1],
119
+ };
120
+ }
121
+ return rval;
122
+ }
123
+
124
+ public static teamRoleToRoleString(tr: WardenTeamRoleMapping): string {
125
+ let rval: string = null;
126
+ if (tr?.roleId && tr?.teamId) {
127
+ rval = tr.teamId + '_/_' + tr.roleId;
128
+ }
129
+ return rval;
130
+ }
131
+
132
+ public static stringToContactType(input: string): WardenContactType {
133
+ let rval: WardenContactType = null;
134
+ if (StringRatchet.trimToNull(input)) {
135
+ rval = WardenUtils.stringIsEmailAddress(input) ? WardenContactType.EmailAddress : null;
136
+ rval = !rval && WardenUtils.stringIsPhoneNumber(input) ? WardenContactType.TextCapablePhoneNumber : rval;
137
+ }
138
+ return rval;
139
+ }
140
+
141
+ public static validContact(contact: WardenContact): boolean {
142
+ let rval: boolean = false;
143
+ if (contact?.type && StringRatchet.trimToNull(contact?.value)) {
144
+ switch (contact.type) {
145
+ case WardenContactType.EmailAddress:
146
+ rval = WardenUtils.stringIsEmailAddress(contact.value);
147
+ break;
148
+ case WardenContactType.TextCapablePhoneNumber:
149
+ rval = WardenUtils.stringIsPhoneNumber(contact.value);
150
+ break;
151
+ default:
152
+ rval = false;
153
+ }
154
+ }
155
+
156
+ return rval;
157
+ }
158
+
159
+ public static stringIsEmailAddress(value: string): boolean {
160
+ return !!value.match(/^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9-]+(?:\.[a-zA-Z0-9-]+)*$/);
161
+ }
162
+
163
+ public static stringIsPhoneNumber(value: string): boolean {
164
+ return !!value.match(/^[\\+]?[(]?[0-9]{3}[)]?[-\\s\\.]?[0-9]{3}[-\\s\\.]?[0-9]{4,6}$/im);
165
+ }
166
+
167
+ public static stripWardenEntryToSummary(we: WardenEntry): WardenEntrySummary {
168
+ const rval: WardenEntrySummary = we
169
+ ? {
170
+ userId: we.userId,
171
+ userLabel: we.userLabel,
172
+ contactMethods: we.contactMethods,
173
+ webAuthnAuthenticatorSummaries: (we?.webAuthnAuthenticators || []).map((s) => WardenUtils.stripWardenWebAuthnEntryToSummary(s)),
174
+ }
175
+ : null;
176
+ return rval;
177
+ }
178
+
179
+ public static stripWardenWebAuthnEntryToSummary(we: WardenWebAuthnEntry): WardenWebAuthnEntrySummary {
180
+ const rval: WardenWebAuthnEntrySummary = we
181
+ ? {
182
+ origin: we.origin,
183
+ applicationName: we.applicationName,
184
+ deviceLabel: we.deviceLabel,
185
+ credentialIdBase64: we.credentialIdBase64,
186
+ }
187
+ : null;
188
+ return rval;
189
+ }
190
+
191
+ public static wrapperIsExpired(value: WardenLoggedInUserWrapper<any>): boolean {
192
+ const rval: boolean = value?.userObject?.exp && value.expirationEpochSeconds < Date.now() / 1000;
193
+ return rval;
194
+ }
195
+
196
+ public static userHasGlobalRole(user: WardenUserDecoration<any>, roleId: string): boolean {
197
+ return WardenUtils.userHasGlobalRoles(user, [roleId], true);
198
+ }
199
+
200
+ public static userHasRoleOnTeam(user: WardenUserDecoration<any>,teamId: string, roleId: string): boolean {
201
+ return WardenUtils.userHasRolesOnTeam(user, teamId, [roleId], true);
202
+ }
203
+
204
+ public static userHasAtLeastOneGlobalRole(user: WardenUserDecoration<any>, roleIds: string[]): boolean {
205
+ return WardenUtils.userHasGlobalRoles(user, roleIds, false);
206
+ }
207
+
208
+ public static userHasAtLeastOneRoleOnTeam(user: WardenUserDecoration<any>, teamId: string, roleIds: string[]): boolean {
209
+ return WardenUtils.userHasRolesOnTeam(user, teamId, roleIds, false);
210
+ }
211
+
212
+
213
+ public static userHasAllGlobalRoles(user: WardenUserDecoration<any>, roleIds: string[]): boolean {
214
+ return WardenUtils.userHasGlobalRoles(user, roleIds, true);
215
+ }
216
+
217
+ public static userHasAllRolesOnTeam(user: WardenUserDecoration<any>, teamId: string, roleIds: string[]): boolean {
218
+ return WardenUtils.userHasRolesOnTeam(user, teamId, roleIds, true);
219
+ }
220
+
221
+
222
+ public static userHasGlobalRoles(user: WardenUserDecoration<any>, inRoleIds: string[], combineWithAnd: boolean): boolean {
223
+ let rval: boolean = false;
224
+ const roleIds: string[] = inRoleIds ? inRoleIds.map(r=>StringRatchet.trimToNull(r)?.toLowerCase()) : null
225
+ if (user && roleIds && roleIds.length > 0) {
226
+ const hasMap: boolean[] = user ? roleIds.map(r=>!!user.globalRoleIds.find(gr=>StringRatchet.trimToEmpty(gr).toLowerCase()===r)) : [false];
227
+ rval = combineWithAnd ? BooleanRatchet.allTrue(hasMap) : BooleanRatchet.anyTrue(hasMap);
228
+ }
229
+ return rval;
230
+ }
231
+
232
+ public static userHasRolesOnTeam(user: WardenUserDecoration<any>, inTeamId: string, inRoleIds: string[], combineWithAnd: boolean): boolean {
233
+ let rval: boolean = false;
234
+ const teamId: string = StringRatchet.trimToNull(inTeamId)?.toLowerCase();
235
+ const roleIds: string[] = inRoleIds ? inRoleIds.map(r=>StringRatchet.trimToNull(r)?.toLowerCase()) : null
236
+ if (user && teamId && roleIds && roleIds.length > 0) {
237
+ const hasMap: boolean[] = user ? roleIds.map(r=>!!(user?.teamRoleMappings?.find(s=>StringRatchet.trimToEmpty(s.teamId).toLowerCase()===teamId && StringRatchet.trimToEmpty(s.roleId).toLowerCase()===r))) : [false];
238
+ rval = combineWithAnd ? BooleanRatchet.allTrue(hasMap) : BooleanRatchet.anyTrue(hasMap);
239
+ }
240
+
241
+ return rval;
242
+ }
243
+
244
+ // Just a synonym since that is how some people think
245
+ public static userIsTeamMember(user: WardenUserDecoration<any>, inTeamId: string): boolean {
246
+ return WardenUtils.userHasAnyRoleOnTeam(user, inTeamId);
247
+ }
248
+
249
+ public static userHasAnyRoleOnTeam(user: WardenUserDecoration<any>, inTeamId: string): boolean {
250
+ let rval: boolean = false;
251
+ const teamId: string = StringRatchet.trimToNull(inTeamId)?.toLowerCase();
252
+ if (user && teamId) {
253
+ rval = !!user.teamRoleMappings.find(s=>StringRatchet.trimToNull(s.teamId).toLowerCase()===teamId);
254
+ }
255
+
256
+ return rval;
257
+ }
258
+
259
+ public static usersTeamMemberships(user: WardenUserDecoration<any>): string[] {
260
+ let rval: string[] = [];
261
+ if (user) {
262
+ const s = new Set<string>(user.teamRoleMappings.map(s=>StringRatchet.trimToNull(s.teamId).toLowerCase()));
263
+ rval = Array.from(s);
264
+ }
265
+ return rval;
266
+ }
267
+
268
+ public static wardenUserDecorationFromToken<T>(jwt: WardenJwtToken<T>): WardenUserDecoration<T> {
269
+ let rval: WardenUserDecoration<any> = null;
270
+ if (jwt) {
271
+ rval = {
272
+ userTokenData: jwt.user,
273
+ proxyUserTokenData: jwt.proxy,
274
+ userTokenExpirationSeconds: null,
275
+
276
+ globalRoleIds: jwt.globalRoleIds,
277
+ teamRoleMappings: jwt.teamRoleMappings
278
+ }
279
+ }
280
+ return rval;
281
+ }
282
+
283
+
284
+ }