@backstage-community/plugin-rbac 1.32.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (133) hide show
  1. package/CHANGELOG.md +761 -0
  2. package/README.md +106 -0
  3. package/app-config.yaml +19 -0
  4. package/dist/api/LicensedUsersClient.esm.js +45 -0
  5. package/dist/api/LicensedUsersClient.esm.js.map +1 -0
  6. package/dist/api/RBACBackendClient.esm.js +337 -0
  7. package/dist/api/RBACBackendClient.esm.js.map +1 -0
  8. package/dist/components/Administration.esm.js +23 -0
  9. package/dist/components/Administration.esm.js.map +1 -0
  10. package/dist/components/ConditionalAccess/AddNestedConditionButton.esm.js +23 -0
  11. package/dist/components/ConditionalAccess/AddNestedConditionButton.esm.js.map +1 -0
  12. package/dist/components/ConditionalAccess/ComplexConditionRow.esm.js +149 -0
  13. package/dist/components/ConditionalAccess/ComplexConditionRow.esm.js.map +1 -0
  14. package/dist/components/ConditionalAccess/ComplexConditionRowButtons.esm.js +62 -0
  15. package/dist/components/ConditionalAccess/ComplexConditionRowButtons.esm.js.map +1 -0
  16. package/dist/components/ConditionalAccess/ConditionRule.esm.js +34 -0
  17. package/dist/components/ConditionalAccess/ConditionRule.esm.js.map +1 -0
  18. package/dist/components/ConditionalAccess/ConditionalAccessSidebar.esm.js +93 -0
  19. package/dist/components/ConditionalAccess/ConditionalAccessSidebar.esm.js.map +1 -0
  20. package/dist/components/ConditionalAccess/ConditionsForm.esm.js +204 -0
  21. package/dist/components/ConditionalAccess/ConditionsForm.esm.js.map +1 -0
  22. package/dist/components/ConditionalAccess/ConditionsFormRow.esm.js +459 -0
  23. package/dist/components/ConditionalAccess/ConditionsFormRow.esm.js.map +1 -0
  24. package/dist/components/ConditionalAccess/ConditionsFormRowFields.esm.js +209 -0
  25. package/dist/components/ConditionalAccess/ConditionsFormRowFields.esm.js.map +1 -0
  26. package/dist/components/ConditionalAccess/CriteriaToggleButton.esm.js +33 -0
  27. package/dist/components/ConditionalAccess/CriteriaToggleButton.esm.js.map +1 -0
  28. package/dist/components/ConditionalAccess/CustomArrayField.esm.js +47 -0
  29. package/dist/components/ConditionalAccess/CustomArrayField.esm.js.map +1 -0
  30. package/dist/components/ConditionalAccess/RulesDropdownOption.esm.js +24 -0
  31. package/dist/components/ConditionalAccess/RulesDropdownOption.esm.js.map +1 -0
  32. package/dist/components/ConditionalAccess/const.esm.js +21 -0
  33. package/dist/components/ConditionalAccess/const.esm.js.map +1 -0
  34. package/dist/components/ConditionalAccess/types.esm.js +8 -0
  35. package/dist/components/ConditionalAccess/types.esm.js.map +1 -0
  36. package/dist/components/CreateRole/AddMembersForm.esm.js +94 -0
  37. package/dist/components/CreateRole/AddMembersForm.esm.js.map +1 -0
  38. package/dist/components/CreateRole/AddedMembersTable.esm.js +31 -0
  39. package/dist/components/CreateRole/AddedMembersTable.esm.js.map +1 -0
  40. package/dist/components/CreateRole/AddedMembersTableColumn.esm.js +77 -0
  41. package/dist/components/CreateRole/AddedMembersTableColumn.esm.js.map +1 -0
  42. package/dist/components/CreateRole/CreateRolePage.esm.js +53 -0
  43. package/dist/components/CreateRole/CreateRolePage.esm.js.map +1 -0
  44. package/dist/components/CreateRole/EditRolePage.esm.js +65 -0
  45. package/dist/components/CreateRole/EditRolePage.esm.js.map +1 -0
  46. package/dist/components/CreateRole/MembersDropdownOption.esm.js +40 -0
  47. package/dist/components/CreateRole/MembersDropdownOption.esm.js.map +1 -0
  48. package/dist/components/CreateRole/PermissionPoliciesForm.esm.js +144 -0
  49. package/dist/components/CreateRole/PermissionPoliciesForm.esm.js.map +1 -0
  50. package/dist/components/CreateRole/PermissionPoliciesFormRow.esm.js +179 -0
  51. package/dist/components/CreateRole/PermissionPoliciesFormRow.esm.js.map +1 -0
  52. package/dist/components/CreateRole/PoliciesCheckboxGroup.esm.js +76 -0
  53. package/dist/components/CreateRole/PoliciesCheckboxGroup.esm.js.map +1 -0
  54. package/dist/components/CreateRole/ReviewStep.esm.js +50 -0
  55. package/dist/components/CreateRole/ReviewStep.esm.js.map +1 -0
  56. package/dist/components/CreateRole/ReviewStepTable.esm.js +29 -0
  57. package/dist/components/CreateRole/ReviewStepTable.esm.js.map +1 -0
  58. package/dist/components/CreateRole/RoleDetailsForm.esm.js +57 -0
  59. package/dist/components/CreateRole/RoleDetailsForm.esm.js.map +1 -0
  60. package/dist/components/CreateRole/RoleForm.esm.js +271 -0
  61. package/dist/components/CreateRole/RoleForm.esm.js.map +1 -0
  62. package/dist/components/CreateRole/SelectedPermissionPoliciesColumn.esm.js +34 -0
  63. package/dist/components/CreateRole/SelectedPermissionPoliciesColumn.esm.js.map +1 -0
  64. package/dist/components/CreateRole/const.esm.js +14 -0
  65. package/dist/components/CreateRole/const.esm.js.map +1 -0
  66. package/dist/components/DownloadUserStatistics.esm.js +51 -0
  67. package/dist/components/DownloadUserStatistics.esm.js.map +1 -0
  68. package/dist/components/EditRole.esm.js +30 -0
  69. package/dist/components/EditRole.esm.js.map +1 -0
  70. package/dist/components/RbacPage.esm.js +18 -0
  71. package/dist/components/RbacPage.esm.js.map +1 -0
  72. package/dist/components/RoleOverview/AboutCard.esm.js +89 -0
  73. package/dist/components/RoleOverview/AboutCard.esm.js.map +1 -0
  74. package/dist/components/RoleOverview/MembersCard.esm.js +87 -0
  75. package/dist/components/RoleOverview/MembersCard.esm.js.map +1 -0
  76. package/dist/components/RoleOverview/MembersListColumns.esm.js +48 -0
  77. package/dist/components/RoleOverview/MembersListColumns.esm.js.map +1 -0
  78. package/dist/components/RoleOverview/PermissionsCard.esm.js +99 -0
  79. package/dist/components/RoleOverview/PermissionsCard.esm.js.map +1 -0
  80. package/dist/components/RoleOverview/PermissionsListColumns.esm.js +43 -0
  81. package/dist/components/RoleOverview/PermissionsListColumns.esm.js.map +1 -0
  82. package/dist/components/RoleOverview/RoleOverviewPage.esm.js +49 -0
  83. package/dist/components/RoleOverview/RoleOverviewPage.esm.js.map +1 -0
  84. package/dist/components/RolesList/DeleteRole.esm.js +32 -0
  85. package/dist/components/RolesList/DeleteRole.esm.js.map +1 -0
  86. package/dist/components/RolesList/DeleteRoleDialog.esm.js +142 -0
  87. package/dist/components/RolesList/DeleteRoleDialog.esm.js.map +1 -0
  88. package/dist/components/RolesList/RolesList.esm.js +102 -0
  89. package/dist/components/RolesList/RolesList.esm.js.map +1 -0
  90. package/dist/components/RolesList/RolesListColumns.esm.js +76 -0
  91. package/dist/components/RolesList/RolesListColumns.esm.js.map +1 -0
  92. package/dist/components/RolesList/RolesListToolbar.esm.js +48 -0
  93. package/dist/components/RolesList/RolesListToolbar.esm.js.map +1 -0
  94. package/dist/components/Router.esm.js +57 -0
  95. package/dist/components/Router.esm.js.map +1 -0
  96. package/dist/components/SnackbarAlert.esm.js +23 -0
  97. package/dist/components/SnackbarAlert.esm.js.map +1 -0
  98. package/dist/components/ToastContext.esm.js +19 -0
  99. package/dist/components/ToastContext.esm.js.map +1 -0
  100. package/dist/components/index.esm.js +5 -0
  101. package/dist/components/index.esm.js.map +1 -0
  102. package/dist/hooks/useCheckIfLicensePluginEnabled.esm.js +20 -0
  103. package/dist/hooks/useCheckIfLicensePluginEnabled.esm.js.map +1 -0
  104. package/dist/hooks/useConditionRules.esm.js +64 -0
  105. package/dist/hooks/useConditionRules.esm.js.map +1 -0
  106. package/dist/hooks/useLocationToast.esm.js +15 -0
  107. package/dist/hooks/useLocationToast.esm.js.map +1 -0
  108. package/dist/hooks/useMembers.esm.js +92 -0
  109. package/dist/hooks/useMembers.esm.js.map +1 -0
  110. package/dist/hooks/usePermissionPolicies.esm.js +80 -0
  111. package/dist/hooks/usePermissionPolicies.esm.js.map +1 -0
  112. package/dist/hooks/useRole.esm.js +23 -0
  113. package/dist/hooks/useRole.esm.js.map +1 -0
  114. package/dist/hooks/useRoles.esm.js +169 -0
  115. package/dist/hooks/useRoles.esm.js.map +1 -0
  116. package/dist/hooks/useSelectedMembers.esm.js +39 -0
  117. package/dist/hooks/useSelectedMembers.esm.js.map +1 -0
  118. package/dist/index.d.ts +17 -0
  119. package/dist/index.esm.js +4 -0
  120. package/dist/index.esm.js.map +1 -0
  121. package/dist/plugin.esm.js +49 -0
  122. package/dist/plugin.esm.js.map +1 -0
  123. package/dist/routes.esm.js +23 -0
  124. package/dist/routes.esm.js.map +1 -0
  125. package/dist/utils/conditional-access-utils.esm.js +268 -0
  126. package/dist/utils/conditional-access-utils.esm.js.map +1 -0
  127. package/dist/utils/create-role-utils.esm.js +231 -0
  128. package/dist/utils/create-role-utils.esm.js.map +1 -0
  129. package/dist/utils/rbac-utils.esm.js +256 -0
  130. package/dist/utils/rbac-utils.esm.js.map +1 -0
  131. package/dist/utils/role-form-utils.esm.js +66 -0
  132. package/dist/utils/role-form-utils.esm.js.map +1 -0
  133. package/package.json +120 -0
