@wxn0brp/gate-warden 0.4.0 → 0.5.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/README.md CHANGED
@@ -13,9 +13,13 @@ Gate Warden is a lightweight access control library that supports ACL, RBAC, and
13
13
  Install the library using npm or yarn:
14
14
 
15
15
  ```bash
16
- yarn add @wxn0brp/gate-warden
16
+ npm i @wxn0brp/gate-warden
17
17
  ```
18
18
 
19
+ ## DOCS
20
+
21
+ See [Docs](https://wxn0brp.github.io/gate-warden/)
22
+
19
23
  ## Usage
20
24
 
21
25
  ### GateWarden
@@ -76,14 +80,6 @@ Set the `debugLog` level in `GateWarden` to enable debug messages:
76
80
  - `1`: Basic logs
77
81
  - `2`: Detailed logs
78
82
 
79
- ## Project Structure
80
-
81
- - **src/warden.ts:** Core access control logic.
82
- - **src/user.ts:** User management.
83
- - **src/mgr.ts:** Role and rule management.
84
- - **src/types/system.ts:** Type definitions for roles, rules, and users.
85
- - **src/log.ts:** Logging utilities.
86
-
87
83
  ## License
88
84
 
89
85
  This project is licensed under the MIT License. See the [LICENSE](LICENSE) file for details.
@@ -0,0 +1,32 @@
1
+ import { CheckParams } from "./types/check.js";
2
+ /**
3
+ * Checks if a user has the given flag on the given entity by checking the entity's ACL.
4
+ * @param db The DB instance
5
+ * @param flag The flag to check
6
+ * @param user The user to check
7
+ * @param entityId The ID of the entity to check
8
+ * @returns If the user has the flag on the entity:
9
+ * - 1 if the user has the flag
10
+ * - 0 if the user does not have the flag
11
+ * - -1 if the entity does not have an ACL
12
+ */
13
+ export declare function aclCheck({ db, entityId, flag, user }: CheckParams): Promise<number>;
14
+ /**
15
+ * Checks if a user has the given flag on the given entity by checking each role the user has.
16
+ * @param db The DB instance
17
+ * @param flag The flag to check
18
+ * @param user The user to check
19
+ * @param entityId The ID of the entity to check
20
+ * @returns If the user has the flag on the entity
21
+ */
22
+ export declare function rbacCheck({ db, flag, user, entityId }: CheckParams): Promise<boolean>;
23
+ /**
24
+ * ABAC (Attribute-Based Access Control) check
25
+ * @param db The DB instance
26
+ * @param entityId The ID of the entity to check
27
+ * @param flag The flag to check
28
+ * @param user The user to check
29
+ * @param debugLog The debug log level
30
+ * @returns `true` if access is granted, `false` otherwise
31
+ */
32
+ export declare function abacCheck({ db, entityId, flag, user, debugLog }: CheckParams): Promise<boolean>;
package/dist/check.js ADDED
@@ -0,0 +1,97 @@
1
+ import hasFieldsAdvanced from "@wxn0brp/db-core/utils/hasFieldsAdvanced";
2
+ import { COLORS } from "./log.js";
3
+ import { convertPath } from "./utils.js";
4
+ /**
5
+ * Checks if a user has the given flag on the given entity by checking the entity's ACL.
6
+ * @param db The DB instance
7
+ * @param flag The flag to check
8
+ * @param user The user to check
9
+ * @param entityId The ID of the entity to check
10
+ * @returns If the user has the flag on the entity:
11
+ * - 1 if the user has the flag
12
+ * - 0 if the user does not have the flag
13
+ * - -1 if the entity does not have an ACL
14
+ */
15
+ export async function aclCheck({ db, entityId, flag, user }) {
16
+ if (!await db.issetCollection("acl/" + entityId))
17
+ return -1;
18
+ const rules = await db.find("acl/" + entityId, {
19
+ $or: [
20
+ { uid: user._id },
21
+ {
22
+ $not: {
23
+ $exists: { "uid": true }
24
+ }
25
+ }
26
+ ]
27
+ });
28
+ if (rules.length === 0)
29
+ return -1;
30
+ for (const rule of rules) {
31
+ if (rule.p & flag)
32
+ return 1;
33
+ }
34
+ return 0;
35
+ }
36
+ /**
37
+ * Checks if a user has the given flag on the given entity by checking each role the user has.
38
+ * @param db The DB instance
39
+ * @param flag The flag to check
40
+ * @param user The user to check
41
+ * @param entityId The ID of the entity to check
42
+ * @returns If the user has the flag on the entity
43
+ */
44
+ export async function rbacCheck({ db, flag, user, entityId }) {
45
+ for (const role of user.roles) {
46
+ const rolesEntity = await db.find("role/" + role, { _id: entityId });
47
+ for (const entity of rolesEntity) {
48
+ if (entity.p & flag)
49
+ return true;
50
+ }
51
+ }
52
+ return false;
53
+ }
54
+ /**
55
+ * ABAC (Attribute-Based Access Control) check
56
+ * @param db The DB instance
57
+ * @param entityId The ID of the entity to check
58
+ * @param flag The flag to check
59
+ * @param user The user to check
60
+ * @param debugLog The debug log level
61
+ * @returns `true` if access is granted, `false` otherwise
62
+ */
63
+ export async function abacCheck({ db, entityId, flag, user, debugLog }) {
64
+ if (!await db.issetCollection("abac/" + entityId))
65
+ return false;
66
+ const rules = await db.find("abac/" + entityId, { flag });
67
+ if (rules.length === 0)
68
+ return false;
69
+ for (const rule of rules) {
70
+ let authorized = true;
71
+ if (debugLog >= 1)
72
+ console.log(COLORS.blue + `[GW] ABAC rule: ${COLORS.yellow}${JSON.stringify(rule.condition)}${COLORS.blue} ` +
73
+ `-> checking...` + COLORS.reset);
74
+ for (const key in rule.condition) {
75
+ const expectedValue = rule.condition[key];
76
+ let actualValue;
77
+ if (key === "_")
78
+ actualValue = user;
79
+ else
80
+ actualValue = convertPath(user, key);
81
+ if (actualValue === undefined) {
82
+ authorized = false;
83
+ break;
84
+ }
85
+ if (!hasFieldsAdvanced(actualValue, expectedValue)) {
86
+ authorized = false;
87
+ break;
88
+ }
89
+ }
90
+ if (authorized) {
91
+ if (debugLog >= 1)
92
+ console.log(COLORS.green + `[GW] Access granted by this rule.` + COLORS.reset);
93
+ return true;
94
+ }
95
+ }
96
+ return false;
97
+ }
package/dist/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
- import GateWarden from "./warden.js";
2
- import UserManager from "./user.js";
3
- import WardenManager from "./mgr.js";
1
+ export * from "./warden.js";
2
+ export * from "./user.js";
3
+ export * from "./mgr.js";
4
4
  import { AccessResult } from "./types/system.js";
5
- export { GateWarden, UserManager, WardenManager, AccessResult };
5
+ export type { AccessResult };
package/dist/index.js CHANGED
@@ -1,4 +1,3 @@
1
- import GateWarden from "./warden.js";
2
- import UserManager from "./user.js";
3
- import WardenManager from "./mgr.js";
4
- export { GateWarden, UserManager, WardenManager };
1
+ export * from "./warden.js";
2
+ export * from "./user.js";
3
+ export * from "./mgr.js";
package/dist/log.d.ts CHANGED
@@ -1,3 +1,4 @@
1
+ import Id from "@wxn0brp/db-core/types/Id";
1
2
  export declare const COLORS: {
2
3
  reset: string;
3
4
  green: string;
@@ -5,3 +6,4 @@ export declare const COLORS: {
5
6
  red: string;
6
7
  blue: string;
7
8
  };
9
+ export declare function logAccess(userId: Id, entityId: Id, via: string, debugLog: number): void;
package/dist/log.js CHANGED
@@ -5,3 +5,9 @@ export const COLORS = {
5
5
  red: "\x1b[31m",
6
6
  blue: "\x1b[34m",
7
7
  };
8
+ export function logAccess(userId, entityId, via, debugLog) {
9
+ if (debugLog < 1)
10
+ return;
11
+ console.log(`${COLORS.green}[GW] Access granted to ${COLORS.yellow}${entityId}${COLORS.green} via ` +
12
+ `${COLORS.yellow}${via}${COLORS.green} by ${COLORS.yellow}${userId}${COLORS.reset}`);
13
+ }
package/dist/mgr.d.ts CHANGED
@@ -1,10 +1,10 @@
1
1
  import { Id, ValtheraCompatible } from "@wxn0brp/db-core";
2
2
  import { ABACRule, ACLRule, Role, RoleEntity } from "./types/system.js";
3
- declare class WardenManager {
3
+ export declare class WardenManager {
4
4
  private db;
5
5
  constructor(db: ValtheraCompatible);
6
6
  changeRoleNameToId(name: string): Promise<Id>;
7
- addRole(role: Role): Promise<Role>;
7
+ addRole(role: Role | Omit<Role, "_id">): Promise<Role>;
8
8
  addACLRule(entityId: string, p: number, uid?: Id): Promise<ACLRule>;
9
9
  addRBACRule(role_id: string, entity_id: string, p: number): Promise<RoleEntity>;
10
10
  addABACRule(entity_id: string, flag: number, condition: ABACRule["condition"]): Promise<ABACRule>;
@@ -13,4 +13,3 @@ declare class WardenManager {
13
13
  removeRBACRule(roleId: string, entityId: string): Promise<boolean>;
14
14
  removeABACRule(entityId: string, flag: number): Promise<boolean>;
15
15
  }
16
- export default WardenManager;
package/dist/mgr.js CHANGED
@@ -1,4 +1,4 @@
1
- class WardenManager {
1
+ export class WardenManager {
2
2
  db;
3
3
  constructor(db) {
4
4
  this.db = db;
@@ -37,4 +37,3 @@ class WardenManager {
37
37
  return await this.db.removeOne("abac/" + entityId, { flag });
38
38
  }
39
39
  }
40
- export default WardenManager;
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,148 @@
1
+ import { createMemoryValthera } from "@wxn0brp/db-core";
2
+ import { describe, expect, it } from "bun:test";
3
+ import { abacCheck } from "../check.js";
4
+ describe("Access Control Checks", () => {
5
+ describe("abacCheck", () => {
6
+ it("should return false when entity has no ABAC collection", async () => {
7
+ const db = createMemoryValthera({});
8
+ const user = { _id: "user1", roles: [], attrib: { department: "engineering" } };
9
+ const result = await abacCheck({
10
+ db,
11
+ entityId: "entity1",
12
+ flag: 1,
13
+ user,
14
+ debugLog: 0
15
+ });
16
+ expect(result).toBe(false);
17
+ });
18
+ it("should return true when user attributes match ABAC rule", async () => {
19
+ const abacRules = [
20
+ {
21
+ flag: 1,
22
+ condition: {
23
+ attrib: {
24
+ department: "engineering"
25
+ }
26
+ }
27
+ }
28
+ ];
29
+ const db = createMemoryValthera({
30
+ "abac/entity1": abacRules
31
+ });
32
+ const user = { _id: "user1", roles: [], attrib: { department: "engineering" } };
33
+ const result = await abacCheck({
34
+ db,
35
+ entityId: "entity1",
36
+ flag: 1,
37
+ user,
38
+ debugLog: 0
39
+ });
40
+ expect(result).toBe(true);
41
+ });
42
+ it("should return false when user attributes don't match ABAC rule", async () => {
43
+ const abacRules = [
44
+ {
45
+ flag: 1,
46
+ condition: {
47
+ attrib: {
48
+ department: "engineering"
49
+ }
50
+ }
51
+ }
52
+ ];
53
+ const db = createMemoryValthera({
54
+ "abac/entity1": abacRules
55
+ });
56
+ const user = { _id: "user1", roles: [], attrib: { department: "marketing" } };
57
+ const result = await abacCheck({
58
+ db,
59
+ entityId: "entity1",
60
+ flag: 1,
61
+ user,
62
+ debugLog: 0
63
+ });
64
+ expect(result).toBe(false);
65
+ });
66
+ it("should handle nested attribute paths correctly", async () => {
67
+ const abacRules = [
68
+ {
69
+ flag: 1,
70
+ condition: {
71
+ "attrib.profile": {
72
+ level: 5
73
+ }
74
+ }
75
+ }
76
+ ];
77
+ const db = createMemoryValthera({
78
+ "abac/entity1": abacRules
79
+ });
80
+ const user = {
81
+ _id: "user1",
82
+ roles: [],
83
+ attrib: {
84
+ profile: {
85
+ level: 5,
86
+ name: "test"
87
+ }
88
+ }
89
+ };
90
+ const result = await abacCheck({
91
+ db,
92
+ entityId: "entity1",
93
+ flag: 1,
94
+ user,
95
+ debugLog: 0
96
+ });
97
+ expect(result).toBe(true);
98
+ });
99
+ it("should return false when user attributes are undefined", async () => {
100
+ const abacRules = [
101
+ {
102
+ flag: 1,
103
+ condition: {
104
+ attrib: {
105
+ department: "engineering"
106
+ }
107
+ }
108
+ }
109
+ ];
110
+ const db = createMemoryValthera({
111
+ "abac/entity1": abacRules
112
+ });
113
+ const user = { _id: "user1", roles: [], attrib: {} }; // No department
114
+ const result = await abacCheck({
115
+ db,
116
+ entityId: "entity1",
117
+ flag: 1,
118
+ user,
119
+ debugLog: 0
120
+ });
121
+ expect(result).toBe(false);
122
+ });
123
+ it("should return true when checking user root properties with _", async () => {
124
+ const abacRules = [
125
+ {
126
+ flag: 1,
127
+ condition: {
128
+ _: {
129
+ _id: "user1"
130
+ }
131
+ }
132
+ }
133
+ ];
134
+ const db = createMemoryValthera({
135
+ "abac/entity1": abacRules
136
+ });
137
+ const user = { _id: "user1", roles: [], attrib: {} };
138
+ const result = await abacCheck({
139
+ db,
140
+ entityId: "entity1",
141
+ flag: 1,
142
+ user,
143
+ debugLog: 0
144
+ });
145
+ expect(result).toBe(true);
146
+ });
147
+ });
148
+ });
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,85 @@
1
+ import { createMemoryValthera } from "@wxn0brp/db-core";
2
+ import { describe, expect, it } from "bun:test";
3
+ import { aclCheck } from "../check.js";
4
+ describe("Access Control Checks", () => {
5
+ describe("aclCheck", () => {
6
+ it("should return -1 when entity has no ACL collection", async () => {
7
+ const db = createMemoryValthera({});
8
+ const user = { _id: "user1", roles: [], attrib: {} };
9
+ const result = await aclCheck({
10
+ db,
11
+ entityId: "entity1",
12
+ flag: 1,
13
+ user
14
+ });
15
+ expect(result).toBe(-1);
16
+ });
17
+ it("should return 1 when user has the specific flag in ACL", async () => {
18
+ const aclRules = [
19
+ { uid: "user1", p: 5 } // User has flags 1 and 4 (1 | 4 = 5)
20
+ ];
21
+ const db = createMemoryValthera({
22
+ "acl/entity1": aclRules
23
+ });
24
+ const user = { _id: "user1", roles: [], attrib: {} };
25
+ // Check for flag 1 (should be granted)
26
+ const result = await aclCheck({
27
+ db,
28
+ entityId: "entity1",
29
+ flag: 1,
30
+ user
31
+ });
32
+ expect(result).toBe(1);
33
+ });
34
+ it("should return 0 when user does not have the flag in ACL", async () => {
35
+ const aclRules = [
36
+ { uid: "user1", p: 4 } // User has only flag 4
37
+ ];
38
+ const db = createMemoryValthera({
39
+ "acl/entity1": aclRules
40
+ });
41
+ const user = { _id: "user1", roles: [], attrib: {} };
42
+ // Check for flag 1 (should not be granted)
43
+ const result = await aclCheck({
44
+ db,
45
+ entityId: "entity1",
46
+ flag: 1,
47
+ user
48
+ });
49
+ expect(result).toBe(0);
50
+ });
51
+ it("should return 1 when there is a public rule with the flag", async () => {
52
+ const aclRules = [
53
+ { p: 3 } // Public rule with flags 1 and 2
54
+ ];
55
+ const db = createMemoryValthera({
56
+ "acl/entity1": aclRules
57
+ });
58
+ const user = { _id: "user1", roles: [], attrib: {} };
59
+ // Check for flag 1 (should be granted via public rule)
60
+ const result = await aclCheck({
61
+ db,
62
+ entityId: "entity1",
63
+ flag: 1,
64
+ user
65
+ });
66
+ expect(result).toBe(1);
67
+ });
68
+ it("should return -1 when no rules match the user or public access", async () => {
69
+ const aclRules = [
70
+ { uid: "user2", p: 1 } // Different user has the flag
71
+ ];
72
+ const db = createMemoryValthera({
73
+ "acl/entity1": aclRules
74
+ });
75
+ const user = { _id: "user1", roles: [], attrib: {} };
76
+ const result = await aclCheck({
77
+ db,
78
+ entityId: "entity1",
79
+ flag: 1,
80
+ user
81
+ });
82
+ expect(result).toBe(-1);
83
+ });
84
+ });
85
+ });
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,69 @@
1
+ import { createMemoryValthera } from "@wxn0brp/db-core";
2
+ import { describe, expect, it } from "bun:test";
3
+ import { rbacCheck } from "../check.js";
4
+ describe("Access Control Checks", () => {
5
+ describe("rbacCheck", () => {
6
+ it("should return true when user has role with the required flag", async () => {
7
+ const rolesData = [
8
+ { _id: "resource1", p: 5 } // Flags 1 and 4
9
+ ];
10
+ const db = createMemoryValthera({
11
+ "role/admin": rolesData
12
+ });
13
+ const user = { _id: "user1", roles: ["admin"], attrib: {} };
14
+ // Check for flag 1 (should be granted by admin role)
15
+ const result = await rbacCheck({
16
+ db,
17
+ entityId: "resource1",
18
+ flag: 1,
19
+ user
20
+ });
21
+ expect(result).toBe(true);
22
+ });
23
+ it("should return false when user has role without the required flag", async () => {
24
+ const rolesData = [
25
+ { _id: "resource1", p: 4 } // Only flag 4
26
+ ];
27
+ const db = createMemoryValthera({
28
+ "role/admin": rolesData
29
+ });
30
+ const user = { _id: "user1", roles: ["admin"], attrib: {} };
31
+ // Check for flag 1 (should not be granted)
32
+ const result = await rbacCheck({
33
+ db,
34
+ entityId: "resource1",
35
+ flag: 1,
36
+ user
37
+ });
38
+ expect(result).toBe(false);
39
+ });
40
+ it("should return false when user has no matching role-entity permissions", async () => {
41
+ const rolesData = [
42
+ { _id: "resource2", p: 1 } // Different resource
43
+ ];
44
+ const db = createMemoryValthera({
45
+ "role/admin": rolesData
46
+ });
47
+ const user = { _id: "user1", roles: ["admin"], attrib: {} };
48
+ // Check for resource1 when only resource2 permissions exist
49
+ const result = await rbacCheck({
50
+ db,
51
+ entityId: "resource1",
52
+ flag: 1,
53
+ user
54
+ });
55
+ expect(result).toBe(false);
56
+ });
57
+ it("should return false when user has no roles", async () => {
58
+ const db = createMemoryValthera({});
59
+ const user = { _id: "user1", roles: [], attrib: {} };
60
+ const result = await rbacCheck({
61
+ db,
62
+ entityId: "resource1",
63
+ flag: 1,
64
+ user
65
+ });
66
+ expect(result).toBe(false);
67
+ });
68
+ });
69
+ });
@@ -0,0 +1,10 @@
1
+ import { ValtheraCompatible } from "@wxn0brp/db-core";
2
+ import Id from "@wxn0brp/db-core/types/Id";
3
+ import { User } from "./system.js";
4
+ export interface CheckParams {
5
+ db: ValtheraCompatible;
6
+ entityId: Id;
7
+ flag: number;
8
+ user: User;
9
+ debugLog?: number;
10
+ }
@@ -0,0 +1 @@
1
+ export {};
package/dist/user.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import { Id, ValtheraCompatible } from "@wxn0brp/db-core";
2
2
  import { User } from "./types/system.js";
3
- declare class UserManager<A = any> {
3
+ export declare class UserManager<A = any> {
4
4
  private db;
5
5
  constructor(db: ValtheraCompatible);
6
6
  /**
@@ -48,4 +48,3 @@ declare class UserManager<A = any> {
48
48
  */
49
49
  updateAttributes(user_id: Id, attributes: Partial<A>): Promise<void>;
50
50
  }
51
- export default UserManager;
package/dist/user.js CHANGED
@@ -1,4 +1,4 @@
1
- class UserManager {
1
+ export class UserManager {
2
2
  db;
3
3
  constructor(db) {
4
4
  this.db = db;
@@ -84,4 +84,3 @@ class UserManager {
84
84
  await this.db.update("users", { _id: user_id }, user);
85
85
  }
86
86
  }
87
- export default UserManager;
@@ -0,0 +1 @@
1
+ export declare function convertPath(user: any, path: string): any;
package/dist/utils.js ADDED
@@ -0,0 +1,10 @@
1
+ export function convertPath(user, path) {
2
+ const keys = path.split(".");
3
+ let data = user;
4
+ for (const key of keys) {
5
+ data = data?.[key];
6
+ if (data === undefined)
7
+ return undefined;
8
+ }
9
+ return data;
10
+ }
package/dist/warden.d.ts CHANGED
@@ -1,9 +1,10 @@
1
- import { ValtheraCompatible } from "@wxn0brp/db-core";
2
- import { AccessResult } from "./types/system.js";
3
- declare class GateWarden<A = any> {
4
- private db;
1
+ import { Id, ValtheraCompatible } from "@wxn0brp/db-core";
2
+ import { AccessResult, User } from "./types/system.js";
3
+ export declare function fetchUser(db: ValtheraCompatible, userId: Id): Promise<User>;
4
+ export declare function matchPermission(db: ValtheraCompatible, entityId: Id, flag: number, user: User, debugLog: number): Promise<AccessResult>;
5
+ export declare class GateWarden {
6
+ db: ValtheraCompatible;
5
7
  debugLog: number;
6
8
  constructor(db: ValtheraCompatible, debugLog?: number);
7
9
  hasAccess(userId: string, entityId: string, flag: number): Promise<AccessResult>;
8
10
  }
9
- export default GateWarden;
package/dist/warden.js CHANGED
@@ -1,122 +1,12 @@
1
- import { COLORS } from "./log.js";
2
- import hasFieldsAdvanced from "@wxn0brp/db-core/utils/hasFieldsAdvanced";
3
- function logAccess(userId, entityId, via, debugLog) {
4
- if (debugLog < 1)
5
- return;
6
- console.log(`${COLORS.green}[GW] Access granted to ${COLORS.yellow}${entityId}${COLORS.green} via ` +
7
- `${COLORS.yellow}${via}${COLORS.green} by ${COLORS.yellow}${userId}${COLORS.reset}`);
8
- }
9
- async function fetchUser(db, userId) {
1
+ import { abacCheck, aclCheck, rbacCheck } from "./check.js";
2
+ import { COLORS, logAccess } from "./log.js";
3
+ export async function fetchUser(db, userId) {
10
4
  const user = await db.findOne("users", { _id: userId });
11
5
  if (!user)
12
6
  throw new Error("User not found");
13
7
  return user;
14
8
  }
15
- /**
16
- * Checks if a user has the given flag on the given entity by checking the entity's ACL.
17
- * @param db The DB instance
18
- * @param flag The flag to check
19
- * @param user The user to check
20
- * @param entityId The ID of the entity to check
21
- * @returns If the user has the flag on the entity:
22
- * - 1 if the user has the flag
23
- * - 0 if the user does not have the flag
24
- * - -1 if the entity does not have an ACL
25
- */
26
- async function aclCheck({ db, entityId, flag, user }) {
27
- if (!await db.issetCollection("acl/" + entityId))
28
- return -1;
29
- const rules = await db.find("acl/" + entityId, {
30
- $or: [
31
- { uid: user._id },
32
- {
33
- $not: {
34
- $exists: { "uid": true }
35
- }
36
- }
37
- ]
38
- });
39
- if (rules.length === 0)
40
- return -1;
41
- for (const rule of rules) {
42
- if (rule.p & flag)
43
- return 1;
44
- }
45
- return 0;
46
- }
47
- /**
48
- * Checks if a user has the given flag on the given entity by checking each role the user has.
49
- * @param db The DB instance
50
- * @param flag The flag to check
51
- * @param user The user to check
52
- * @param entityId The ID of the entity to check
53
- * @returns If the user has the flag on the entity
54
- */
55
- async function rbacCheck({ db, flag, user, entityId }) {
56
- for (const role of user.roles) {
57
- const rolesEntity = await db.find("role/" + role, { _id: entityId });
58
- for (const entity of rolesEntity) {
59
- if (entity.p & flag)
60
- return true;
61
- }
62
- }
63
- return false;
64
- }
65
- function convertPath(user, path) {
66
- const keys = path.split(".");
67
- let data = user;
68
- for (const key of keys) {
69
- data = data?.[key];
70
- if (data === undefined)
71
- return undefined;
72
- }
73
- return data;
74
- }
75
- /**
76
- * ABAC (Attribute-Based Access Control) check
77
- * @param db The DB instance
78
- * @param entityId The ID of the entity to check
79
- * @param flag The flag to check
80
- * @param user The user to check
81
- * @param debugLog The debug log level
82
- * @returns `true` if access is granted, `false` otherwise
83
- */
84
- async function abacCheck({ db, entityId, flag, user, debugLog }) {
85
- if (!await db.issetCollection("abac/" + entityId))
86
- return false;
87
- const rules = await db.find("abac/" + entityId, { flag });
88
- if (rules.length === 0)
89
- return false;
90
- for (const rule of rules) {
91
- let authorized = true;
92
- if (debugLog >= 1)
93
- console.log(COLORS.blue + `[GW] ABAC rule: ${COLORS.yellow}${JSON.stringify(rule.condition)}${COLORS.blue} ` +
94
- `-> checking...` + COLORS.reset);
95
- for (const key in rule.condition) {
96
- const expectedValue = rule.condition[key];
97
- let actualValue;
98
- if (key === "_")
99
- actualValue = user;
100
- else
101
- actualValue = convertPath(user, key);
102
- if (actualValue === undefined) {
103
- authorized = false;
104
- break;
105
- }
106
- if (!hasFieldsAdvanced(actualValue, expectedValue)) {
107
- authorized = false;
108
- break;
109
- }
110
- }
111
- if (authorized) {
112
- if (debugLog >= 1)
113
- console.log(COLORS.green + `[GW] Access granted by this rule.` + COLORS.reset);
114
- return true;
115
- }
116
- }
117
- return false;
118
- }
119
- async function matchPermission(db, entityId, flag, user, debugLog) {
9
+ export async function matchPermission(db, entityId, flag, user, debugLog) {
120
10
  const checks = [
121
11
  { name: "ACL", method: aclCheck },
122
12
  { name: "RBAC", method: rbacCheck },
@@ -136,7 +26,7 @@ async function matchPermission(db, entityId, flag, user, debugLog) {
136
26
  }
137
27
  return { granted: false, via: null };
138
28
  }
139
- class GateWarden {
29
+ export class GateWarden {
140
30
  db;
141
31
  debugLog;
142
32
  constructor(db, debugLog = 0) {
@@ -171,4 +61,3 @@ class GateWarden {
171
61
  };
172
62
  }
173
63
  }
174
- export default GateWarden;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wxn0brp/gate-warden",
3
- "version": "0.4.0",
3
+ "version": "0.5.0",
4
4
  "main": "dist/index.js",
5
5
  "types": "dist/index.d.ts",
6
6
  "author": "wxn0brP",
@@ -13,13 +13,14 @@
13
13
  },
14
14
  "homepage": "https://github.com/wxn0brP/gate-warden",
15
15
  "devDependencies": {
16
- "@types/node": "^24.1.0",
17
- "@wxn0brp/db-core": "^0.1.2",
18
- "tsc-alias": "^1.8.16",
19
- "typescript": "^5.8.3"
16
+ "@types/bun": "*",
17
+ "@types/node": "*",
18
+ "@wxn0brp/db-core": "^0.2.7",
19
+ "tsc-alias": "*",
20
+ "typescript": "*"
20
21
  },
21
22
  "peerDependencies": {
22
- "@wxn0brp/db-core": ">=0.1.2"
23
+ "@wxn0brp/db-core": ">=0.2.7"
23
24
  },
24
25
  "files": [
25
26
  "dist"
@@ -27,12 +28,13 @@
27
28
  "exports": {
28
29
  ".": {
29
30
  "types": "./dist/index.d.ts",
31
+ "default": "./dist/index.js",
30
32
  "import": "./dist/index.js"
31
33
  },
32
34
  "./*": {
33
- "types": "./dist/*",
34
- "import": "./dist/*"
35
+ "types": "./dist/*.d.ts",
36
+ "default": "./dist/*.js",
37
+ "import": "./dist/*.js"
35
38
  }
36
- },
37
- "dependencies": {}
38
- }
39
+ }
40
+ }