@iushev/react-rbac 1.0.41 → 1.0.43

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/eslint.config.mjs CHANGED
@@ -1,3 +1,6 @@
1
+ import { fixupConfigRules, fixupPluginRules } from "@eslint/compat";
2
+ import react from "eslint-plugin-react";
3
+ import reactHooks from "eslint-plugin-react-hooks";
1
4
  import typescriptEslint from "@typescript-eslint/eslint-plugin";
2
5
  import prettier from "eslint-plugin-prettier";
3
6
  import globals from "globals";
@@ -19,10 +22,21 @@ export default [
19
22
  {
20
23
  ignores: ["**/lib/", "**/node_modules/"],
21
24
  },
22
- ...compat.extends("eslint:recommended", "plugin:@typescript-eslint/recommended", "plugin:prettier/recommended"),
25
+ ...fixupConfigRules(
26
+ compat.extends(
27
+ "eslint:recommended",
28
+ "plugin:react/recommended",
29
+ "plugin:react/jsx-runtime",
30
+ "plugin:react-hooks/recommended",
31
+ "plugin:@typescript-eslint/recommended",
32
+ "prettier",
33
+ ),
34
+ ),
23
35
  {
24
36
  plugins: {
25
- "@typescript-eslint": typescriptEslint,
37
+ react: fixupPluginRules(react),
38
+ "react-hooks": fixupPluginRules(reactHooks),
39
+ "@typescript-eslint": fixupPluginRules(typescriptEslint),
26
40
  prettier,
27
41
  },
28
42
 
@@ -37,6 +51,12 @@ export default [
37
51
  sourceType: "module",
38
52
  },
39
53
 
54
+ settings: {
55
+ react: {
56
+ version: "detect",
57
+ },
58
+ },
59
+
40
60
  rules: {
41
61
  "@typescript-eslint/no-namespace": "off",
42
62
  "@typescript-eslint/no-explicit-any": "off",
@@ -1 +1 @@
1
- {"version":3,"file":"CheckAccess.d.ts","sourceRoot":"","sources":["../src/CheckAccess.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAE1B,OAAO,EAAE,kBAAkB,EAAE,MAAM,eAAe,CAAC;AAInD,MAAM,MAAM,gBAAgB,GAAG,kBAAkB,GAAG;IAClD,IAAI,CAAC,EAAE,KAAK,CAAC,aAAa,CAAC;IAC3B,QAAQ,CAAC,EAAE,KAAK,CAAC,aAAa,CAAC;IAC/B,QAAQ,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;CAC5B,CAAC;AAEF,QAAA,MAAM,WAAW,EAAE,KAAK,CAAC,EAAE,CAAC,gBAAgB,CAgB3C,CAAC;AAEF,eAAe,WAAW,CAAC"}
1
+ {"version":3,"file":"CheckAccess.d.ts","sourceRoot":"","sources":["../src/CheckAccess.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAE1B,OAAO,EAAE,kBAAkB,EAAE,MAAM,eAAe,CAAC;AAKnD,MAAM,MAAM,gBAAgB,GAAG,kBAAkB,GAAG;IAClD,IAAI,CAAC,EAAE,KAAK,CAAC,aAAa,CAAC;IAC3B,QAAQ,CAAC,EAAE,KAAK,CAAC,aAAa,CAAC;IAC/B,QAAQ,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;CAC5B,CAAC;AAEF,QAAA,MAAM,WAAW,EAAE,KAAK,CAAC,EAAE,CAAC,gBAAgB,CAgB3C,CAAC;AAEF,eAAe,WAAW,CAAC"}
@@ -6,7 +6,8 @@ Object.defineProperty(exports, "__esModule", { value: true });
6
6
  const jsx_runtime_1 = require("react/jsx-runtime");
7
7
  const useCheckAccess_1 = __importDefault(require("./useCheckAccess"));
8
8
  const NoAccess_1 = __importDefault(require("./NoAccess"));
9
- const CheckAccess = ({ roles, allow, params, match, busy: BusyComponent = () => null, noAccess: NoAccessComponent = NoAccess_1.default, children, }) => {
9
+ const NullComponent_1 = __importDefault(require("./NullComponent"));
10
+ const CheckAccess = ({ roles, allow, params, match, busy: BusyComponent = NullComponent_1.default, noAccess: NoAccessComponent = NoAccess_1.default, children, }) => {
10
11
  const { checking, hasAccess } = (0, useCheckAccess_1.default)({ roles, allow, params, match });
11
12
  if (checking) {
12
13
  return (0, jsx_runtime_1.jsx)(BusyComponent, {});
@@ -0,0 +1,4 @@
1
+ import React from "react";
2
+ declare const NullComponent: React.FC;
3
+ export default NullComponent;
4
+ //# sourceMappingURL=NullComponent.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"NullComponent.d.ts","sourceRoot":"","sources":["../src/NullComponent.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAE1B,QAAA,MAAM,aAAa,EAAE,KAAK,CAAC,EAAe,CAAC;AAE3C,eAAe,aAAa,CAAC"}
@@ -0,0 +1,4 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const NullComponent = () => null;
4
+ exports.default = NullComponent;
@@ -1,6 +1,5 @@
1
1
  import React from "react";
2
- import { RuleParams, Rule, RuleCtor } from "@iushev/rbac";
3
- import RbacCheckAccess from "./RbacCheckAccess";
2
+ import { RuleParams, Rule, RuleCtor, Identity, BaseManager } from "@iushev/rbac";
4
3
  export type RuleParamsFunction = () => RuleParams;
5
4
  export type MatchFunction = () => boolean;
6
5
  export type CheckAccessOptions = {
@@ -10,19 +9,19 @@ export type CheckAccessOptions = {
10
9
  match?: MatchFunction;
11
10
  };
12
11
  export type RbacContextProps = {
13
- rbac: RbacCheckAccess | null;
12
+ rbacManager: BaseManager | null;
14
13
  checkAccess: (options: CheckAccessOptions) => Promise<boolean>;
15
14
  };
16
15
  declare const RbacContext: React.Context<RbacContextProps | null>;
17
16
  export declare const useRbac: () => RbacContextProps;
18
17
  export type RbacProviderProps = {
19
- username: string;
18
+ identity: Identity | null;
20
19
  rbacUrl: string;
21
20
  token: string;
22
- isSuperuser: boolean;
23
- isGuest: boolean;
24
21
  ruleClasses: Map<string, RuleCtor<Rule>>;
22
+ afterInitManager?: (webManager: any) => void;
25
23
  children: React.ReactNode;
24
+ logging?: false | ((...args: any[]) => void);
26
25
  };
27
26
  export declare const RbacProvider: React.FC<RbacProviderProps>;
28
27
  export declare const RbacConsumer: React.Consumer<RbacContextProps | null>;
@@ -1 +1 @@
1
- {"version":3,"file":"RbacContext.d.ts","sourceRoot":"","sources":["../src/RbacContext.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAgE,MAAM,OAAO,CAAC;AAErF,OAAO,EAAE,UAAU,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAC;AAC1D,OAAO,eAAe,MAAM,mBAAmB,CAAC;AAEhD,MAAM,MAAM,kBAAkB,GAAG,MAAM,UAAU,CAAC;AAClD,MAAM,MAAM,aAAa,GAAG,MAAM,OAAO,CAAC;AAC1C,MAAM,MAAM,kBAAkB,GAAG;IAC/B,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,MAAM,CAAC,EAAE,UAAU,GAAG,kBAAkB,CAAC;IACzC,KAAK,CAAC,EAAE,aAAa,CAAC;CACvB,CAAC;AAEF,MAAM,MAAM,gBAAgB,GAAG;IAC7B,IAAI,EAAE,eAAe,GAAG,IAAI,CAAC;IAC7B,WAAW,EAAE,CAAC,OAAO,EAAE,kBAAkB,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;CAChE,CAAC;AAEF,QAAA,MAAM,WAAW,wCAAqD,CAAC;AAEvE,eAAO,MAAM,OAAO,QAAO,gBAM1B,CAAC;AAEF,MAAM,MAAM,iBAAiB,GAAG;IAC9B,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,OAAO,CAAC;IACrB,OAAO,EAAE,OAAO,CAAC;IACjB,WAAW,EAAE,GAAG,CAAC,MAAM,EAAE,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC;IACzC,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAC;CAC3B,CAAC;AAEF,eAAO,MAAM,YAAY,EAAE,KAAK,CAAC,EAAE,CAAC,iBAAiB,CAqFpD,CAAC;AAEF,eAAO,MAAM,YAAY,yCAAuB,CAAC;AAEjD,eAAe,WAAW,CAAC"}
1
+ {"version":3,"file":"RbacContext.d.ts","sourceRoot":"","sources":["../src/RbacContext.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAgE,MAAM,OAAO,CAAC;AAErF,OAAO,EAAE,UAAU,EAAE,IAAI,EAAE,QAAQ,EAAuB,QAAQ,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAGtG,MAAM,MAAM,kBAAkB,GAAG,MAAM,UAAU,CAAC;AAClD,MAAM,MAAM,aAAa,GAAG,MAAM,OAAO,CAAC;AAC1C,MAAM,MAAM,kBAAkB,GAAG;IAC/B,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,MAAM,CAAC,EAAE,UAAU,GAAG,kBAAkB,CAAC;IACzC,KAAK,CAAC,EAAE,aAAa,CAAC;CACvB,CAAC;AAEF,MAAM,MAAM,gBAAgB,GAAG;IAC7B,WAAW,EAAE,WAAW,GAAG,IAAI,CAAC;IAChC,WAAW,EAAE,CAAC,OAAO,EAAE,kBAAkB,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;CAChE,CAAC;AAEF,QAAA,MAAM,WAAW,wCAAqD,CAAC;AAEvE,eAAO,MAAM,OAAO,QAAO,gBAM1B,CAAC;AAEF,MAAM,MAAM,iBAAiB,GAAG;IAC9B,QAAQ,EAAE,QAAQ,GAAG,IAAI,CAAC;IAC1B,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,GAAG,CAAC,MAAM,EAAE,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC;IACzC,gBAAgB,CAAC,EAAE,CAAC,UAAU,EAAE,GAAG,KAAK,IAAI,CAAC;IAC7C,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAC;IAC1B,OAAO,CAAC,EAAE,KAAK,GAAG,CAAC,CAAC,GAAG,IAAI,EAAE,GAAG,EAAE,KAAK,IAAI,CAAC,CAAC;CAC9C,CAAC;AAEF,eAAO,MAAM,YAAY,EAAE,KAAK,CAAC,EAAE,CAAC,iBAAiB,CAyDpD,CAAC;AAEF,eAAO,MAAM,YAAY,yCAAuB,CAAC;AAEjD,eAAe,WAAW,CAAC"}
@@ -41,14 +41,12 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
41
41
  step((generator = generator.apply(thisArg, _arguments || [])).next());
42
42
  });
43
43
  };
44
- var __importDefault = (this && this.__importDefault) || function (mod) {
45
- return (mod && mod.__esModule) ? mod : { "default": mod };
46
- };
47
44
  Object.defineProperty(exports, "__esModule", { value: true });
48
45
  exports.RbacConsumer = exports.RbacProvider = exports.useRbac = void 0;
49
46
  const jsx_runtime_1 = require("react/jsx-runtime");
50
47
  const react_1 = __importStar(require("react"));
51
- const RbacCheckAccess_1 = __importDefault(require("./RbacCheckAccess"));
48
+ const rbac_1 = require("@iushev/rbac");
49
+ const rbac_web_manager_1 = require("@iushev/rbac-web-manager");
52
50
  const RbacContext = react_1.default.createContext(null);
53
51
  const useRbac = () => {
54
52
  const ctx = (0, react_1.useContext)(RbacContext);
@@ -58,70 +56,44 @@ const useRbac = () => {
58
56
  return ctx;
59
57
  };
60
58
  exports.useRbac = useRbac;
61
- const RbacProvider = ({ username, rbacUrl, token, isSuperuser, isGuest, ruleClasses, children, }) => {
62
- const [rbac, setRbac] = (0, react_1.useState)(null);
59
+ const RbacProvider = ({ identity, rbacUrl, token, ruleClasses, children, logging, }) => {
60
+ const [rbacManager, setRbacManager] = (0, react_1.useState)(null);
63
61
  (0, react_1.useEffect)(() => {
64
62
  const initRbac = () => __awaiter(void 0, void 0, void 0, function* () {
65
- if (!token) {
66
- setRbac(null);
67
- }
68
- else {
69
- const _rbac = new RbacCheckAccess_1.default({
70
- path: rbacUrl,
71
- authorization: () => {
72
- return token;
73
- },
74
- });
75
- ruleClasses.forEach((RuleClass, ruleName) => {
76
- _rbac.ruleClasses.set(ruleName, RuleClass);
77
- });
78
- yield _rbac.load();
79
- setRbac(_rbac);
80
- }
63
+ const manager = new rbac_web_manager_1.WebManager({
64
+ path: rbacUrl,
65
+ authorization: () => {
66
+ return token;
67
+ },
68
+ logging,
69
+ });
70
+ ruleClasses.forEach((RuleClass, ruleName) => {
71
+ manager.ruleClasses.set(ruleName, RuleClass);
72
+ });
73
+ setRbacManager(manager);
81
74
  });
82
75
  initRbac();
83
- }, [rbacUrl, token, ruleClasses]);
84
- const matchRole = (0, react_1.useCallback)((roles, params) => __awaiter(void 0, void 0, void 0, function* () {
85
- if (!roles || roles.length === 0) {
86
- return true;
87
- }
88
- if (!rbac) {
76
+ }, [logging, rbacUrl, ruleClasses, token]);
77
+ const checkAccess = (0, react_1.useCallback)((_a) => __awaiter(void 0, [_a], void 0, function* ({ roles, allow = true, match, params = {} }) {
78
+ if (!rbacManager) {
89
79
  return false;
90
80
  }
91
- for (const role of roles) {
92
- if (role === "?" && isGuest) {
93
- // only guest users
94
- return true;
95
- }
96
- else if (role === "@" && !isGuest) {
97
- // only authenticated users
81
+ const matchCustom = (match) => {
82
+ if (!match) {
98
83
  return true;
99
84
  }
100
- else if (yield rbac.checkAccess(username, role, typeof params === "function" ? params() : params)) {
101
- // only authenticated users that has permission
102
- return true;
103
- }
104
- else {
105
- continue;
106
- }
107
- }
108
- return false;
109
- }), [isGuest, rbac, username]);
110
- const matchCustom = (0, react_1.useCallback)((match) => {
111
- if (!match) {
112
- return true;
113
- }
114
- return match();
115
- }, []);
116
- const checkAccess = (0, react_1.useCallback)((_a) => __awaiter(void 0, [_a], void 0, function* ({ roles, allow = true, match, params = {} }) {
117
- return isSuperuser || ((yield matchRole(roles, params)) && matchCustom(match) && allow);
118
- }), [isSuperuser, matchCustom, matchRole]);
85
+ return match();
86
+ };
87
+ const user = new rbac_1.RbacUser(rbacManager);
88
+ user.identity = identity;
89
+ return user.isSuperuser || ((yield (0, rbac_1.matchRole)({ user, roles, params })) && matchCustom(match) && allow);
90
+ }), [identity, rbacManager]);
119
91
  const value = (0, react_1.useMemo)(() => {
120
92
  return {
121
- rbac,
93
+ rbacManager,
122
94
  checkAccess,
123
95
  };
124
- }, [rbac, checkAccess]);
96
+ }, [rbacManager, checkAccess]);
125
97
  return (0, jsx_runtime_1.jsx)(RbacContext.Provider, { value: value, children: children });
126
98
  };
127
99
  exports.RbacProvider = RbacProvider;
package/lib/index.d.ts CHANGED
@@ -1,5 +1,6 @@
1
1
  export { default as RbacContext, RbacConsumer, RbacProvider, useRbac } from "./RbacContext";
2
2
  export { default as CheckAccess, CheckAccessProps } from "./CheckAccess";
3
3
  export { default as NoAccess } from "./NoAccess";
4
+ export { default as NullComponent } from "./NullComponent";
4
5
  export { default as useCheckAccess } from "./useCheckAccess";
5
6
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,IAAI,WAAW,EAAE,YAAY,EAAE,YAAY,EAAE,OAAO,EAAE,MAAM,eAAe,CAAC;AAC5F,OAAO,EAAE,OAAO,IAAI,WAAW,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;AACzE,OAAO,EAAE,OAAO,IAAI,QAAQ,EAAE,MAAM,YAAY,CAAC;AACjD,OAAO,EAAE,OAAO,IAAI,cAAc,EAAE,MAAM,kBAAkB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,IAAI,WAAW,EAAE,YAAY,EAAE,YAAY,EAAE,OAAO,EAAE,MAAM,eAAe,CAAC;AAC5F,OAAO,EAAE,OAAO,IAAI,WAAW,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;AACzE,OAAO,EAAE,OAAO,IAAI,QAAQ,EAAE,MAAM,YAAY,CAAC;AACjD,OAAO,EAAE,OAAO,IAAI,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAC3D,OAAO,EAAE,OAAO,IAAI,cAAc,EAAE,MAAM,kBAAkB,CAAC"}
package/lib/index.js CHANGED
@@ -3,7 +3,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
3
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.useCheckAccess = exports.NoAccess = exports.CheckAccess = exports.useRbac = exports.RbacProvider = exports.RbacConsumer = exports.RbacContext = void 0;
6
+ exports.useCheckAccess = exports.NullComponent = exports.NoAccess = exports.CheckAccess = exports.useRbac = exports.RbacProvider = exports.RbacConsumer = exports.RbacContext = void 0;
7
7
  var RbacContext_1 = require("./RbacContext");
8
8
  Object.defineProperty(exports, "RbacContext", { enumerable: true, get: function () { return __importDefault(RbacContext_1).default; } });
9
9
  Object.defineProperty(exports, "RbacConsumer", { enumerable: true, get: function () { return RbacContext_1.RbacConsumer; } });
@@ -13,5 +13,7 @@ var CheckAccess_1 = require("./CheckAccess");
13
13
  Object.defineProperty(exports, "CheckAccess", { enumerable: true, get: function () { return __importDefault(CheckAccess_1).default; } });
14
14
  var NoAccess_1 = require("./NoAccess");
15
15
  Object.defineProperty(exports, "NoAccess", { enumerable: true, get: function () { return __importDefault(NoAccess_1).default; } });
16
+ var NullComponent_1 = require("./NullComponent");
17
+ Object.defineProperty(exports, "NullComponent", { enumerable: true, get: function () { return __importDefault(NullComponent_1).default; } });
16
18
  var useCheckAccess_1 = require("./useCheckAccess");
17
19
  Object.defineProperty(exports, "useCheckAccess", { enumerable: true, get: function () { return __importDefault(useCheckAccess_1).default; } });
@@ -1 +1 @@
1
- {"version":3,"file":"useCheckAccess.d.ts","sourceRoot":"","sources":["../src/useCheckAccess.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,kBAAkB,EAAW,MAAM,eAAe,CAAC;AAE5D,MAAM,MAAM,gBAAgB,GAAG;IAC7B,QAAQ,EAAE,OAAO,CAAC;IAClB,SAAS,EAAE,OAAO,CAAC;CACpB,CAAC;AAEF,iBAAS,cAAc,CAAC,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,EAAE,kBAAkB,oBAyC1E;AAED,eAAe,cAAc,CAAC"}
1
+ {"version":3,"file":"useCheckAccess.d.ts","sourceRoot":"","sources":["../src/useCheckAccess.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,kBAAkB,EAAW,MAAM,eAAe,CAAC;AAE5D,MAAM,MAAM,gBAAgB,GAAG;IAC7B,QAAQ,EAAE,OAAO,CAAC;IAClB,SAAS,EAAE,OAAO,CAAC;CACpB,CAAC;AAEF,iBAAS,cAAc,CAAC,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,EAAE,kBAAkB,oBA4C1E;AAED,eAAe,cAAc,CAAC"}
@@ -1,4 +1,13 @@
1
1
  "use strict";
2
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
+ return new (P || (P = Promise))(function (resolve, reject) {
5
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
9
+ });
10
+ };
2
11
  Object.defineProperty(exports, "__esModule", { value: true });
3
12
  const react_1 = require("react");
4
13
  const RbacContext_1 = require("./RbacContext");
@@ -10,29 +19,31 @@ function useCheckAccess({ roles, allow, params, match }) {
10
19
  });
11
20
  (0, react_1.useEffect)(() => {
12
21
  let subscribed = true;
13
- setState({
14
- checking: true,
15
- hasAccess: false,
16
- });
17
- rbac
18
- .checkAccess({ roles, allow, params, match })
19
- .then((result) => {
20
- if (subscribed) {
21
- setState({
22
- checking: false,
23
- hasAccess: result,
24
- });
22
+ const checkAccess = () => __awaiter(this, void 0, void 0, function* () {
23
+ setState({
24
+ checking: true,
25
+ hasAccess: false,
26
+ });
27
+ try {
28
+ const result = yield rbac.checkAccess({ roles, allow, params, match });
29
+ if (subscribed) {
30
+ setState({
31
+ checking: false,
32
+ hasAccess: result,
33
+ });
34
+ }
25
35
  }
26
- })
27
- .catch((err) => {
28
- console.error(err);
29
- if (subscribed) {
30
- setState({
31
- checking: false,
32
- hasAccess: false,
33
- });
36
+ catch (err) {
37
+ console.error(err);
38
+ if (subscribed) {
39
+ setState({
40
+ checking: false,
41
+ hasAccess: false,
42
+ });
43
+ }
34
44
  }
35
45
  });
46
+ checkAccess();
36
47
  return () => {
37
48
  subscribed = false;
38
49
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@iushev/react-rbac",
3
- "version": "1.0.41",
3
+ "version": "1.0.43",
4
4
  "private": false,
5
5
  "author": "Ivaylo Ushev",
6
6
  "description": "",
@@ -9,23 +9,24 @@
9
9
  "main": "./lib/index",
10
10
  "types": "./lib/index",
11
11
  "scripts": {
12
- "build": "tsc",
12
+ "build": "rimraf ./lib && tsc",
13
13
  "watch": "tsc -w --preserveWatchOutput",
14
14
  "test": "jest --runInBand",
15
15
  "test:watch": "jest --watchAll --runInBand",
16
16
  "coverage": "jest --coverage --runInBand",
17
17
  "lint": "eslint src",
18
- "preversion": "rimraf ./lib && npm run build"
18
+ "preversion": "npm run build"
19
19
  },
20
20
  "dependencies": {
21
21
  "http-status-codes": "^2.3.0"
22
22
  },
23
23
  "devDependencies": {
24
+ "@eslint/compat": "^2.0.0",
24
25
  "@types/react": "^19.2.7",
25
26
  "@types/react-dom": "^19.2.3",
26
- "@typescript-eslint/eslint-plugin": "^8.49.0",
27
- "@typescript-eslint/parser": "^8.49.0",
28
- "eslint": "^9.39.1",
27
+ "@typescript-eslint/eslint-plugin": "^8.50.0",
28
+ "@typescript-eslint/parser": "^8.50.0",
29
+ "eslint": "^9.39.2",
29
30
  "eslint-config-prettier": "^10.1.8",
30
31
  "eslint-plugin-prettier": "^5.5.4",
31
32
  "eslint-plugin-react": "^7.37.5",
@@ -37,7 +38,8 @@
37
38
  "typescript": "^5.9.3"
38
39
  },
39
40
  "peerDependencies": {
40
- "@iushev/rbac": "^1.0.51",
41
+ "@iushev/rbac": "^1.0.54",
42
+ "@iushev/rbac-web-manager": "^1.0.2",
41
43
  "axios": "^1.13.2",
42
44
  "react": "^17.0.0 || ^18.0.0 || ^19.0.0",
43
45
  "react-dom": "^17.0.0 || ^18.0.0 || ^19.0.0"
@@ -3,6 +3,7 @@ import React from "react";
3
3
  import { CheckAccessOptions } from "./RbacContext";
4
4
  import useCheckAccess from "./useCheckAccess";
5
5
  import NoAccess from "./NoAccess";
6
+ import NullComponent from "./NullComponent";
6
7
 
7
8
  export type CheckAccessProps = CheckAccessOptions & {
8
9
  busy?: React.ComponentType;
@@ -15,7 +16,7 @@ const CheckAccess: React.FC<CheckAccessProps> = ({
15
16
  allow,
16
17
  params,
17
18
  match,
18
- busy: BusyComponent = () => null,
19
+ busy: BusyComponent = NullComponent,
19
20
  noAccess: NoAccessComponent = NoAccess,
20
21
  children,
21
22
  }) => {
@@ -0,0 +1,5 @@
1
+ import React from "react";
2
+
3
+ const NullComponent: React.FC = () => null;
4
+
5
+ export default NullComponent;
@@ -1,7 +1,7 @@
1
1
  import React, { useCallback, useContext, useEffect, useMemo, useState } from "react";
2
2
 
3
- import { RuleParams, Rule, RuleCtor } from "@iushev/rbac";
4
- import RbacCheckAccess from "./RbacCheckAccess";
3
+ import { RuleParams, Rule, RuleCtor, RbacUser, matchRole, Identity, BaseManager } from "@iushev/rbac";
4
+ import { WebManager } from "@iushev/rbac-web-manager";
5
5
 
6
6
  export type RuleParamsFunction = () => RuleParams;
7
7
  export type MatchFunction = () => boolean;
@@ -13,7 +13,7 @@ export type CheckAccessOptions = {
13
13
  };
14
14
 
15
15
  export type RbacContextProps = {
16
- rbac: RbacCheckAccess | null;
16
+ rbacManager: BaseManager | null;
17
17
  checkAccess: (options: CheckAccessOptions) => Promise<boolean>;
18
18
  };
19
19
 
@@ -28,98 +28,70 @@ export const useRbac = (): RbacContextProps => {
28
28
  };
29
29
 
30
30
  export type RbacProviderProps = {
31
- username: string;
31
+ identity: Identity | null;
32
32
  rbacUrl: string;
33
33
  token: string;
34
- isSuperuser: boolean;
35
- isGuest: boolean;
36
34
  ruleClasses: Map<string, RuleCtor<Rule>>;
35
+ afterInitManager?: (webManager: any) => void;
37
36
  children: React.ReactNode;
37
+ logging?: false | ((...args: any[]) => void);
38
38
  };
39
39
 
40
40
  export const RbacProvider: React.FC<RbacProviderProps> = ({
41
- username,
41
+ identity,
42
42
  rbacUrl,
43
43
  token,
44
- isSuperuser,
45
- isGuest,
46
44
  ruleClasses,
47
45
  children,
46
+ logging,
48
47
  }) => {
49
- const [rbac, setRbac] = useState<RbacCheckAccess | null>(null);
48
+ const [rbacManager, setRbacManager] = useState<BaseManager | null>(null);
50
49
 
51
50
  useEffect(() => {
52
51
  const initRbac = async () => {
53
- if (!token) {
54
- setRbac(null);
55
- } else {
56
- const _rbac = new RbacCheckAccess({
57
- path: rbacUrl,
58
- authorization: () => {
59
- return token;
60
- },
61
- });
62
- ruleClasses.forEach((RuleClass, ruleName) => {
63
- _rbac.ruleClasses.set(ruleName, RuleClass);
64
- });
65
- await _rbac.load();
66
- setRbac(_rbac);
67
- }
52
+ const manager = new WebManager({
53
+ path: rbacUrl,
54
+ authorization: () => {
55
+ return token;
56
+ },
57
+ logging,
58
+ });
59
+ ruleClasses.forEach((RuleClass, ruleName) => {
60
+ manager.ruleClasses.set(ruleName, RuleClass);
61
+ });
62
+ setRbacManager(manager);
68
63
  };
69
64
 
70
65
  initRbac();
71
- }, [rbacUrl, token, ruleClasses]);
72
-
73
- const matchRole = useCallback(
74
- async (roles: string[], params: RuleParams | RuleParamsFunction) => {
75
- if (!roles || roles.length === 0) {
76
- return true;
77
- }
66
+ }, [logging, rbacUrl, ruleClasses, token]);
78
67
 
79
- if (!rbac) {
68
+ const checkAccess = useCallback(
69
+ async ({ roles, allow = true, match, params = {} }: CheckAccessOptions) => {
70
+ if (!rbacManager) {
80
71
  return false;
81
72
  }
82
73
 
83
- for (const role of roles) {
84
- if (role === "?" && isGuest) {
85
- // only guest users
86
- return true;
87
- } else if (role === "@" && !isGuest) {
88
- // only authenticated users
89
- return true;
90
- } else if (await rbac.checkAccess(username, role, typeof params === "function" ? params() : params)) {
91
- // only authenticated users that has permission
74
+ const matchCustom = (match?: MatchFunction) => {
75
+ if (!match) {
92
76
  return true;
93
- } else {
94
- continue;
95
77
  }
96
- }
97
-
98
- return false;
99
- },
100
- [isGuest, rbac, username],
101
- );
78
+ return match();
79
+ };
102
80
 
103
- const matchCustom = useCallback((match?: MatchFunction) => {
104
- if (!match) {
105
- return true;
106
- }
107
- return match();
108
- }, []);
81
+ const user = new RbacUser(rbacManager);
82
+ user.identity = identity;
109
83
 
110
- const checkAccess = useCallback(
111
- async ({ roles, allow = true, match, params = {} }: CheckAccessOptions) => {
112
- return isSuperuser || ((await matchRole(roles, params)) && matchCustom(match) && allow);
84
+ return user.isSuperuser || ((await matchRole({ user, roles, params })) && matchCustom(match) && allow);
113
85
  },
114
- [isSuperuser, matchCustom, matchRole],
86
+ [identity, rbacManager],
115
87
  );
116
88
 
117
89
  const value = useMemo(() => {
118
90
  return {
119
- rbac,
91
+ rbacManager,
120
92
  checkAccess,
121
93
  };
122
- }, [rbac, checkAccess]);
94
+ }, [rbacManager, checkAccess]);
123
95
 
124
96
  return <RbacContext.Provider value={value}>{children}</RbacContext.Provider>;
125
97
  };
package/src/index.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  export { default as RbacContext, RbacConsumer, RbacProvider, useRbac } from "./RbacContext";
2
2
  export { default as CheckAccess, CheckAccessProps } from "./CheckAccess";
3
3
  export { default as NoAccess } from "./NoAccess";
4
+ export { default as NullComponent } from "./NullComponent";
4
5
  export { default as useCheckAccess } from "./useCheckAccess";
@@ -16,22 +16,22 @@ function useCheckAccess({ roles, allow, params, match }: CheckAccessOptions) {
16
16
 
17
17
  useEffect(() => {
18
18
  let subscribed = true;
19
- setState({
20
- checking: true,
21
- hasAccess: false,
22
- });
23
-
24
- rbac
25
- .checkAccess({ roles, allow, params, match })
26
- .then((result) => {
19
+
20
+ const checkAccess = async () => {
21
+ setState({
22
+ checking: true,
23
+ hasAccess: false,
24
+ });
25
+
26
+ try {
27
+ const result = await rbac.checkAccess({ roles, allow, params, match });
27
28
  if (subscribed) {
28
29
  setState({
29
30
  checking: false,
30
31
  hasAccess: result,
31
32
  });
32
33
  }
33
- })
34
- .catch((err) => {
34
+ } catch (err) {
35
35
  console.error(err);
36
36
  if (subscribed) {
37
37
  setState({
@@ -39,7 +39,10 @@ function useCheckAccess({ roles, allow, params, match }: CheckAccessOptions) {
39
39
  hasAccess: false,
40
40
  });
41
41
  }
42
- });
42
+ }
43
+ };
44
+
45
+ checkAccess();
43
46
 
44
47
  return () => {
45
48
  subscribed = false;
@@ -1,18 +0,0 @@
1
- import { AxiosRequestConfig } from "axios";
2
- import { Assignment, BaseCheckAccess, BaseCheckAccessOptions, RuleParams } from "@iushev/rbac";
3
- export type RbacCheckAccessOptions = BaseCheckAccessOptions & {
4
- path: string;
5
- authorization: () => string;
6
- };
7
- export default class RbacCheckAccess extends BaseCheckAccess {
8
- private readonly axiosInstance;
9
- protected assignments: Map<string, Map<string, Assignment>>;
10
- constructor(options: RbacCheckAccessOptions);
11
- checkAccess(username: string, itemName: string, params: RuleParams): Promise<boolean>;
12
- load(config?: AxiosRequestConfig): Promise<void>;
13
- private getRbacItems;
14
- private getRbacParents;
15
- private getRbacRules;
16
- private getRbacAssignments;
17
- }
18
- //# sourceMappingURL=RbacCheckAccess.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"RbacCheckAccess.d.ts","sourceRoot":"","sources":["../src/RbacCheckAccess.ts"],"names":[],"mappings":"AAAA,OAAc,EAAiB,kBAAkB,EAAE,MAAM,OAAO,CAAC;AAGjE,OAAO,EACL,UAAU,EACV,eAAe,EACf,sBAAsB,EAOtB,UAAU,EACX,MAAM,cAAc,CAAC;AAEtB,MAAM,MAAM,sBAAsB,GAAG,sBAAsB,GAAG;IAC5D,IAAI,EAAE,MAAM,CAAC;IACb,aAAa,EAAE,MAAM,MAAM,CAAC;CAC7B,CAAC;AAEF,MAAM,CAAC,OAAO,OAAO,eAAgB,SAAQ,eAAe;IAC1D,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAgB;IAE9C,SAAS,CAAC,WAAW,EAAE,GAAG,CAAC,MAAM,EAAE,GAAG,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC,CAAa;gBAE5D,OAAO,EAAE,sBAAsB;IAkB9B,WAAW,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,UAAU,GAAG,OAAO,CAAC,OAAO,CAAC;IAQrF,IAAI,CAAC,MAAM,CAAC,EAAE,kBAAkB,GAAG,OAAO,CAAC,IAAI,CAAC;IAwB7D,OAAO,CAAC,YAAY;IAgBpB,OAAO,CAAC,cAAc;IA4BtB,OAAO,CAAC,YAAY;IAUpB,OAAO,CAAC,kBAAkB;CAa3B"}
@@ -1,137 +0,0 @@
1
- "use strict";
2
- var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
- function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
- return new (P || (P = Promise))(function (resolve, reject) {
5
- function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
- function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
- function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
- step((generator = generator.apply(thisArg, _arguments || [])).next());
9
- });
10
- };
11
- var __importDefault = (this && this.__importDefault) || function (mod) {
12
- return (mod && mod.__esModule) ? mod : { "default": mod };
13
- };
14
- Object.defineProperty(exports, "__esModule", { value: true });
15
- const axios_1 = __importDefault(require("axios"));
16
- const http_status_codes_1 = __importDefault(require("http-status-codes"));
17
- const rbac_1 = require("@iushev/rbac");
18
- class RbacCheckAccess extends rbac_1.BaseCheckAccess {
19
- constructor(options) {
20
- super(options);
21
- this.assignments = new Map();
22
- this.axiosInstance = axios_1.default.create({
23
- baseURL: options.path,
24
- });
25
- this.axiosInstance.interceptors.request.use((config) => {
26
- if (options.authorization) {
27
- const token = options.authorization();
28
- config.headers.Authorization = "Bearer " + token;
29
- }
30
- else {
31
- config.headers.Authorization = undefined;
32
- }
33
- return config;
34
- });
35
- }
36
- checkAccess(username, itemName, params) {
37
- const _super = Object.create(null, {
38
- checkAccess: { get: () => super.checkAccess }
39
- });
40
- return __awaiter(this, void 0, void 0, function* () {
41
- var _a;
42
- if (this.items.size === 0) {
43
- yield this.load();
44
- }
45
- return _super.checkAccess.call(this, username, itemName, params, (_a = this.assignments.get(username)) !== null && _a !== void 0 ? _a : new Map());
46
- });
47
- }
48
- load(config) {
49
- return __awaiter(this, void 0, void 0, function* () {
50
- var _a;
51
- let _rbac;
52
- try {
53
- console.log("Load RBAC");
54
- const response = yield this.axiosInstance.get("/rbac", config);
55
- _rbac = response.data;
56
- }
57
- catch (err) {
58
- if (axios_1.default.isAxiosError(err) && ((_a = err.response) === null || _a === void 0 ? void 0 : _a.status) === http_status_codes_1.default.NOT_FOUND) {
59
- _rbac = {
60
- assignments: {},
61
- items: {},
62
- rules: {},
63
- };
64
- }
65
- else {
66
- throw err;
67
- }
68
- }
69
- this.items = this.getRbacItems(_rbac);
70
- this.parents = this.getRbacParents(_rbac);
71
- this.rules = this.getRbacRules(_rbac);
72
- this.assignments = this.getRbacAssignments(_rbac);
73
- });
74
- }
75
- getRbacItems({ items }) {
76
- return Object.keys(items).reduce((prevValue, name) => {
77
- var _a, _b;
78
- const item = items[name];
79
- const ItemClass = item.type === rbac_1.ItemType.permission ? rbac_1.Permission : rbac_1.Role;
80
- prevValue.set(name, new ItemClass({
81
- name,
82
- description: (_a = item.description) !== null && _a !== void 0 ? _a : null,
83
- ruleName: (_b = item.ruleName) !== null && _b !== void 0 ? _b : null,
84
- }));
85
- return prevValue;
86
- }, new Map());
87
- }
88
- getRbacParents({ items }) {
89
- return Object.keys(items).reduce((prevValue, name) => {
90
- const item = items[name];
91
- if (!item.children || item.children.length === 0) {
92
- return prevValue;
93
- }
94
- item.children.forEach((childName) => {
95
- if (!this.items.has(childName)) {
96
- return;
97
- }
98
- let child = prevValue.get(childName);
99
- if (!child) {
100
- child = new Map();
101
- prevValue.set(childName, child);
102
- }
103
- const item = this.items.get(name);
104
- if (item) {
105
- child.set(name, item);
106
- }
107
- });
108
- return prevValue;
109
- }, new Map());
110
- }
111
- getRbacRules({ rules }) {
112
- return Object.keys(rules).reduce((prevValue, name) => {
113
- var _a;
114
- const ruleData = rules[name];
115
- const RuleClass = (_a = this.ruleClasses.get(ruleData.data.typeName)) !== null && _a !== void 0 ? _a : rbac_1.Rule;
116
- const rule = new RuleClass(name, JSON.parse(ruleData.data.ruleData));
117
- prevValue.set(name, rule);
118
- return prevValue;
119
- }, new Map());
120
- }
121
- getRbacAssignments({ assignments }) {
122
- return Object.keys(assignments).reduce((prevValue, username) => {
123
- const _assignments = assignments[username];
124
- _assignments.forEach((itemName) => {
125
- var _a;
126
- if (prevValue.has(username)) {
127
- (_a = prevValue.get(username)) === null || _a === void 0 ? void 0 : _a.set(itemName, new rbac_1.Assignment(username, itemName));
128
- }
129
- else {
130
- prevValue.set(username, new Map([[itemName, new rbac_1.Assignment(username, itemName)]]));
131
- }
132
- });
133
- return prevValue;
134
- }, new Map());
135
- }
136
- }
137
- exports.default = RbacCheckAccess;
@@ -1,144 +0,0 @@
1
- import axios, { AxiosInstance, AxiosRequestConfig } from "axios";
2
- import statusCodes from "http-status-codes";
3
-
4
- import {
5
- Assignment,
6
- BaseCheckAccess,
7
- BaseCheckAccessOptions,
8
- IItem,
9
- ItemType,
10
- Permission,
11
- RBACResponse,
12
- Role,
13
- Rule,
14
- RuleParams,
15
- } from "@iushev/rbac";
16
-
17
- export type RbacCheckAccessOptions = BaseCheckAccessOptions & {
18
- path: string;
19
- authorization: () => string;
20
- };
21
-
22
- export default class RbacCheckAccess extends BaseCheckAccess {
23
- private readonly axiosInstance: AxiosInstance;
24
-
25
- protected assignments: Map<string, Map<string, Assignment>> = new Map();
26
-
27
- constructor(options: RbacCheckAccessOptions) {
28
- super(options);
29
-
30
- this.axiosInstance = axios.create({
31
- baseURL: options.path,
32
- });
33
-
34
- this.axiosInstance.interceptors.request.use((config) => {
35
- if (options.authorization) {
36
- const token = options.authorization();
37
- config.headers.Authorization = "Bearer " + token;
38
- } else {
39
- config.headers.Authorization = undefined;
40
- }
41
- return config;
42
- });
43
- }
44
-
45
- public async checkAccess(username: string, itemName: string, params: RuleParams): Promise<boolean> {
46
- if (this.items.size === 0) {
47
- await this.load();
48
- }
49
-
50
- return super.checkAccess(username, itemName, params, this.assignments.get(username) ?? new Map());
51
- }
52
-
53
- public async load(config?: AxiosRequestConfig): Promise<void> {
54
- let _rbac: RBACResponse;
55
- try {
56
- console.log("Load RBAC");
57
- const response = await this.axiosInstance.get<RBACResponse>("/rbac", config);
58
- _rbac = response.data;
59
- } catch (err) {
60
- if (axios.isAxiosError(err) && err.response?.status === statusCodes.NOT_FOUND) {
61
- _rbac = {
62
- assignments: {},
63
- items: {},
64
- rules: {},
65
- };
66
- } else {
67
- throw err;
68
- }
69
- }
70
-
71
- this.items = this.getRbacItems(_rbac);
72
- this.parents = this.getRbacParents(_rbac);
73
- this.rules = this.getRbacRules(_rbac);
74
- this.assignments = this.getRbacAssignments(_rbac);
75
- }
76
-
77
- private getRbacItems({ items }: RBACResponse) {
78
- return Object.keys(items).reduce<Map<string, IItem>>((prevValue, name) => {
79
- const item = items[name];
80
- const ItemClass = item.type === ItemType.permission ? Permission : Role;
81
- prevValue.set(
82
- name,
83
- new ItemClass({
84
- name,
85
- description: item.description ?? null,
86
- ruleName: item.ruleName ?? null,
87
- }),
88
- );
89
- return prevValue;
90
- }, new Map());
91
- }
92
-
93
- private getRbacParents({ items }: RBACResponse) {
94
- return Object.keys(items).reduce<Map<string, Map<string, IItem>>>((prevValue, name) => {
95
- const item = items[name];
96
- if (!item.children || item.children.length === 0) {
97
- return prevValue;
98
- }
99
- item.children.forEach((childName: string) => {
100
- if (!this.items.has(childName)) {
101
- return;
102
- }
103
-
104
- let child = prevValue.get(childName);
105
-
106
- if (!child) {
107
- child = new Map();
108
- prevValue.set(childName, child);
109
- }
110
-
111
- const item = this.items.get(name);
112
- if (item) {
113
- child.set(name, item);
114
- }
115
- });
116
-
117
- return prevValue;
118
- }, new Map());
119
- }
120
-
121
- private getRbacRules({ rules }: RBACResponse) {
122
- return Object.keys(rules).reduce<Map<string, Rule>>((prevValue, name) => {
123
- const ruleData = rules[name];
124
- const RuleClass = this.ruleClasses.get(ruleData.data.typeName) ?? Rule;
125
- const rule = new RuleClass(name, JSON.parse(ruleData.data.ruleData));
126
- prevValue.set(name, rule);
127
- return prevValue;
128
- }, new Map());
129
- }
130
-
131
- private getRbacAssignments({ assignments }: RBACResponse) {
132
- return Object.keys(assignments).reduce<Map<string, Map<string, Assignment>>>((prevValue, username) => {
133
- const _assignments = assignments[username];
134
- _assignments.forEach((itemName) => {
135
- if (prevValue.has(username)) {
136
- prevValue.get(username)?.set(itemName, new Assignment(username, itemName));
137
- } else {
138
- prevValue.set(username, new Map([[itemName, new Assignment(username, itemName)]]));
139
- }
140
- });
141
- return prevValue;
142
- }, new Map());
143
- }
144
- }