@@ -0,0 +1,23 @@
1
+ import React from 'react';
2
+ import Snackbar from '@material-ui/core/Snackbar';
3
+ import Alert from '@material-ui/lab/Alert';
4
+
5
+ const SnackbarAlert = ({
6
+ toastMessage,
7
+ onAlertClose
8
+ }) => {
9
+ return /* @__PURE__ */ React.createElement(
10
+ Snackbar,
11
+ {
12
+ open: toastMessage !== "",
13
+ autoHideDuration: 1e4,
14
+ anchorOrigin: { vertical: "top", horizontal: "right" },
15
+ style: { top: "100px", left: "0px", justifyContent: "center" },
16
+ onClose: onAlertClose
17
+ },
18
+ /* @__PURE__ */ React.createElement(Alert, { onClose: onAlertClose, severity: "success" }, toastMessage)
19
+ );
20
+ };
21
+
22
+ export { SnackbarAlert };
23
+ //# sourceMappingURL=SnackbarAlert.esm.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SnackbarAlert.esm.js","sources":["../../src/components/SnackbarAlert.tsx"],"sourcesContent":["/*\n * Copyright 2024 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport React from 'react';\n\nimport Snackbar from '@material-ui/core/Snackbar';\nimport Alert from '@material-ui/lab/Alert';\n\nexport const SnackbarAlert = ({\n toastMessage,\n onAlertClose,\n}: {\n toastMessage: string;\n onAlertClose: () => void;\n}) => {\n return (\n <Snackbar\n open={toastMessage !== ''}\n autoHideDuration={10000}\n anchorOrigin={{ vertical: 'top', horizontal: 'right' }}\n style={{ top: '100px', left: '0px', justifyContent: 'center' }}\n onClose={onAlertClose}\n >\n <Alert onClose={onAlertClose} severity=\"success\">\n {toastMessage}\n </Alert>\n </Snackbar>\n );\n};\n"],"names":[],"mappings":";;;;AAoBO,MAAM,gBAAgB,CAAC;AAAA,EAC5B,YAAA;AAAA,EACA,YAAA;AACF,CAGM,KAAA;AACJ,EACE,uBAAA,KAAA,CAAA,aAAA;AAAA,IAAC,QAAA;AAAA,IAAA;AAAA,MACC,MAAM,YAAiB,KAAA,EAAA;AAAA,MACvB,gBAAkB,EAAA,GAAA;AAAA,MAClB,YAAc,EAAA,EAAE,QAAU,EAAA,KAAA,EAAO,YAAY,OAAQ,EAAA;AAAA,MACrD,OAAO,EAAE,GAAA,EAAK,SAAS,IAAM,EAAA,KAAA,EAAO,gBAAgB,QAAS,EAAA;AAAA,MAC7D,OAAS,EAAA,YAAA;AAAA,KAAA;AAAA,wCAER,KAAM,EAAA,EAAA,OAAA,EAAS,YAAc,EAAA,QAAA,EAAS,aACpC,YACH,CAAA;AAAA,GACF,CAAA;AAEJ;;;;"}
@@ -0,0 +1,19 @@
1
+ import React, { createContext, useContext } from 'react';
2
+
3
+ const ToastContext = createContext({
4
+ toastMessage: "",
5
+ setToastMessage: () => {
6
+ }
7
+ });
8
+ const ToastContextProvider = (props) => {
9
+ const [toastMessage, setToastMessage] = React.useState("");
10
+ const toastContextProviderValue = React.useMemo(
11
+ () => ({ setToastMessage, toastMessage }),
12
+ [setToastMessage, toastMessage]
13
+ );
14
+ return /* @__PURE__ */ React.createElement(ToastContext.Provider, { value: toastContextProviderValue }, props.children);
15
+ };
16
+ const useToast = () => useContext(ToastContext);
17
+
18
+ export { ToastContext, ToastContextProvider, useToast };
19
+ //# sourceMappingURL=ToastContext.esm.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ToastContext.esm.js","sources":["../../src/components/ToastContext.tsx"],"sourcesContent":["/*\n * Copyright 2024 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport React, { createContext, useContext } from 'react';\n\ntype ToastContextType = {\n toastMessage: string;\n setToastMessage: (message: string) => void;\n};\n\nexport const ToastContext = createContext<ToastContextType>({\n toastMessage: '',\n setToastMessage: () => {},\n});\n\nexport const ToastContextProvider = (props: any) => {\n const [toastMessage, setToastMessage] = React.useState('');\n const toastContextProviderValue = React.useMemo(\n () => ({ setToastMessage, toastMessage }),\n [setToastMessage, toastMessage],\n );\n return (\n <ToastContext.Provider value={toastContextProviderValue}>\n {props.children}\n </ToastContext.Provider>\n );\n};\nexport const useToast = () => useContext(ToastContext);\n"],"names":[],"mappings":";;AAsBO,MAAM,eAAe,aAAgC,CAAA;AAAA,EAC1D,YAAc,EAAA,EAAA;AAAA,EACd,iBAAiB,MAAM;AAAA,GAAC;AAC1B,CAAC,EAAA;AAEY,MAAA,oBAAA,GAAuB,CAAC,KAAe,KAAA;AAClD,EAAA,MAAM,CAAC,YAAc,EAAA,eAAe,CAAI,GAAA,KAAA,CAAM,SAAS,EAAE,CAAA,CAAA;AACzD,EAAA,MAAM,4BAA4B,KAAM,CAAA,OAAA;AAAA,IACtC,OAAO,EAAE,eAAA,EAAiB,YAAa,EAAA,CAAA;AAAA,IACvC,CAAC,iBAAiB,YAAY,CAAA;AAAA,GAChC,CAAA;AACA,EAAA,2CACG,YAAa,CAAA,QAAA,EAAb,EAAsB,KAAO,EAAA,yBAAA,EAAA,EAC3B,MAAM,QACT,CAAA,CAAA;AAEJ,EAAA;AACa,MAAA,QAAA,GAAW,MAAM,UAAA,CAAW,YAAY;;;;"}
@@ -0,0 +1,5 @@
1
+ export { RbacPage } from './RbacPage.esm.js';
2
+ export { Administration } from './Administration.esm.js';
3
+ export { RoleOverviewPage } from './RoleOverview/RoleOverviewPage.esm.js';
4
+ export { Router } from './Router.esm.js';
5
+ //# sourceMappingURL=index.esm.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.esm.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;"}
@@ -0,0 +1,20 @@
1
+ import { useAsync } from 'react-use';
2
+ import { useApi } from '@backstage/core-plugin-api';
3
+ import { licensedUsersApiRef } from '../api/LicensedUsersClient.esm.js';
4
+
5
+ const useCheckIfLicensePluginEnabled = () => {
6
+ const licensedUsersClient = useApi(licensedUsersApiRef);
7
+ const {
8
+ value: isEnabled,
9
+ loading,
10
+ error: licenseCheckError
11
+ } = useAsync(async () => await licensedUsersClient.isLicensePluginEnabled());
12
+ return {
13
+ loading,
14
+ isEnabled,
15
+ licenseCheckError
16
+ };
17
+ };
18
+
19
+ export { useCheckIfLicensePluginEnabled };
20
+ //# sourceMappingURL=useCheckIfLicensePluginEnabled.esm.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useCheckIfLicensePluginEnabled.esm.js","sources":["../../src/hooks/useCheckIfLicensePluginEnabled.ts"],"sourcesContent":["/*\n * Copyright 2024 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport { useAsync } from 'react-use';\n\nimport { useApi } from '@backstage/core-plugin-api';\n\nimport { licensedUsersApiRef } from '../api/LicensedUsersClient';\n\nexport const useCheckIfLicensePluginEnabled = (): {\n loading: boolean;\n isEnabled: boolean | undefined;\n licenseCheckError: Error;\n} => {\n const licensedUsersClient = useApi(licensedUsersApiRef);\n const {\n value: isEnabled,\n loading,\n error: licenseCheckError,\n } = useAsync(async () => await licensedUsersClient.isLicensePluginEnabled());\n\n return {\n loading,\n isEnabled,\n licenseCheckError: licenseCheckError as Error,\n };\n};\n"],"names":[],"mappings":";;;;AAqBO,MAAM,iCAAiC,MAIzC;AACH,EAAM,MAAA,mBAAA,GAAsB,OAAO,mBAAmB,CAAA,CAAA;AACtD,EAAM,MAAA;AAAA,IACJ,KAAO,EAAA,SAAA;AAAA,IACP,OAAA;AAAA,IACA,KAAO,EAAA,iBAAA;AAAA,MACL,QAAS,CAAA,YAAY,MAAM,mBAAA,CAAoB,wBAAwB,CAAA,CAAA;AAE3E,EAAO,OAAA;AAAA,IACL,OAAA;AAAA,IACA,SAAA;AAAA,IACA,iBAAA;AAAA,GACF,CAAA;AACF;;;;"}
@@ -0,0 +1,64 @@
1
+ import { useAsync } from 'react-use';
2
+ import { useApi } from '@backstage/core-plugin-api';
3
+ import { rbacApiRef } from '../api/RBACBackendClient.esm.js';
4
+ import { uniqBy } from '../utils/create-role-utils.esm.js';
5
+
6
+ const getPluginsResourceTypes = (conditionRules) => {
7
+ return conditionRules.reduce((acc, pluginRules) => {
8
+ return {
9
+ ...acc,
10
+ [`${pluginRules.pluginId}`]: uniqBy(
11
+ pluginRules.rules.map((rule) => rule.resourceType),
12
+ (val) => val
13
+ )
14
+ };
15
+ }, {});
16
+ };
17
+ const getRuleData = (pluginRules, resType) => {
18
+ return pluginRules.rules.reduce(
19
+ (ruleAcc, rule) => {
20
+ return rule.resourceType === resType ? {
21
+ ...ruleAcc,
22
+ [`${rule.name}`]: {
23
+ schema: rule.paramsSchema,
24
+ description: rule.description
25
+ },
26
+ rules: [...ruleAcc.rules, rule.name]
27
+ } : ruleAcc;
28
+ },
29
+ { rules: [] }
30
+ );
31
+ };
32
+ const getConditionRulesData = (conditionRules) => {
33
+ const pluginsResourceTypes = getPluginsResourceTypes(conditionRules);
34
+ return conditionRules.reduce((acc, pluginRules) => {
35
+ return {
36
+ ...acc,
37
+ [`${pluginRules.pluginId}`]: pluginsResourceTypes[pluginRules.pluginId].reduce((resAcc, resType) => {
38
+ return {
39
+ ...resAcc,
40
+ [`${resType}`]: getRuleData(pluginRules, resType)
41
+ };
42
+ }, {})
43
+ };
44
+ }, {});
45
+ };
46
+ const useConditionRules = () => {
47
+ const rbacApi = useApi(rbacApiRef);
48
+ const {
49
+ value: conditionRules,
50
+ loading: conditionRulesLoading,
51
+ error: conditionRulesErr
52
+ } = useAsync(async () => {
53
+ return await rbacApi.getPluginsConditionRules();
54
+ });
55
+ const isConditionRulesAvailable = !conditionRulesLoading && Array.isArray(conditionRules);
56
+ const conditionRulesData = isConditionRulesAvailable ? getConditionRulesData(conditionRules) : void 0;
57
+ return {
58
+ data: conditionRulesData,
59
+ error: conditionRulesErr
60
+ };
61
+ };
62
+
63
+ export { useConditionRules };
64
+ //# sourceMappingURL=useConditionRules.esm.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useConditionRules.esm.js","sources":["../../src/hooks/useConditionRules.ts"],"sourcesContent":["/*\n * Copyright 2024 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport { useAsync } from 'react-use';\n\nimport { useApi } from '@backstage/core-plugin-api';\n\nimport { rbacApiRef } from '../api/RBACBackendClient';\nimport {\n ConditionRules,\n ConditionRulesData,\n ResourceTypeRuleData,\n RulesData,\n} from '../components/ConditionalAccess/types';\nimport { ConditionRule, PluginConditionRules } from '../types';\nimport { uniqBy } from '../utils/create-role-utils';\n\nconst getPluginsResourceTypes = (\n conditionRules: PluginConditionRules[],\n): { [plugin: string]: string[] } => {\n return conditionRules.reduce((acc, pluginRules) => {\n return {\n ...acc,\n [`${pluginRules.pluginId}`]: uniqBy(\n pluginRules.rules.map(rule => rule.resourceType),\n val => val,\n ),\n };\n }, {});\n};\n\nconst getRuleData = (pluginRules: PluginConditionRules, resType: string) => {\n return pluginRules.rules.reduce(\n (ruleAcc: RulesData, rule: ConditionRule) => {\n return rule.resourceType === resType\n ? {\n ...ruleAcc,\n [`${rule.name}`]: {\n schema: rule.paramsSchema,\n description: rule.description,\n },\n rules: [...ruleAcc.rules, rule.name],\n }\n : ruleAcc;\n },\n { rules: [] },\n );\n};\n\nconst getConditionRulesData = (conditionRules: PluginConditionRules[]) => {\n const pluginsResourceTypes = getPluginsResourceTypes(conditionRules);\n\n return conditionRules.reduce((acc: ConditionRulesData, pluginRules) => {\n return {\n ...acc,\n [`${pluginRules.pluginId}`]: pluginsResourceTypes[\n pluginRules.pluginId\n ].reduce((resAcc: ResourceTypeRuleData, resType: string) => {\n return {\n ...resAcc,\n [`${resType}`]: getRuleData(pluginRules, resType),\n };\n }, {}),\n };\n }, {});\n};\n\nexport const useConditionRules = (): ConditionRules => {\n const rbacApi = useApi(rbacApiRef);\n\n const {\n value: conditionRules,\n loading: conditionRulesLoading,\n error: conditionRulesErr,\n } = useAsync(async () => {\n return await rbacApi.getPluginsConditionRules();\n });\n\n const isConditionRulesAvailable =\n !conditionRulesLoading && Array.isArray(conditionRules);\n\n const conditionRulesData = isConditionRulesAvailable\n ? getConditionRulesData(conditionRules)\n : undefined;\n\n return {\n data: conditionRulesData,\n error: conditionRulesErr,\n };\n};\n"],"names":[],"mappings":";;;;;AA6BA,MAAM,uBAAA,GAA0B,CAC9B,cACmC,KAAA;AACnC,EAAA,OAAO,cAAe,CAAA,MAAA,CAAO,CAAC,GAAA,EAAK,WAAgB,KAAA;AACjD,IAAO,OAAA;AAAA,MACL,GAAG,GAAA;AAAA,MACH,CAAC,CAAA,EAAG,WAAY,CAAA,QAAQ,EAAE,GAAG,MAAA;AAAA,QAC3B,WAAY,CAAA,KAAA,CAAM,GAAI,CAAA,CAAA,IAAA,KAAQ,KAAK,YAAY,CAAA;AAAA,QAC/C,CAAO,GAAA,KAAA,GAAA;AAAA,OACT;AAAA,KACF,CAAA;AAAA,GACF,EAAG,EAAE,CAAA,CAAA;AACP,CAAA,CAAA;AAEA,MAAM,WAAA,GAAc,CAAC,WAAA,EAAmC,OAAoB,KAAA;AAC1E,EAAA,OAAO,YAAY,KAAM,CAAA,MAAA;AAAA,IACvB,CAAC,SAAoB,IAAwB,KAAA;AAC3C,MAAO,OAAA,IAAA,CAAK,iBAAiB,OACzB,GAAA;AAAA,QACE,GAAG,OAAA;AAAA,QACH,CAAC,CAAA,EAAG,IAAK,CAAA,IAAI,EAAE,GAAG;AAAA,UAChB,QAAQ,IAAK,CAAA,YAAA;AAAA,UACb,aAAa,IAAK,CAAA,WAAA;AAAA,SACpB;AAAA,QACA,OAAO,CAAC,GAAG,OAAQ,CAAA,KAAA,EAAO,KAAK,IAAI,CAAA;AAAA,OAErC,GAAA,OAAA,CAAA;AAAA,KACN;AAAA,IACA,EAAE,KAAO,EAAA,EAAG,EAAA;AAAA,GACd,CAAA;AACF,CAAA,CAAA;AAEA,MAAM,qBAAA,GAAwB,CAAC,cAA2C,KAAA;AACxE,EAAM,MAAA,oBAAA,GAAuB,wBAAwB,cAAc,CAAA,CAAA;AAEnE,EAAA,OAAO,cAAe,CAAA,MAAA,CAAO,CAAC,GAAA,EAAyB,WAAgB,KAAA;AACrE,IAAO,OAAA;AAAA,MACL,GAAG,GAAA;AAAA,MACH,CAAC,CAAA,EAAG,WAAY,CAAA,QAAQ,CAAE,CAAA,GAAG,oBAC3B,CAAA,WAAA,CAAY,QACd,CAAA,CAAE,MAAO,CAAA,CAAC,QAA8B,OAAoB,KAAA;AAC1D,QAAO,OAAA;AAAA,UACL,GAAG,MAAA;AAAA,UACH,CAAC,CAAG,EAAA,OAAO,EAAE,GAAG,WAAA,CAAY,aAAa,OAAO,CAAA;AAAA,SAClD,CAAA;AAAA,OACF,EAAG,EAAE,CAAA;AAAA,KACP,CAAA;AAAA,GACF,EAAG,EAAE,CAAA,CAAA;AACP,CAAA,CAAA;AAEO,MAAM,oBAAoB,MAAsB;AACrD,EAAM,MAAA,OAAA,GAAU,OAAO,UAAU,CAAA,CAAA;AAEjC,EAAM,MAAA;AAAA,IACJ,KAAO,EAAA,cAAA;AAAA,IACP,OAAS,EAAA,qBAAA;AAAA,IACT,KAAO,EAAA,iBAAA;AAAA,GACT,GAAI,SAAS,YAAY;AACvB,IAAO,OAAA,MAAM,QAAQ,wBAAyB,EAAA,CAAA;AAAA,GAC/C,CAAA,CAAA;AAED,EAAA,MAAM,yBACJ,GAAA,CAAC,qBAAyB,IAAA,KAAA,CAAM,QAAQ,cAAc,CAAA,CAAA;AAExD,EAAA,MAAM,kBAAqB,GAAA,yBAAA,GACvB,qBAAsB,CAAA,cAAc,CACpC,GAAA,KAAA,CAAA,CAAA;AAEJ,EAAO,OAAA;AAAA,IACL,IAAM,EAAA,kBAAA;AAAA,IACN,KAAO,EAAA,iBAAA;AAAA,GACT,CAAA;AACF;;;;"}
@@ -0,0 +1,15 @@
1
+ import { useEffect } from 'react';
2
+ import { useLocation } from 'react-router-dom';
3
+
4
+ const useLocationToast = (setToastMessage) => {
5
+ const location = useLocation();
6
+ useEffect(() => {
7
+ if (location?.state?.toastMessage) {
8
+ setToastMessage(location.state.toastMessage);
9
+ }
10
+ return () => setToastMessage("");
11
+ }, [location, setToastMessage]);
12
+ };
13
+
14
+ export { useLocationToast };
15
+ //# sourceMappingURL=useLocationToast.esm.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useLocationToast.esm.js","sources":["../../src/hooks/useLocationToast.ts"],"sourcesContent":["/*\n * Copyright 2024 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport { useEffect } from 'react';\nimport { useLocation } from 'react-router-dom';\n\nexport const useLocationToast = (\n setToastMessage: (message: string) => void,\n) => {\n const location = useLocation();\n\n useEffect(() => {\n if (location?.state?.toastMessage) {\n setToastMessage(location.state.toastMessage);\n }\n return () => setToastMessage('');\n }, [location, setToastMessage]);\n};\n"],"names":[],"mappings":";;;AAkBa,MAAA,gBAAA,GAAmB,CAC9B,eACG,KAAA;AACH,EAAA,MAAM,WAAW,WAAY,EAAA,CAAA;AAE7B,EAAA,SAAA,CAAU,MAAM;AACd,IAAI,IAAA,QAAA,EAAU,OAAO,YAAc,EAAA;AACjC,MAAgB,eAAA,CAAA,QAAA,CAAS,MAAM,YAAY,CAAA,CAAA;AAAA,KAC7C;AACA,IAAO,OAAA,MAAM,gBAAgB,EAAE,CAAA,CAAA;AAAA,GAC9B,EAAA,CAAC,QAAU,EAAA,eAAe,CAAC,CAAA,CAAA;AAChC;;;;"}
@@ -0,0 +1,92 @@
1
+ import React from 'react';
2
+ import { useAsyncRetry, useInterval } from 'react-use';
3
+ import { stringifyEntityRef, parseEntityRef } from '@backstage/catalog-model';
4
+ import { useApi } from '@backstage/core-plugin-api';
5
+ import { rbacApiRef } from '../api/RBACBackendClient.esm.js';
6
+ import { getMembersFromGroup } from '../utils/rbac-utils.esm.js';
7
+
8
+ const getErrorText = (role, members) => {
9
+ if (!Array.isArray(role) && role?.statusText) {
10
+ return {
11
+ message: `Unable to fetch role: ${role.statusText}`
12
+ };
13
+ } else if (!Array.isArray(members) && members?.statusText) {
14
+ return {
15
+ message: `Unable to fetch members: ${members.statusText}`
16
+ };
17
+ }
18
+ return void 0;
19
+ };
20
+ const getMemberData = (memberResource, ref) => {
21
+ if (memberResource) {
22
+ return {
23
+ name: memberResource.spec.profile?.displayName ?? memberResource.metadata.name,
24
+ type: memberResource.kind,
25
+ ref: {
26
+ namespace: memberResource.metadata.namespace,
27
+ kind: memberResource.kind.toLocaleLowerCase("en-US"),
28
+ name: memberResource.metadata.name
29
+ },
30
+ members: memberResource.kind === "Group" ? getMembersFromGroup(memberResource) : 0
31
+ };
32
+ }
33
+ const { kind, namespace, name } = parseEntityRef(ref);
34
+ return {
35
+ name,
36
+ type: kind === "user" ? "User" : "Group",
37
+ ref: {
38
+ namespace,
39
+ kind,
40
+ name
41
+ },
42
+ members: 0
43
+ };
44
+ };
45
+ const useMembers = (roleName, pollInterval) => {
46
+ const rbacApi = useApi(rbacApiRef);
47
+ let data = [];
48
+ const {
49
+ value: role,
50
+ retry: roleRetry,
51
+ error: roleError
52
+ } = useAsyncRetry(async () => {
53
+ return await rbacApi.getRole(roleName);
54
+ });
55
+ const {
56
+ value: members,
57
+ retry: membersRetry,
58
+ error: membersError
59
+ } = useAsyncRetry(async () => {
60
+ return await rbacApi.getMembers();
61
+ });
62
+ const canReadUsersAndGroups = !membersError && Array.isArray(members) && members.length > 0;
63
+ const loading = !roleError && !membersError && !role && !members;
64
+ data = React.useMemo(
65
+ () => Array.isArray(role) ? role[0].memberReferences.reduce((acc, ref) => {
66
+ const memberResource = Array.isArray(
67
+ members
68
+ ) ? members.find((member) => stringifyEntityRef(member) === ref) : void 0;
69
+ const memberData = getMemberData(memberResource, ref);
70
+ acc.push(memberData);
71
+ return acc;
72
+ }, []) : [],
73
+ [role, members]
74
+ );
75
+ useInterval(
76
+ () => {
77
+ roleRetry();
78
+ membersRetry();
79
+ },
80
+ loading ? null : 1e4
81
+ );
82
+ return {
83
+ loading,
84
+ data,
85
+ retry: { roleRetry, membersRetry },
86
+ error: getErrorText(role, members) || roleError || membersError,
87
+ canReadUsersAndGroups
88
+ };
89
+ };
90
+
91
+ export { useMembers };
92
+ //# sourceMappingURL=useMembers.esm.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useMembers.esm.js","sources":["../../src/hooks/useMembers.ts"],"sourcesContent":["/*\n * Copyright 2024 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport React from 'react';\nimport { useAsyncRetry, useInterval } from 'react-use';\n\nimport { parseEntityRef, stringifyEntityRef } from '@backstage/catalog-model';\nimport { useApi } from '@backstage/core-plugin-api';\n\nimport { rbacApiRef } from '../api/RBACBackendClient';\nimport { MemberEntity, MembersData } from '../types';\nimport { getMembersFromGroup } from '../utils/rbac-utils';\n\nexport type MembersInfo = {\n loading: boolean;\n data: MembersData[];\n retry: { roleRetry: () => void; membersRetry: () => void };\n error?: { message: string };\n canReadUsersAndGroups: boolean;\n};\n\nconst getErrorText = (\n role: any,\n members: any,\n): { message: string } | undefined => {\n if (!Array.isArray(role) && (role as Response)?.statusText) {\n return {\n message: `Unable to fetch role: ${(role as Response).statusText}`,\n };\n } else if (!Array.isArray(members) && (members as Response)?.statusText) {\n return {\n message: `Unable to fetch members: ${(members as Response).statusText}`,\n };\n }\n return undefined;\n};\n\nconst getMemberData = (\n memberResource: MemberEntity | undefined,\n ref: string,\n) => {\n if (memberResource) {\n return {\n name:\n memberResource.spec.profile?.displayName ??\n memberResource.metadata.name,\n type: memberResource.kind,\n ref: {\n namespace: memberResource.metadata.namespace as string,\n kind: memberResource.kind.toLocaleLowerCase('en-US'),\n name: memberResource.metadata.name,\n },\n members:\n memberResource.kind === 'Group'\n ? getMembersFromGroup(memberResource)\n : 0,\n };\n }\n const { kind, namespace, name } = parseEntityRef(ref);\n return {\n name,\n type: kind === 'user' ? 'User' : ('Group' as 'User' | 'Group'),\n ref: {\n namespace,\n kind,\n name,\n },\n members: 0,\n };\n};\n\nexport const useMembers = (\n roleName: string,\n pollInterval?: number,\n): MembersInfo => {\n const rbacApi = useApi(rbacApiRef);\n let data: MembersData[] = [];\n const {\n value: role,\n retry: roleRetry,\n error: roleError,\n } = useAsyncRetry(async () => {\n return await rbacApi.getRole(roleName);\n });\n\n const {\n value: members,\n retry: membersRetry,\n error: membersError,\n } = useAsyncRetry(async () => {\n return await rbacApi.getMembers();\n });\n\n const canReadUsersAndGroups =\n !membersError && Array.isArray(members) && members.length > 0;\n\n const loading = !roleError && !membersError && !role && !members;\n\n data = React.useMemo(\n () =>\n Array.isArray(role)\n ? role[0].memberReferences.reduce((acc: MembersData[], ref: string) => {\n const memberResource: MemberEntity | undefined = Array.isArray(\n members,\n )\n ? members.find(member => stringifyEntityRef(member) === ref)\n : undefined;\n const memberData = getMemberData(memberResource, ref);\n acc.push(memberData);\n return acc;\n }, [])\n : [],\n [role, members],\n );\n\n useInterval(\n () => {\n roleRetry();\n membersRetry();\n },\n loading ? null : pollInterval || 10000,\n );\n\n return {\n loading,\n data,\n retry: { roleRetry, membersRetry },\n error: getErrorText(role, members) || roleError || membersError,\n canReadUsersAndGroups,\n };\n};\n"],"names":[],"mappings":"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"}
@@ -0,0 +1,80 @@
1
+ import React from 'react';
2
+ import { useAsyncRetry, useInterval } from 'react-use';
3
+ import { useApi } from '@backstage/core-plugin-api';
4
+ import { rbacApiRef } from '../api/RBACBackendClient.esm.js';
5
+ import { getPluginsPermissionPoliciesData } from '../utils/create-role-utils.esm.js';
6
+ import { getPermissionsData, getConditionalPermissionsData } from '../utils/rbac-utils.esm.js';
7
+
8
+ const getErrorText = (policies, permissionPolicies, conditionalPolicies) => {
9
+ if (!Array.isArray(policies) && policies?.statusText) {
10
+ return {
11
+ name: policies.status,
12
+ message: `Error fetching policies. ${policies.statusText}`
13
+ };
14
+ } else if (!Array.isArray(permissionPolicies) && permissionPolicies?.statusText) {
15
+ return {
16
+ name: permissionPolicies.status,
17
+ message: `Error fetching the plugins. ${permissionPolicies.statusText}`
18
+ };
19
+ } else if (!Array.isArray(conditionalPolicies) && conditionalPolicies?.statusText) {
20
+ return {
21
+ name: conditionalPolicies.status,
22
+ message: `Error fetching the conditional permission policies. ${conditionalPolicies.statusText}`
23
+ };
24
+ }
25
+ return void 0;
26
+ };
27
+ const usePermissionPolicies = (entityReference, pollInterval) => {
28
+ const rbacApi = useApi(rbacApiRef);
29
+ const {
30
+ value: policies,
31
+ retry: policiesRetry,
32
+ error: policiesError
33
+ } = useAsyncRetry(async () => {
34
+ return await rbacApi.getAssociatedPolicies(entityReference);
35
+ });
36
+ const {
37
+ value: conditionalPolicies,
38
+ retry: conditionalPoliciesRetry,
39
+ error: conditionalPoliciesError
40
+ } = useAsyncRetry(async () => {
41
+ return await rbacApi.getRoleConditions(entityReference);
42
+ });
43
+ const {
44
+ value: permissionPolicies,
45
+ error: permissionPoliciesError,
46
+ retry: permissionPoliciesRetry
47
+ } = useAsyncRetry(async () => {
48
+ return await rbacApi.listPermissions();
49
+ });
50
+ const loading = !permissionPoliciesError && !policiesError && !conditionalPoliciesError && (!permissionPolicies || !policies || !conditionalPolicies);
51
+ const allPermissionPolicies = React.useMemo(
52
+ () => Array.isArray(permissionPolicies) ? permissionPolicies : [],
53
+ [permissionPolicies]
54
+ );
55
+ const data = React.useMemo(() => {
56
+ return Array.isArray(policies) ? getPermissionsData(policies, allPermissionPolicies) : [];
57
+ }, [allPermissionPolicies, policies]);
58
+ const conditionsData = React.useMemo(() => {
59
+ const cpp = Array.isArray(conditionalPolicies) ? conditionalPolicies : [];
60
+ const pluginsPermissionsPoliciesData = allPermissionPolicies.length > 0 ? getPluginsPermissionPoliciesData(allPermissionPolicies) : void 0;
61
+ return pluginsPermissionsPoliciesData ? getConditionalPermissionsData(cpp, pluginsPermissionsPoliciesData) : [];
62
+ }, [allPermissionPolicies, conditionalPolicies]);
63
+ useInterval(
64
+ () => {
65
+ policiesRetry();
66
+ permissionPoliciesRetry();
67
+ conditionalPoliciesRetry();
68
+ },
69
+ loading ? null : null
70
+ );
71
+ return {
72
+ loading,
73
+ data: [...conditionsData, ...data],
74
+ retry: { policiesRetry, permissionPoliciesRetry, conditionalPoliciesRetry },
75
+ error: policiesError || permissionPoliciesError || conditionalPoliciesError || getErrorText(policies, permissionPolicies, conditionalPolicies)
76
+ };
77
+ };
78
+
79
+ export { usePermissionPolicies };
80
+ //# sourceMappingURL=usePermissionPolicies.esm.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"usePermissionPolicies.esm.js","sources":["../../src/hooks/usePermissionPolicies.ts"],"sourcesContent":["/*\n * Copyright 2024 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport React from 'react';\nimport { useAsyncRetry, useInterval } from 'react-use';\n\nimport { useApi } from '@backstage/core-plugin-api';\n\nimport { rbacApiRef } from '../api/RBACBackendClient';\nimport { getPluginsPermissionPoliciesData } from '../utils/create-role-utils';\nimport {\n getConditionalPermissionsData,\n getPermissionsData,\n} from '../utils/rbac-utils';\n\nconst getErrorText = (\n policies: any,\n permissionPolicies: any,\n conditionalPolicies: any,\n): { name: number; message: string } | undefined => {\n if (!Array.isArray(policies) && (policies as Response)?.statusText) {\n return {\n name: (policies as Response).status,\n message: `Error fetching policies. ${(policies as Response).statusText}`,\n };\n } else if (\n !Array.isArray(permissionPolicies) &&\n (permissionPolicies as Response)?.statusText\n ) {\n return {\n name: (permissionPolicies as Response).status,\n message: `Error fetching the plugins. ${\n (permissionPolicies as Response).statusText\n }`,\n };\n } else if (\n !Array.isArray(conditionalPolicies) &&\n (conditionalPolicies as Response)?.statusText\n ) {\n return {\n name: (conditionalPolicies as Response).status,\n message: `Error fetching the conditional permission policies. ${\n (conditionalPolicies as Response).statusText\n }`,\n };\n }\n return undefined;\n};\n\nexport const usePermissionPolicies = (\n entityReference: string,\n pollInterval?: number,\n) => {\n const rbacApi = useApi(rbacApiRef);\n const {\n value: policies,\n retry: policiesRetry,\n error: policiesError,\n } = useAsyncRetry(async () => {\n return await rbacApi.getAssociatedPolicies(entityReference);\n });\n\n const {\n value: conditionalPolicies,\n retry: conditionalPoliciesRetry,\n error: conditionalPoliciesError,\n } = useAsyncRetry(async () => {\n return await rbacApi.getRoleConditions(entityReference);\n });\n\n const {\n value: permissionPolicies,\n error: permissionPoliciesError,\n retry: permissionPoliciesRetry,\n } = useAsyncRetry(async () => {\n return await rbacApi.listPermissions();\n });\n\n const loading =\n !permissionPoliciesError &&\n !policiesError &&\n !conditionalPoliciesError &&\n (!permissionPolicies || !policies || !conditionalPolicies);\n\n const allPermissionPolicies = React.useMemo(\n () => (Array.isArray(permissionPolicies) ? permissionPolicies : []),\n [permissionPolicies],\n );\n\n const data = React.useMemo(() => {\n return Array.isArray(policies)\n ? getPermissionsData(policies, allPermissionPolicies)\n : [];\n }, [allPermissionPolicies, policies]);\n\n const conditionsData = React.useMemo(() => {\n const cpp = Array.isArray(conditionalPolicies) ? conditionalPolicies : [];\n const pluginsPermissionsPoliciesData =\n allPermissionPolicies.length > 0\n ? getPluginsPermissionPoliciesData(allPermissionPolicies)\n : undefined;\n return pluginsPermissionsPoliciesData\n ? getConditionalPermissionsData(cpp, pluginsPermissionsPoliciesData)\n : [];\n }, [allPermissionPolicies, conditionalPolicies]);\n\n useInterval(\n () => {\n policiesRetry();\n permissionPoliciesRetry();\n conditionalPoliciesRetry();\n },\n loading ? null : pollInterval || null,\n );\n return {\n loading,\n data: [...conditionsData, ...data],\n retry: { policiesRetry, permissionPoliciesRetry, conditionalPoliciesRetry },\n error:\n policiesError ||\n permissionPoliciesError ||\n conditionalPoliciesError ||\n getErrorText(policies, permissionPolicies, conditionalPolicies),\n };\n};\n"],"names":[],"mappings":"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"}
@@ -0,0 +1,23 @@
1
+ import { useAsync } from 'react-use';
2
+ import { useApi } from '@backstage/core-plugin-api';
3
+ import { rbacApiRef } from '../api/RBACBackendClient.esm.js';
4
+
5
+ const useRole = (roleEntityRef) => {
6
+ const rbacApi = useApi(rbacApiRef);
7
+ const {
8
+ value: roles,
9
+ loading,
10
+ error: roleError
11
+ } = useAsync(async () => await rbacApi.getRole(roleEntityRef));
12
+ return {
13
+ loading,
14
+ role: Array.isArray(roles) ? roles[0] : void 0,
15
+ roleError: roleError || {
16
+ name: roles?.status,
17
+ message: `Error fetching the role. ${roles?.statusText}`
18
+ }
19
+ };
20
+ };
21
+
22
+ export { useRole };
23
+ //# sourceMappingURL=useRole.esm.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useRole.esm.js","sources":["../../src/hooks/useRole.ts"],"sourcesContent":["/*\n * Copyright 2024 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport { useAsync } from 'react-use';\n\nimport { useApi } from '@backstage/core-plugin-api';\n\nimport { Role } from '@backstage-community/plugin-rbac-common';\n\nimport { rbacApiRef } from '../api/RBACBackendClient';\n\nexport const useRole = (\n roleEntityRef: string,\n): {\n loading: boolean;\n role: Role | undefined;\n roleError: Error;\n} => {\n const rbacApi = useApi(rbacApiRef);\n const {\n value: roles,\n loading,\n error: roleError,\n } = useAsync(async () => await rbacApi.getRole(roleEntityRef));\n\n return {\n loading,\n role: Array.isArray(roles) ? roles[0] : undefined,\n roleError: (roleError as Error) || {\n name: (roles as Response)?.status,\n message: `Error fetching the role. ${(roles as Response)?.statusText}`,\n },\n };\n};\n"],"names":[],"mappings":";;;;AAuBa,MAAA,OAAA,GAAU,CACrB,aAKG,KAAA;AACH,EAAM,MAAA,OAAA,GAAU,OAAO,UAAU,CAAA,CAAA;AACjC,EAAM,MAAA;AAAA,IACJ,KAAO,EAAA,KAAA;AAAA,IACP,OAAA;AAAA,IACA,KAAO,EAAA,SAAA;AAAA,MACL,QAAS,CAAA,YAAY,MAAM,OAAQ,CAAA,OAAA,CAAQ,aAAa,CAAC,CAAA,CAAA;AAE7D,EAAO,OAAA;AAAA,IACL,OAAA;AAAA,IACA,MAAM,KAAM,CAAA,OAAA,CAAQ,KAAK,CAAI,GAAA,KAAA,CAAM,CAAC,CAAI,GAAA,KAAA,CAAA;AAAA,IACxC,WAAY,SAAuB,IAAA;AAAA,MACjC,MAAO,KAAoB,EAAA,MAAA;AAAA,MAC3B,OAAA,EAAS,CAA6B,yBAAA,EAAA,KAAA,EAAoB,UAAU,CAAA,CAAA;AAAA,KACtE;AAAA,GACF,CAAA;AACF;;;;"}
@@ -0,0 +1,169 @@
1
+ import React from 'react';
2
+ import { useAsyncRetry, useAsync, useInterval } from 'react-use';
3
+ import { useApi } from '@backstage/core-plugin-api';
4
+ import { usePermission } from '@backstage/plugin-permission-react';
5
+ import { policyEntityDeletePermission, policyEntityCreatePermission, policyEntityUpdatePermission, isResourcedPolicy } from '@backstage-community/plugin-rbac-common';
6
+ import { rbacApiRef } from '../api/RBACBackendClient.esm.js';
7
+ import { getPermissions, getPermissionsArray } from '../utils/rbac-utils.esm.js';
8
+
9
+ const useRoles = (pollInterval) => {
10
+ const rbacApi = useApi(rbacApiRef);
11
+ const [newRoles, setNewRoles] = React.useState([]);
12
+ const [roleConditionError, setRoleConditionError] = React.useState("");
13
+ const {
14
+ value: roles,
15
+ retry: roleRetry,
16
+ error: rolesError
17
+ } = useAsyncRetry(async () => await rbacApi.getRoles());
18
+ const {
19
+ value: policies,
20
+ retry: policiesRetry,
21
+ error: policiesError
22
+ } = useAsyncRetry(async () => await rbacApi.getPolicies(), []);
23
+ const {
24
+ loading: membersLoading,
25
+ value: members,
26
+ error: membersError
27
+ } = useAsync(async () => {
28
+ return await rbacApi.getMembers();
29
+ });
30
+ const {
31
+ value: permissionPolicies,
32
+ loading: loadingPermissionPolicies,
33
+ error: permissionPoliciesError
34
+ } = useAsync(async () => {
35
+ return await rbacApi.listPermissions();
36
+ });
37
+ const canReadUsersAndGroups = !membersLoading && !membersError && Array.isArray(members) && members.length > 0;
38
+ const deletePermissionResult = usePermission({
39
+ permission: policyEntityDeletePermission,
40
+ resourceRef: policyEntityDeletePermission.resourceType
41
+ });
42
+ const policyEntityCreatePermissionResult = usePermission({
43
+ permission: policyEntityCreatePermission,
44
+ resourceRef: policyEntityCreatePermission.resourceType
45
+ });
46
+ const createRoleLoading = policyEntityCreatePermissionResult.loading || membersLoading;
47
+ const createRoleAllowed = policyEntityCreatePermissionResult.allowed && canReadUsersAndGroups;
48
+ const editPermissionResult = usePermission({
49
+ permission: policyEntityUpdatePermission,
50
+ resourceRef: policyEntityUpdatePermission.resourceType
51
+ });
52
+ React.useEffect(() => {
53
+ const fetchAllPermissionPolicies = async () => {
54
+ if (!Array.isArray(roles)) return;
55
+ const failedFetchConditionRoles = [];
56
+ const conditionPromises = roles.map(async (role) => {
57
+ try {
58
+ const conditionalPolicies = await rbacApi.getRoleConditions(
59
+ role.name
60
+ );
61
+ if (conditionalPolicies?.statusText) {
62
+ failedFetchConditionRoles.push(role.name);
63
+ throw new Error(
64
+ conditionalPolicies.statusText
65
+ );
66
+ }
67
+ const accessiblePlugins = Array.isArray(conditionalPolicies) && conditionalPolicies.length > 0 ? conditionalPolicies.map((c) => c.pluginId) : [];
68
+ return {
69
+ ...role,
70
+ conditionalPoliciesCount: Array.isArray(conditionalPolicies) ? conditionalPolicies.length : 0,
71
+ accessiblePlugins
72
+ };
73
+ } catch (error) {
74
+ setRoleConditionError(
75
+ `Error fetching role conditions for ${failedFetchConditionRoles.length > 1 ? "roles" : "role"} ${failedFetchConditionRoles.join(", ")}, please try again later.`
76
+ );
77
+ return {
78
+ ...role,
79
+ conditionalPoliciesCount: 0,
80
+ accessiblePlugins: []
81
+ };
82
+ }
83
+ });
84
+ const updatedRoles = await Promise.all(conditionPromises);
85
+ setNewRoles(updatedRoles);
86
+ };
87
+ fetchAllPermissionPolicies();
88
+ }, [roles, rbacApi]);
89
+ const data = React.useMemo(
90
+ () => Array.isArray(newRoles) && newRoles?.length > 0 ? newRoles.reduce(
91
+ (acc, role) => {
92
+ const permissions = getPermissions(
93
+ role.name,
94
+ policies
95
+ );
96
+ let accPls = role.accessiblePlugins;
97
+ if (!loadingPermissionPolicies && !permissionPoliciesError && permissionPolicies?.length > 0) {
98
+ const pls = getPermissionsArray(
99
+ role.name,
100
+ policies
101
+ ).map(
102
+ (po) => permissionPolicies.find(
103
+ (pp) => pp.policies?.find(
104
+ (pol) => isResourcedPolicy(pol) ? po.permission === pol.resourceType : po.permission === pol.name
105
+ )
106
+ )?.pluginId
107
+ );
108
+ accPls = [...accPls, ...pls].filter((val) => !!val);
109
+ }
110
+ const accessiblePlugins = accPls.filter((val, index, plugins) => plugins.indexOf(val) === index).sort();
111
+ return [
112
+ ...acc,
113
+ {
114
+ id: role.name,
115
+ name: role.name,
116
+ description: role.metadata?.description ?? "-",
117
+ members: role.memberReferences,
118
+ permissions: role.conditionalPoliciesCount + permissions,
119
+ modifiedBy: "-",
120
+ lastModified: "-",
121
+ actionsPermissionResults: {
122
+ delete: deletePermissionResult,
123
+ edit: {
124
+ allowed: editPermissionResult.allowed && canReadUsersAndGroups,
125
+ loading: editPermissionResult.loading
126
+ }
127
+ },
128
+ accessiblePlugins
129
+ }
130
+ ];
131
+ },
132
+ []
133
+ ) : [],
134
+ [
135
+ newRoles,
136
+ policies,
137
+ loadingPermissionPolicies,
138
+ permissionPoliciesError,
139
+ permissionPolicies,
140
+ deletePermissionResult,
141
+ editPermissionResult.allowed,
142
+ editPermissionResult.loading,
143
+ canReadUsersAndGroups
144
+ ]
145
+ );
146
+ const loading = !rolesError && !policiesError && !roles && !policies;
147
+ useInterval(
148
+ () => {
149
+ roleRetry();
150
+ policiesRetry();
151
+ },
152
+ loading ? null : 1e4
153
+ );
154
+ return {
155
+ loading,
156
+ data,
157
+ error: {
158
+ rolesError: rolesError?.message || (typeof roles === "object" ? roles?.statusText : ""),
159
+ policiesError: policiesError?.message || (typeof policies === "object" ? policies?.statusText : ""),
160
+ roleConditionError
161
+ },
162
+ createRoleLoading,
163
+ createRoleAllowed,
164
+ retry: { roleRetry, policiesRetry }
165
+ };
166
+ };
167
+
168
+ export { useRoles };
169
+ //# sourceMappingURL=useRoles.esm.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useRoles.esm.js","sources":["../../src/hooks/useRoles.ts"],"sourcesContent":["/*\n * Copyright 2024 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport React from 'react';\nimport { useAsync, useAsyncRetry, useInterval } from 'react-use';\n\nimport { useApi } from '@backstage/core-plugin-api';\nimport { usePermission } from '@backstage/plugin-permission-react';\n\nimport {\n isResourcedPolicy,\n PluginPermissionMetaData,\n policyEntityCreatePermission,\n policyEntityDeletePermission,\n policyEntityUpdatePermission,\n Role,\n RoleBasedPolicy,\n} from '@backstage-community/plugin-rbac-common';\n\nimport { rbacApiRef } from '../api/RBACBackendClient';\nimport { RolesData } from '../types';\nimport { getPermissions, getPermissionsArray } from '../utils/rbac-utils';\n\ntype RoleWithConditionalPoliciesCount = Role & {\n conditionalPoliciesCount: number;\n accessiblePlugins: string[];\n};\n\nexport const useRoles = (\n pollInterval?: number,\n): {\n loading: boolean;\n data: RolesData[];\n createRoleLoading: boolean;\n createRoleAllowed: boolean;\n error: {\n rolesError: string;\n policiesError: string;\n roleConditionError: string;\n };\n retry: { roleRetry: () => void; policiesRetry: () => void };\n} => {\n const rbacApi = useApi(rbacApiRef);\n const [newRoles, setNewRoles] = React.useState<\n RoleWithConditionalPoliciesCount[]\n >([]);\n const [roleConditionError, setRoleConditionError] =\n React.useState<string>('');\n const {\n value: roles,\n retry: roleRetry,\n error: rolesError,\n } = useAsyncRetry(async () => await rbacApi.getRoles());\n\n const {\n value: policies,\n retry: policiesRetry,\n error: policiesError,\n } = useAsyncRetry(async () => await rbacApi.getPolicies(), []);\n\n const {\n loading: membersLoading,\n value: members,\n error: membersError,\n } = useAsync(async () => {\n return await rbacApi.getMembers();\n });\n\n const {\n value: permissionPolicies,\n loading: loadingPermissionPolicies,\n error: permissionPoliciesError,\n } = useAsync(async () => {\n return await rbacApi.listPermissions();\n });\n\n const canReadUsersAndGroups =\n !membersLoading &&\n !membersError &&\n Array.isArray(members) &&\n members.length > 0;\n\n const deletePermissionResult = usePermission({\n permission: policyEntityDeletePermission,\n resourceRef: policyEntityDeletePermission.resourceType,\n });\n\n const policyEntityCreatePermissionResult = usePermission({\n permission: policyEntityCreatePermission,\n resourceRef: policyEntityCreatePermission.resourceType,\n });\n\n const createRoleLoading =\n policyEntityCreatePermissionResult.loading || membersLoading;\n\n const createRoleAllowed =\n policyEntityCreatePermissionResult.allowed && canReadUsersAndGroups;\n\n const editPermissionResult = usePermission({\n permission: policyEntityUpdatePermission,\n resourceRef: policyEntityUpdatePermission.resourceType,\n });\n\n React.useEffect(() => {\n const fetchAllPermissionPolicies = async () => {\n if (!Array.isArray(roles)) return;\n const failedFetchConditionRoles: string[] = [];\n const conditionPromises = roles.map(async role => {\n try {\n const conditionalPolicies = await rbacApi.getRoleConditions(\n role.name,\n );\n\n if ((conditionalPolicies as any as Response)?.statusText) {\n failedFetchConditionRoles.push(role.name);\n throw new Error(\n (conditionalPolicies as any as Response).statusText,\n );\n }\n const accessiblePlugins =\n Array.isArray(conditionalPolicies) && conditionalPolicies.length > 0\n ? conditionalPolicies.map(c => c.pluginId)\n : [];\n return {\n ...role,\n conditionalPoliciesCount: Array.isArray(conditionalPolicies)\n ? conditionalPolicies.length\n : 0,\n accessiblePlugins,\n };\n } catch (error) {\n setRoleConditionError(\n `Error fetching role conditions for ${\n failedFetchConditionRoles.length > 1 ? 'roles' : 'role'\n } ${failedFetchConditionRoles.join(', ')}, please try again later.`,\n );\n return {\n ...role,\n conditionalPoliciesCount: 0,\n accessiblePlugins: [],\n };\n }\n });\n\n const updatedRoles = await Promise.all(conditionPromises);\n setNewRoles(updatedRoles);\n };\n\n fetchAllPermissionPolicies();\n }, [roles, rbacApi]);\n\n const data: RolesData[] = React.useMemo(\n () =>\n Array.isArray(newRoles) && newRoles?.length > 0\n ? newRoles.reduce(\n (acc: RolesData[], role: RoleWithConditionalPoliciesCount) => {\n const permissions = getPermissions(\n role.name,\n policies as RoleBasedPolicy[],\n );\n\n let accPls = role.accessiblePlugins;\n if (\n !loadingPermissionPolicies &&\n !permissionPoliciesError &&\n (permissionPolicies as PluginPermissionMetaData[])?.length > 0\n ) {\n const pls = getPermissionsArray(\n role.name,\n policies as RoleBasedPolicy[],\n ).map(\n po =>\n (permissionPolicies as PluginPermissionMetaData[]).find(\n pp =>\n pp.policies?.find(pol =>\n isResourcedPolicy(pol)\n ? po.permission === pol.resourceType\n : po.permission === pol.name,\n ),\n )?.pluginId,\n );\n accPls = [...accPls, ...pls].filter(val => !!val) as string[];\n }\n const accessiblePlugins = accPls\n .filter((val, index, plugins) => plugins.indexOf(val) === index)\n .sort();\n\n return [\n ...acc,\n {\n id: role.name,\n name: role.name,\n description: role.metadata?.description ?? '-',\n members: role.memberReferences,\n permissions: role.conditionalPoliciesCount + permissions,\n modifiedBy: '-',\n lastModified: '-',\n actionsPermissionResults: {\n delete: deletePermissionResult,\n edit: {\n allowed:\n editPermissionResult.allowed && canReadUsersAndGroups,\n loading: editPermissionResult.loading,\n },\n },\n accessiblePlugins,\n },\n ];\n },\n [],\n )\n : [],\n [\n newRoles,\n policies,\n loadingPermissionPolicies,\n permissionPoliciesError,\n permissionPolicies,\n deletePermissionResult,\n editPermissionResult.allowed,\n editPermissionResult.loading,\n canReadUsersAndGroups,\n ],\n );\n const loading = !rolesError && !policiesError && !roles && !policies;\n\n useInterval(\n () => {\n roleRetry();\n policiesRetry();\n },\n loading ? null : pollInterval || 10000,\n );\n\n return {\n loading,\n data,\n error: {\n rolesError: (rolesError?.message ||\n (typeof roles === 'object'\n ? (roles as any as Response)?.statusText\n : '')) as string,\n policiesError: (policiesError?.message ||\n (typeof policies === 'object'\n ? (policies as any as Response)?.statusText\n : '')) as string,\n roleConditionError,\n },\n createRoleLoading,\n createRoleAllowed,\n retry: { roleRetry, policiesRetry },\n };\n};\n"],"names":[],"mappings":"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"}