reslib 2.1.1 → 2.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/build/auth/errors.d.ts +4 -0
- package/build/auth/errors.js +5 -0
- package/build/auth/index.d.ts +65 -1963
- package/build/auth/index.js +2 -2
- package/build/auth/types.d.ts +42 -0
- package/build/countries/index.js +1 -1
- package/build/currency/index.js +1 -1
- package/build/currency/session.js +1 -1
- package/build/exception/index.js +2 -2
- package/build/i18n/index.js +1 -1
- package/build/inputFormatter/index.js +1 -1
- package/build/logger/index.js +1 -1
- package/build/resources/index.d.ts +5 -5
- package/build/resources/index.js +3 -3
- package/build/session/index.js +1 -1
- package/build/utils/date/dateHelper.js +1 -1
- package/build/utils/date/index.js +1 -1
- package/build/utils/index.js +1 -1
- package/build/utils/json.d.ts +13 -6
- package/build/utils/json.js +1 -1
- package/build/utils/numbers.js +1 -1
- package/build/validator/index.js +3 -3
- package/build/validator/rules/array.js +2 -2
- package/build/validator/rules/boolean.js +2 -2
- package/build/validator/rules/date.js +2 -2
- package/build/validator/rules/default.js +2 -2
- package/build/validator/rules/enum.d.ts +5 -5
- package/build/validator/rules/enum.js +2 -2
- package/build/validator/rules/file.js +2 -2
- package/build/validator/rules/format.d.ts +1 -1
- package/build/validator/rules/format.js +3 -3
- package/build/validator/rules/ifRule.d.ts +76 -0
- package/build/validator/rules/ifRule.js +5 -0
- package/build/validator/rules/index.d.ts +1 -0
- package/build/validator/rules/index.js +3 -3
- package/build/validator/rules/multiRules.js +2 -2
- package/build/validator/rules/numeric.js +2 -2
- package/build/validator/rules/object.js +2 -2
- package/build/validator/rules/string.js +2 -2
- package/build/validator/rules/target.js +2 -2
- package/build/validator/rulesMarkers.d.ts +1 -0
- package/build/validator/rulesMarkers.js +1 -1
- package/build/validator/types.d.ts +399 -48
- package/build/validator/validator.d.ts +425 -5
- package/build/validator/validator.js +2 -2
- package/package.json +1 -3
package/build/auth/index.d.ts
CHANGED
|
@@ -2,1957 +2,90 @@ import { Observable } from '../observable';
|
|
|
2
2
|
import { ClassConstructor } from '../types';
|
|
3
3
|
import { ResourceActionName, ResourceName } from '../resources/types';
|
|
4
4
|
import 'reflect-metadata';
|
|
5
|
-
import { Dictionary } from '../types/dictionary';
|
|
6
5
|
import './types';
|
|
7
|
-
import { AuthEvent, AuthPerm, AuthPerms, AuthSecureStorage,
|
|
6
|
+
import { AuthConfig, AuthEvent, AuthPerm, AuthPerms, AuthSecureStorage, AuthUser } from './types';
|
|
7
|
+
export * from './errors';
|
|
8
8
|
export * from './types';
|
|
9
|
-
|
|
10
|
-
* A class that provides methods for managing session data.
|
|
11
|
-
*
|
|
12
|
-
*/
|
|
13
|
-
declare class Session {
|
|
14
|
-
/**
|
|
15
|
-
* Retrieves a specific value from the session data based on the provided session name and key.
|
|
16
|
-
*
|
|
17
|
-
* This function first checks if the provided key is a non-null string. If it is not, it returns undefined.
|
|
18
|
-
* It then retrieves the session data using `getData` and returns the value associated with the specified key.
|
|
19
|
-
*
|
|
20
|
-
* @param sessionName - An optional string that represents the name of the session.
|
|
21
|
-
* @param key - A string representing the key of the value to retrieve from the session data.
|
|
22
|
-
*
|
|
23
|
-
* @returns The value associated with the specified key in the session data, or undefined if the key is invalid.
|
|
24
|
-
*
|
|
25
|
-
* @example
|
|
26
|
-
* // Example of retrieving a specific value from session data
|
|
27
|
-
* const value = get('mySession', 'userPreference'); // Returns: 'darkMode'
|
|
28
|
-
*
|
|
29
|
-
* @example
|
|
30
|
-
* // Example of trying to retrieve a value with an invalid key
|
|
31
|
-
* const value = get('mySession', null); // Returns: undefined
|
|
32
|
-
*/
|
|
33
|
-
static get(sessionName?: string, key?: string): any;
|
|
34
|
-
/**
|
|
35
|
-
* Sets a value or an object in the session data for a specific session name.
|
|
36
|
-
*
|
|
37
|
-
* This function retrieves the current session data using `getData`. If a valid key is provided, it sets the
|
|
38
|
-
* corresponding value in the session data. If an object is provided as the key, it replaces the entire session data.
|
|
39
|
-
* Finally, it saves the updated session data back to the session storage.
|
|
40
|
-
*
|
|
41
|
-
* @param sessionName - An optional string that represents the name of the session.
|
|
42
|
-
* @param key - A string representing the key to set a value for, or an object containing multiple key-value pairs.
|
|
43
|
-
* @param value - The value to set for the specified key. This parameter is ignored if an object is provided as the key.
|
|
44
|
-
*
|
|
45
|
-
* @returns The updated session data as an object.
|
|
46
|
-
*
|
|
47
|
-
* @example
|
|
48
|
-
* // Example of setting a single value in session data
|
|
49
|
-
* const updatedData = set('mySession', 'userPreference', 'darkMode'); // Returns: { userPreference: 'darkMode' }
|
|
50
|
-
*
|
|
51
|
-
* @example
|
|
52
|
-
* // Example of replacing the entire session data with an object
|
|
53
|
-
* const updatedData = set('mySession', { userPreference: 'lightMode', language: ' English' }); // Returns: { userPreference: 'lightMode', language: 'English' }
|
|
54
|
-
*/
|
|
55
|
-
static set(sessionName?: string, key?: string | Dictionary, value?: any): Dictionary;
|
|
56
|
-
/**
|
|
57
|
-
* Generates a unique session key based on the authenticated user's ID and an optional session name.
|
|
58
|
-
*
|
|
59
|
-
* The session key is constructed in the format: `auth-{userId}-{sessionName}`. If the user is not signed in,
|
|
60
|
-
* the user ID will be an empty string. This key is used to store and retrieve session data.
|
|
61
|
-
*
|
|
62
|
-
* @param sessionName - An optional string that represents the name of the session. If not provided,
|
|
63
|
-
* an empty string will be used in the key.
|
|
64
|
-
*
|
|
65
|
-
* @returns A string representing the unique session key.
|
|
66
|
-
*
|
|
67
|
-
* @example
|
|
68
|
-
* // Example of generating a session key for a user with ID '12345'
|
|
69
|
-
* const sessionKey = getKey('mySession'); // Returns: 'auth-12345-mySession'
|
|
70
|
-
*
|
|
71
|
-
* @example
|
|
72
|
-
* // Example of generating a session key when no user is signed in
|
|
73
|
-
* const sessionKey = getKey(); // Returns: 'auth--'
|
|
74
|
-
*/
|
|
75
|
-
static getKey(sessionName?: string): string;
|
|
76
|
-
/**
|
|
77
|
-
* Retrieves session data associated with a specific session name.
|
|
78
|
-
*
|
|
79
|
-
* This function checks if the provided session name is a non-null string. If it is not, an empty object is returned.
|
|
80
|
-
* Otherwise, it constructs a key using `getKey` and retrieves the corresponding data from the session storage.
|
|
81
|
-
*
|
|
82
|
-
* @param sessionName - An optional string that represents the name of the session. If not provided or invalid,
|
|
83
|
-
* an empty object will be returned.
|
|
84
|
-
*
|
|
85
|
-
* @returns An object containing the session data associated with the specified session name.
|
|
86
|
-
* If the session name is invalid, an empty object is returned.
|
|
87
|
-
*
|
|
88
|
-
* @example
|
|
89
|
-
* // Example of retrieving session data for a specific session name
|
|
90
|
-
* const sessionData = getData('mySession'); // Returns: { }
|
|
91
|
-
*
|
|
92
|
-
* @example
|
|
93
|
-
* // Example of retrieving session data with an invalid session name
|
|
94
|
-
* const sessionData = getData(null); // Returns: {}
|
|
95
|
-
*/
|
|
96
|
-
static getData(sessionName?: string): Dictionary;
|
|
97
|
-
/**
|
|
98
|
-
* Retrieves a session storage object that provides methods for managing session data.
|
|
99
|
-
*
|
|
100
|
-
* This function creates an object that allows you to interact with session storage
|
|
101
|
-
* using a specified session name. It provides methods to get, set, and retrieve data
|
|
102
|
-
* associated with the session, as well as to retrieve the session key.
|
|
103
|
-
*
|
|
104
|
-
* @param sessionName - An optional string that represents the name of the session.
|
|
105
|
-
* If provided, it will be used as a prefix for the keys stored
|
|
106
|
-
* in session storage. If not provided, the session will be
|
|
107
|
-
* treated as anonymous.
|
|
108
|
-
*
|
|
109
|
-
* @returns An object implementing the `AuthSessionStorage` interface, which includes
|
|
110
|
-
* methods for session management:
|
|
111
|
-
* - `get(key?: string): any`: Retrieves the value associated with the specified key.
|
|
112
|
-
* - `set(key?: string | Dictionary, value?: any): void`: Stores a value under the specified key.
|
|
113
|
-
* - `getData(): Dictionary`: Returns all data stored in the session as a dictionary.
|
|
114
|
-
* - `getKey(): string`: Returns the session key used for storage.
|
|
115
|
-
*
|
|
116
|
-
* @example
|
|
117
|
-
* // Create a session storage object with a specific session name
|
|
118
|
-
* const session = getSessionStorage('userSession');
|
|
119
|
-
*
|
|
120
|
-
* // Get the session key
|
|
121
|
-
* const sessionKey = session.getKey(); // 'userSession'
|
|
122
|
-
*
|
|
123
|
-
* @remarks
|
|
124
|
-
* This function is particularly useful for managing user authentication sessions
|
|
125
|
-
* in web applications. By using session storage, data persists across page reloads
|
|
126
|
-
* but is cleared when the tab or browser is closed.
|
|
127
|
-
*
|
|
128
|
-
* Ensure that the keys used for storing data are unique to avoid collisions with
|
|
129
|
-
* other session data. Consider using a structured naming convention for keys.
|
|
130
|
-
*/
|
|
131
|
-
static getStorage(sessionName?: string): AuthSessionStorage;
|
|
132
|
-
}
|
|
133
|
-
export declare class Auth {
|
|
134
|
-
/**
|
|
135
|
-
* Authentication event handler.
|
|
136
|
-
* Initializes an observable event handler for authentication Auth.events.
|
|
137
|
-
*
|
|
138
|
-
* This constant `events` is assigned an instance of `Observable<AuthEvent>`, which is used to manage
|
|
139
|
-
* authentication-related events in the application. The initialization checks if the global
|
|
140
|
-
* `Global.eventsResourcesObservableHandler` exists and is an object. If it does, it assigns it to
|
|
141
|
-
* `events`; otherwise, it defaults to an empty object cast as `Observable<AuthEvent>`.
|
|
142
|
-
*
|
|
143
|
-
* This pattern allows for flexible handling of events, ensuring that the application can respond
|
|
144
|
-
* to authentication actions such as sign-in, sign-out, and sign-up.
|
|
145
|
-
*
|
|
146
|
-
* @type {Observable<AuthEvent>}
|
|
147
|
-
*
|
|
148
|
-
* @example
|
|
149
|
-
* import {Auth} from 'reslib/auth';
|
|
150
|
-
* Auth.events.on('SIGN_IN', (user) => {
|
|
151
|
-
* console.log(`User signed in: ${user.username}`);
|
|
152
|
-
* });
|
|
153
|
-
*
|
|
154
|
-
* function userSignIn(user) {
|
|
155
|
-
* Auth.events.trigger('SIGN_IN', user);
|
|
156
|
-
* }
|
|
157
|
-
*/
|
|
158
|
-
static events: Observable<AuthEvent>;
|
|
159
|
-
private static localUserRef;
|
|
160
|
-
/**
|
|
161
|
-
* Checks if the user is a master admin.
|
|
162
|
-
*
|
|
163
|
-
* The `isMasterAdmin` function determines whether the provided user
|
|
164
|
-
* has master admin privileges. If no user is provided, it will
|
|
165
|
-
* attempt to retrieve the signed user from the session.
|
|
166
|
-
*
|
|
167
|
-
* ### Parameters
|
|
168
|
-
*
|
|
169
|
-
* - `user` (AuthUser , optional): The user object to check. If not
|
|
170
|
-
* provided, the function will attempt to retrieve the signed user
|
|
171
|
-
* from the session.
|
|
172
|
-
*
|
|
173
|
-
* ### Returns
|
|
174
|
-
*
|
|
175
|
-
* - `boolean`: Returns `true` if the user is a master admin, or `false` otherwise.
|
|
176
|
-
*
|
|
177
|
-
* ### Example Usage
|
|
178
|
-
*
|
|
179
|
-
* ```typescript
|
|
180
|
-
* const user: AuthUser = { id: "admin123" };
|
|
181
|
-
* Auth.isMasterAdmin = (user)=>{
|
|
182
|
-
* return checkSomeCondition(user);
|
|
183
|
-
* }; // false (assuming the user is not a master admin)
|
|
184
|
-
* ```
|
|
185
|
-
* @see {@link AuthUser} for the `AuthUser` type.
|
|
186
|
-
*/
|
|
187
|
-
static isMasterAdmin?: (user?: AuthUser) => boolean;
|
|
188
|
-
private static _isMasterAdmin;
|
|
189
|
-
/**
|
|
190
|
-
* Retrieves the currently authenticated user from secure session storage.
|
|
191
|
-
*
|
|
192
|
-
* This method implements a secure user session retrieval system that handles encrypted
|
|
193
|
-
* user data storage. It first checks for a cached user reference in memory for performance
|
|
194
|
-
* optimization, then attempts to decrypt and parse the user data from session storage
|
|
195
|
-
* if no cached version exists. The method includes comprehensive error handling for
|
|
196
|
-
* decryption failures and data corruption scenarios.
|
|
197
|
-
*
|
|
198
|
-
* ### Security Features:
|
|
199
|
-
* - **Encrypted Storage**: User data is encrypted using AES encryption before storage
|
|
200
|
-
* - **Memory Caching**: Cached in `localUserRef` for improved performance and reduced decryption overhead
|
|
201
|
-
* - **Safe Parsing**: Uses `JsonHelper.parse` for secure JSON deserialization
|
|
202
|
-
* - **Error Recovery**: Gracefully handles corrupted or invalid session data
|
|
203
|
-
*
|
|
204
|
-
* ### Performance Optimization:
|
|
205
|
-
* The method implements a two-tier retrieval strategy:
|
|
206
|
-
* 1. **Memory Cache**: Returns immediately if user is already loaded in memory
|
|
207
|
-
* 2. **Session Storage**: Decrypts and parses data from persistent storage only when needed
|
|
208
|
-
*
|
|
209
|
-
* @returns The authenticated user object containing user information, permissions, and roles,
|
|
210
|
-
* or `null` if no user is currently signed in, session data is corrupted, or
|
|
211
|
-
* decryption fails. The returned object conforms to the `AuthUser` interface.
|
|
212
|
-
*
|
|
213
|
-
* @example
|
|
214
|
-
* ```typescript
|
|
215
|
-
* // Basic usage - check if user is signed in
|
|
216
|
-
* const currentUser = Auth.getSignedUser();
|
|
217
|
-
* if (currentUser) {
|
|
218
|
-
* console.log(`Welcome back, ${currentUser.username}!`);
|
|
219
|
-
* console.log(`User ID: ${currentUser.id}`);
|
|
220
|
-
* } else {
|
|
221
|
-
* console.log("No user is currently signed in");
|
|
222
|
-
* }
|
|
223
|
-
* ```
|
|
224
|
-
*
|
|
225
|
-
* @example
|
|
226
|
-
* ```typescript
|
|
227
|
-
* // Using with permission checking
|
|
228
|
-
* const user = Auth.getSignedUser();
|
|
229
|
-
* if (user) {
|
|
230
|
-
* const canEditDocuments = Auth.checkUserPermission(user, "documents", "update");
|
|
231
|
-
* if (canEditDocuments) {
|
|
232
|
-
* showEditButton();
|
|
233
|
-
* }
|
|
234
|
-
* }
|
|
235
|
-
* ```
|
|
236
|
-
*
|
|
237
|
-
* @example
|
|
238
|
-
* ```typescript
|
|
239
|
-
* // Handling authentication state in React component
|
|
240
|
-
* function UserProfile() {
|
|
241
|
-
* const [user, setUser] = useState<AuthUser | null>(null);
|
|
242
|
-
*
|
|
243
|
-
* useEffect(() => {
|
|
244
|
-
* const currentUser = Auth.getSignedUser();
|
|
245
|
-
* setUser(currentUser);
|
|
246
|
-
*
|
|
247
|
-
* if (!currentUser) {
|
|
248
|
-
* // Redirect to login page
|
|
249
|
-
* router.push('/login');
|
|
250
|
-
* }
|
|
251
|
-
* }, []);
|
|
252
|
-
*
|
|
253
|
-
* return user ? <div>Hello, {user.username}</div> : <div>Loading...</div>;
|
|
254
|
-
* }
|
|
255
|
-
* ```
|
|
256
|
-
*
|
|
257
|
-
* @example
|
|
258
|
-
* ```typescript
|
|
259
|
-
* // Using with authentication guards
|
|
260
|
-
* class AuthGuard {
|
|
261
|
-
* static requireAuth(): boolean {
|
|
262
|
-
* const user = Auth.getSignedUser();
|
|
263
|
-
* if (!user) {
|
|
264
|
-
* throw new Error("Authentication required");
|
|
265
|
-
* }
|
|
266
|
-
* return true;
|
|
267
|
-
* }
|
|
268
|
-
*
|
|
269
|
-
* static requireRole(roleName: string): boolean {
|
|
270
|
-
* const user = Auth.getSignedUser();
|
|
271
|
-
* return user?.roles?.some(role => role.name === roleName) ?? false;
|
|
272
|
-
* }
|
|
273
|
-
* }
|
|
274
|
-
* ```
|
|
275
|
-
*
|
|
276
|
-
* @example
|
|
277
|
-
*
|
|
278
|
-
* @throws {CryptoError} May throw during decryption if session data is corrupted
|
|
279
|
-
* @throws {SyntaxError} May throw during JSON parsing if decrypted data is malformed
|
|
280
|
-
*
|
|
281
|
-
* @see {@link AuthUser} - Complete user object interface definition
|
|
282
|
-
* @see {@link setSignedUser} - Method to store user in session
|
|
283
|
-
* @see {@link signIn} - High-level user authentication method
|
|
284
|
-
* @see {@link signOut} - Method to clear user session
|
|
285
|
-
* @see {@link checkUserPermission} - Check specific user permissions
|
|
286
|
-
*
|
|
287
|
-
*
|
|
288
|
-
* @public
|
|
289
|
-
*
|
|
290
|
-
* @remarks
|
|
291
|
-
* **Security Considerations:**
|
|
292
|
-
* - Session data is automatically encrypted using AES encryption
|
|
293
|
-
* - The encryption key `SESSION_ENCRYPT_KEY` should be kept secure
|
|
294
|
-
* - User data includes sensitive information like tokens and permissions
|
|
295
|
-
* - Always validate user data before using in security-critical operations
|
|
296
|
-
*
|
|
297
|
-
* **Performance Notes:**
|
|
298
|
-
* - First call after page load requires decryption (slower)
|
|
299
|
-
* - Subsequent calls return cached data (faster)
|
|
300
|
-
* - Cache is automatically cleared when user signs out
|
|
301
|
-
* - Consider the performance impact in render loops
|
|
302
|
-
*
|
|
303
|
-
* **Error Handling:**
|
|
304
|
-
* - Returns `null` instead of throwing errors for better UX
|
|
305
|
-
* - Logs errors to console for debugging purposes
|
|
306
|
-
* - Gracefully handles session storage corruption
|
|
307
|
-
* - Automatically recovers from temporary decryption failures
|
|
308
|
-
*/
|
|
309
|
-
static getSignedUser(): AuthUser | null;
|
|
310
|
-
/**
|
|
311
|
-
* Securely stores an authenticated user in encrypted session storage with event broadcasting.
|
|
312
|
-
*
|
|
313
|
-
* This method is the core user session management function that handles secure storage of user
|
|
314
|
-
* authentication data. It encrypts user information using AES encryption before persisting to
|
|
315
|
-
* session storage, maintains local memory cache for performance, and optionally broadcasts
|
|
316
|
-
* authentication events to notify other parts of the application about user state changes.
|
|
317
|
-
*
|
|
318
|
-
* ### Security Architecture:
|
|
319
|
-
* - **AES Encryption**: User data is encrypted before storage to protect sensitive information
|
|
320
|
-
* - **Session Timestamping**: Automatically adds `sessionCreatedAt` timestamp for session tracking
|
|
321
|
-
* - **Error Isolation**: Encryption failures don't crash the application, user reference is safely cleared
|
|
322
|
-
* - **Memory Management**: Updates local cache reference for immediate access
|
|
323
|
-
*
|
|
324
|
-
* ### Event System Integration:
|
|
325
|
-
* The method integrates with the authentication event system to broadcast state changes:
|
|
326
|
-
* - **SIGN_IN Event**: Triggered when a valid user is stored with successful encryption
|
|
327
|
-
* - **SIGN_OUT Event**: Triggered when user is cleared (null) or encryption fails
|
|
328
|
-
* - **Event Payload**: Includes the user object for event handlers to process
|
|
329
|
-
*
|
|
330
|
-
* @param u - The user object to store in session, or `null` to clear the current session.
|
|
331
|
-
* When providing a user object, it should contain all necessary authentication
|
|
332
|
-
* information including permissions, roles, and tokens. The object will be
|
|
333
|
-
* automatically timestamped with `sessionCreatedAt`.
|
|
334
|
-
*
|
|
335
|
-
* @param triggerEvent - Optional flag controlling whether to broadcast authentication events.
|
|
336
|
-
* When `true` (default), triggers either 'SIGN_IN' or 'SIGN_OUT' events
|
|
337
|
-
* based on the operation result. Set to `false` to perform silent
|
|
338
|
-
* session updates without notifying event listeners.
|
|
339
|
-
*
|
|
340
|
-
* @returns A promise that resolves to the result of the session storage operation.
|
|
341
|
-
* The promise completes after the encrypted data has been written to storage.
|
|
342
|
-
* Returns the storage operation result, typically indicating success or failure.
|
|
343
|
-
*
|
|
344
|
-
* @example
|
|
345
|
-
* ```typescript
|
|
346
|
-
* // Standard user sign-in with event broadcasting
|
|
347
|
-
* const user: AuthUser = {
|
|
348
|
-
* id: "user123",
|
|
349
|
-
* username: "john_doe",
|
|
350
|
-
* email: "john@example.com",
|
|
351
|
-
* perms: { documents: ["read", "write"] },
|
|
352
|
-
* roles: [{ name: "editor", perms: { images: ["upload"] } }]
|
|
353
|
-
* };
|
|
354
|
-
*
|
|
355
|
-
* await Auth.setSignedUser(user, true);
|
|
356
|
-
* console.log("User signed in with events triggered");
|
|
357
|
-
* ```
|
|
358
|
-
*
|
|
359
|
-
* @example
|
|
360
|
-
* ```typescript
|
|
361
|
-
* // Silent session update without triggering events
|
|
362
|
-
* const updatedUser = { ...currentUser, lastActivity: new Date() };
|
|
363
|
-
* await Auth.setSignedUser(updatedUser, false);
|
|
364
|
-
* console.log("User session updated silently");
|
|
365
|
-
* ```
|
|
366
|
-
*
|
|
367
|
-
* @example
|
|
368
|
-
* ```typescript
|
|
369
|
-
* // Clear user session (sign out)
|
|
370
|
-
* await Auth.setSignedUser(null, true);
|
|
371
|
-
* console.log("User signed out, SIGN_OUT event triggered");
|
|
372
|
-
* ```
|
|
373
|
-
*
|
|
374
|
-
* @example
|
|
375
|
-
* ```typescript
|
|
376
|
-
* // Using with event listeners
|
|
377
|
-
* Auth.events.on('SIGN_IN', (user) => {
|
|
378
|
-
* console.log(`Welcome ${user.username}!`);
|
|
379
|
-
* initializeUserDashboard(user);
|
|
380
|
-
* trackUserLogin(user.id);
|
|
381
|
-
* });
|
|
382
|
-
*
|
|
383
|
-
* Auth.events.on('SIGN_OUT', () => {
|
|
384
|
-
* console.log('User signed out');
|
|
385
|
-
* clearUserDashboard();
|
|
386
|
-
* redirectToLogin();
|
|
387
|
-
* });
|
|
388
|
-
*
|
|
389
|
-
* // Now when we set a user, events will fire automatically
|
|
390
|
-
* await Auth.setSignedUser(authenticatedUser);
|
|
391
|
-
* ```
|
|
392
|
-
*
|
|
393
|
-
* @example
|
|
394
|
-
* ```typescript
|
|
395
|
-
* // Error handling with session management
|
|
396
|
-
* try {
|
|
397
|
-
* await Auth.setSignedUser(userFromAPI);
|
|
398
|
-
* showSuccessMessage("Login successful");
|
|
399
|
-
* } catch (error) {
|
|
400
|
-
* console.error("Failed to store user session:", error);
|
|
401
|
-
* showErrorMessage("Login failed, please try again");
|
|
402
|
-
* }
|
|
403
|
-
* ```
|
|
404
|
-
*
|
|
405
|
-
* @example
|
|
406
|
-
* ```typescript
|
|
407
|
-
* // Updating user permissions after role change
|
|
408
|
-
* const currentUser = Auth.getSignedUser();
|
|
409
|
-
* if (currentUser) {
|
|
410
|
-
* const updatedUser = {
|
|
411
|
-
* ...currentUser,
|
|
412
|
-
* roles: [...currentUser.roles, newRole],
|
|
413
|
-
* perms: { ...currentUser.perms, ...newPermissions }
|
|
414
|
-
* };
|
|
415
|
-
*
|
|
416
|
-
* await Auth.setSignedUser(updatedUser, false); // Silent update
|
|
417
|
-
* console.log("User permissions updated");
|
|
418
|
-
* }
|
|
419
|
-
* ```
|
|
420
|
-
*
|
|
421
|
-
* @throws {CryptoError} May throw if encryption fails due to invalid encryption key
|
|
422
|
-
* @throws {StorageError} May throw if session storage is unavailable or quota exceeded
|
|
423
|
-
*
|
|
424
|
-
* @see {@link AuthUser} - Complete user object interface with all required fields
|
|
425
|
-
* @see {@link getSignedUser} - Retrieve the currently stored user from session
|
|
426
|
-
* @see {@link signIn} - High-level user authentication wrapper
|
|
427
|
-
* @see {@link signOut} - High-level user sign-out wrapper
|
|
428
|
-
* @see {@link Auth.events} - Authentication event system for state change notifications
|
|
429
|
-
* @see {@link AuthEvent} - Available authentication event types and payloads
|
|
430
|
-
*
|
|
431
|
-
*
|
|
432
|
-
* @public
|
|
433
|
-
* @async
|
|
434
|
-
*
|
|
435
|
-
* @remarks
|
|
436
|
-
* **Security Best Practices:**
|
|
437
|
-
* - User objects should be validated before storage
|
|
438
|
-
* - Sensitive data like tokens are automatically encrypted
|
|
439
|
-
* - Session timestamps help with security auditing
|
|
440
|
-
* - Always handle encryption errors gracefully
|
|
441
|
-
*
|
|
442
|
-
* **Performance Considerations:**
|
|
443
|
-
* - Encryption/decryption adds computational overhead
|
|
444
|
-
* - Local cache is updated synchronously for immediate access
|
|
445
|
-
* - Event broadcasting may trigger multiple listeners
|
|
446
|
-
* - Consider batching multiple user updates to reduce I/O
|
|
447
|
-
*
|
|
448
|
-
* **Event System:**
|
|
449
|
-
* - Events are asynchronous and don't block the storage operation
|
|
450
|
-
* - Multiple listeners can subscribe to the same event
|
|
451
|
-
* - Event payload includes the full user object for flexibility
|
|
452
|
-
* - Use `triggerEvent: false` for internal operations to avoid recursion
|
|
453
|
-
*/
|
|
454
|
-
static setSignedUser(u: AuthUser | null, triggerEvent?: boolean): Promise<AuthUser | null>;
|
|
455
|
-
/**
|
|
456
|
-
* Authenticates a user and establishes a secure session with comprehensive validation.
|
|
457
|
-
*
|
|
458
|
-
* This high-level authentication method provides a complete user sign-in workflow with
|
|
459
|
-
* input validation, secure session establishment, and event broadcasting. It serves as
|
|
460
|
-
* the primary entry point for user authentication in applications, handling all the
|
|
461
|
-
* complexity of secure session management while providing a simple, promise-based API.
|
|
462
|
-
*
|
|
463
|
-
* ### Authentication Workflow:
|
|
464
|
-
* 1. **Input Validation**: Validates that the provided user object is properly structured
|
|
465
|
-
* 2. **Session Creation**: Calls `setSignedUser` to securely store user data
|
|
466
|
-
* 3. **Event Broadcasting**: Optionally triggers authentication events for app-wide notifications
|
|
467
|
-
* 4. **Return Confirmation**: Returns the authenticated user object on successful completion
|
|
468
|
-
*
|
|
469
|
-
* ### Security Features:
|
|
470
|
-
* - **Object Validation**: Ensures user object is valid before processing
|
|
471
|
-
* - **Encrypted Storage**: Leverages `setSignedUser` for secure data persistence
|
|
472
|
-
* - **Error Handling**: Provides meaningful error messages for failed authentication
|
|
473
|
-
* - **Session Timestamping**: Automatically tracks when authentication session was created
|
|
474
|
-
*
|
|
475
|
-
* @param user - The authenticated user object containing all necessary user information.
|
|
476
|
-
* Must be a valid object conforming to the `AuthUser` interface, including
|
|
477
|
-
* properties like id, username, email, permissions, roles.
|
|
478
|
-
* The object should come from a successful authentication process (login API, OAuth, etc.).
|
|
479
|
-
*
|
|
480
|
-
* @param triggerEvent - Optional flag controlling whether to broadcast authentication events.
|
|
481
|
-
* When `true` (default), triggers a 'SIGN_IN' event that other parts of
|
|
482
|
-
* the application can listen to for initialization, analytics, or UI updates.
|
|
483
|
-
* Set to `false` for silent authentication without event notifications.
|
|
484
|
-
*
|
|
485
|
-
* @returns A promise that resolves to the authenticated user object when sign-in is successful.
|
|
486
|
-
* The returned user object is the same as the input but may include additional
|
|
487
|
-
* properties added during the authentication process (like session timestamps).
|
|
488
|
-
*
|
|
489
|
-
* @throws {Error} Throws an error with internationalized message if the user object is invalid,
|
|
490
|
-
* null, undefined, or not a proper object structure. The error message is
|
|
491
|
-
* retrieved from the i18n system using key "auth.invalidSignInUser".
|
|
492
|
-
*
|
|
493
|
-
* @example
|
|
494
|
-
* ```typescript
|
|
495
|
-
* // Basic user sign-in after successful API authentication
|
|
496
|
-
* try {
|
|
497
|
-
* const response = await fetch('/api/auth/login', {
|
|
498
|
-
* method: 'POST',
|
|
499
|
-
* body: JSON.stringify({ username, password }),
|
|
500
|
-
* headers: { 'Content-Type': 'application/json' }
|
|
501
|
-
* });
|
|
502
|
-
*
|
|
503
|
-
* const userData = await response.json();
|
|
504
|
-
* const authenticatedUser = await Auth.signIn(userData);
|
|
505
|
-
*
|
|
506
|
-
* console.log(`Welcome ${authenticatedUser.username}!`);
|
|
507
|
-
* router.push('/dashboard');
|
|
508
|
-
* } catch (error) {
|
|
509
|
-
* console.error('Sign-in failed:', error.message);
|
|
510
|
-
* showErrorMessage('Invalid credentials');
|
|
511
|
-
* }
|
|
512
|
-
* ```
|
|
513
|
-
*
|
|
514
|
-
* @example
|
|
515
|
-
* ```typescript
|
|
516
|
-
* // OAuth authentication workflow
|
|
517
|
-
* async function handleOAuthCallback(authCode: string) {
|
|
518
|
-
* try {
|
|
519
|
-
* // Exchange auth code for user data
|
|
520
|
-
* const tokenResponse = await exchangeCodeForToken(authCode);
|
|
521
|
-
* const userProfile = await fetchUserProfile(tokenResponse.access_token);
|
|
522
|
-
*
|
|
523
|
-
* const user: AuthUser = {
|
|
524
|
-
* id: userProfile.id,
|
|
525
|
-
* username: userProfile.login,
|
|
526
|
-
* email: userProfile.email,
|
|
527
|
-
* perms: await fetchUserPermissions(userProfile.id),
|
|
528
|
-
* roles: await fetchUserRoles(userProfile.id),
|
|
529
|
-
* provider: 'oauth'
|
|
530
|
-
* };
|
|
531
|
-
*
|
|
532
|
-
* await Auth.signIn(user);
|
|
533
|
-
* console.log('OAuth sign-in successful');
|
|
534
|
-
* } catch (error) {
|
|
535
|
-
* console.error('OAuth sign-in failed:', error);
|
|
536
|
-
* }
|
|
537
|
-
* }
|
|
538
|
-
* ```
|
|
539
|
-
*
|
|
540
|
-
* @example
|
|
541
|
-
* ```typescript
|
|
542
|
-
* // Silent authentication without triggering events
|
|
543
|
-
* async function silentAuth(sessionToken: string) {
|
|
544
|
-
* try {
|
|
545
|
-
* const userData = await validateSessionToken(sessionToken);
|
|
546
|
-
* const user = await Auth.signIn(userData, false); // No events
|
|
547
|
-
*
|
|
548
|
-
* console.log('Silent authentication successful');
|
|
549
|
-
* return user;
|
|
550
|
-
* } catch (error) {
|
|
551
|
-
* console.log('Silent auth failed, user needs to login');
|
|
552
|
-
* return null;
|
|
553
|
-
* }
|
|
554
|
-
* }
|
|
555
|
-
* ```
|
|
556
|
-
*
|
|
557
|
-
* @example
|
|
558
|
-
* ```typescript
|
|
559
|
-
* // Complete authentication flow with event handling
|
|
560
|
-
* // Set up event listeners first
|
|
561
|
-
* Auth.events.on('SIGN_IN', (user) => {
|
|
562
|
-
* // Initialize user-specific features
|
|
563
|
-
* initializeUserPreferences(user.id);
|
|
564
|
-
* loadUserDashboard(user.perms);
|
|
565
|
-
* trackAnalyticsEvent('user_signin', { userId: user.id });
|
|
566
|
-
*
|
|
567
|
-
* // Update UI state
|
|
568
|
-
* updateNavigationForUser(user.roles);
|
|
569
|
-
* showWelcomeMessage(user.username);
|
|
570
|
-
* });
|
|
571
|
-
*
|
|
572
|
-
* // Perform authentication
|
|
573
|
-
* async function loginUser(credentials: LoginCredentials) {
|
|
574
|
-
* try {
|
|
575
|
-
* const authResult = await authenticateWithAPI(credentials);
|
|
576
|
-
* const user = await Auth.signIn(authResult.user); // Events will fire
|
|
577
|
-
*
|
|
578
|
-
* return { success: true, user };
|
|
579
|
-
* } catch (error) {
|
|
580
|
-
* return { success: false, error: error.message };
|
|
581
|
-
* }
|
|
582
|
-
* }
|
|
583
|
-
* ```
|
|
584
|
-
*
|
|
585
|
-
* @example
|
|
586
|
-
* ```typescript
|
|
587
|
-
* // Multi-step authentication with role-based redirection
|
|
588
|
-
* async function signInWithRoleRedirect(userData: any) {
|
|
589
|
-
* try {
|
|
590
|
-
* const user = await Auth.signIn(userData);
|
|
591
|
-
*
|
|
592
|
-
* // Redirect based on user role
|
|
593
|
-
* if (user.roles?.some(role => role.name === 'admin')) {
|
|
594
|
-
* router.push('/admin/dashboard');
|
|
595
|
-
* } else if (user.roles?.some(role => role.name === 'moderator')) {
|
|
596
|
-
* router.push('/moderator/panel');
|
|
597
|
-
* } else {
|
|
598
|
-
* router.push('/user/dashboard');
|
|
599
|
-
* }
|
|
600
|
-
*
|
|
601
|
-
* return user;
|
|
602
|
-
* } catch (error) {
|
|
603
|
-
* console.error('Role-based sign-in failed:', error);
|
|
604
|
-
* throw error;
|
|
605
|
-
* }
|
|
606
|
-
* }
|
|
607
|
-
* ```
|
|
608
|
-
*
|
|
609
|
-
* @see {@link AuthUser} - Complete user object interface specification
|
|
610
|
-
* @see {@link setSignedUser} - Lower-level method for secure user storage
|
|
611
|
-
* @see {@link getSignedUser} - Retrieve currently authenticated user
|
|
612
|
-
* @see {@link signOut} - Sign out and clear user session
|
|
613
|
-
* @see {@link Auth.events} - Authentication event system for state notifications
|
|
614
|
-
* @see {@link isAllowed} - Check user permissions for access control
|
|
615
|
-
*
|
|
616
|
-
*
|
|
617
|
-
* @public
|
|
618
|
-
* @async
|
|
619
|
-
*
|
|
620
|
-
* @remarks
|
|
621
|
-
* **Best Practices:**
|
|
622
|
-
* - Always validate user data before calling this method
|
|
623
|
-
* - Use try-catch blocks to handle authentication failures gracefully
|
|
624
|
-
* - Use event listeners to initialize user-specific application features
|
|
625
|
-
*
|
|
626
|
-
* **Error Handling:**
|
|
627
|
-
* - The method throws immediately on invalid input for fast failure
|
|
628
|
-
* - Use internationalized error messages for better user experience
|
|
629
|
-
* - Consider logging authentication attempts for security monitoring
|
|
630
|
-
* - Implement retry logic for transient authentication failures
|
|
631
|
-
*
|
|
632
|
-
* **Integration Notes:**
|
|
633
|
-
* - Works seamlessly with any authentication provider (JWT, OAuth, custom)
|
|
634
|
-
* - Integrates with the permission system for access control
|
|
635
|
-
* - Compatible with SSR/SPA applications through secure session storage
|
|
636
|
-
* - Supports both traditional and modern authentication workflows
|
|
637
|
-
*/
|
|
638
|
-
static signIn(user: AuthUser, triggerEvent?: boolean): Promise<AuthUser>;
|
|
639
|
-
/**
|
|
640
|
-
* Signs out the currently authenticated user and securely clears their session.
|
|
641
|
-
*
|
|
642
|
-
* This method provides a high-level, convenient interface for user sign-out operations.
|
|
643
|
-
* It handles the complete user session termination workflow by clearing the encrypted
|
|
644
|
-
* user data from session storage, removing the cached user reference from memory, and
|
|
645
|
-
* optionally broadcasting sign-out events to notify other parts of the application
|
|
646
|
-
* about the authentication state change.
|
|
647
|
-
*
|
|
648
|
-
* ### Sign-Out Workflow:
|
|
649
|
-
* 1. **Session Clearing**: Calls `setSignedUser(null)` to remove user data from encrypted storage
|
|
650
|
-
* 2. **Memory Cleanup**: Clears the local user reference cache for immediate effect
|
|
651
|
-
* 3. **Event Broadcasting**: Optionally triggers 'SIGN_OUT' events for application-wide notifications
|
|
652
|
-
* 4. **Security Cleanup**: Ensures no sensitive user data remains in browser storage
|
|
653
|
-
*
|
|
654
|
-
* ### Security Features:
|
|
655
|
-
* - **Complete Data Removal**: Eliminates all traces of user session from storage
|
|
656
|
-
* - **Memory Safety**: Clears in-memory user references to prevent data leakage
|
|
657
|
-
* - **Event Coordination**: Allows other components to perform cleanup operations
|
|
658
|
-
* - **Immediate Effect**: Session termination is effective immediately after method completion
|
|
659
|
-
*
|
|
660
|
-
* ### Application Integration:
|
|
661
|
-
* The method integrates seamlessly with the authentication event system, allowing
|
|
662
|
-
* other parts of the application to react to sign-out events by clearing user-specific
|
|
663
|
-
* data, redirecting to login pages, or performing cleanup operations.
|
|
664
|
-
*
|
|
665
|
-
* @param triggerEvent - Optional flag controlling whether to broadcast authentication events.
|
|
666
|
-
* When `true` (default), triggers a 'SIGN_OUT' event that other parts
|
|
667
|
-
* of the application can listen to for cleanup operations, analytics,
|
|
668
|
-
* or UI state updates. Set to `false` to perform silent sign-out
|
|
669
|
-
* without notifying event listeners (useful for internal operations).
|
|
670
|
-
*
|
|
671
|
-
* @returns A promise that resolves when the sign-out operation is complete and all
|
|
672
|
-
* user session data has been successfully removed from storage. The promise
|
|
673
|
-
* resolves to `void` as no return value is needed after successful sign-out.
|
|
674
|
-
*
|
|
675
|
-
* @example
|
|
676
|
-
* ```typescript
|
|
677
|
-
* // Standard user sign-out with event broadcasting
|
|
678
|
-
* async function handleUserSignOut() {
|
|
679
|
-
* try {
|
|
680
|
-
* await Auth.signOut();
|
|
681
|
-
* console.log('User signed out successfully');
|
|
682
|
-
*
|
|
683
|
-
* // Redirect to login page
|
|
684
|
-
* window.location.href = '/login';
|
|
685
|
-
* } catch (error) {
|
|
686
|
-
* console.error('Sign-out failed:', error);
|
|
687
|
-
* showErrorMessage('Failed to sign out');
|
|
688
|
-
* }
|
|
689
|
-
* }
|
|
690
|
-
* ```
|
|
691
|
-
*
|
|
692
|
-
* @example
|
|
693
|
-
* ```typescript
|
|
694
|
-
* // Silent sign-out without triggering events
|
|
695
|
-
* async function silentSignOut() {
|
|
696
|
-
* await Auth.signOut(false); // No events triggered
|
|
697
|
-
* console.log('Silent sign-out completed');
|
|
698
|
-
*
|
|
699
|
-
* // Manually handle post-signout operations
|
|
700
|
-
* clearUserSpecificData();
|
|
701
|
-
* redirectToPublicArea();
|
|
702
|
-
* }
|
|
703
|
-
* ```
|
|
704
|
-
*
|
|
705
|
-
* @example
|
|
706
|
-
* ```typescript
|
|
707
|
-
* // Sign-out with comprehensive event handling
|
|
708
|
-
* // Set up event listener first
|
|
709
|
-
* Auth.events.on('SIGN_OUT', () => {
|
|
710
|
-
* console.log('User signed out - cleaning up...');
|
|
711
|
-
*
|
|
712
|
-
* // Clear user-specific application state
|
|
713
|
-
* clearUserPreferences();
|
|
714
|
-
* clearUserCache();
|
|
715
|
-
* resetApplicationState();
|
|
716
|
-
*
|
|
717
|
-
* // Update UI
|
|
718
|
-
* hideUserMenus();
|
|
719
|
-
* showGuestContent();
|
|
720
|
-
*
|
|
721
|
-
* // Analytics and logging
|
|
722
|
-
* trackAnalyticsEvent('user_signout');
|
|
723
|
-
* logSecurityEvent('session_terminated');
|
|
724
|
-
* });
|
|
725
|
-
*
|
|
726
|
-
* // Perform sign-out - events will fire automatically
|
|
727
|
-
* await Auth.signOut();
|
|
728
|
-
* ```
|
|
729
|
-
*
|
|
730
|
-
* @example
|
|
731
|
-
* ```typescript
|
|
732
|
-
* // Session timeout handling
|
|
733
|
-
* class SessionManager {
|
|
734
|
-
* private timeoutId: NodeJS.Timeout | null = null;
|
|
735
|
-
*
|
|
736
|
-
* startSessionTimer(durationMs: number) {
|
|
737
|
-
* this.clearSessionTimer();
|
|
738
|
-
*
|
|
739
|
-
* this.timeoutId = setTimeout(async () => {
|
|
740
|
-
* console.log('Session expired - signing out user');
|
|
741
|
-
* await Auth.signOut(); // Will trigger events
|
|
742
|
-
*
|
|
743
|
-
* showNotification('Session expired. Please sign in again.');
|
|
744
|
-
* }, durationMs);
|
|
745
|
-
* }
|
|
746
|
-
*
|
|
747
|
-
* clearSessionTimer() {
|
|
748
|
-
* if (this.timeoutId) {
|
|
749
|
-
* clearTimeout(this.timeoutId);
|
|
750
|
-
* this.timeoutId = null;
|
|
751
|
-
* }
|
|
752
|
-
* }
|
|
753
|
-
* }
|
|
754
|
-
* ```
|
|
755
|
-
*
|
|
756
|
-
* @example
|
|
757
|
-
* ```typescript
|
|
758
|
-
* // Multi-tab sign-out coordination
|
|
759
|
-
* // Listen for storage events to handle sign-out in other tabs
|
|
760
|
-
* window.addEventListener('storage', (event) => {
|
|
761
|
-
* if (event.key === USER_SESSION_KEY && event.newValue === null) {
|
|
762
|
-
* console.log('User signed out in another tab');
|
|
763
|
-
*
|
|
764
|
-
* // Perform silent sign-out in this tab without triggering events
|
|
765
|
-
* Auth.signOut(false);
|
|
766
|
-
*
|
|
767
|
-
* // Update UI to reflect signed-out state
|
|
768
|
-
* updateUIForSignedOutUser();
|
|
769
|
-
* }
|
|
770
|
-
* });
|
|
771
|
-
* ```
|
|
772
|
-
*
|
|
773
|
-
* @example
|
|
774
|
-
* ```typescript
|
|
775
|
-
* // Complete authentication flow with error handling
|
|
776
|
-
* class AuthenticationService {
|
|
777
|
-
* async performSignOut(): Promise<boolean> {
|
|
778
|
-
* try {
|
|
779
|
-
* // Check if user is actually signed in
|
|
780
|
-
* const currentUser = Auth.getSignedUser();
|
|
781
|
-
* if (!currentUser) {
|
|
782
|
-
* console.log('No user to sign out');
|
|
783
|
-
* return true;
|
|
784
|
-
* }
|
|
785
|
-
*
|
|
786
|
-
* // Sign out locally
|
|
787
|
-
* await Auth.signOut();
|
|
788
|
-
*
|
|
789
|
-
* console.log('Complete sign-out successful');
|
|
790
|
-
* return true;
|
|
791
|
-
* } catch (error) {
|
|
792
|
-
* console.error('Sign-out process failed:', error);
|
|
793
|
-
*
|
|
794
|
-
* // Force local sign-out even if server call failed
|
|
795
|
-
* await Auth.signOut(false);
|
|
796
|
-
* return false;
|
|
797
|
-
* }
|
|
798
|
-
* }
|
|
799
|
-
* }
|
|
800
|
-
* ```
|
|
801
|
-
*
|
|
802
|
-
* @throws {StorageError} May throw if session storage is unavailable during cleanup
|
|
803
|
-
* @throws {CryptoError} May throw if there are issues clearing encrypted session data
|
|
804
|
-
*
|
|
805
|
-
* @see {@link setSignedUser} - Lower-level method used internally for session management
|
|
806
|
-
* @see {@link getSignedUser} - Check if a user is currently signed in before sign-out
|
|
807
|
-
* @see {@link signIn} - Corresponding method for user authentication
|
|
808
|
-
* @see {@link Auth.events} - Event system for handling sign-out notifications
|
|
809
|
-
* @see {@link AuthEvent} - Authentication event types including 'SIGN_OUT'
|
|
810
|
-
* @see {@link USER_SESSION_KEY} - Storage key used for session data
|
|
811
|
-
*
|
|
812
|
-
*
|
|
813
|
-
* @public
|
|
814
|
-
* @async
|
|
815
|
-
*
|
|
816
|
-
* @remarks
|
|
817
|
-
* **Security Considerations:**
|
|
818
|
-
* - Always sign out users when suspicious activity is detected
|
|
819
|
-
* - Consider notifying the server about client-side sign-outs for security auditing
|
|
820
|
-
* - Be aware that local sign-out doesn't invalidate server-side sessions automatically
|
|
821
|
-
* - Use HTTPS to prevent session hijacking during the sign-out process
|
|
822
|
-
*
|
|
823
|
-
* **Best Practices:**
|
|
824
|
-
* - Always handle sign-out errors gracefully to avoid leaving users in inconsistent states
|
|
825
|
-
* - Use event listeners to coordinate sign-out across multiple application components
|
|
826
|
-
* - Consider implementing automatic sign-out for security-sensitive applications
|
|
827
|
-
* - Provide clear feedback to users about successful sign-out operations
|
|
828
|
-
*
|
|
829
|
-
* **Performance Notes:**
|
|
830
|
-
* - Sign-out is typically fast as it only involves storage cleanup
|
|
831
|
-
* - Event broadcasting may trigger multiple listeners, consider the performance impact
|
|
832
|
-
* - Silent sign-out (`triggerEvent: false`) is faster as it skips event processing
|
|
833
|
-
* - Consider batching multiple sign-out operations if needed programmatically
|
|
834
|
-
*
|
|
835
|
-
* **Multi-Tab Considerations:**
|
|
836
|
-
* - Sign-out in one tab affects session storage visible to all tabs
|
|
837
|
-
* - Other tabs should listen for storage events to stay synchronized
|
|
838
|
-
* - Consider implementing cross-tab communication for better user experience
|
|
839
|
-
* - Be careful with silent sign-outs in multi-tab scenarios to avoid confusion
|
|
840
|
-
*/
|
|
841
|
-
static signOut(triggerEvent?: boolean): Promise<AuthUser | null>;
|
|
842
|
-
private static isResourceActionTupleArray;
|
|
843
|
-
private static isResourceActionTupleObject;
|
|
844
|
-
/**
|
|
845
|
-
* Determines whether a user has permission to access a resource or perform an action.
|
|
846
|
-
*
|
|
847
|
-
* This comprehensive authorization method evaluates various types of permission configurations
|
|
848
|
-
* to determine if the specified user (or currently signed-in user) is allowed to perform
|
|
849
|
-
* the requested operation. It supports multiple permission formats including boolean flags,
|
|
850
|
-
* function-based permissions, resource-action tuples, and complex permission arrays.
|
|
851
|
-
*
|
|
852
|
-
* The method follows a priority-based evaluation system:
|
|
853
|
-
* 1. Boolean permissions are returned directly
|
|
854
|
-
* 2. Master admin users are always granted access
|
|
855
|
-
* 3. Null/undefined permissions default to `true` (open access)
|
|
856
|
-
* 4. Function permissions are evaluated with the user context
|
|
857
|
-
* 5. Resource-action permissions are checked against user's role permissions
|
|
858
|
-
* 6. Array permissions are evaluated with OR logic (any match grants access)
|
|
859
|
-
*
|
|
860
|
-
* @template TResourceName - The resource name type, extending ResourceName
|
|
861
|
-
*
|
|
862
|
-
* @param perm - The permission configuration to evaluate. Can be:
|
|
863
|
-
* - `boolean`: Direct permission flag (true = allowed, false = denied)
|
|
864
|
-
* - `function`: Custom permission evaluator receiving user context
|
|
865
|
-
* - `ResourceActionTupleObject`: Object with resourceName and action properties
|
|
866
|
-
* - `ResourceActionTupleArray`: Array tuple [resourceName, action]
|
|
867
|
-
* - `Array<AuthPerm>`: Multiple permission configurations (OR logic)
|
|
868
|
-
* - `null|undefined`: Defaults to allowing access
|
|
869
|
-
*
|
|
870
|
-
* @param user - Optional user object to check permissions against.
|
|
871
|
-
* If not provided, uses the currently signed-in user from session.
|
|
872
|
-
* The user object should contain permissions and role information.
|
|
873
|
-
*
|
|
874
|
-
* @returns `true` if the user is authorized to perform the action, `false` otherwise
|
|
875
|
-
*
|
|
876
|
-
* @example
|
|
877
|
-
* ```typescript
|
|
878
|
-
* // Boolean permission - direct access control
|
|
879
|
-
* const canAccess = Auth.isAllowed(true); // Returns: true
|
|
880
|
-
* const cannotAccess = Auth.isAllowed(false); // Returns: false
|
|
881
|
-
*
|
|
882
|
-
* // Function-based permission - custom logic
|
|
883
|
-
* const customPerm = (user: AuthUser) => user.age >= 18;
|
|
884
|
-
* const canAccessAdultContent = Auth.isAllowed(customPerm); // Returns: true if user is 18+
|
|
885
|
-
* ```
|
|
886
|
-
*
|
|
887
|
-
* @example
|
|
888
|
-
* ```typescript
|
|
889
|
-
* // Resource-action tuple object - structured permissions
|
|
890
|
-
* const documentEditPerm = { resourceName: "documents", action: "update" };
|
|
891
|
-
* const canEditDocs = Auth.isAllowed(documentEditPerm);
|
|
892
|
-
* // Returns: true if user has "update" permission on "documents" resource
|
|
893
|
-
*
|
|
894
|
-
* // Resource-action tuple array - compact format
|
|
895
|
-
* const userDeletePerm: [string, string] = ["users", "delete"];
|
|
896
|
-
* const canDeleteUsers = Auth.isAllowed(userDeletePerm);
|
|
897
|
-
* // Returns: true if user has "delete" permission on "users" resource
|
|
898
|
-
* ```
|
|
899
|
-
*
|
|
900
|
-
* @example
|
|
901
|
-
* ```typescript
|
|
902
|
-
* // Array of permissions - OR logic (any match grants access)
|
|
903
|
-
* const multiplePerms = [
|
|
904
|
-
* { resourceName: "documents", action: "read" },
|
|
905
|
-
* { resourceName: "documents", action: "update" },
|
|
906
|
-
* ["admin", "all"]
|
|
907
|
-
* ];
|
|
908
|
-
* const canAccessDocs = Auth.isAllowed(multiplePerms);
|
|
909
|
-
* // Returns: true if user has any of the specified permissions
|
|
910
|
-
* ```
|
|
911
|
-
*
|
|
912
|
-
* @example
|
|
913
|
-
* ```typescript
|
|
914
|
-
* // Checking permissions for a specific user
|
|
915
|
-
* const specificUser: AuthUser = {
|
|
916
|
-
* id: "user123",
|
|
917
|
-
* perms: { documents: ["read", "update"] },
|
|
918
|
-
* roles: [{ name: "editor", perms: { images: ["upload"] } }]
|
|
919
|
-
* };
|
|
920
|
-
*
|
|
921
|
-
* const canEdit = Auth.isAllowed(
|
|
922
|
-
* { resourceName: "documents", action: "update" },
|
|
923
|
-
* specificUser
|
|
924
|
-
* ); // Returns: true
|
|
925
|
-
* ```
|
|
926
|
-
*
|
|
927
|
-
* @example
|
|
928
|
-
* ```typescript
|
|
929
|
-
* // Master admin bypass - always returns true
|
|
930
|
-
* Auth.isMasterAdmin = (user) => user.id === "admin";
|
|
931
|
-
* const adminUser = { id: "admin" };
|
|
932
|
-
* const canDoAnything = Auth.isAllowed(
|
|
933
|
-
* { resourceName: "secret", action: "delete" },
|
|
934
|
-
* adminUser
|
|
935
|
-
* ); // Returns: true (master admin bypass)
|
|
936
|
-
* ```
|
|
937
|
-
*
|
|
938
|
-
* @see {@link AuthPerm} - Permission configuration type definitions
|
|
939
|
-
* @see {@link AuthUser} - User object structure with permissions and roles
|
|
940
|
-
* @see {@link ResourceName} - Valid resource name types
|
|
941
|
-
* @see {@link ResourceActionName} - Valid action name types
|
|
942
|
-
* @see {@link checkUserPermission} - Low-level permission checking
|
|
943
|
-
* @see {@link isMasterAdmin} - Master admin detection function
|
|
944
|
-
*
|
|
945
|
-
*
|
|
946
|
-
* @public
|
|
947
|
-
*/
|
|
948
|
-
static isAllowed<TResourceName extends ResourceName = ResourceName>(perm: AuthPerm<TResourceName>, user?: AuthUser): boolean;
|
|
949
|
-
/**
|
|
950
|
-
* Validates whether a specific user has permission to perform an action on a resource.
|
|
951
|
-
*
|
|
952
|
-
* This core authorization method performs comprehensive permission checking by evaluating
|
|
953
|
-
* both direct user permissions and role-based permissions. It serves as the foundation
|
|
954
|
-
* for access control throughout the application, providing a reliable and secure way to
|
|
955
|
-
* determine if a user is authorized to perform specific operations on protected resources.
|
|
956
|
-
*
|
|
957
|
-
* ### Permission Evaluation Strategy:
|
|
958
|
-
* The method implements a hierarchical permission checking system:
|
|
959
|
-
* 1. **Input Validation**: Ensures the user object is valid and properly structured
|
|
960
|
-
* 2. **Direct Permissions**: Checks permissions directly assigned to the user
|
|
961
|
-
* 3. **Role-Based Permissions**: Iterates through user roles to check role-specific permissions
|
|
962
|
-
* 4. **First Match Wins**: Returns `true` as soon as any valid permission is found
|
|
963
|
-
* 5. **Secure Default**: Returns `false` if no matching permissions are discovered
|
|
964
|
-
*
|
|
965
|
-
* ### Security Architecture:
|
|
966
|
-
* - **Fail-Safe Design**: Defaults to denying access when permissions are unclear
|
|
967
|
-
* - **Comprehensive Validation**: Validates user object structure and permission data
|
|
968
|
-
* - **Role Inheritance**: Supports complex permission models through role-based access
|
|
969
|
-
* - **Performance Optimized**: Uses early return to minimize computation time
|
|
970
|
-
*
|
|
971
|
-
* ### Permission Hierarchy:
|
|
972
|
-
* The method checks permissions in the following order:
|
|
973
|
-
* 1. User's direct permissions (`user.perms`)
|
|
974
|
-
* 2. Permissions inherited from user roles (`user.roles[].perms`)
|
|
975
|
-
*
|
|
976
|
-
* @param user - The user object containing permission and role information.
|
|
977
|
-
* Must be a valid `AuthUser` object with properly structured
|
|
978
|
-
* permissions and roles. The object should include `perms` for
|
|
979
|
-
* direct permissions and optionally `roles` array for role-based permissions.
|
|
980
|
-
*
|
|
981
|
-
* @param resource - The resource name to check permissions against.
|
|
982
|
-
* Should be a valid resource identifier from the `ResourceName` type.
|
|
983
|
-
* Examples include "documents", "users", "admin", "reports", etc.
|
|
984
|
-
* The resource name is case-sensitive and should match exactly.
|
|
985
|
-
*
|
|
986
|
-
* @param action - The specific action to check permission for on the given resource.
|
|
987
|
-
* Defaults to "read" if not specified. Common actions include:
|
|
988
|
-
* "read", "create", "update", "delete", "all", or custom actions
|
|
989
|
-
* specific to your application's permission model.
|
|
990
|
-
*
|
|
991
|
-
* @returns `true` if the user has permission to perform the specified action
|
|
992
|
-
* on the resource, either through direct permissions or role inheritance.
|
|
993
|
-
* Returns `false` if the user lacks permission, has invalid data,
|
|
994
|
-
* or if any validation checks fail.
|
|
995
|
-
*
|
|
996
|
-
* @example
|
|
997
|
-
* ```typescript
|
|
998
|
-
* // Basic permission checking - user with direct permissions
|
|
999
|
-
* const user: AuthUser = {
|
|
1000
|
-
* id: "user123",
|
|
1001
|
-
* username: "john_doe",
|
|
1002
|
-
* perms: {
|
|
1003
|
-
* documents: ["read", "create", "update"],
|
|
1004
|
-
* reports: ["read"]
|
|
1005
|
-
* }
|
|
1006
|
-
* };
|
|
1007
|
-
*
|
|
1008
|
-
* const canReadDocs = Auth.checkUserPermission(user, "documents", "read");
|
|
1009
|
-
* console.log(canReadDocs); // true
|
|
1010
|
-
*
|
|
1011
|
-
* const canDeleteDocs = Auth.checkUserPermission(user, "documents", "delete");
|
|
1012
|
-
* console.log(canDeleteDocs); // false
|
|
1013
|
-
* ```
|
|
1014
|
-
*
|
|
1015
|
-
* @example
|
|
1016
|
-
* ```typescript
|
|
1017
|
-
* // Role-based permission checking
|
|
1018
|
-
* const userWithRoles: AuthUser = {
|
|
1019
|
-
* id: "user456",
|
|
1020
|
-
* username: "jane_smith",
|
|
1021
|
-
* perms: {
|
|
1022
|
-
* profile: ["read", "update"]
|
|
1023
|
-
* },
|
|
1024
|
-
* roles: [
|
|
1025
|
-
* {
|
|
1026
|
-
* name: "editor",
|
|
1027
|
-
* perms: {
|
|
1028
|
-
* documents: ["read", "create", "update"],
|
|
1029
|
-
* images: ["upload", "edit"]
|
|
1030
|
-
* }
|
|
1031
|
-
* },
|
|
1032
|
-
* {
|
|
1033
|
-
* name: "moderator",
|
|
1034
|
-
* perms: {
|
|
1035
|
-
* comments: ["read", "update", "delete"],
|
|
1036
|
-
* users: ["read", "suspend"]
|
|
1037
|
-
* }
|
|
1038
|
-
* }
|
|
1039
|
-
* ]
|
|
1040
|
-
* };
|
|
1041
|
-
*
|
|
1042
|
-
* // Check direct permission
|
|
1043
|
-
* const canUpdateProfile = Auth.checkUserPermission(userWithRoles, "profile", "update");
|
|
1044
|
-
* console.log(canUpdateProfile); // true (from direct perms)
|
|
1045
|
-
*
|
|
1046
|
-
* // Check role-inherited permission
|
|
1047
|
-
* const canEditDocs = Auth.checkUserPermission(userWithRoles, "documents", "update");
|
|
1048
|
-
* console.log(canEditDocs); // true (from editor role)
|
|
1049
|
-
*
|
|
1050
|
-
* // Check another role permission
|
|
1051
|
-
* const canDeleteComments = Auth.checkUserPermission(userWithRoles, "comments", "delete");
|
|
1052
|
-
* console.log(canDeleteComments); // true (from moderator role)
|
|
1053
|
-
* ```
|
|
1054
|
-
*
|
|
1055
|
-
* @example
|
|
1056
|
-
* ```typescript
|
|
1057
|
-
* // Default action parameter (read)
|
|
1058
|
-
* const user: AuthUser = {
|
|
1059
|
-
* id: "reader",
|
|
1060
|
-
* perms: {
|
|
1061
|
-
* articles: ["read"],
|
|
1062
|
-
* news: ["read", "create"]
|
|
1063
|
-
* }
|
|
1064
|
-
* };
|
|
1065
|
-
*
|
|
1066
|
-
* // These calls are equivalent
|
|
1067
|
-
* const canRead1 = Auth.checkUserPermission(user, "articles", "read");
|
|
1068
|
-
* const canRead2 = Auth.checkUserPermission(user, "articles"); // defaults to "read"
|
|
1069
|
-
* console.log(canRead1 === canRead2); // true
|
|
1070
|
-
* ```
|
|
1071
|
-
*
|
|
1072
|
-
* @example
|
|
1073
|
-
* ```typescript
|
|
1074
|
-
* // Error handling and validation
|
|
1075
|
-
* const invalidUser = null;
|
|
1076
|
-
* const emptyUser = {};
|
|
1077
|
-
* const validUser = { id: "test", perms: { docs: ["read"] } };
|
|
1078
|
-
*
|
|
1079
|
-
* console.log(Auth.checkUserPermission(invalidUser, "documents", "read")); // false
|
|
1080
|
-
* console.log(Auth.checkUserPermission(emptyUser, "documents", "read")); // false
|
|
1081
|
-
* console.log(Auth.checkUserPermission(validUser, "docs", "read")); // true
|
|
1082
|
-
* ```
|
|
1083
|
-
*
|
|
1084
|
-
* @example
|
|
1085
|
-
* ```typescript
|
|
1086
|
-
* // Integration with access control middleware
|
|
1087
|
-
* class PermissionGuard {
|
|
1088
|
-
* static requirePermission(resource: ResourceName, action: ResourceActionName = "read") {
|
|
1089
|
-
* return (req: Request, res: Response, next: NextFunction) => {
|
|
1090
|
-
* const user = Auth.getSignedUser();
|
|
1091
|
-
*
|
|
1092
|
-
* if (!user) {
|
|
1093
|
-
* return res.status(401).json({ error: "Authentication required" });
|
|
1094
|
-
* }
|
|
1095
|
-
*
|
|
1096
|
-
* if (!Auth.checkUserPermission(user, resource, action)) {
|
|
1097
|
-
* return res.status(403).json({
|
|
1098
|
-
* error: `Permission denied: ${action} on ${resource}`
|
|
1099
|
-
* });
|
|
1100
|
-
* }
|
|
1101
|
-
*
|
|
1102
|
-
* next();
|
|
1103
|
-
* };
|
|
1104
|
-
* }
|
|
1105
|
-
* }
|
|
1106
|
-
*
|
|
1107
|
-
* // Usage in Express routes
|
|
1108
|
-
* app.get('/documents', PermissionGuard.requirePermission('documents', 'read'), getDocuments);
|
|
1109
|
-
* app.post('/documents', PermissionGuard.requirePermission('documents', 'create'), createDocument);
|
|
1110
|
-
* app.delete('/documents/:id', PermissionGuard.requirePermission('documents', 'delete'), deleteDocument);
|
|
1111
|
-
* ```
|
|
1112
|
-
*
|
|
1113
|
-
* @example
|
|
1114
|
-
* ```typescript
|
|
1115
|
-
* // Batch permission checking for UI elements
|
|
1116
|
-
* function getUserCapabilities(user: AuthUser) {
|
|
1117
|
-
* const capabilities = {
|
|
1118
|
-
* canReadDocs: Auth.checkUserPermission(user, "documents", "read"),
|
|
1119
|
-
* canCreateDocs: Auth.checkUserPermission(user, "documents", "create"),
|
|
1120
|
-
* canUpdateDocs: Auth.checkUserPermission(user, "documents", "update"),
|
|
1121
|
-
* canDeleteDocs: Auth.checkUserPermission(user, "documents", "delete"),
|
|
1122
|
-
* canManageUsers: Auth.checkUserPermission(user, "users", "update"),
|
|
1123
|
-
* canViewReports: Auth.checkUserPermission(user, "reports", "read"),
|
|
1124
|
-
* canConfigureSystem: Auth.checkUserPermission(user, "system", "configure")
|
|
1125
|
-
* };
|
|
1126
|
-
*
|
|
1127
|
-
* return capabilities;
|
|
1128
|
-
* }
|
|
1129
|
-
*
|
|
1130
|
-
* // Usage in React component
|
|
1131
|
-
* function DocumentToolbar() {
|
|
1132
|
-
* const user = Auth.getSignedUser();
|
|
1133
|
-
* const capabilities = getUserCapabilities(user);
|
|
1134
|
-
*
|
|
1135
|
-
* return (
|
|
1136
|
-
* <div className="toolbar">
|
|
1137
|
-
* {capabilities.canCreateDocs && <CreateButton />}
|
|
1138
|
-
* {capabilities.canUpdateDocs && <EditButton />}
|
|
1139
|
-
* {capabilities.canDeleteDocs && <DeleteButton />}
|
|
1140
|
-
* </div>
|
|
1141
|
-
* );
|
|
1142
|
-
* }
|
|
1143
|
-
* ```
|
|
1144
|
-
*
|
|
1145
|
-
* @example
|
|
1146
|
-
* ```typescript
|
|
1147
|
-
* // Complex permission scenarios with multiple roles
|
|
1148
|
-
* const adminUser: AuthUser = {
|
|
1149
|
-
* id: "admin001",
|
|
1150
|
-
* perms: {
|
|
1151
|
-
* profile: ["read", "update"]
|
|
1152
|
-
* },
|
|
1153
|
-
* roles: [
|
|
1154
|
-
* {
|
|
1155
|
-
* name: "super_admin",
|
|
1156
|
-
* perms: {
|
|
1157
|
-
* users: ["all"],
|
|
1158
|
-
* system: ["all"],
|
|
1159
|
-
* reports: ["all"]
|
|
1160
|
-
* }
|
|
1161
|
-
* },
|
|
1162
|
-
* {
|
|
1163
|
-
* name: "content_manager",
|
|
1164
|
-
* perms: {
|
|
1165
|
-
* documents: ["read", "create", "update", "delete"],
|
|
1166
|
-
* media: ["upload", "edit", "delete"]
|
|
1167
|
-
* }
|
|
1168
|
-
* }
|
|
1169
|
-
* ]
|
|
1170
|
-
* };
|
|
1171
|
-
*
|
|
1172
|
-
* // Check various permissions
|
|
1173
|
-
* console.log(Auth.checkUserPermission(adminUser, "users", "delete")); // true (super_admin role)
|
|
1174
|
-
* console.log(Auth.checkUserPermission(adminUser, "documents", "create")); // true (content_manager role)
|
|
1175
|
-
* console.log(Auth.checkUserPermission(adminUser, "profile", "update")); // true (direct permission)
|
|
1176
|
-
* console.log(Auth.checkUserPermission(adminUser, "billing", "read")); // false (no permission)
|
|
1177
|
-
* ```
|
|
1178
|
-
*
|
|
1179
|
-
* @see {@link AuthUser} - Complete user object interface with permissions and roles
|
|
1180
|
-
* @see {@link ResourceName} - Valid resource name types for permission checking
|
|
1181
|
-
* @see {@link ResourceActionName} - Valid action types for permission operations
|
|
1182
|
-
* @see {@link checkPermission} - Lower-level permission checking against permission objects
|
|
1183
|
-
* @see {@link isAllowed} - Higher-level permission checking with multiple formats
|
|
1184
|
-
* @see {@link AuthPerms} - Permission object structure and format
|
|
1185
|
-
* @see {@link AuthRole} - Role object structure with embedded permissions
|
|
1186
|
-
*
|
|
1187
|
-
*
|
|
1188
|
-
* @public
|
|
1189
|
-
*
|
|
1190
|
-
* @remarks
|
|
1191
|
-
* **Security Best Practices:**
|
|
1192
|
-
* - Always validate user input before calling this method
|
|
1193
|
-
* - Use this method as the primary gate for access control decisions
|
|
1194
|
-
* - Consider implementing audit logging for permission checks in security-sensitive applications
|
|
1195
|
-
* - Regularly review and update permission models as application requirements evolve
|
|
1196
|
-
*
|
|
1197
|
-
* **Performance Considerations:**
|
|
1198
|
-
* - The method uses early return for optimal performance
|
|
1199
|
-
* - Role iteration stops at the first matching permission found
|
|
1200
|
-
* - Consider caching results for frequently checked permissions
|
|
1201
|
-
* - Large role hierarchies may impact performance; consider flattening permissions when possible
|
|
1202
|
-
*
|
|
1203
|
-
* **Design Patterns:**
|
|
1204
|
-
* - Implements the "Fail-Safe Defaults" security pattern
|
|
1205
|
-
* - Supports both direct and inherited permission models
|
|
1206
|
-
* - Compatible with Role-Based Access Control (RBAC) systems
|
|
1207
|
-
* - Integrates seamlessly with middleware and guard patterns
|
|
1208
|
-
*
|
|
1209
|
-
* **Error Handling:**
|
|
1210
|
-
* - Returns `false` for any invalid or malformed input
|
|
1211
|
-
* - Gracefully handles missing permission structures
|
|
1212
|
-
* - Does not throw exceptions, making it safe for use in conditional statements
|
|
1213
|
-
* - Logs errors internally for debugging purposes without exposing sensitive information
|
|
1214
|
-
*/
|
|
1215
|
-
static checkUserPermission<TResourceName extends ResourceName = ResourceName>(user: AuthUser, resource: TResourceName, action?: ResourceActionName<TResourceName>): boolean;
|
|
1216
|
-
/**
|
|
1217
|
-
* Evaluates whether a permission object grants access to perform a specific action on a resource.
|
|
1218
|
-
*
|
|
1219
|
-
* This fundamental permission checking method serves as the core authorization engine for
|
|
1220
|
-
* the authentication system. It performs low-level permission validation by examining
|
|
1221
|
-
* permission objects and determining if they contain the necessary action permissions
|
|
1222
|
-
* for a given resource. The method implements case-insensitive resource matching and
|
|
1223
|
-
* supports both specific action permissions and universal "all" permissions.
|
|
1224
|
-
*
|
|
1225
|
-
* ### Permission Evaluation Algorithm:
|
|
1226
|
-
* 1. **Input Sanitization**: Normalizes and validates input parameters for consistent processing
|
|
1227
|
-
* 2. **Resource Matching**: Performs case-insensitive resource name matching in permission object
|
|
1228
|
-
* 3. **Action Discovery**: Extracts the list of allowed actions for the matched resource
|
|
1229
|
-
* 4. **Universal Permission Check**: Returns `true` immediately if "all" permission is found
|
|
1230
|
-
* 5. **Specific Action Validation**: Iterates through actions to find exact or compatible matches
|
|
1231
|
-
* 6. **Fail-Safe Return**: Returns `false` if no matching permissions are discovered
|
|
1232
|
-
*
|
|
1233
|
-
* ### Security Features:
|
|
1234
|
-
* - **Defensive Programming**: Validates all inputs and handles malformed data gracefully
|
|
1235
|
-
* - **Case-Insensitive Matching**: Prevents permission bypass through case manipulation
|
|
1236
|
-
* - **Universal Permission Support**: Recognizes "all" as a wildcard permission
|
|
1237
|
-
* - **Strict Validation**: Requires exact resource and action matches for security
|
|
1238
|
-
*
|
|
1239
|
-
* ### Performance Optimization:
|
|
1240
|
-
* - **Early Exit Strategy**: Returns immediately when "all" permission is found
|
|
1241
|
-
* - **Efficient Iteration**: Stops processing at the first matching permission
|
|
1242
|
-
* - **Memory Safety**: Creates defensive copies of input objects to prevent mutation
|
|
1243
|
-
* - **Minimal Processing**: Only processes relevant permission entries
|
|
1244
|
-
*
|
|
1245
|
-
* @template TResourceName - The resource name type constraint extending ResourceName
|
|
1246
|
-
*
|
|
1247
|
-
* @param perms - The permission object containing resource-to-actions mappings.
|
|
1248
|
-
* Must be a valid `AuthPerms` object where keys are resource names
|
|
1249
|
-
* and values are arrays of allowed actions. The object is defensively
|
|
1250
|
-
* copied to prevent external mutations during processing.
|
|
1251
|
-
*
|
|
1252
|
-
* @param resource - The resource name to check permissions for.
|
|
1253
|
-
* Should be a valid identifier from the `ResourceName` type.
|
|
1254
|
-
* The resource name undergoes case-insensitive matching, so
|
|
1255
|
-
* "Documents", "documents", and "DOCUMENTS" are treated as equivalent.
|
|
1256
|
-
* Empty or invalid resource names result in permission denial.
|
|
1257
|
-
*
|
|
1258
|
-
* @param action - The specific action to validate against the resource permissions.
|
|
1259
|
-
* Defaults to "read" if not specified. The action must match exactly
|
|
1260
|
-
* (case-insensitive) with one of the allowed actions in the permission
|
|
1261
|
-
* array, or the resource must have "all" permission for universal access.
|
|
1262
|
-
*
|
|
1263
|
-
* @returns `true` if the permission object grants the specified action on the resource,
|
|
1264
|
-
* either through explicit action permission or universal "all" permission.
|
|
1265
|
-
* Returns `false` if permissions are insufficient, invalid, or if input
|
|
1266
|
-
* validation fails.
|
|
1267
|
-
*
|
|
1268
|
-
* @example
|
|
1269
|
-
* ```typescript
|
|
1270
|
-
* // Basic permission checking with explicit actions
|
|
1271
|
-
* const permissions: AuthPerms = {
|
|
1272
|
-
* documents: ["read", "create", "update"],
|
|
1273
|
-
* users: ["read"],
|
|
1274
|
-
* reports: ["read", "export"]
|
|
1275
|
-
* };
|
|
1276
|
-
*
|
|
1277
|
-
* // Check various permissions
|
|
1278
|
-
* const canReadDocs = Auth.checkPermission(permissions, "documents", "read");
|
|
1279
|
-
* console.log(canReadDocs); // true
|
|
1280
|
-
*
|
|
1281
|
-
* const canDeleteDocs = Auth.checkPermission(permissions, "documents", "delete");
|
|
1282
|
-
* console.log(canDeleteDocs); // false
|
|
1283
|
-
*
|
|
1284
|
-
* const canCreateUsers = Auth.checkPermission(permissions, "users", "create");
|
|
1285
|
-
* console.log(canCreateUsers); // false
|
|
1286
|
-
* ```
|
|
1287
|
-
*
|
|
1288
|
-
* @example
|
|
1289
|
-
* ```typescript
|
|
1290
|
-
* // Universal "all" permission handling
|
|
1291
|
-
* const adminPermissions: AuthPerms = {
|
|
1292
|
-
* system: ["all"],
|
|
1293
|
-
* documents: ["read", "create"],
|
|
1294
|
-
* users: ["all"]
|
|
1295
|
-
* };
|
|
1296
|
-
*
|
|
1297
|
-
* // "all" permission grants access to any action
|
|
1298
|
-
* console.log(Auth.checkPermission(adminPermissions, "system", "configure")); // true
|
|
1299
|
-
* console.log(Auth.checkPermission(adminPermissions, "system", "delete")); // true
|
|
1300
|
-
* console.log(Auth.checkPermission(adminPermissions, "system", "custom_action")); // true
|
|
1301
|
-
*
|
|
1302
|
-
* // Specific permissions still work normally
|
|
1303
|
-
* console.log(Auth.checkPermission(adminPermissions, "documents", "read")); // true
|
|
1304
|
-
* console.log(Auth.checkPermission(adminPermissions, "documents", "delete")); // false
|
|
1305
|
-
* ```
|
|
1306
|
-
*
|
|
1307
|
-
* @example
|
|
1308
|
-
* ```typescript
|
|
1309
|
-
* // Case-insensitive resource matching
|
|
1310
|
-
* const permissions: AuthPerms = {
|
|
1311
|
-
* Documents: ["read", "write"],
|
|
1312
|
-
* USERS: ["read"],
|
|
1313
|
-
* api_endpoints: ["access"]
|
|
1314
|
-
* };
|
|
1315
|
-
*
|
|
1316
|
-
* // All of these will match the "Documents" resource
|
|
1317
|
-
* console.log(Auth.checkPermission(permissions, "documents", "read")); // true
|
|
1318
|
-
* console.log(Auth.checkPermission(permissions, "DOCUMENTS", "read")); // true
|
|
1319
|
-
* console.log(Auth.checkPermission(permissions, "DoCtMeNtS", "read")); // true
|
|
1320
|
-
*
|
|
1321
|
-
* // Case-insensitive matching for all resources
|
|
1322
|
-
* console.log(Auth.checkPermission(permissions, "users", "read")); // true
|
|
1323
|
-
* console.log(Auth.checkPermission(permissions, "API_ENDPOINTS", "access")); // true
|
|
1324
|
-
* ```
|
|
1325
|
-
*
|
|
1326
|
-
* @example
|
|
1327
|
-
* ```typescript
|
|
1328
|
-
* // Default action parameter behavior
|
|
1329
|
-
* const readOnlyPermissions: AuthPerms = {
|
|
1330
|
-
* articles: ["read"],
|
|
1331
|
-
* news: ["read", "comment"],
|
|
1332
|
-
* profiles: ["read", "update"]
|
|
1333
|
-
* };
|
|
1334
|
-
*
|
|
1335
|
-
* // These calls are equivalent (default action is "read")
|
|
1336
|
-
* const canRead1 = Auth.checkPermission(readOnlyPermissions, "articles", "read");
|
|
1337
|
-
* const canRead2 = Auth.checkPermission(readOnlyPermissions, "articles");
|
|
1338
|
-
* console.log(canRead1 === canRead2); // true, both return true
|
|
1339
|
-
*
|
|
1340
|
-
* // Default "read" action checking
|
|
1341
|
-
* console.log(Auth.checkPermission(readOnlyPermissions, "news")); // true (has read)
|
|
1342
|
-
* console.log(Auth.checkPermission(readOnlyPermissions, "profiles")); // true (has read)
|
|
1343
|
-
* ```
|
|
1344
|
-
*
|
|
1345
|
-
* @example
|
|
1346
|
-
* ```typescript
|
|
1347
|
-
* // Error handling and edge cases
|
|
1348
|
-
* const validPermissions: AuthPerms = { docs: ["read"] };
|
|
1349
|
-
* const emptyPermissions: AuthPerms = {};
|
|
1350
|
-
* const nullPermissions = null;
|
|
1351
|
-
*
|
|
1352
|
-
* // Valid permission object
|
|
1353
|
-
* console.log(Auth.checkPermission(validPermissions, "docs", "read")); // true
|
|
1354
|
-
*
|
|
1355
|
-
* // Empty permission object
|
|
1356
|
-
* console.log(Auth.checkPermission(emptyPermissions, "docs", "read")); // false
|
|
1357
|
-
*
|
|
1358
|
-
* // Invalid permission object
|
|
1359
|
-
* console.log(Auth.checkPermission(nullPermissions as any, "docs", "read")); // false
|
|
1360
|
-
*
|
|
1361
|
-
* // Invalid resource name
|
|
1362
|
-
* console.log(Auth.checkPermission(validPermissions, "" as ResourceName, "read")); // false
|
|
1363
|
-
* console.log(Auth.checkPermission(validPermissions, null as any, "read")); // false
|
|
1364
|
-
* ```
|
|
1365
|
-
*
|
|
1366
|
-
* @example
|
|
1367
|
-
* ```typescript
|
|
1368
|
-
* // Permission validation utility function
|
|
1369
|
-
* function validateUserAccess(
|
|
1370
|
-
* userPerms: AuthPerms,
|
|
1371
|
-
* requiredResource: ResourceName,
|
|
1372
|
-
* requiredAction: ResourceActionName
|
|
1373
|
-
* ): { allowed: boolean; reason: string } {
|
|
1374
|
-
* if (!userPerms || typeof userPerms !== 'object') {
|
|
1375
|
-
* return { allowed: false, reason: 'Invalid permission object' };
|
|
1376
|
-
* }
|
|
1377
|
-
*
|
|
1378
|
-
* if (!requiredResource) {
|
|
1379
|
-
* return { allowed: false, reason: 'Resource name is required' };
|
|
1380
|
-
* }
|
|
1381
|
-
*
|
|
1382
|
-
* const hasPermission = Auth.checkPermission(userPerms, requiredResource, requiredAction);
|
|
1383
|
-
*
|
|
1384
|
-
* return {
|
|
1385
|
-
* allowed: hasPermission,
|
|
1386
|
-
* reason: hasPermission
|
|
1387
|
-
* ? `Access granted for ${requiredAction} on ${requiredResource}`
|
|
1388
|
-
* : `Access denied: insufficient permissions for ${requiredAction} on ${requiredResource}`
|
|
1389
|
-
* };
|
|
1390
|
-
* }
|
|
1391
|
-
*
|
|
1392
|
-
* // Usage example
|
|
1393
|
-
* const userPermissions: AuthPerms = {
|
|
1394
|
-
* documents: ["read", "create"],
|
|
1395
|
-
* users: ["read"]
|
|
1396
|
-
* };
|
|
1397
|
-
*
|
|
1398
|
-
* const result = validateUserAccess(userPermissions, "documents", "update");
|
|
1399
|
-
* console.log(result); // { allowed: false, reason: "Access denied: insufficient permissions..." }
|
|
1400
|
-
* ```
|
|
1401
|
-
*
|
|
1402
|
-
* @example
|
|
1403
|
-
* ```typescript
|
|
1404
|
-
* // Integration with role-based permission systems
|
|
1405
|
-
* class PermissionManager {
|
|
1406
|
-
* static combinePermissions(...permissionSets: AuthPerms[]): AuthPerms {
|
|
1407
|
-
* const combined: AuthPerms = {};
|
|
1408
|
-
*
|
|
1409
|
-
* for (const perms of permissionSets) {
|
|
1410
|
-
* if (!perms) continue;
|
|
1411
|
-
*
|
|
1412
|
-
* for (const [resource, actions] of Object.entries(perms)) {
|
|
1413
|
-
* if (!combined[resource as keyof AuthPerms]) {
|
|
1414
|
-
* combined[resource as keyof AuthPerms] = [];
|
|
1415
|
-
* }
|
|
1416
|
-
*
|
|
1417
|
-
* const existingActions = combined[resource as keyof AuthPerms] as ResourceActionName[];
|
|
1418
|
-
* const newActions = actions as ResourceActionName[];
|
|
1419
|
-
*
|
|
1420
|
-
* // Merge actions, avoiding duplicates
|
|
1421
|
-
* for (const action of newActions) {
|
|
1422
|
-
* if (!existingActions.includes(action)) {
|
|
1423
|
-
* existingActions.push(action);
|
|
1424
|
-
* }
|
|
1425
|
-
* }
|
|
1426
|
-
* }
|
|
1427
|
-
* }
|
|
1428
|
-
*
|
|
1429
|
-
* return combined;
|
|
1430
|
-
* }
|
|
1431
|
-
*
|
|
1432
|
-
* static hasAnyPermission(perms: AuthPerms, checks: Array<[ResourceName, ResourceActionName]>): boolean {
|
|
1433
|
-
* return checks.some(([resource, action]) =>
|
|
1434
|
-
* Auth.checkPermission(perms, resource, action)
|
|
1435
|
-
* );
|
|
1436
|
-
* }
|
|
1437
|
-
*
|
|
1438
|
-
* static hasAllPermissions(perms: AuthPerms, checks: Array<[ResourceName, ResourceActionName]>): boolean {
|
|
1439
|
-
* return checks.every(([resource, action]) =>
|
|
1440
|
-
* Auth.checkPermission(perms, resource, action)
|
|
1441
|
-
* );
|
|
1442
|
-
* }
|
|
1443
|
-
* }
|
|
1444
|
-
*
|
|
1445
|
-
* // Usage
|
|
1446
|
-
* const userPerms: AuthPerms = { documents: ["read"], users: ["read", "update"] };
|
|
1447
|
-
* const rolePerms: AuthPerms = { documents: ["create"], reports: ["read"] };
|
|
1448
|
-
*
|
|
1449
|
-
* const combinedPerms = PermissionManager.combinePermissions(userPerms, rolePerms);
|
|
1450
|
-
* console.log(Auth.checkPermission(combinedPerms, "documents", "create")); // true
|
|
1451
|
-
* ```
|
|
1452
|
-
*
|
|
1453
|
-
* @see {@link AuthPerms} - Permission object structure and type definitions
|
|
1454
|
-
* @see {@link ResourceName} - Valid resource name types for permission checking
|
|
1455
|
-
* @see {@link ResourceActionName} - Valid action types for permission operations
|
|
1456
|
-
* @see {@link checkUserPermission} - Higher-level user permission checking method
|
|
1457
|
-
* @see {@link isAllowed} - Comprehensive permission evaluation with multiple formats
|
|
1458
|
-
* @see {@link isAllowedForAction} - Action-specific permission matching utility
|
|
1459
|
-
*
|
|
1460
|
-
*
|
|
1461
|
-
* @public
|
|
1462
|
-
*
|
|
1463
|
-
* @remarks
|
|
1464
|
-
* **Security Considerations:**
|
|
1465
|
-
* - This method performs case-insensitive matching to prevent security bypasses
|
|
1466
|
-
* - Always validates input parameters to prevent injection or manipulation attacks
|
|
1467
|
-
* - Returns `false` by default for any ambiguous or invalid scenarios
|
|
1468
|
-
* - The "all" permission should be used carefully as it grants universal access
|
|
1469
|
-
*
|
|
1470
|
-
* **Performance Best Practices:**
|
|
1471
|
-
* - The method creates defensive copies of input objects, consider caching results for frequently checked permissions
|
|
1472
|
-
* - Early termination when "all" permission is found improves performance for administrative users
|
|
1473
|
-
* - Use this method as the foundation for higher-level permission checking utilities
|
|
1474
|
-
* - Consider implementing permission result caching for high-frequency checks
|
|
1475
|
-
*
|
|
1476
|
-
* **Design Patterns:**
|
|
1477
|
-
* - Implements the "Secure by Default" principle with fail-safe returns
|
|
1478
|
-
* - Supports the Strategy pattern through flexible action matching
|
|
1479
|
-
* - Compatible with Role-Based Access Control (RBAC) and Attribute-Based Access Control (ABAC) systems
|
|
1480
|
-
* - Follows the Single Responsibility Principle by focusing solely on permission object evaluation
|
|
1481
|
-
*
|
|
1482
|
-
* **Error Handling Strategy:**
|
|
1483
|
-
* - Never throws exceptions, always returns boolean values for predictable behavior
|
|
1484
|
-
* - Handles null, undefined, and malformed inputs gracefully
|
|
1485
|
-
* - Provides meaningful return values that can be safely used in conditional statements
|
|
1486
|
-
* - Logs internal errors for debugging without exposing sensitive permission details
|
|
1487
|
-
*/
|
|
1488
|
-
static checkPermission<TResourceName extends ResourceName = ResourceName>(perms: AuthPerms, resource: TResourceName, action?: ResourceActionName<TResourceName>): boolean;
|
|
9
|
+
export declare class Auth {
|
|
1489
10
|
/**
|
|
1490
|
-
*
|
|
1491
|
-
*
|
|
1492
|
-
* This utility method performs precise action-to-action comparison for permission validation.
|
|
1493
|
-
* It serves as the atomic-level matching function in the permission checking hierarchy,
|
|
1494
|
-
* providing case-insensitive string comparison with proper normalization. The method
|
|
1495
|
-
* ensures that permission actions are evaluated consistently and securely across the
|
|
1496
|
-
* entire authentication system.
|
|
1497
|
-
*
|
|
1498
|
-
* ### Matching Algorithm:
|
|
1499
|
-
* 1. **Input Validation**: Ensures both parameters are valid non-null strings
|
|
1500
|
-
* 2. **String Normalization**: Trims whitespace and converts to lowercase for comparison
|
|
1501
|
-
* 3. **Exact Matching**: Performs strict equality comparison after normalization
|
|
1502
|
-
* 4. **Binary Result**: Returns boolean result for immediate conditional usage
|
|
1503
|
-
*
|
|
1504
|
-
* ### Security Features:
|
|
1505
|
-
* - **Case-Insensitive Matching**: Prevents permission bypass through case manipulation
|
|
1506
|
-
* - **Whitespace Normalization**: Eliminates accidental whitespace-based security issues
|
|
1507
|
-
* - **Strict Validation**: Rejects null, undefined, or non-string inputs for security
|
|
1508
|
-
* - **Deterministic Behavior**: Always produces consistent results for identical inputs
|
|
1509
|
-
*
|
|
1510
|
-
* ### Performance Characteristics:
|
|
1511
|
-
* - **Minimal Overhead**: Lightweight string operations with O(1) complexity
|
|
1512
|
-
* - **Early Termination**: Returns immediately on validation failure
|
|
1513
|
-
* - **String Optimization**: Uses native JavaScript string methods for efficiency
|
|
1514
|
-
* - **Memory Efficient**: No intermediate object creation or complex processing
|
|
1515
|
-
*
|
|
1516
|
-
* @template TResourceName - The resource name type constraint extending ResourceName
|
|
1517
|
-
*
|
|
1518
|
-
* @param permFromResource - The permission action to check against.
|
|
1519
|
-
* Must be a valid string conforming to `ResourceActionName<TResourceName>`.
|
|
1520
|
-
* This represents the action that is granted by a permission entry.
|
|
1521
|
-
* Examples: "read", "write", "delete", "create", "all", "custom_action".
|
|
1522
|
-
* Empty strings, null, or undefined values will result in `false`.
|
|
1523
|
-
*
|
|
1524
|
-
* @param action - The requested action to validate.
|
|
1525
|
-
* Must be a valid string conforming to `ResourceActionName<TResourceName>`.
|
|
1526
|
-
* This represents the action that a user is attempting to perform.
|
|
1527
|
-
* The comparison is performed case-insensitively with whitespace trimming.
|
|
1528
|
-
* Examples: "READ", "Write", " delete ", "CREATE", "All".
|
|
1529
|
-
*
|
|
1530
|
-
* @returns `true` if the permission action exactly matches the requested action
|
|
1531
|
-
* after case-insensitive comparison and whitespace normalization.
|
|
1532
|
-
* Returns `false` if either parameter is invalid, null, undefined,
|
|
1533
|
-
* empty, or if the normalized strings do not match exactly.
|
|
1534
|
-
*
|
|
1535
|
-
* @example
|
|
1536
|
-
* ```typescript
|
|
1537
|
-
* // Basic exact matching (case-insensitive)
|
|
1538
|
-
* console.log(Auth.isAllowedForAction("read", "read")); // true
|
|
1539
|
-
* console.log(Auth.isAllowedForAction("READ", "read")); // true
|
|
1540
|
-
* console.log(Auth.isAllowedForAction("Read", "READ")); // true
|
|
1541
|
-
* console.log(Auth.isAllowedForAction("write", "read")); // false
|
|
1542
|
-
* ```
|
|
1543
|
-
*
|
|
1544
|
-
* @example
|
|
1545
|
-
* ```typescript
|
|
1546
|
-
* // Whitespace handling and normalization
|
|
1547
|
-
* console.log(Auth.isAllowedForAction(" read ", "read")); // true
|
|
1548
|
-
* console.log(Auth.isAllowedForAction("update", " UPDATE ")); // true
|
|
1549
|
-
* console.log(Auth.isAllowedForAction("\tdelete\n", "delete")); // true
|
|
1550
|
-
* console.log(Auth.isAllowedForAction("create ", " create")); // true
|
|
1551
|
-
* ```
|
|
1552
|
-
*
|
|
1553
|
-
* @example
|
|
1554
|
-
* ```typescript
|
|
1555
|
-
* // Input validation and edge cases
|
|
1556
|
-
* console.log(Auth.isAllowedForAction("", "read")); // false (empty permission)
|
|
1557
|
-
* console.log(Auth.isAllowedForAction("read", "")); // false (empty action)
|
|
1558
|
-
* console.log(Auth.isAllowedForAction(null as any, "read")); // false (null permission)
|
|
1559
|
-
* console.log(Auth.isAllowedForAction("read", undefined as any)); // false (undefined action)
|
|
1560
|
-
* console.log(Auth.isAllowedForAction("valid", "valid")); // true (both valid)
|
|
1561
|
-
* ```
|
|
1562
|
-
*
|
|
1563
|
-
* @example
|
|
1564
|
-
* ```typescript
|
|
1565
|
-
* // Integration with permission checking workflow
|
|
1566
|
-
* function checkSpecificPermission(
|
|
1567
|
-
* userActions: ResourceActionName[],
|
|
1568
|
-
* requiredAction: ResourceActionName
|
|
1569
|
-
* ): boolean {
|
|
1570
|
-
* // Check if user has "all" permission (universal access)
|
|
1571
|
-
* if (userActions.includes("all")) {
|
|
1572
|
-
* return true;
|
|
1573
|
-
* }
|
|
1574
|
-
*
|
|
1575
|
-
* // Check each specific action permission
|
|
1576
|
-
* for (const userAction of userActions) {
|
|
1577
|
-
* if (Auth.isAllowedForAction(userAction, requiredAction)) {
|
|
1578
|
-
* return true;
|
|
1579
|
-
* }
|
|
1580
|
-
* }
|
|
1581
|
-
*
|
|
1582
|
-
* return false;
|
|
1583
|
-
* }
|
|
1584
|
-
*
|
|
1585
|
-
* // Usage example
|
|
1586
|
-
* const userPermissions: ResourceActionName[] = ["read", "UPDATE", " create "];
|
|
1587
|
-
*
|
|
1588
|
-
* console.log(checkSpecificPermission(userPermissions, "read")); // true
|
|
1589
|
-
* console.log(checkSpecificPermission(userPermissions, "update")); // true (case-insensitive)
|
|
1590
|
-
* console.log(checkSpecificPermission(userPermissions, "create")); // true (whitespace normalized)
|
|
1591
|
-
* console.log(checkSpecificPermission(userPermissions, "delete")); // false
|
|
1592
|
-
* ```
|
|
1593
|
-
*
|
|
1594
|
-
* @example
|
|
1595
|
-
* ```typescript
|
|
1596
|
-
* // Custom action validation system
|
|
1597
|
-
* class ActionValidator {
|
|
1598
|
-
* private static readonly VALID_ACTIONS = [
|
|
1599
|
-
* "read", "create", "update", "delete",
|
|
1600
|
-
* "list", "search", "export", "import", "all"
|
|
1601
|
-
* ];
|
|
1602
|
-
*
|
|
1603
|
-
* static isValidAction(action: string): boolean {
|
|
1604
|
-
* return this.VALID_ACTIONS.some(validAction =>
|
|
1605
|
-
* Auth.isAllowedForAction(validAction, action)
|
|
1606
|
-
* );
|
|
1607
|
-
* }
|
|
1608
|
-
*
|
|
1609
|
-
* static normalizeAction(action: string): string {
|
|
1610
|
-
* if (!action || typeof action !== 'string') {
|
|
1611
|
-
* return '';
|
|
1612
|
-
* }
|
|
1613
|
-
* return action.trim().toLowerCase();
|
|
1614
|
-
* }
|
|
1615
|
-
*
|
|
1616
|
-
* static compareActions(action1: string, action2: string): boolean {
|
|
1617
|
-
* return Auth.isAllowedForAction(action1, action2);
|
|
1618
|
-
* }
|
|
1619
|
-
* }
|
|
1620
|
-
*
|
|
1621
|
-
* // Usage
|
|
1622
|
-
* console.log(ActionValidator.isValidAction("READ")); // true
|
|
1623
|
-
* console.log(ActionValidator.isValidAction("INVALID")); // false
|
|
1624
|
-
* console.log(ActionValidator.compareActions("Create", "create")); // true
|
|
1625
|
-
* ```
|
|
1626
|
-
*
|
|
1627
|
-
* @example
|
|
1628
|
-
* ```typescript
|
|
1629
|
-
* // Permission matrix validation
|
|
1630
|
-
* interface PermissionCheck {
|
|
1631
|
-
* granted: ResourceActionName;
|
|
1632
|
-
* requested: ResourceActionName;
|
|
1633
|
-
* expected: boolean;
|
|
1634
|
-
* }
|
|
11
|
+
* Authentication event handler.
|
|
12
|
+
* Initializes an observable event handler for authentication Auth.events.
|
|
1635
13
|
*
|
|
1636
|
-
*
|
|
1637
|
-
*
|
|
1638
|
-
*
|
|
1639
|
-
*
|
|
1640
|
-
* console.error(`Permission check failed: ${check.granted} vs ${check.requested}`);
|
|
1641
|
-
* return false;
|
|
1642
|
-
* }
|
|
1643
|
-
* return true;
|
|
1644
|
-
* });
|
|
1645
|
-
* }
|
|
14
|
+
* This constant `events` is assigned an instance of `Observable<AuthEvent>`, which is used to manage
|
|
15
|
+
* authentication-related events in the application. The initialization checks if the global
|
|
16
|
+
* `Global.eventsResourcesObservableHandler` exists and is an object. If it does, it assigns it to
|
|
17
|
+
* `events`; otherwise, it defaults to an empty object cast as `Observable<AuthEvent>`.
|
|
1646
18
|
*
|
|
1647
|
-
*
|
|
1648
|
-
*
|
|
1649
|
-
* { granted: "read", requested: "read", expected: true },
|
|
1650
|
-
* { granted: "READ", requested: "read", expected: true },
|
|
1651
|
-
* { granted: "write", requested: "read", expected: false },
|
|
1652
|
-
* { granted: " update ", requested: "UPDATE", expected: true },
|
|
1653
|
-
* { granted: "all", requested: "anything", expected: true }
|
|
1654
|
-
* ];
|
|
19
|
+
* This pattern allows for flexible handling of events, ensuring that the application can respond
|
|
20
|
+
* to authentication actions such as sign-in, sign-out, and sign-up.
|
|
1655
21
|
*
|
|
1656
|
-
*
|
|
1657
|
-
* console.log(`All permission tests passed: ${allTestsPassed}`);
|
|
1658
|
-
* ```
|
|
22
|
+
* @type {Observable<AuthEvent>}
|
|
1659
23
|
*
|
|
1660
24
|
* @example
|
|
1661
|
-
*
|
|
1662
|
-
*
|
|
1663
|
-
*
|
|
1664
|
-
*
|
|
1665
|
-
* grantedActions: ResourceActionName[],
|
|
1666
|
-
* requestedAction: ResourceActionName
|
|
1667
|
-
* ): { allowed: boolean; matchedAction?: ResourceActionName } {
|
|
1668
|
-
* // Check for universal permission first
|
|
1669
|
-
* if (grantedActions.includes("all")) {
|
|
1670
|
-
* return { allowed: true, matchedAction: "all" };
|
|
1671
|
-
* }
|
|
1672
|
-
*
|
|
1673
|
-
* // Find specific matching action
|
|
1674
|
-
* for (const grantedAction of grantedActions) {
|
|
1675
|
-
* if (Auth.isAllowedForAction(grantedAction, requestedAction)) {
|
|
1676
|
-
* return { allowed: true, matchedAction: grantedAction };
|
|
1677
|
-
* }
|
|
1678
|
-
* }
|
|
25
|
+
* import {Auth} from 'reslib/auth';
|
|
26
|
+
* Auth.events.on('SIGN_IN', (user) => {
|
|
27
|
+
* console.log(`User signed in: ${user.username}`);
|
|
28
|
+
* });
|
|
1679
29
|
*
|
|
1680
|
-
*
|
|
1681
|
-
*
|
|
30
|
+
* function userSignIn(user) {
|
|
31
|
+
* Auth.events.trigger('SIGN_IN', user);
|
|
1682
32
|
* }
|
|
1683
|
-
*
|
|
1684
|
-
* // Usage
|
|
1685
|
-
* const result = PermissionEngine.evaluateActionPermission(
|
|
1686
|
-
* ["read", "CREATE", " update "],
|
|
1687
|
-
* "create"
|
|
1688
|
-
* );
|
|
1689
|
-
* console.log(result); // { allowed: true, matchedAction: "CREATE" }
|
|
1690
|
-
* ```
|
|
1691
|
-
*
|
|
1692
|
-
* @see {@link ResourceActionName} - Type definition for valid action names
|
|
1693
|
-
* @see {@link checkPermission} - Higher-level permission checking method that uses this function
|
|
1694
|
-
* @see {@link checkUserPermission} - User-specific permission validation
|
|
1695
|
-
* @see {@link isAllowed} - Comprehensive permission evaluation system
|
|
1696
|
-
*
|
|
1697
|
-
*
|
|
1698
|
-
* @public
|
|
1699
|
-
*
|
|
1700
|
-
* @remarks
|
|
1701
|
-
* **Implementation Notes:**
|
|
1702
|
-
* - This method is used internally by `checkPermission` for action matching
|
|
1703
|
-
* - The case-insensitive comparison helps prevent common permission configuration errors
|
|
1704
|
-
* - Whitespace trimming ensures that formatting inconsistencies don't affect security
|
|
1705
|
-
* - The method is designed to be called frequently, so performance is optimized
|
|
1706
|
-
*
|
|
1707
|
-
* **Security Considerations:**
|
|
1708
|
-
* - Input validation prevents type confusion attacks
|
|
1709
|
-
* - Normalization prevents case-based permission bypass attempts
|
|
1710
|
-
* - Exact matching after normalization ensures no unexpected permission grants
|
|
1711
|
-
* - No regular expressions are used to avoid ReDoS vulnerabilities
|
|
1712
|
-
*
|
|
1713
|
-
* **Performance Optimization:**
|
|
1714
|
-
* - Uses native string methods for maximum performance
|
|
1715
|
-
* - Early return on invalid inputs minimizes processing time
|
|
1716
|
-
* - No object allocation or complex logic for minimal memory footprint
|
|
1717
|
-
* - Suitable for high-frequency permission checking scenarios
|
|
1718
|
-
*
|
|
1719
|
-
* **Testing Strategy:**
|
|
1720
|
-
* - Test with various case combinations (lowercase, uppercase, mixed)
|
|
1721
|
-
* - Verify whitespace handling (leading, trailing, internal spaces)
|
|
1722
|
-
* - Validate edge cases (empty strings, null, undefined)
|
|
1723
|
-
* - Ensure consistent behavior across different JavaScript environments
|
|
1724
33
|
*/
|
|
1725
|
-
static
|
|
34
|
+
static events: Observable<AuthEvent>;
|
|
35
|
+
private static localUserRef;
|
|
36
|
+
private static config;
|
|
37
|
+
static isMasterAdmin?: (user?: AuthUser) => Promise<boolean> | boolean;
|
|
1726
38
|
/**
|
|
1727
|
-
*
|
|
1728
|
-
*
|
|
1729
|
-
* This static getter exposes the Session class, which contains utility methods and properties
|
|
1730
|
-
* for managing browser session storage, encrypted data persistence, and session-related
|
|
1731
|
-
* operations. It serves as a convenient access point to session functionality without
|
|
1732
|
-
* requiring separate imports, maintaining the cohesive design of the Auth class API.
|
|
1733
|
-
*
|
|
1734
|
-
* ### Session Management Features:
|
|
1735
|
-
* The Session class provides comprehensive session management capabilities including:
|
|
1736
|
-
* - **Encrypted Storage**: Secure storage and retrieval of sensitive session data
|
|
1737
|
-
* - **Cross-Tab Synchronization**: Session state management across multiple browser tabs
|
|
1738
|
-
* - **Storage Abstraction**: Unified interface for localStorage and sessionStorage
|
|
1739
|
-
* - **Data Serialization**: Automatic JSON serialization/deserialization with error handling
|
|
1740
|
-
* - **Storage Events**: Event-driven session updates and notifications
|
|
1741
|
-
*
|
|
1742
|
-
* ### Integration Benefits:
|
|
1743
|
-
* - **Unified API**: Access session functionality through the Auth class namespace
|
|
1744
|
-
* - **Consistent Interface**: Maintains the same design patterns as other Auth methods
|
|
1745
|
-
* - **Type Safety**: Full TypeScript support with proper type definitions
|
|
1746
|
-
* - **Documentation**: Comprehensive JSDoc documentation for all session methods
|
|
1747
|
-
*
|
|
1748
|
-
* @returns The Session class containing static methods for session management.
|
|
1749
|
-
* This includes methods for storing, retrieving, and managing encrypted
|
|
1750
|
-
* session data, as well as utilities for session lifecycle management.
|
|
1751
|
-
*
|
|
1752
|
-
* @example
|
|
1753
|
-
* ```typescript
|
|
1754
|
-
* // Basic session storage operations
|
|
1755
|
-
* const session = Auth.Session;
|
|
1756
|
-
*
|
|
1757
|
-
* // Store data in session
|
|
1758
|
-
* await session.set('user_preferences', { theme: 'dark', language: 'en' });
|
|
1759
|
-
*
|
|
1760
|
-
* // Retrieve data from session
|
|
1761
|
-
* const preferences = session.get('user_preferences');
|
|
1762
|
-
* console.log(preferences); // { theme: 'dark', language: 'en' }
|
|
1763
|
-
*
|
|
1764
|
-
* // Remove data from session
|
|
1765
|
-
* await session.remove('user_preferences');
|
|
1766
|
-
* ```
|
|
1767
|
-
*
|
|
1768
|
-
* @example
|
|
1769
|
-
* ```typescript
|
|
1770
|
-
* // Encrypted session data management
|
|
1771
|
-
* const session = Auth.Session;
|
|
1772
|
-
*
|
|
1773
|
-
* // Store sensitive data with encryption
|
|
1774
|
-
* const sensitiveData = {
|
|
1775
|
-
* apiKey: 'secret_api_key_12345',
|
|
1776
|
-
* userToken: 'bearer_token_xyz789',
|
|
1777
|
-
* personalInfo: { ssn: '123-45-6789', creditCard: '4111-1111-1111-1111' }
|
|
1778
|
-
* };
|
|
1779
|
-
*
|
|
1780
|
-
* await session.setEncrypted('sensitive_data', sensitiveData, 'encryption_key');
|
|
1781
|
-
*
|
|
1782
|
-
* // Retrieve and decrypt sensitive data
|
|
1783
|
-
* const decryptedData = session.getEncrypted('sensitive_data', 'encryption_key');
|
|
1784
|
-
* console.log(decryptedData.apiKey); // 'secret_api_key_12345'
|
|
1785
|
-
* ```
|
|
1786
|
-
*
|
|
1787
|
-
* @example
|
|
1788
|
-
* ```typescript
|
|
1789
|
-
* // Session lifecycle management
|
|
1790
|
-
* const session = Auth.Session;
|
|
1791
|
-
*
|
|
1792
|
-
* // Initialize session with configuration
|
|
1793
|
-
* session.configure({
|
|
1794
|
-
* storageType: 'sessionStorage', // or 'localStorage'
|
|
1795
|
-
* encryptionEnabled: true,
|
|
1796
|
-
* keyPrefix: 'myapp_',
|
|
1797
|
-
* maxAge: 3600000 // 1 hour in milliseconds
|
|
1798
|
-
* });
|
|
1799
|
-
*
|
|
1800
|
-
* // Check session validity
|
|
1801
|
-
* const isValid = session.isValid('user_session');
|
|
1802
|
-
* if (isValid) {
|
|
1803
|
-
* console.log('Session is still valid');
|
|
1804
|
-
* } else {
|
|
1805
|
-
* console.log('Session has expired or is invalid');
|
|
1806
|
-
* }
|
|
1807
|
-
*
|
|
1808
|
-
* // Clear expired sessions
|
|
1809
|
-
* session.clearExpired();
|
|
1810
|
-
* ```
|
|
39
|
+
* Configures the Auth module with custom settings.
|
|
1811
40
|
*
|
|
41
|
+
* @param options - Configuration options
|
|
1812
42
|
* @example
|
|
1813
|
-
*
|
|
1814
|
-
*
|
|
1815
|
-
*
|
|
1816
|
-
*
|
|
1817
|
-
* // Listen for session changes across tabs
|
|
1818
|
-
* session.onStorageChange((event) => {
|
|
1819
|
-
* console.log('Session changed in another tab:', event);
|
|
1820
|
-
* if (event.key === 'user_session' && event.newValue === null) {
|
|
1821
|
-
* console.log('User logged out in another tab');
|
|
1822
|
-
* handleCrossTabLogout();
|
|
1823
|
-
* }
|
|
43
|
+
* Auth.configure({
|
|
44
|
+
* encryptionKey: process.env.AUTH_KEY,
|
|
45
|
+
* sessionTTL: 7 * 24 * 60 * 60 * 1000, // 7 days
|
|
46
|
+
* masterAdminCheck: (user) => user?.roles?.includes('admin')
|
|
1824
47
|
* });
|
|
48
|
+
*/
|
|
49
|
+
static configure(options: AuthConfig): void;
|
|
50
|
+
private static _isMasterAdmin;
|
|
51
|
+
/**
|
|
52
|
+
* Validates if a session is expired based on configured TTL.
|
|
53
|
+
* @param sessionCreatedAt - Session creation timestamp
|
|
54
|
+
* @returns true if session is valid, false if expired
|
|
55
|
+
*/
|
|
56
|
+
private static isSessionValid;
|
|
57
|
+
/**
|
|
58
|
+
* Retrieves the currently authenticated user from secure storage.
|
|
1825
59
|
*
|
|
1826
|
-
*
|
|
1827
|
-
|
|
1828
|
-
|
|
1829
|
-
|
|
1830
|
-
*
|
|
1831
|
-
*
|
|
1832
|
-
* // Session-based caching system
|
|
1833
|
-
* class SessionCache {
|
|
1834
|
-
* private static session = Auth.Session;
|
|
1835
|
-
*
|
|
1836
|
-
* static async cacheApiResponse<T>(key: string, data: T, ttl: number = 300000): Promise<void> {
|
|
1837
|
-
* const cacheEntry = {
|
|
1838
|
-
* data,
|
|
1839
|
-
* timestamp: Date.now(),
|
|
1840
|
-
* ttl
|
|
1841
|
-
* };
|
|
1842
|
-
*
|
|
1843
|
-
* await this.session.set(`cache_${key}`, cacheEntry);
|
|
1844
|
-
* }
|
|
1845
|
-
*
|
|
1846
|
-
* static getCachedData<T>(key: string): T | null {
|
|
1847
|
-
* const cacheEntry = this.session.get(`cache_${key}`);
|
|
1848
|
-
*
|
|
1849
|
-
* if (!cacheEntry) return null;
|
|
1850
|
-
*
|
|
1851
|
-
* const { data, timestamp, ttl } = cacheEntry;
|
|
1852
|
-
* const isExpired = Date.now() - timestamp > ttl;
|
|
1853
|
-
*
|
|
1854
|
-
* if (isExpired) {
|
|
1855
|
-
* this.session.remove(`cache_${key}`);
|
|
1856
|
-
* return null;
|
|
1857
|
-
* }
|
|
1858
|
-
*
|
|
1859
|
-
* return data;
|
|
1860
|
-
* }
|
|
1861
|
-
*
|
|
1862
|
-
* static clearCache(): void {
|
|
1863
|
-
* this.session.clearByPrefix('cache_');
|
|
1864
|
-
* }
|
|
1865
|
-
* }
|
|
1866
|
-
*
|
|
1867
|
-
* // Usage
|
|
1868
|
-
* await SessionCache.cacheApiResponse('user_profile', userData, 600000); // 10 minutes
|
|
1869
|
-
* const cachedProfile = SessionCache.getCachedData('user_profile');
|
|
1870
|
-
* ```
|
|
1871
|
-
*
|
|
1872
|
-
* @example
|
|
1873
|
-
* ```typescript
|
|
1874
|
-
* // Integration with authentication workflow
|
|
1875
|
-
* class AuthSessionManager {
|
|
1876
|
-
* private static session = Auth.Session;
|
|
1877
|
-
*
|
|
1878
|
-
* static async storeAuthSession(user: AuthUser, rememberMe: boolean = false): Promise<void> {
|
|
1879
|
-
* const sessionData = {
|
|
1880
|
-
* user,
|
|
1881
|
-
* timestamp: Date.now(),
|
|
1882
|
-
* rememberMe,
|
|
1883
|
-
* sessionId: crypto.randomUUID()
|
|
1884
|
-
* };
|
|
1885
|
-
*
|
|
1886
|
-
* const storageType = rememberMe ? 'localStorage' : 'sessionStorage';
|
|
1887
|
-
*
|
|
1888
|
-
* await this.session.setWithOptions('auth_session', sessionData, {
|
|
1889
|
-
* storage: storageType,
|
|
1890
|
-
* encrypted: true,
|
|
1891
|
-
* maxAge: rememberMe ? 2592000000 : 86400000 // 30 days or 1 day
|
|
1892
|
-
* });
|
|
1893
|
-
* }
|
|
1894
|
-
*
|
|
1895
|
-
* static getAuthSession(): { user: AuthUser; sessionId: string } | null {
|
|
1896
|
-
* const sessionData = this.session.getDecrypted('auth_session');
|
|
1897
|
-
*
|
|
1898
|
-
* if (!sessionData || this.isSessionExpired(sessionData)) {
|
|
1899
|
-
* this.clearAuthSession();
|
|
1900
|
-
* return null;
|
|
1901
|
-
* }
|
|
1902
|
-
*
|
|
1903
|
-
* return {
|
|
1904
|
-
* user: sessionData.user,
|
|
1905
|
-
* sessionId: sessionData.sessionId
|
|
1906
|
-
* };
|
|
1907
|
-
* }
|
|
1908
|
-
*
|
|
1909
|
-
* static clearAuthSession(): void {
|
|
1910
|
-
* this.session.remove('auth_session');
|
|
1911
|
-
* this.session.clearByPrefix('user_');
|
|
1912
|
-
* }
|
|
1913
|
-
*
|
|
1914
|
-
* private static isSessionExpired(sessionData: any): boolean {
|
|
1915
|
-
* const maxAge = sessionData.rememberMe ? 2592000000 : 86400000;
|
|
1916
|
-
* return Date.now() - sessionData.timestamp > maxAge;
|
|
1917
|
-
* }
|
|
1918
|
-
* }
|
|
1919
|
-
* ```
|
|
1920
|
-
*
|
|
1921
|
-
* @see {@link Session} - The Session class with complete session management functionality
|
|
1922
|
-
* @see {@link setSignedUser} - Method that uses Session for storing encrypted user data
|
|
1923
|
-
* @see {@link getSignedUser} - Method that uses Session for retrieving encrypted user data
|
|
1924
|
-
* @see {@link AuthUser} - User interface that may be stored in session
|
|
1925
|
-
*
|
|
1926
|
-
*
|
|
1927
|
-
* @public
|
|
1928
|
-
* @readonly
|
|
1929
|
-
*
|
|
1930
|
-
* @remarks
|
|
1931
|
-
* **Usage Patterns:**
|
|
1932
|
-
* - Use `Auth.Session` for all session-related operations within authentication workflows
|
|
1933
|
-
* - The Session class provides both synchronous and asynchronous methods for flexibility
|
|
1934
|
-
* - Encrypted storage methods should be used for sensitive authentication data
|
|
1935
|
-
* - Consider session storage vs local storage based on data persistence requirements
|
|
1936
|
-
*
|
|
1937
|
-
* **Security Considerations:**
|
|
1938
|
-
* - Always use encrypted storage methods for sensitive authentication data
|
|
1939
|
-
* - Be aware that session storage is cleared when the browser tab closes
|
|
1940
|
-
* - Local storage persists across browser sessions and should be used carefully
|
|
1941
|
-
* - Implement proper session timeout and cleanup mechanisms
|
|
60
|
+
* @returns The authenticated user or null if not signed in
|
|
61
|
+
*/
|
|
62
|
+
static getSignedUser(): Promise<AuthUser | null>;
|
|
63
|
+
/**
|
|
64
|
+
* Forces a refresh of the user session from storage.
|
|
65
|
+
* Clears the cache and retrieves fresh data.
|
|
1942
66
|
*
|
|
1943
|
-
*
|
|
1944
|
-
|
|
1945
|
-
|
|
1946
|
-
|
|
1947
|
-
*
|
|
67
|
+
* @returns The refreshed user object or null
|
|
68
|
+
*/
|
|
69
|
+
static refreshSignedUser(): Promise<AuthUser | null>;
|
|
70
|
+
/**
|
|
71
|
+
* Securely stores an authenticated user in secure storage.
|
|
72
|
+
* Only triggers events if storage operation succeeds.
|
|
1948
73
|
*
|
|
1949
|
-
*
|
|
1950
|
-
*
|
|
1951
|
-
*
|
|
1952
|
-
*
|
|
1953
|
-
* - Encryption features require a compatible JavaScript environment
|
|
74
|
+
* @param u - The user object to store, or null to sign out
|
|
75
|
+
* @param triggerEvent - Whether to trigger SIGN_IN/SIGN_OUT events
|
|
76
|
+
* @returns The stored user object
|
|
77
|
+
* @throws {AuthError} If storage operation fails
|
|
1954
78
|
*/
|
|
1955
|
-
static
|
|
79
|
+
static setSignedUser(u: AuthUser | null, triggerEvent?: boolean): Promise<AuthUser | null>;
|
|
80
|
+
static signIn(user: AuthUser, triggerEvent?: boolean): Promise<AuthUser>;
|
|
81
|
+
static signOut(triggerEvent?: boolean): Promise<void>;
|
|
82
|
+
private static isResourceActionTupleArray;
|
|
83
|
+
private static isResourceActionTupleObject;
|
|
84
|
+
static isValidUser(user: unknown): user is AuthUser;
|
|
85
|
+
static isAllowed<TResourceName extends ResourceName = ResourceName>(perm: AuthPerm<TResourceName>, user?: AuthUser | null): Promise<boolean>;
|
|
86
|
+
static checkUserPermission<TResourceName extends ResourceName = ResourceName>(user: AuthUser, resource: TResourceName, action?: ResourceActionName<TResourceName>): boolean;
|
|
87
|
+
static checkPermission<TResourceName extends ResourceName = ResourceName>(perms: AuthPerms, resource: TResourceName, action?: ResourceActionName<TResourceName>): boolean;
|
|
88
|
+
static isAllowedForAction<TResourceName extends ResourceName = ResourceName>(permFromResource: ResourceActionName<TResourceName>, action: ResourceActionName<TResourceName>): boolean;
|
|
1956
89
|
private static _secureStorage;
|
|
1957
90
|
/**
|
|
1958
91
|
* Gets the currently configured secure storage implementation for authentication data.
|
|
@@ -2202,37 +335,6 @@ export declare class Auth {
|
|
|
2202
335
|
* - Validate behavior across different platforms
|
|
2203
336
|
*/
|
|
2204
337
|
static set secureStorage(secureStorage: AuthSecureStorage);
|
|
2205
|
-
/**
|
|
2206
|
-
* Retrieves the authentication token from secure storage.
|
|
2207
|
-
*
|
|
2208
|
-
* This method provides secure, cross-platform access to the stored authentication token.
|
|
2209
|
-
* The token is stored separately from user data for enhanced security and to prevent
|
|
2210
|
-
* accidental exposure through session data serialization.
|
|
2211
|
-
*
|
|
2212
|
-
* @returns Promise resolving to the token string, or null if no token is stored
|
|
2213
|
-
* @example
|
|
2214
|
-
* ```typescript
|
|
2215
|
-
* const token = await Auth.getToken();
|
|
2216
|
-
* if (token) {
|
|
2217
|
-
* // Use token for API calls
|
|
2218
|
-
* }
|
|
2219
|
-
* ```
|
|
2220
|
-
*/
|
|
2221
|
-
static getToken(): Promise<string | null>;
|
|
2222
|
-
/**
|
|
2223
|
-
* Stores the authentication token in secure storage.
|
|
2224
|
-
*
|
|
2225
|
-
* This method securely stores the authentication token using the configured secure storage
|
|
2226
|
-
* implementation. The token is isolated from user session data for better security practices.
|
|
2227
|
-
*
|
|
2228
|
-
* @param token - The authentication token to store
|
|
2229
|
-
* @returns Promise that resolves when the token is stored
|
|
2230
|
-
* @example
|
|
2231
|
-
* ```typescript
|
|
2232
|
-
* await Auth.setToken('jwt-token-here');
|
|
2233
|
-
* ```
|
|
2234
|
-
*/
|
|
2235
|
-
static setToken(token: string): Promise<void>;
|
|
2236
338
|
private static readonly authStorageMetaData;
|
|
2237
339
|
}
|
|
2238
340
|
/**
|