@open-core/identity 1.2.5 → 1.2.6
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/dist/contracts.d.ts +51 -10
- package/dist/index.js +47 -0
- package/dist/providers/auth/credentials-auth.provider.d.ts +4 -11
- package/dist/providers/auth/credentials-auth.provider.js +16 -24
- package/dist/providers/auth/local-auth.provider.d.ts +1 -2
- package/dist/providers/auth/local-auth.provider.js +2 -3
- package/dist/providers/principal/local-principal.provider.js +4 -1
- package/dist/services/account.service.d.ts +24 -39
- package/dist/services/account.service.js +43 -40
- package/dist/services/role.service.d.ts +16 -25
- package/dist/services/role.service.js +22 -26
- package/dist/types.d.ts +42 -10
- package/package.json +2 -2
package/dist/contracts.d.ts
CHANGED
|
@@ -8,7 +8,7 @@ import type { IdentityAccount, IdentityRole } from "./types";
|
|
|
8
8
|
*
|
|
9
9
|
* @public
|
|
10
10
|
*/
|
|
11
|
-
export declare abstract class IdentityStore {
|
|
11
|
+
export declare abstract class IdentityStore<TId = any, TLinkedId = any, TRoleId = any> {
|
|
12
12
|
/**
|
|
13
13
|
* Retrieves an account by its primary connection identifier.
|
|
14
14
|
*
|
|
@@ -16,13 +16,20 @@ export declare abstract class IdentityStore {
|
|
|
16
16
|
* @returns A promise resolving to the account or null if not found.
|
|
17
17
|
*/
|
|
18
18
|
abstract findByIdentifier(identifier: string): Promise<IdentityAccount | null>;
|
|
19
|
+
/**
|
|
20
|
+
* Retrieves an account by its unique numeric or internal ID.
|
|
21
|
+
*
|
|
22
|
+
* @param id - The internal account identifier (database ID).
|
|
23
|
+
* @returns A promise resolving to the account or null if not found.
|
|
24
|
+
*/
|
|
25
|
+
abstract findById(id: TId): Promise<IdentityAccount | null>;
|
|
19
26
|
/**
|
|
20
27
|
* Retrieves an account by its linked stable ID.
|
|
21
28
|
*
|
|
22
29
|
* @param linkedId - The stable ID (e.g., a UUID).
|
|
23
30
|
* @returns A promise resolving to the account or null if not found.
|
|
24
31
|
*/
|
|
25
|
-
abstract findByLinkedId(linkedId:
|
|
32
|
+
abstract findByLinkedId(linkedId: TLinkedId): Promise<IdentityAccount | null>;
|
|
26
33
|
/**
|
|
27
34
|
* Retrieves an account by its unique username.
|
|
28
35
|
*
|
|
@@ -30,6 +37,19 @@ export declare abstract class IdentityStore {
|
|
|
30
37
|
* @returns A promise resolving to the account or null if not found.
|
|
31
38
|
*/
|
|
32
39
|
abstract findByUsername(username: string): Promise<IdentityAccount | null>;
|
|
40
|
+
/**
|
|
41
|
+
* Retrieves all accounts that are currently banned.
|
|
42
|
+
*
|
|
43
|
+
* @returns A promise resolving to an array of banned accounts.
|
|
44
|
+
*/
|
|
45
|
+
abstract findBanned(): Promise<IdentityAccount[]>;
|
|
46
|
+
/**
|
|
47
|
+
* Retrieves all accounts assigned to a specific role.
|
|
48
|
+
*
|
|
49
|
+
* @param roleId - The role identifier.
|
|
50
|
+
* @returns A promise resolving to an array of accounts.
|
|
51
|
+
*/
|
|
52
|
+
abstract findByRole(roleId: TRoleId): Promise<IdentityAccount[]>;
|
|
33
53
|
/**
|
|
34
54
|
* Persists a new identity account.
|
|
35
55
|
*
|
|
@@ -37,7 +57,7 @@ export declare abstract class IdentityStore {
|
|
|
37
57
|
* @returns A promise resolving to the fully created account object.
|
|
38
58
|
*/
|
|
39
59
|
abstract create(data: Omit<IdentityAccount, "id"> & {
|
|
40
|
-
id?:
|
|
60
|
+
id?: TId;
|
|
41
61
|
passwordHash?: string;
|
|
42
62
|
}): Promise<IdentityAccount>;
|
|
43
63
|
/**
|
|
@@ -46,7 +66,7 @@ export declare abstract class IdentityStore {
|
|
|
46
66
|
* @param id - The internal account ID.
|
|
47
67
|
* @param data - Partial object containing fields to update.
|
|
48
68
|
*/
|
|
49
|
-
abstract update(id:
|
|
69
|
+
abstract update(id: TId, data: Partial<Omit<IdentityAccount, "id">>): Promise<void>;
|
|
50
70
|
/**
|
|
51
71
|
* Prohibits or allows an account from connecting.
|
|
52
72
|
*
|
|
@@ -55,7 +75,7 @@ export declare abstract class IdentityStore {
|
|
|
55
75
|
* @param reason - Optional explanation for the ban.
|
|
56
76
|
* @param expiresAt - Optional expiration timestamp.
|
|
57
77
|
*/
|
|
58
|
-
abstract setBan(id:
|
|
78
|
+
abstract setBan(id: TId, banned: boolean, reason?: string, expiresAt?: Date | null): Promise<void>;
|
|
59
79
|
}
|
|
60
80
|
/**
|
|
61
81
|
* Persistence contract for security roles.
|
|
@@ -65,14 +85,35 @@ export declare abstract class IdentityStore {
|
|
|
65
85
|
*
|
|
66
86
|
* @public
|
|
67
87
|
*/
|
|
68
|
-
export declare abstract class RoleStore {
|
|
88
|
+
export declare abstract class RoleStore<TId = any> {
|
|
69
89
|
/**
|
|
70
90
|
* Retrieves a role definition by its technical identifier.
|
|
71
91
|
*
|
|
72
92
|
* @param id - Technical identifier (e.g., 'admin' or 1).
|
|
73
93
|
* @returns A promise resolving to the role or null if not found.
|
|
74
94
|
*/
|
|
75
|
-
abstract findById(id:
|
|
95
|
+
abstract findById(id: TId): Promise<IdentityRole | null>;
|
|
96
|
+
abstract findByName(name: string): Promise<IdentityRole | null>;
|
|
97
|
+
/**
|
|
98
|
+
* Retrieves a role by its hierarchical rank.
|
|
99
|
+
*
|
|
100
|
+
* @param rank - The numeric rank to search for.
|
|
101
|
+
* @returns A promise resolving to the role or null if not found.
|
|
102
|
+
*/
|
|
103
|
+
abstract findByRank(rank: number): Promise<IdentityRole | null>;
|
|
104
|
+
/**
|
|
105
|
+
* Retrieves all roles that grant a specific permission.
|
|
106
|
+
*
|
|
107
|
+
* @param permission - The permission string to search for.
|
|
108
|
+
* @returns A promise resolving to an array of roles.
|
|
109
|
+
*/
|
|
110
|
+
abstract findByPermission(permission: string): Promise<IdentityRole[]>;
|
|
111
|
+
/**
|
|
112
|
+
* Retrieves all registered roles in the system.
|
|
113
|
+
*
|
|
114
|
+
* @returns A promise resolving to an array of all roles.
|
|
115
|
+
*/
|
|
116
|
+
abstract findAll(): Promise<IdentityRole[]>;
|
|
76
117
|
/**
|
|
77
118
|
* Resolves the default role for newly connected accounts.
|
|
78
119
|
*
|
|
@@ -86,7 +127,7 @@ export declare abstract class RoleStore {
|
|
|
86
127
|
* @returns A promise resolving to the fully created role object.
|
|
87
128
|
*/
|
|
88
129
|
abstract create(role: Omit<IdentityRole, "id"> & {
|
|
89
|
-
id?:
|
|
130
|
+
id?: TId;
|
|
90
131
|
}): Promise<IdentityRole>;
|
|
91
132
|
/**
|
|
92
133
|
* Updates an existing role definition.
|
|
@@ -94,11 +135,11 @@ export declare abstract class RoleStore {
|
|
|
94
135
|
* @param id - Technical identifier of the role to update.
|
|
95
136
|
* @param role - Partial role object containing the fields to modify.
|
|
96
137
|
*/
|
|
97
|
-
abstract update(id:
|
|
138
|
+
abstract update(id: TId, role: Partial<Omit<IdentityRole, "id">>): Promise<void>;
|
|
98
139
|
/**
|
|
99
140
|
* Removes a role from the system.
|
|
100
141
|
*
|
|
101
142
|
* @param id - Technical identifier of the role to delete.
|
|
102
143
|
*/
|
|
103
|
-
abstract delete(id:
|
|
144
|
+
abstract delete(id: TId): Promise<void>;
|
|
104
145
|
}
|
package/dist/index.js
CHANGED
|
@@ -93,10 +93,57 @@ export var Identity;
|
|
|
93
93
|
// Configure Principal SPI based on mode
|
|
94
94
|
if (options.principal.mode === "api") {
|
|
95
95
|
Server.setPrincipalProvider(ApiPrincipalImpl);
|
|
96
|
+
if (options.principal.defaultRole && typeof options.principal.defaultRole !== "string") {
|
|
97
|
+
throw new Error("[OpenCore-Identity] In 'api' principal mode, 'defaultRole' must be a string (the ID returned by the API).");
|
|
98
|
+
}
|
|
96
99
|
}
|
|
97
100
|
else {
|
|
98
101
|
Server.setPrincipalProvider(PrincipalProviderImpl);
|
|
102
|
+
// Handle default role auto-creation or validation
|
|
103
|
+
const defaultRole = options.principal.defaultRole;
|
|
104
|
+
if (typeof defaultRole === "object") {
|
|
105
|
+
const roles = options.principal.roles || {};
|
|
106
|
+
const defaultId = "default_auto";
|
|
107
|
+
// Inject the role into the configuration if it doesn't exist
|
|
108
|
+
if (!roles[defaultId]) {
|
|
109
|
+
options.principal.roles = {
|
|
110
|
+
...roles,
|
|
111
|
+
[defaultId]: { ...defaultRole, id: defaultId },
|
|
112
|
+
};
|
|
113
|
+
options.principal.defaultRole = defaultId;
|
|
114
|
+
console.log(`[OpenCore-Identity] Default role '${defaultId}' created from configuration.`);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
99
117
|
}
|
|
118
|
+
// Handle onReady and waitFor
|
|
119
|
+
const runInitialization = async () => {
|
|
120
|
+
// 1. Wait for dependencies if specified
|
|
121
|
+
if (options.hooks?.waitFor) {
|
|
122
|
+
const waits = Array.isArray(options.hooks.waitFor)
|
|
123
|
+
? options.hooks.waitFor
|
|
124
|
+
: [options.hooks.waitFor];
|
|
125
|
+
try {
|
|
126
|
+
await Promise.all(waits);
|
|
127
|
+
}
|
|
128
|
+
catch (err) {
|
|
129
|
+
console.error("[OpenCore-Identity] Error waiting for dependencies in 'waitFor':", err);
|
|
130
|
+
return;
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
// 2. Execute onReady hook
|
|
134
|
+
if (options.hooks?.onReady) {
|
|
135
|
+
const accountService = container.resolve(AccountServiceImpl);
|
|
136
|
+
const roleService = container.resolve(RoleServiceImpl);
|
|
137
|
+
try {
|
|
138
|
+
await options.hooks.onReady({ accounts: accountService, roles: roleService, container });
|
|
139
|
+
}
|
|
140
|
+
catch (err) {
|
|
141
|
+
console.error("[OpenCore-Identity] Error in onReady hook:", err);
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
};
|
|
145
|
+
// Execute the async flow without blocking the main install call
|
|
146
|
+
runInitialization();
|
|
100
147
|
}
|
|
101
148
|
Identity.install = install;
|
|
102
149
|
})(Identity || (Identity = {}));
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { Server } from "@open-core/framework";
|
|
2
|
-
import { IdentityStore } from "../../contracts";
|
|
3
|
-
import type { IdentityOptions } from "../../types";
|
|
2
|
+
import { IdentityStore, RoleStore } from "../../contracts";
|
|
4
3
|
/**
|
|
5
4
|
* Authentication provider for username and password credentials.
|
|
6
5
|
*
|
|
@@ -12,17 +11,11 @@ import type { IdentityOptions } from "../../types";
|
|
|
12
11
|
* @public
|
|
13
12
|
*/
|
|
14
13
|
export declare class CredentialsAuthProvider extends Server.AuthProviderContract {
|
|
15
|
-
private readonly
|
|
16
|
-
private readonly
|
|
14
|
+
private readonly accountStore;
|
|
15
|
+
private readonly roleStore;
|
|
17
16
|
/** Cost factor for bcrypt hashing */
|
|
18
17
|
private readonly saltRounds;
|
|
19
|
-
|
|
20
|
-
* Initializes a new instance of the CredentialsAuthProvider.
|
|
21
|
-
*
|
|
22
|
-
* @param options - Identity system configuration options.
|
|
23
|
-
* @param store - Persistence layer for account and credential data.
|
|
24
|
-
*/
|
|
25
|
-
constructor(options: IdentityOptions, store: IdentityStore);
|
|
18
|
+
constructor(accountStore: IdentityStore<string, string, string>, roleStore: RoleStore<string>);
|
|
26
19
|
/**
|
|
27
20
|
* Authenticates a player using a username and password.
|
|
28
21
|
*
|
|
@@ -7,13 +7,9 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key,
|
|
|
7
7
|
var __metadata = (this && this.__metadata) || function (k, v) {
|
|
8
8
|
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
9
9
|
};
|
|
10
|
-
|
|
11
|
-
return function (target, key) { decorator(target, key, paramIndex); }
|
|
12
|
-
};
|
|
13
|
-
import { injectable, inject } from "tsyringe";
|
|
10
|
+
import { injectable } from "tsyringe";
|
|
14
11
|
import { Server } from "@open-core/framework";
|
|
15
|
-
import {
|
|
16
|
-
import { IdentityStore } from "../../contracts";
|
|
12
|
+
import { IdentityStore, RoleStore } from "../../contracts";
|
|
17
13
|
import bcrypt from "bcryptjs";
|
|
18
14
|
/**
|
|
19
15
|
* Authentication provider for username and password credentials.
|
|
@@ -26,16 +22,10 @@ import bcrypt from "bcryptjs";
|
|
|
26
22
|
* @public
|
|
27
23
|
*/
|
|
28
24
|
let CredentialsAuthProvider = class CredentialsAuthProvider extends Server.AuthProviderContract {
|
|
29
|
-
|
|
30
|
-
* Initializes a new instance of the CredentialsAuthProvider.
|
|
31
|
-
*
|
|
32
|
-
* @param options - Identity system configuration options.
|
|
33
|
-
* @param store - Persistence layer for account and credential data.
|
|
34
|
-
*/
|
|
35
|
-
constructor(options, store) {
|
|
25
|
+
constructor(accountStore, roleStore) {
|
|
36
26
|
super();
|
|
37
|
-
this.
|
|
38
|
-
this.
|
|
27
|
+
this.accountStore = accountStore;
|
|
28
|
+
this.roleStore = roleStore;
|
|
39
29
|
/** Cost factor for bcrypt hashing */
|
|
40
30
|
this.saltRounds = 10;
|
|
41
31
|
}
|
|
@@ -52,9 +42,9 @@ let CredentialsAuthProvider = class CredentialsAuthProvider extends Server.AuthP
|
|
|
52
42
|
if (!username || !password) {
|
|
53
43
|
return { success: false, error: "Username and password are required" };
|
|
54
44
|
}
|
|
55
|
-
const account = await this.
|
|
45
|
+
const account = await this.accountStore.findByUsername(username);
|
|
56
46
|
if (!account) {
|
|
57
|
-
return { success: false, error: "
|
|
47
|
+
return { success: false, error: "Account not found" };
|
|
58
48
|
}
|
|
59
49
|
const passwordHash = account.passwordHash;
|
|
60
50
|
if (!passwordHash) {
|
|
@@ -62,6 +52,7 @@ let CredentialsAuthProvider = class CredentialsAuthProvider extends Server.AuthP
|
|
|
62
52
|
}
|
|
63
53
|
const isValid = await bcrypt.compare(password, passwordHash);
|
|
64
54
|
if (!isValid) {
|
|
55
|
+
console.log(`compared and is not the same password!`);
|
|
65
56
|
return { success: false, error: "Invalid credentials" };
|
|
66
57
|
}
|
|
67
58
|
if (this.isBanned(account)) {
|
|
@@ -84,18 +75,19 @@ let CredentialsAuthProvider = class CredentialsAuthProvider extends Server.AuthP
|
|
|
84
75
|
if (!username || !password) {
|
|
85
76
|
return { success: false, error: "Username and password are required" };
|
|
86
77
|
}
|
|
87
|
-
const existing = await this.
|
|
78
|
+
const existing = await this.accountStore.findByUsername(username);
|
|
88
79
|
if (existing) {
|
|
89
80
|
return { success: false, error: "Username already taken" };
|
|
90
81
|
}
|
|
91
82
|
const passwordHash = await bcrypt.hash(password, this.saltRounds);
|
|
92
83
|
const identifiers = player.getIdentifiers();
|
|
93
84
|
const primaryIdentifier = identifiers[0] || `internal:${username}`;
|
|
94
|
-
const
|
|
85
|
+
const defaultRole = await this.roleStore.getDefaultRole();
|
|
86
|
+
const account = await this.accountStore.create({
|
|
95
87
|
username,
|
|
96
88
|
passwordHash,
|
|
97
89
|
identifier: primaryIdentifier,
|
|
98
|
-
roleId:
|
|
90
|
+
roleId: defaultRole.id,
|
|
99
91
|
customPermissions: [],
|
|
100
92
|
isBanned: false,
|
|
101
93
|
});
|
|
@@ -113,7 +105,7 @@ let CredentialsAuthProvider = class CredentialsAuthProvider extends Server.AuthP
|
|
|
113
105
|
const accountId = player.accountID;
|
|
114
106
|
if (!accountId)
|
|
115
107
|
return { success: false, error: "Not authenticated" };
|
|
116
|
-
const account = await this.
|
|
108
|
+
const account = await this.accountStore.findByLinkedId(accountId);
|
|
117
109
|
if (!account || this.isBanned(account)) {
|
|
118
110
|
return { success: false, error: "Session invalid or account banned" };
|
|
119
111
|
}
|
|
@@ -125,7 +117,7 @@ let CredentialsAuthProvider = class CredentialsAuthProvider extends Server.AuthP
|
|
|
125
117
|
* @param player - The framework player entity.
|
|
126
118
|
*/
|
|
127
119
|
async logout(player) {
|
|
128
|
-
|
|
120
|
+
player.unlinkAccount();
|
|
129
121
|
}
|
|
130
122
|
/**
|
|
131
123
|
* Internal helper to determine if an account is currently prohibited.
|
|
@@ -145,7 +137,7 @@ let CredentialsAuthProvider = class CredentialsAuthProvider extends Server.AuthP
|
|
|
145
137
|
};
|
|
146
138
|
CredentialsAuthProvider = __decorate([
|
|
147
139
|
injectable(),
|
|
148
|
-
|
|
149
|
-
|
|
140
|
+
__metadata("design:paramtypes", [IdentityStore,
|
|
141
|
+
RoleStore])
|
|
150
142
|
], CredentialsAuthProvider);
|
|
151
143
|
export { CredentialsAuthProvider };
|
|
@@ -28,7 +28,6 @@ interface AuthResult {
|
|
|
28
28
|
export declare class LocalAuthProvider extends Server.AuthProviderContract {
|
|
29
29
|
private readonly options;
|
|
30
30
|
private readonly store;
|
|
31
|
-
private readonly config;
|
|
32
31
|
/**
|
|
33
32
|
* Initializes a new instance of the IdentityAuthProvider.
|
|
34
33
|
*
|
|
@@ -36,7 +35,7 @@ export declare class LocalAuthProvider extends Server.AuthProviderContract {
|
|
|
36
35
|
* @param store - Persistence layer for account data.
|
|
37
36
|
* @param config - Framework configuration service.
|
|
38
37
|
*/
|
|
39
|
-
constructor(options: IdentityOptions, store: IdentityStore
|
|
38
|
+
constructor(options: IdentityOptions, store: IdentityStore);
|
|
40
39
|
/**
|
|
41
40
|
* Authenticates a player based on the configured strategy.
|
|
42
41
|
*
|
|
@@ -31,11 +31,10 @@ let LocalAuthProvider = class LocalAuthProvider extends Server.AuthProviderContr
|
|
|
31
31
|
* @param store - Persistence layer for account data.
|
|
32
32
|
* @param config - Framework configuration service.
|
|
33
33
|
*/
|
|
34
|
-
constructor(options, store
|
|
34
|
+
constructor(options, store) {
|
|
35
35
|
super();
|
|
36
36
|
this.options = options;
|
|
37
37
|
this.store = store;
|
|
38
|
-
this.config = config;
|
|
39
38
|
}
|
|
40
39
|
/**
|
|
41
40
|
* Authenticates a player based on the configured strategy.
|
|
@@ -147,6 +146,6 @@ let LocalAuthProvider = class LocalAuthProvider extends Server.AuthProviderContr
|
|
|
147
146
|
LocalAuthProvider = __decorate([
|
|
148
147
|
injectable(),
|
|
149
148
|
__param(0, inject(IDENTITY_OPTIONS)),
|
|
150
|
-
__metadata("design:paramtypes", [Object, IdentityStore
|
|
149
|
+
__metadata("design:paramtypes", [Object, IdentityStore])
|
|
151
150
|
], LocalAuthProvider);
|
|
152
151
|
export { LocalAuthProvider };
|
|
@@ -116,7 +116,10 @@ let IdentityPrincipalProvider = class IdentityPrincipalProvider extends Server.P
|
|
|
116
116
|
if (!role) {
|
|
117
117
|
const defaultRoleId = this.options.principal.defaultRole;
|
|
118
118
|
if (defaultRoleId !== undefined && defaultRoleId !== null && defaultRoleId !== "") {
|
|
119
|
-
|
|
119
|
+
// We ensure defaultRoleId is a valid key (string | number) because Identity.install
|
|
120
|
+
// converts any IdentityRole object into a registered 'default_auto' string ID.
|
|
121
|
+
const roleKey = typeof defaultRoleId === "object" ? "default_auto" : defaultRoleId;
|
|
122
|
+
role = this.options.principal.roles?.[roleKey];
|
|
120
123
|
if (!role && this.roleStore && this.options.principal.mode === "db") {
|
|
121
124
|
role = await this.roleStore.getDefaultRole();
|
|
122
125
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { IdentityStore } from "../contracts";
|
|
1
|
+
import { IdentityStore, RoleStore } from "../contracts";
|
|
2
2
|
import type { IdentityAccount } from "../types";
|
|
3
3
|
/**
|
|
4
4
|
* High-level service for managing identity accounts and security policies.
|
|
@@ -9,48 +9,33 @@ import type { IdentityAccount } from "../types";
|
|
|
9
9
|
* @public
|
|
10
10
|
* @injectable
|
|
11
11
|
*/
|
|
12
|
-
export declare class AccountService {
|
|
13
|
-
|
|
14
|
-
constructor(store: IdentityStore);
|
|
12
|
+
export declare class AccountService<TId = any, TLinkedId = any, TRoleId = any> {
|
|
13
|
+
readonly store: IdentityStore<TId, TLinkedId, TRoleId>;
|
|
14
|
+
constructor(store: IdentityStore<TId, TLinkedId, TRoleId>);
|
|
15
15
|
/**
|
|
16
|
-
* Retrieves
|
|
16
|
+
* Retrieves all accounts assigned to a specific role.
|
|
17
17
|
*
|
|
18
|
-
* @param
|
|
19
|
-
* @returns A promise resolving to
|
|
18
|
+
* @param roleId - The role identifier.
|
|
19
|
+
* @returns A promise resolving to an array of accounts.
|
|
20
20
|
*/
|
|
21
|
-
|
|
21
|
+
findByRole(roleId: TRoleId): Promise<IdentityAccount[]>;
|
|
22
22
|
/**
|
|
23
|
-
* Retrieves
|
|
23
|
+
* Retrieves all accounts that are currently prohibited from connecting.
|
|
24
24
|
*
|
|
25
|
-
* @
|
|
26
|
-
* @returns A promise resolving to the account or null if not found.
|
|
25
|
+
* @returns A promise resolving to an array of banned accounts.
|
|
27
26
|
*/
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
* @param data - Initial account properties. ID can be provided or left to the store.
|
|
33
|
-
* @returns A promise resolving to the fully created account object.
|
|
34
|
-
*/
|
|
35
|
-
create(data: Omit<IdentityAccount, "id"> & {
|
|
36
|
-
id?: string | number;
|
|
37
|
-
passwordHash?: string;
|
|
38
|
-
}): Promise<IdentityAccount>;
|
|
39
|
-
/**
|
|
40
|
-
* Updates an existing account's metadata or status.
|
|
41
|
-
*
|
|
42
|
-
* @param id - The internal account ID.
|
|
43
|
-
* @param data - Partial object containing fields to update.
|
|
44
|
-
* @returns A promise that resolves when the update is complete.
|
|
45
|
-
*/
|
|
46
|
-
update(id: string | number, data: Partial<Omit<IdentityAccount, "id">>): Promise<void>;
|
|
27
|
+
findBanned(): Promise<IdentityAccount[]>;
|
|
28
|
+
assignRole(accountId: TId, roleId: TRoleId, options?: {
|
|
29
|
+
clearCustomPermissions?: boolean;
|
|
30
|
+
}): Promise<void>;
|
|
47
31
|
/**
|
|
48
|
-
*
|
|
32
|
+
* Checks if an account has a specific permission, considering both role and custom overrides.
|
|
49
33
|
*
|
|
50
|
-
* @param accountId - The
|
|
51
|
-
* @param
|
|
34
|
+
* @param accountId - The account identifier.
|
|
35
|
+
* @param permission - The permission string to check.
|
|
36
|
+
* @param roleStore - Required to resolve the role's base permissions.
|
|
52
37
|
*/
|
|
53
|
-
|
|
38
|
+
hasPermission(linkedID: TLinkedId, permission: string, roleStore: RoleStore): Promise<boolean>;
|
|
54
39
|
/**
|
|
55
40
|
* Grants a custom permission override to an account.
|
|
56
41
|
*
|
|
@@ -60,24 +45,24 @@ export declare class AccountService {
|
|
|
60
45
|
* @param accountId - The linked ID of the account.
|
|
61
46
|
* @param permission - The permission string to grant.
|
|
62
47
|
*/
|
|
63
|
-
addCustomPermission(
|
|
48
|
+
addCustomPermission(linkedID: TLinkedId, permission: string): Promise<void>;
|
|
64
49
|
/**
|
|
65
50
|
* Revokes a custom permission override.
|
|
66
51
|
*
|
|
67
52
|
* To explicitly deny a permission that a role might grant, use the `-` prefix
|
|
68
53
|
* (e.g., `-chat.use`).
|
|
69
54
|
*
|
|
70
|
-
* @param
|
|
55
|
+
* @param linkedID - The linked ID of the account.
|
|
71
56
|
* @param permission - The permission string to remove or revoke.
|
|
72
57
|
*/
|
|
73
|
-
removeCustomPermission(
|
|
58
|
+
removeCustomPermission(linkedID: TLinkedId, permission: string): Promise<void>;
|
|
74
59
|
/**
|
|
75
60
|
* Prohibits an account from connecting to the server.
|
|
76
61
|
*
|
|
77
62
|
* @param accountId - The linked ID of the account.
|
|
78
63
|
* @param options - Ban details including optional reason and duration.
|
|
79
64
|
*/
|
|
80
|
-
ban(accountId:
|
|
65
|
+
ban(accountId: TId, options?: {
|
|
81
66
|
reason?: string;
|
|
82
67
|
durationMs?: number;
|
|
83
68
|
}): Promise<void>;
|
|
@@ -86,5 +71,5 @@ export declare class AccountService {
|
|
|
86
71
|
*
|
|
87
72
|
* @param accountId - The linked ID of the account.
|
|
88
73
|
*/
|
|
89
|
-
unban(accountId:
|
|
74
|
+
unban(accountId: TId): Promise<void>;
|
|
90
75
|
}
|
|
@@ -23,50 +23,53 @@ let AccountService = class AccountService {
|
|
|
23
23
|
this.store = store;
|
|
24
24
|
}
|
|
25
25
|
/**
|
|
26
|
-
* Retrieves
|
|
26
|
+
* Retrieves all accounts assigned to a specific role.
|
|
27
27
|
*
|
|
28
|
-
* @param
|
|
29
|
-
* @returns A promise resolving to
|
|
28
|
+
* @param roleId - The role identifier.
|
|
29
|
+
* @returns A promise resolving to an array of accounts.
|
|
30
30
|
*/
|
|
31
|
-
async
|
|
32
|
-
return this.store.
|
|
31
|
+
async findByRole(roleId) {
|
|
32
|
+
return this.store.findByRole(roleId);
|
|
33
33
|
}
|
|
34
34
|
/**
|
|
35
|
-
* Retrieves
|
|
35
|
+
* Retrieves all accounts that are currently prohibited from connecting.
|
|
36
36
|
*
|
|
37
|
-
* @
|
|
38
|
-
* @returns A promise resolving to the account or null if not found.
|
|
37
|
+
* @returns A promise resolving to an array of banned accounts.
|
|
39
38
|
*/
|
|
40
|
-
async
|
|
41
|
-
return this.store.
|
|
39
|
+
async findBanned() {
|
|
40
|
+
return this.store.findBanned();
|
|
42
41
|
}
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
async create(data) {
|
|
50
|
-
return this.store.create(data);
|
|
42
|
+
async assignRole(accountId, roleId, options = {}) {
|
|
43
|
+
const updateData = { roleId };
|
|
44
|
+
if (options.clearCustomPermissions) {
|
|
45
|
+
updateData.customPermissions = [];
|
|
46
|
+
}
|
|
47
|
+
await this.store.update(accountId, updateData);
|
|
51
48
|
}
|
|
52
49
|
/**
|
|
53
|
-
*
|
|
50
|
+
* Checks if an account has a specific permission, considering both role and custom overrides.
|
|
54
51
|
*
|
|
55
|
-
* @param
|
|
56
|
-
* @param
|
|
57
|
-
* @
|
|
52
|
+
* @param accountId - The account identifier.
|
|
53
|
+
* @param permission - The permission string to check.
|
|
54
|
+
* @param roleStore - Required to resolve the role's base permissions.
|
|
58
55
|
*/
|
|
59
|
-
async
|
|
60
|
-
await this.store.
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
56
|
+
async hasPermission(linkedID, permission, roleStore) {
|
|
57
|
+
const account = await this.store.findByLinkedId(linkedID);
|
|
58
|
+
if (!account)
|
|
59
|
+
return false;
|
|
60
|
+
if (!account.roleId)
|
|
61
|
+
return account.customPermissions.includes(permission);
|
|
62
|
+
const role = await roleStore.findById(account.roleId);
|
|
63
|
+
const basePermissions = role?.permissions || [];
|
|
64
|
+
// Simple resolution logic (could be more complex with wildcards)
|
|
65
|
+
const permissions = new Set(basePermissions);
|
|
66
|
+
for (const override of account.customPermissions) {
|
|
67
|
+
if (override === `+${permission}` || override === permission)
|
|
68
|
+
return true;
|
|
69
|
+
if (override === `-${permission}`)
|
|
70
|
+
return false;
|
|
71
|
+
}
|
|
72
|
+
return permissions.has(permission) || permissions.has("*");
|
|
70
73
|
}
|
|
71
74
|
/**
|
|
72
75
|
* Grants a custom permission override to an account.
|
|
@@ -77,13 +80,13 @@ let AccountService = class AccountService {
|
|
|
77
80
|
* @param accountId - The linked ID of the account.
|
|
78
81
|
* @param permission - The permission string to grant.
|
|
79
82
|
*/
|
|
80
|
-
async addCustomPermission(
|
|
81
|
-
const account = await this.store.findByLinkedId(
|
|
83
|
+
async addCustomPermission(linkedID, permission) {
|
|
84
|
+
const account = await this.store.findByLinkedId(linkedID);
|
|
82
85
|
if (!account)
|
|
83
86
|
return;
|
|
84
87
|
const permissions = new Set(account.customPermissions);
|
|
85
88
|
permissions.add(permission);
|
|
86
|
-
await this.store.update(
|
|
89
|
+
await this.store.update(account.id, {
|
|
87
90
|
customPermissions: Array.from(permissions),
|
|
88
91
|
});
|
|
89
92
|
}
|
|
@@ -93,16 +96,16 @@ let AccountService = class AccountService {
|
|
|
93
96
|
* To explicitly deny a permission that a role might grant, use the `-` prefix
|
|
94
97
|
* (e.g., `-chat.use`).
|
|
95
98
|
*
|
|
96
|
-
* @param
|
|
99
|
+
* @param linkedID - The linked ID of the account.
|
|
97
100
|
* @param permission - The permission string to remove or revoke.
|
|
98
101
|
*/
|
|
99
|
-
async removeCustomPermission(
|
|
100
|
-
const account = await this.store.findByLinkedId(
|
|
102
|
+
async removeCustomPermission(linkedID, permission) {
|
|
103
|
+
const account = await this.store.findByLinkedId(linkedID);
|
|
101
104
|
if (!account)
|
|
102
105
|
return;
|
|
103
106
|
const permissions = new Set(account.customPermissions);
|
|
104
107
|
permissions.delete(permission);
|
|
105
|
-
await this.store.update(
|
|
108
|
+
await this.store.update(account.id, {
|
|
106
109
|
customPermissions: Array.from(permissions),
|
|
107
110
|
});
|
|
108
111
|
}
|
|
@@ -10,38 +10,29 @@ import type { IdentityRole } from "../types";
|
|
|
10
10
|
* @public
|
|
11
11
|
* @injectable
|
|
12
12
|
*/
|
|
13
|
-
export declare class RoleService {
|
|
14
|
-
|
|
15
|
-
constructor(store: RoleStore);
|
|
13
|
+
export declare class RoleService<TId = any> {
|
|
14
|
+
readonly store: RoleStore<TId>;
|
|
15
|
+
constructor(store: RoleStore<TId>);
|
|
16
16
|
/**
|
|
17
|
-
*
|
|
17
|
+
* Retrieves all roles that grant a specific permission.
|
|
18
18
|
*
|
|
19
|
-
* @param
|
|
20
|
-
* @returns A promise resolving to
|
|
19
|
+
* @param permission - The permission string to search for.
|
|
20
|
+
* @returns A promise resolving to an array of roles.
|
|
21
21
|
*/
|
|
22
|
-
|
|
23
|
-
id?: string | number;
|
|
24
|
-
}): Promise<IdentityRole>;
|
|
22
|
+
findByPermission(permission: string): Promise<IdentityRole[]>;
|
|
25
23
|
/**
|
|
26
|
-
*
|
|
24
|
+
* Retrieves a role by its hierarchical rank.
|
|
27
25
|
*
|
|
28
|
-
* @param
|
|
29
|
-
* @
|
|
30
|
-
* @returns A promise that resolves when the update is complete.
|
|
26
|
+
* @param rank - The numeric rank to search for.
|
|
27
|
+
* @returns A promise resolving to the role or null if not found.
|
|
31
28
|
*/
|
|
32
|
-
|
|
29
|
+
findByRank(rank: number): Promise<IdentityRole | null>;
|
|
33
30
|
/**
|
|
34
|
-
*
|
|
31
|
+
* Checks if a role is higher or equal than another based on rank.
|
|
35
32
|
*
|
|
36
|
-
* @param
|
|
37
|
-
* @
|
|
33
|
+
* @param roleId - The role to check.
|
|
34
|
+
* @param requiredRoleId - The required role.
|
|
35
|
+
* @returns True if roleId has equal or higher rank.
|
|
38
36
|
*/
|
|
39
|
-
|
|
40
|
-
/**
|
|
41
|
-
* Retrieves the full list of permissions granted to a specific role.
|
|
42
|
-
*
|
|
43
|
-
* @param id - The technical identifier of the role.
|
|
44
|
-
* @returns A promise resolving to an array of permission strings.
|
|
45
|
-
*/
|
|
46
|
-
getPermissions(id: string | number): Promise<string[]>;
|
|
37
|
+
isHigherOrEqual(roleId: TId, requiredRoleId: TId): Promise<boolean>;
|
|
47
38
|
}
|
|
@@ -24,42 +24,38 @@ let RoleService = class RoleService {
|
|
|
24
24
|
this.store = store;
|
|
25
25
|
}
|
|
26
26
|
/**
|
|
27
|
-
*
|
|
27
|
+
* Retrieves all roles that grant a specific permission.
|
|
28
28
|
*
|
|
29
|
-
* @param
|
|
30
|
-
* @returns A promise resolving to
|
|
29
|
+
* @param permission - The permission string to search for.
|
|
30
|
+
* @returns A promise resolving to an array of roles.
|
|
31
31
|
*/
|
|
32
|
-
async
|
|
33
|
-
return this.store.
|
|
32
|
+
async findByPermission(permission) {
|
|
33
|
+
return this.store.findByPermission(permission);
|
|
34
34
|
}
|
|
35
35
|
/**
|
|
36
|
-
*
|
|
36
|
+
* Retrieves a role by its hierarchical rank.
|
|
37
37
|
*
|
|
38
|
-
* @param
|
|
39
|
-
* @
|
|
40
|
-
* @returns A promise that resolves when the update is complete.
|
|
38
|
+
* @param rank - The numeric rank to search for.
|
|
39
|
+
* @returns A promise resolving to the role or null if not found.
|
|
41
40
|
*/
|
|
42
|
-
async
|
|
43
|
-
|
|
41
|
+
async findByRank(rank) {
|
|
42
|
+
return this.store.findByRank(rank);
|
|
44
43
|
}
|
|
45
44
|
/**
|
|
46
|
-
*
|
|
45
|
+
* Checks if a role is higher or equal than another based on rank.
|
|
47
46
|
*
|
|
48
|
-
* @param
|
|
49
|
-
* @
|
|
47
|
+
* @param roleId - The role to check.
|
|
48
|
+
* @param requiredRoleId - The required role.
|
|
49
|
+
* @returns True if roleId has equal or higher rank.
|
|
50
50
|
*/
|
|
51
|
-
async
|
|
52
|
-
await
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
*/
|
|
60
|
-
async getPermissions(id) {
|
|
61
|
-
const role = await this.store.findById(id);
|
|
62
|
-
return role?.permissions || [];
|
|
51
|
+
async isHigherOrEqual(roleId, requiredRoleId) {
|
|
52
|
+
const [role, required] = await Promise.all([
|
|
53
|
+
this.store.findById(roleId),
|
|
54
|
+
this.store.findById(requiredRoleId)
|
|
55
|
+
]);
|
|
56
|
+
if (!role || !required)
|
|
57
|
+
return false;
|
|
58
|
+
return role.rank >= required.rank;
|
|
63
59
|
}
|
|
64
60
|
};
|
|
65
61
|
RoleService = __decorate([
|
package/dist/types.d.ts
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { AccountService } from "./services/account.service";
|
|
2
|
+
import { RoleService } from "./services/role.service";
|
|
1
3
|
/**
|
|
2
4
|
* Authentication strategy modes.
|
|
3
5
|
*
|
|
@@ -29,11 +31,13 @@ export type PrincipalMode = "roles" | "db" | "api";
|
|
|
29
31
|
*
|
|
30
32
|
* @public
|
|
31
33
|
*/
|
|
32
|
-
export interface IdentityRole {
|
|
34
|
+
export interface IdentityRole<TId = any> {
|
|
33
35
|
/**
|
|
34
|
-
* Technical identifier for the role (e.g.,
|
|
36
|
+
* Technical identifier for the role (e.g., 1, 'uuid').
|
|
35
37
|
*/
|
|
36
|
-
id:
|
|
38
|
+
id: TId;
|
|
39
|
+
/** name, common used by identifiers 'admin' */
|
|
40
|
+
name: string;
|
|
37
41
|
/**
|
|
38
42
|
* Hierarchical weight.
|
|
39
43
|
*
|
|
@@ -79,6 +83,15 @@ export interface IdentityOptions {
|
|
|
79
83
|
* @defaultValue 'license'
|
|
80
84
|
*/
|
|
81
85
|
primaryIdentifier?: string;
|
|
86
|
+
/**
|
|
87
|
+
* The ID of the role assigned to newly created accounts.
|
|
88
|
+
*
|
|
89
|
+
* - If string/number: Used as the ID of an existing role.
|
|
90
|
+
* - If IdentityRole object without ID: A default role will be created automatically.
|
|
91
|
+
*
|
|
92
|
+
* @defaultValue 'user'
|
|
93
|
+
*/
|
|
94
|
+
defaultRole?: string | number | Omit<IdentityRole, "id">;
|
|
82
95
|
};
|
|
83
96
|
/**
|
|
84
97
|
* Authorization and permissions configuration.
|
|
@@ -95,10 +108,10 @@ export interface IdentityOptions {
|
|
|
95
108
|
*/
|
|
96
109
|
roles?: Record<string | number, IdentityRole>;
|
|
97
110
|
/**
|
|
98
|
-
* The
|
|
99
|
-
*
|
|
111
|
+
* The default role, if you set a ID (external ID) use string type
|
|
112
|
+
* or use IdentityRole without id to create a new
|
|
100
113
|
*/
|
|
101
|
-
defaultRole?:
|
|
114
|
+
defaultRole?: Omit<IdentityRole, 'id'> | string;
|
|
102
115
|
/**
|
|
103
116
|
* Time-to-live in milliseconds for cached principal data.
|
|
104
117
|
*
|
|
@@ -108,6 +121,25 @@ export interface IdentityOptions {
|
|
|
108
121
|
*/
|
|
109
122
|
cacheTtl?: number;
|
|
110
123
|
};
|
|
124
|
+
/**
|
|
125
|
+
* Lifecycle hooks for the identity system.
|
|
126
|
+
*/
|
|
127
|
+
hooks?: {
|
|
128
|
+
/**
|
|
129
|
+
* Optional promise or array of promises to wait for before finishing initialization.
|
|
130
|
+
* Useful for ensuring database connections are established.
|
|
131
|
+
*/
|
|
132
|
+
waitFor?: Promise<any> | Promise<any>[];
|
|
133
|
+
/**
|
|
134
|
+
* Fired when the identity system is fully initialized and registered.
|
|
135
|
+
* Use this to perform database seeding (e.g., creating default roles).
|
|
136
|
+
*/
|
|
137
|
+
onReady?: (services: {
|
|
138
|
+
accounts: AccountService;
|
|
139
|
+
roles: RoleService;
|
|
140
|
+
container: any;
|
|
141
|
+
}) => Promise<void> | void;
|
|
142
|
+
};
|
|
111
143
|
}
|
|
112
144
|
/**
|
|
113
145
|
* Represents a persistent identity account.
|
|
@@ -117,19 +149,19 @@ export interface IdentityOptions {
|
|
|
117
149
|
*
|
|
118
150
|
* @public
|
|
119
151
|
*/
|
|
120
|
-
export interface IdentityAccount {
|
|
152
|
+
export interface IdentityAccount<TId = any, TRoleId = any> {
|
|
121
153
|
/**
|
|
122
154
|
* Internal unique database/store ID.
|
|
123
155
|
*/
|
|
124
|
-
id:
|
|
156
|
+
id: TId;
|
|
125
157
|
/**
|
|
126
158
|
* Primary connection identifier (e.g., 'license:123...').
|
|
127
159
|
*/
|
|
128
|
-
identifier
|
|
160
|
+
identifier?: string;
|
|
129
161
|
/**
|
|
130
162
|
* Current technical role ID assigned to this account.
|
|
131
163
|
*/
|
|
132
|
-
roleId?:
|
|
164
|
+
roleId?: TRoleId;
|
|
133
165
|
/**
|
|
134
166
|
* Optional technical username for credentials-based authentication.
|
|
135
167
|
*/
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@open-core/identity",
|
|
3
|
-
"version": "1.2.
|
|
3
|
+
"version": "1.2.6",
|
|
4
4
|
"description": "Enterprise-grade identity, authentication, and authorization plugin for OpenCore Framework",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -39,7 +39,7 @@
|
|
|
39
39
|
},
|
|
40
40
|
"packageManager": "pnpm@10.13.1",
|
|
41
41
|
"dependencies": {
|
|
42
|
-
"@open-core/framework": "^0.2.
|
|
42
|
+
"@open-core/framework": "^0.2.7",
|
|
43
43
|
"bcryptjs": "^3.0.3",
|
|
44
44
|
"reflect-metadata": "^0.2.2",
|
|
45
45
|
"tsyringe": "^4.10.0",
|