@jini-ai/user-management 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +77 -0
- package/LICENSE +201 -0
- package/README.md +104 -0
- package/dist/admin/adapters/http.d.ts +29 -0
- package/dist/admin/adapters/http.js +69 -0
- package/dist/admin/adapters/memory.d.ts +61 -0
- package/dist/admin/adapters/memory.js +217 -0
- package/dist/admin/auth.module.d.ts +30 -0
- package/dist/admin/auth.module.js +12 -0
- package/dist/admin/conformance/index.d.ts +56 -0
- package/dist/admin/conformance/index.js +156 -0
- package/dist/admin/controllers/auth.controller.d.ts +23 -0
- package/dist/admin/controllers/auth.controller.js +33 -0
- package/dist/admin/controllers/controller-store.d.ts +18 -0
- package/dist/admin/controllers/controller-store.js +23 -0
- package/dist/admin/controllers/members.controller.d.ts +33 -0
- package/dist/admin/controllers/members.controller.js +119 -0
- package/dist/admin/controllers/roles.controller.d.ts +47 -0
- package/dist/admin/controllers/roles.controller.js +282 -0
- package/dist/admin/controllers/users.controller.d.ts +53 -0
- package/dist/admin/controllers/users.controller.js +220 -0
- package/dist/admin/index.d.ts +21 -0
- package/dist/admin/index.js +15 -0
- package/dist/admin/members.module.d.ts +34 -0
- package/dist/admin/members.module.js +13 -0
- package/dist/admin/messages.en.d.ts +37 -0
- package/dist/admin/messages.en.js +21 -0
- package/dist/admin/models.d.ts +133 -0
- package/dist/admin/models.js +2 -0
- package/dist/admin/people.rules.d.ts +29 -0
- package/dist/admin/people.rules.js +51 -0
- package/dist/admin/ports.d.ts +187 -0
- package/dist/admin/ports.js +17 -0
- package/dist/admin/react/components/DestructiveDialog.d.ts +5 -0
- package/dist/admin/react/components/DestructiveDialog.js +6 -0
- package/dist/admin/react/components/MemberRow.d.ts +5 -0
- package/dist/admin/react/components/MemberRow.js +8 -0
- package/dist/admin/react/components/MembersConfirmation.d.ts +2 -0
- package/dist/admin/react/components/MembersConfirmation.js +8 -0
- package/dist/admin/react/components/PermissionPanel.d.ts +5 -0
- package/dist/admin/react/components/PermissionPanel.js +9 -0
- package/dist/admin/react/components/PolicyRow.d.ts +5 -0
- package/dist/admin/react/components/PolicyRow.js +11 -0
- package/dist/admin/react/components/RoleRow.d.ts +5 -0
- package/dist/admin/react/components/RoleRow.js +10 -0
- package/dist/admin/react/components/UserRow.d.ts +5 -0
- package/dist/admin/react/components/UserRow.js +8 -0
- package/dist/admin/react/components/UsersConfirmation.d.ts +3 -0
- package/dist/admin/react/components/UsersConfirmation.js +9 -0
- package/dist/admin/react/hooks/AuthBinding.hooks.d.ts +39 -0
- package/dist/admin/react/hooks/AuthBinding.hooks.js +21 -0
- package/dist/admin/react/hooks/LoginPage.hooks.d.ts +54 -0
- package/dist/admin/react/hooks/LoginPage.hooks.js +23 -0
- package/dist/admin/react/hooks/MembersBinding.hooks.d.ts +35 -0
- package/dist/admin/react/hooks/MembersBinding.hooks.js +21 -0
- package/dist/admin/react/hooks/MembersPage.hooks.d.ts +99 -0
- package/dist/admin/react/hooks/MembersPage.hooks.js +38 -0
- package/dist/admin/react/hooks/PeopleController.hooks.d.ts +18 -0
- package/dist/admin/react/hooks/PeopleController.hooks.js +20 -0
- package/dist/admin/react/hooks/PermissionPanel.hooks.d.ts +30 -0
- package/dist/admin/react/hooks/PermissionPanel.hooks.js +15 -0
- package/dist/admin/react/hooks/PoliciesTab.hooks.d.ts +47 -0
- package/dist/admin/react/hooks/PoliciesTab.hooks.js +26 -0
- package/dist/admin/react/hooks/RolesBinding.hooks.d.ts +39 -0
- package/dist/admin/react/hooks/RolesBinding.hooks.js +23 -0
- package/dist/admin/react/hooks/RolesController.hooks.d.ts +43 -0
- package/dist/admin/react/hooks/RolesController.hooks.js +25 -0
- package/dist/admin/react/hooks/RolesPage.hooks.d.ts +105 -0
- package/dist/admin/react/hooks/RolesPage.hooks.js +33 -0
- package/dist/admin/react/hooks/RolesTab.hooks.d.ts +35 -0
- package/dist/admin/react/hooks/RolesTab.hooks.js +19 -0
- package/dist/admin/react/hooks/UsersBinding.hooks.d.ts +36 -0
- package/dist/admin/react/hooks/UsersBinding.hooks.js +21 -0
- package/dist/admin/react/hooks/UsersPage.hooks.d.ts +106 -0
- package/dist/admin/react/hooks/UsersPage.hooks.js +22 -0
- package/dist/admin/react/hooks/UsersTab.hooks.d.ts +101 -0
- package/dist/admin/react/hooks/UsersTab.hooks.js +53 -0
- package/dist/admin/react/index.d.ts +203 -0
- package/dist/admin/react/index.js +32 -0
- package/dist/admin/react/pages/LoginPage.d.ts +3 -0
- package/dist/admin/react/pages/LoginPage.js +13 -0
- package/dist/admin/react/pages/MembersPage.d.ts +3 -0
- package/dist/admin/react/pages/MembersPage.js +19 -0
- package/dist/admin/react/pages/RolesPage.d.ts +3 -0
- package/dist/admin/react/pages/RolesPage.js +19 -0
- package/dist/admin/react/pages/UsersPage.d.ts +3 -0
- package/dist/admin/react/pages/UsersPage.js +19 -0
- package/dist/admin/react/tabs/AuthTab.d.ts +4 -0
- package/dist/admin/react/tabs/AuthTab.js +8 -0
- package/dist/admin/react/tabs/MembersTab.d.ts +4 -0
- package/dist/admin/react/tabs/MembersTab.js +9 -0
- package/dist/admin/react/tabs/PoliciesTab.d.ts +4 -0
- package/dist/admin/react/tabs/PoliciesTab.js +9 -0
- package/dist/admin/react/tabs/RolesTab.d.ts +4 -0
- package/dist/admin/react/tabs/RolesTab.js +9 -0
- package/dist/admin/react/tabs/UsersTab.d.ts +4 -0
- package/dist/admin/react/tabs/UsersTab.js +9 -0
- package/dist/admin/roles.module.d.ts +48 -0
- package/dist/admin/roles.module.js +15 -0
- package/dist/admin/rules.d.ts +32 -0
- package/dist/admin/rules.js +32 -0
- package/dist/admin/users.module.d.ts +39 -0
- package/dist/admin/users.module.js +14 -0
- package/dist/core/builtin-permissions.d.ts +10 -0
- package/dist/core/builtin-permissions.js +402 -0
- package/dist/core/index.d.ts +6 -0
- package/dist/core/index.js +9 -0
- package/dist/core/permissions.d.ts +40 -0
- package/dist/core/permissions.js +52 -0
- package/dist/core/ports.d.ts +237 -0
- package/dist/core/ports.js +2 -0
- package/dist/core/runtime-ports.d.ts +8 -0
- package/dist/core/runtime-ports.js +2 -0
- package/dist/core/types.d.ts +224 -0
- package/dist/core/types.js +93 -0
- package/dist/react/auth/Login.d.ts +9 -0
- package/dist/react/auth/Login.js +8 -0
- package/dist/react/auth/hooks/use-login.hooks.d.ts +22 -0
- package/dist/react/auth/hooks/use-login.hooks.js +24 -0
- package/dist/react/errors.d.ts +10 -0
- package/dist/react/errors.js +13 -0
- package/dist/react/index.d.ts +18 -0
- package/dist/react/index.js +9 -0
- package/dist/react/members/Members.d.ts +6 -0
- package/dist/react/members/Members.js +38 -0
- package/dist/react/members/hooks/use-members.hooks.d.ts +32 -0
- package/dist/react/members/hooks/use-members.hooks.js +120 -0
- package/dist/react/members/rules.d.ts +26 -0
- package/dist/react/members/rules.js +35 -0
- package/dist/react/models.d.ts +50 -0
- package/dist/react/models.js +2 -0
- package/dist/react/ports.d.ts +157 -0
- package/dist/react/ports.js +2 -0
- package/dist/react/roles/Roles.d.ts +100 -0
- package/dist/react/roles/Roles.hooks.d.ts +17 -0
- package/dist/react/roles/Roles.hooks.js +40 -0
- package/dist/react/roles/Roles.js +150 -0
- package/dist/react/roles/hooks/use-roles.hooks.d.ts +80 -0
- package/dist/react/roles/hooks/use-roles.hooks.js +303 -0
- package/dist/react/roles/rules.d.ts +35 -0
- package/dist/react/roles/rules.js +31 -0
- package/dist/react/roles/text.d.ts +27 -0
- package/dist/react/roles/text.js +13 -0
- package/dist/react/testing.d.ts +51 -0
- package/dist/react/testing.js +275 -0
- package/dist/react/users/Users.d.ts +72 -0
- package/dist/react/users/Users.js +129 -0
- package/dist/react/users/hooks/use-reset-password-fields.hooks.d.ts +16 -0
- package/dist/react/users/hooks/use-reset-password-fields.hooks.js +21 -0
- package/dist/react/users/hooks/use-users.hooks.d.ts +77 -0
- package/dist/react/users/hooks/use-users.hooks.js +286 -0
- package/dist/react/users/rules.d.ts +33 -0
- package/dist/react/users/rules.js +61 -0
- package/dist/server/admin-crud-service.d.ts +295 -0
- package/dist/server/admin-crud-service.js +561 -0
- package/dist/server/agent-tool-input.d.ts +58 -0
- package/dist/server/agent-tool-input.js +158 -0
- package/dist/server/agent-tool-registrations.d.ts +51 -0
- package/dist/server/agent-tool-registrations.js +417 -0
- package/dist/server/agent-tools.d.ts +88 -0
- package/dist/server/agent-tools.js +229 -0
- package/dist/server/auth-service.d.ts +107 -0
- package/dist/server/auth-service.js +170 -0
- package/dist/server/authorize.d.ts +92 -0
- package/dist/server/authorize.js +96 -0
- package/dist/server/grant-service.d.ts +219 -0
- package/dist/server/grant-service.js +419 -0
- package/dist/server/hasher.d.ts +64 -0
- package/dist/server/hasher.js +53 -0
- package/dist/server/identity-transaction.d.ts +9 -0
- package/dist/server/identity-transaction.js +8 -0
- package/dist/server/index.d.ts +18 -0
- package/dist/server/index.js +20 -0
- package/dist/server/messages.d.ts +7 -0
- package/dist/server/messages.js +5 -0
- package/dist/server/password-policy.d.ts +59 -0
- package/dist/server/password-policy.js +66 -0
- package/dist/server/permission-migrations.d.ts +84 -0
- package/dist/server/permission-migrations.js +77 -0
- package/dist/server/repo.memory-transactions.d.ts +16 -0
- package/dist/server/repo.memory-transactions.js +79 -0
- package/dist/server/repo.memory.d.ts +186 -0
- package/dist/server/repo.memory.js +230 -0
- package/dist/server/seed.d.ts +56 -0
- package/dist/server/seed.js +435 -0
- package/dist/server/session-tokens.d.ts +10 -0
- package/dist/server/session-tokens.js +12 -0
- package/dist/server/username.d.ts +14 -0
- package/dist/server/username.js +14 -0
- package/package.json +144 -0
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
+
import { rolesDescriptionParts, roleDeleteBodyParts, policyDeleteBodyParts, permissionRemoveBodyParts } from "./text.js";
|
|
3
|
+
import { Fragment } from "react";
|
|
4
|
+
import { DataTable, RowMenu, ConfirmDialog } from "@jini-ai/admin/react";
|
|
5
|
+
import { agentHandle } from "@jini-ai/agentic";
|
|
6
|
+
import { buildAgentListHandles } from "@jini-ai/ui/panel-kit";
|
|
7
|
+
import { TabBar } from "@jini-ai/ui/tab-strip";
|
|
8
|
+
import { roleMenuItems, policyMenuItems } from "./rules.js";
|
|
9
|
+
import { useRoles } from "./hooks/use-roles.hooks.js";
|
|
10
|
+
import { resolveRolesTabId, resolveRolesTabs } from "./Roles.hooks.js";
|
|
11
|
+
export function RolesSection({ roles, create, row, t, translate }) {
|
|
12
|
+
const roleMenuHandles = buildAgentListHandles({ prefix: "roles-row", ids: roles.map((role) => role.id) });
|
|
13
|
+
return (_jsxs(_Fragment, { children: [_jsx("h2", { className: "visually-hidden", children: t("Roles") }), _jsxs("form", { onSubmit: create.submit, className: "notice integrations-form form-measure", children: [create.error ? _jsx("span", { className: "save-error", children: create.error }) : null, _jsxs("label", { children: [t("Role name"), _jsx("input", { value: create.name, onChange: (e) => create.setName(e.target.value), required: true, ...agentHandle({ handle: "roles-create-name" }, { role: "field", label: t("New role's name") }) })] }), _jsx("button", { type: "submit", disabled: create.saving || !create.name, ...agentHandle({ handle: "roles-create-submit" }, { role: "button", label: t("Create this new role") }), children: create.saving ? t("Creating…") : t("Create role") })] }), _jsx(DataTable, { rows: roles, rowKey: (role) => role.id, empty: _jsx("div", { className: "card", children: _jsx("div", { className: "empty-state", children: _jsx("p", { children: t("No roles yet.") }) }) }), columns: [
|
|
14
|
+
{
|
|
15
|
+
key: "name",
|
|
16
|
+
header: t("Name"),
|
|
17
|
+
cell: (role, index) => row.editingId === role.id ? (_jsx("input", { value: row.draftName, onChange: (e) => row.setDraftName(e.target.value), ...agentHandle({ handle: `${roleMenuHandles[index]}-rename-name` }, { role: "field", label: t("This role's new name") }) })) : (role.name),
|
|
18
|
+
},
|
|
19
|
+
{ key: "type", header: t("Type"), cell: (role) => (role.isBuiltin ? t("Built-in") : t("Custom")) },
|
|
20
|
+
{
|
|
21
|
+
key: "actions",
|
|
22
|
+
header: t("More"),
|
|
23
|
+
cell: (role, index) => role.isBuiltin ? (_jsx("span", { className: "muted-cell", children: "\u2014" })) : row.editingId === role.id ? (_jsxs("span", { className: "editor-actions", children: [_jsx("button", { type: "button", disabled: row.savingId === role.id, onClick: () => row.saveRename({ roleId: role.id }), ...agentHandle({ handle: `${roleMenuHandles[index]}-save` }, { role: "button", label: t(`Save this role's new name`) }), children: row.savingId === role.id ? t("Saving…") : t("Save") }), _jsx("button", { type: "button", onClick: () => row.setEditingId(null), ...agentHandle({ handle: `${roleMenuHandles[index]}-cancel` }, { role: "button", label: t("Cancel renaming this role") }), children: t("Cancel") })] })) : (_jsx(RowMenu, { triggerLabel: `${t("Actions for role")} "${role.name}"`, agentHandle: `${roleMenuHandles[index]}-menu`, items: roleMenuItems({ role, handlers: { onRename: row.startRename, onDelete: row.requestDelete }, translate }) })),
|
|
24
|
+
},
|
|
25
|
+
] })] }));
|
|
26
|
+
}
|
|
27
|
+
function PolicyRowActions({ policy, row, permission, agentBase, t, translate }) {
|
|
28
|
+
if (policy.isBuiltin || policy.isFrozen)
|
|
29
|
+
return _jsx("span", { className: "muted-cell", children: "\u2014" });
|
|
30
|
+
if (row.editingId === policy.id) {
|
|
31
|
+
return (_jsxs("span", { className: "editor-actions", children: [_jsx("button", { type: "button", disabled: row.savingId === policy.id, onClick: () => row.saveRename({ policyId: policy.id }), ...agentHandle({ handle: `${agentBase}-save` }, { role: "button", label: t("Save this policy's new name and description") }), children: row.savingId === policy.id ? t("Saving…") : t("Save") }), _jsx("button", { type: "button", onClick: () => row.setEditingId(null), ...agentHandle({ handle: `${agentBase}-cancel` }, { role: "button", label: t("Cancel renaming this policy") }), children: t("Cancel") })] }));
|
|
32
|
+
}
|
|
33
|
+
return (_jsx(RowMenu, { triggerLabel: `${t("Actions for policy")} "${policy.name}"`, agentHandle: `${agentBase}-menu`, items: policyMenuItems({
|
|
34
|
+
policy,
|
|
35
|
+
permissionPolicyId: permission.openForPolicyId,
|
|
36
|
+
handlers: {
|
|
37
|
+
onRename: row.startRename,
|
|
38
|
+
onTogglePermissionForm: permission.toggleForm,
|
|
39
|
+
onDelete: row.requestDelete,
|
|
40
|
+
},
|
|
41
|
+
translate,
|
|
42
|
+
}) }));
|
|
43
|
+
}
|
|
44
|
+
function PolicyPermissionList({ policyId, permission, t }) {
|
|
45
|
+
if (permission.loading)
|
|
46
|
+
return _jsx("p", { className: "muted-cell", children: t("Loading permissions…") });
|
|
47
|
+
if (permission.rows.length === 0)
|
|
48
|
+
return _jsx("p", { className: "muted-cell", children: t("No permissions yet.") });
|
|
49
|
+
const removeHandles = buildAgentListHandles({
|
|
50
|
+
prefix: `policy-permission-remove-${policyId}`,
|
|
51
|
+
ids: permission.rows.map((row) => row.id),
|
|
52
|
+
});
|
|
53
|
+
return (_jsx("ul", { className: "permission-list", children: permission.rows.map((row, index) => (_jsxs("li", { children: [_jsx("code", { children: row.permission }), row.resourceType ? _jsxs("span", { className: "muted-cell", children: [" (", row.resourceType, ")"] }) : null, _jsx("button", { type: "button", disabled: permission.removingId === row.id, "aria-label": `${t("Remove permission")} ${row.permission}`, onClick: () => permission.requestRemove({ policyId, row }), ...agentHandle({ handle: removeHandles[index] }, { role: "button", label: t(`Remove the "${row.permission}" permission from this policy`) }), children: permission.removingId === row.id ? t("Removing…") : t("Remove") })] }, row.id))) }));
|
|
54
|
+
}
|
|
55
|
+
function PolicyPermissionForm({ policyId, savingId, permission, t }) {
|
|
56
|
+
const formHandleBase = buildAgentListHandles({ prefix: "policy-permission-form", ids: [policyId] })[0];
|
|
57
|
+
return (_jsx("tr", { children: _jsx("td", { colSpan: 4, children: _jsxs("div", { className: "notice integrations-form", children: [_jsx("p", { children: t("Current permissions") }), _jsx(PolicyPermissionList, { policyId: policyId, permission: permission, t: t }), _jsxs("label", { children: [t("Permission"), _jsxs("span", { className: "editor-actions permission-form-fields", children: [_jsx("input", { value: permission.permission, onChange: (e) => permission.setPermission(e.target.value), placeholder: t("e.g. content.write"), ...agentHandle({ handle: `${formHandleBase}-permission` }, { role: "field", label: t("Permission string to grant, e.g. content.write") }) }), _jsx("input", { value: permission.resourceType, onChange: (e) => permission.setResourceType(e.target.value), placeholder: t("resource type (optional)"), ...agentHandle({ handle: `${formHandleBase}-resource-type` }, { role: "field", label: t("Optional resource type this permission is scoped to") }) }), _jsx("button", { type: "button", disabled: !permission.permission || savingId === policyId, onClick: () => permission.write({ policyId }), ...agentHandle({ handle: `${formHandleBase}-add` }, { role: "button", label: t("Add this permission to the policy") }), children: savingId === policyId ? t("Saving…") : t("Add") })] })] })] }) }) }));
|
|
58
|
+
}
|
|
59
|
+
export function PolicyRow({ policy, row, permission, agentBase, t, translate }) {
|
|
60
|
+
const renaming = row.editingId === policy.id;
|
|
61
|
+
return (_jsxs(_Fragment, { children: [_jsxs("tr", { children: [_jsx("td", { children: renaming ? (_jsx("input", { value: row.draftName, onChange: (e) => row.setDraftName(e.target.value), ...agentHandle({ handle: `${agentBase}-rename-name` }, { role: "field", label: t("This policy's new name") }) })) : (policy.name) }), _jsx("td", { children: renaming ? (_jsx("input", { value: row.draftDescription, onChange: (e) => row.setDraftDescription(e.target.value), ...agentHandle({ handle: `${agentBase}-rename-description` }, { role: "field", label: t("This policy's new description") }) })) : (policy.description ?? _jsx("span", { className: "muted-cell", children: "\u2014" })) }), _jsxs("td", { children: [policy.isBuiltin ? t("Built-in") : t("Custom"), policy.isFrozen ? ` ${t("(frozen)")}` : ""] }), _jsx("td", { children: _jsx(PolicyRowActions, { policy: policy, row: row, permission: permission, agentBase: agentBase, t: t, translate: translate }) })] }), permission.openForPolicyId === policy.id ? (_jsx(PolicyPermissionForm, { policyId: policy.id, savingId: row.savingId, permission: permission, t: t })) : null] }));
|
|
62
|
+
}
|
|
63
|
+
export function PoliciesSection({ policies, create, row, permission, t, translate }) {
|
|
64
|
+
const policyMenuBases = buildAgentListHandles({ prefix: "policies-row", ids: policies.map((policy) => policy.id) });
|
|
65
|
+
return (_jsxs(_Fragment, { children: [_jsx("h2", { className: "visually-hidden", children: t("Policies") }), _jsxs("form", { onSubmit: create.submit, className: "notice integrations-form form-measure", children: [create.error ? _jsx("span", { className: "save-error", children: create.error }) : null, _jsxs("label", { children: [t("Policy name"), _jsx("input", { value: create.name, onChange: (e) => create.setName(e.target.value), required: true, ...agentHandle({ handle: "policies-create-name" }, { role: "field", label: t("New policy's name") }) })] }), _jsxs("label", { children: [t("Description (optional)"), _jsx("input", { value: create.description, onChange: (e) => create.setDescription(e.target.value), ...agentHandle({ handle: "policies-create-description" }, { role: "field", label: t("New policy's optional description") }) })] }), _jsx("button", { type: "submit", disabled: create.saving || !create.name, ...agentHandle({ handle: "policies-create-submit" }, { role: "button", label: t("Create this new policy") }), children: create.saving ? t("Creating…") : t("Create policy") })] }), policies.length === 0 ? (_jsx("div", { className: "card", children: _jsx("div", { className: "empty-state", children: _jsx("p", { children: t("No policies yet.") }) }) })) : (_jsx("div", { className: "table-scroll", children: _jsxs("table", { className: "list-table", children: [_jsx("thead", { children: _jsxs("tr", { children: [_jsx("th", { children: t("Name") }), _jsx("th", { children: t("Description") }), _jsx("th", { children: t("Type") }), _jsx("th", { children: t("More") })] }) }), _jsx("tbody", { children: policies.map((policy, index) => (_jsx(Fragment, { children: _jsx(PolicyRow, { policy: policy, row: row, permission: permission, agentBase: policyMenuBases[index], t: t, translate: translate }) }, policy.id))) })] }) }))] }));
|
|
66
|
+
}
|
|
67
|
+
function RoleDeleteDialog({ pendingRoleDelete, setPendingRoleDelete, rowSavingId, onDeleteRole, t, translate }) {
|
|
68
|
+
const { prefix, suffix } = roleDeleteBodyParts({ translate });
|
|
69
|
+
return (_jsx(ConfirmDialog, { cancelLabel: t("Cancel"), open: pendingRoleDelete !== null, agentHandle: "roles-delete-role", title: t("Delete role?"), body: pendingRoleDelete ? (_jsxs("p", { children: [prefix, pendingRoleDelete.name, suffix] })) : null, confirmLabel: t("Delete"), destructive: true, pending: pendingRoleDelete !== null && rowSavingId === pendingRoleDelete.id, onConfirm: onDeleteRole, onCancel: () => setPendingRoleDelete(null) }));
|
|
70
|
+
}
|
|
71
|
+
function PolicyDeleteDialog({ pendingPolicyDelete, setPendingPolicyDelete, rowSavingId, onDeletePolicy, t, translate }) {
|
|
72
|
+
const { prefix, suffix } = policyDeleteBodyParts({ translate });
|
|
73
|
+
return (_jsx(ConfirmDialog, { cancelLabel: t("Cancel"), open: pendingPolicyDelete !== null, agentHandle: "roles-delete-policy", title: t("Delete policy?"), body: pendingPolicyDelete ? (_jsxs("p", { children: [prefix, pendingPolicyDelete.name, suffix] })) : null, confirmLabel: t("Delete"), destructive: true, pending: pendingPolicyDelete !== null && rowSavingId === pendingPolicyDelete.id, onConfirm: onDeletePolicy, onCancel: () => setPendingPolicyDelete(null) }));
|
|
74
|
+
}
|
|
75
|
+
function PermissionRemoveDialog({ pendingPermissionRemove, setPendingPermissionRemove, removingPermissionId, onConfirmRemovePermission, t, translate, }) {
|
|
76
|
+
const { prefix, suffix } = permissionRemoveBodyParts({ translate });
|
|
77
|
+
const pending = pendingPermissionRemove;
|
|
78
|
+
return (_jsx(ConfirmDialog, { cancelLabel: t("Cancel"), open: pending !== null, agentHandle: "roles-remove-permission", title: t("Remove permission?"), body: pending ? (_jsxs("p", { children: [prefix, _jsx("code", { children: pending.row.permission }), pending.row.resourceType ? ` (${pending.row.resourceType})` : "", suffix] })) : null, confirmLabel: t("Remove"), tone: "warning", pending: pending !== null && removingPermissionId === pending.row.id, onConfirm: onConfirmRemovePermission, onCancel: () => setPendingPermissionRemove(null) }));
|
|
79
|
+
}
|
|
80
|
+
function RolesTab({ controller }) {
|
|
81
|
+
const c = controller;
|
|
82
|
+
return (_jsx(RolesSection, { roles: c.roles ?? [], create: {
|
|
83
|
+
name: c.roleName,
|
|
84
|
+
setName: c.setRoleName,
|
|
85
|
+
saving: c.roleSaving,
|
|
86
|
+
error: c.roleError,
|
|
87
|
+
submit: c.onCreateRole,
|
|
88
|
+
}, row: {
|
|
89
|
+
editingId: c.editingRoleId,
|
|
90
|
+
setEditingId: c.setEditingRoleId,
|
|
91
|
+
draftName: c.editingRoleName,
|
|
92
|
+
setDraftName: c.setEditingRoleName,
|
|
93
|
+
startRename: c.startEditRole,
|
|
94
|
+
saveRename: c.onSaveRole,
|
|
95
|
+
savingId: c.rowSavingId,
|
|
96
|
+
requestDelete: c.setPendingRoleDelete,
|
|
97
|
+
}, t: c.t, translate: c.translate }));
|
|
98
|
+
}
|
|
99
|
+
function PoliciesTab({ controller }) {
|
|
100
|
+
const c = controller;
|
|
101
|
+
return (_jsx(PoliciesSection, { policies: c.policies ?? [], create: {
|
|
102
|
+
name: c.policyName,
|
|
103
|
+
setName: c.setPolicyName,
|
|
104
|
+
description: c.policyDescription,
|
|
105
|
+
setDescription: c.setPolicyDescription,
|
|
106
|
+
saving: c.policySaving,
|
|
107
|
+
error: c.policyError,
|
|
108
|
+
submit: c.onCreatePolicy,
|
|
109
|
+
}, row: {
|
|
110
|
+
editingId: c.editingPolicyId,
|
|
111
|
+
setEditingId: c.setEditingPolicyId,
|
|
112
|
+
draftName: c.editingPolicyName,
|
|
113
|
+
setDraftName: c.setEditingPolicyName,
|
|
114
|
+
draftDescription: c.editingPolicyDescription,
|
|
115
|
+
setDraftDescription: c.setEditingPolicyDescription,
|
|
116
|
+
startRename: c.startEditPolicy,
|
|
117
|
+
saveRename: c.onSavePolicy,
|
|
118
|
+
savingId: c.rowSavingId,
|
|
119
|
+
requestDelete: c.setPendingPolicyDelete,
|
|
120
|
+
}, permission: {
|
|
121
|
+
openForPolicyId: c.permissionPolicyId,
|
|
122
|
+
permission: c.permissionInput,
|
|
123
|
+
setPermission: c.setPermissionInput,
|
|
124
|
+
resourceType: c.resourceTypeInput,
|
|
125
|
+
setResourceType: c.setResourceTypeInput,
|
|
126
|
+
toggleForm: c.togglePermissionForm,
|
|
127
|
+
write: c.onWritePermission,
|
|
128
|
+
rows: c.permissionRows,
|
|
129
|
+
loading: c.permissionsLoading,
|
|
130
|
+
removingId: c.removingPermissionId,
|
|
131
|
+
requestRemove: c.setPendingPermissionRemove,
|
|
132
|
+
}, t: c.t, translate: c.translate }));
|
|
133
|
+
}
|
|
134
|
+
function rolesTabPanel(activeTabId, controller) {
|
|
135
|
+
if (activeTabId === "policies")
|
|
136
|
+
return _jsx(PoliciesTab, { controller: controller });
|
|
137
|
+
return _jsx(RolesTab, { controller: controller });
|
|
138
|
+
}
|
|
139
|
+
export function Roles({ tabId, useRolesHook = useRoles, port, translate: injectedTranslate, queryScope, onTabChange, usersHref }) {
|
|
140
|
+
const controller = useRolesHook({ port, translate: injectedTranslate, queryScope });
|
|
141
|
+
const { roles, policies, error, rowError, rowSavingId, pendingRoleDelete, setPendingRoleDelete, onDeleteRole, pendingPolicyDelete, setPendingPolicyDelete, onDeletePolicy, pendingPermissionRemove, setPendingPermissionRemove, removingPermissionId, onConfirmRemovePermission, t, translate, } = controller;
|
|
142
|
+
const { prefix: descriptionPrefix, linkLabel: descriptionLinkLabel, suffix: descriptionSuffix } = rolesDescriptionParts({ translate });
|
|
143
|
+
if (error)
|
|
144
|
+
return _jsx("div", { className: "notice error", children: error });
|
|
145
|
+
if (!roles || !policies)
|
|
146
|
+
return _jsx("div", { className: "notice", children: t("Loading roles & permissions…") });
|
|
147
|
+
const activeTabId = resolveRolesTabId({ tabId });
|
|
148
|
+
return (_jsxs("div", { className: "page", children: [_jsx("div", { className: "page-header", children: _jsxs("div", { className: "page-header-text", children: [_jsx("p", { className: "page-kicker", children: t("People") }), _jsx("h1", { className: "page-title", children: t("Roles & Permissions") }), _jsxs("p", { className: "page-description", children: [descriptionPrefix, _jsx("a", { href: usersHref, children: descriptionLinkLabel }), descriptionSuffix] })] }) }), rowError ? _jsx("div", { className: "notice error", children: rowError }) : null, _jsx(TabBar, { ariaLabel: t("Roles & Permissions"), tabs: resolveRolesTabs({ translate: t }), activeId: activeTabId, onChange: (next) => onTabChange({ tabId: resolveRolesTabId({ tabId: next }) }), containerHandle: "roles-tab-bar" }), rolesTabPanel(activeTabId, controller), _jsx(RoleDeleteDialog, { pendingRoleDelete: pendingRoleDelete, setPendingRoleDelete: setPendingRoleDelete, rowSavingId: rowSavingId, t: t, translate: translate, onDeleteRole: onDeleteRole }), _jsx(PolicyDeleteDialog, { pendingPolicyDelete: pendingPolicyDelete, setPendingPolicyDelete: setPendingPolicyDelete, rowSavingId: rowSavingId, t: t, translate: translate, onDeletePolicy: onDeletePolicy }), _jsx(PermissionRemoveDialog, { pendingPermissionRemove: pendingPermissionRemove, setPendingPermissionRemove: setPendingPermissionRemove, removingPermissionId: removingPermissionId, onConfirmRemovePermission: onConfirmRemovePermission, t: t, translate: translate })] }));
|
|
149
|
+
}
|
|
150
|
+
//# sourceMappingURL=Roles.js.map
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import type { Translate } from "@jini-ai/ui/panel-kit";
|
|
2
|
+
import { type AdminPolicy, type AdminPolicyPermission, type AdminRole } from "../../models.js";
|
|
3
|
+
import type { RolesPort } from "../../ports.js";
|
|
4
|
+
export interface PendingPermissionRemove {
|
|
5
|
+
policyId: string;
|
|
6
|
+
row: AdminPolicyPermission;
|
|
7
|
+
}
|
|
8
|
+
export interface RolesController {
|
|
9
|
+
roles: AdminRole[] | null;
|
|
10
|
+
policies: AdminPolicy[] | null;
|
|
11
|
+
error: string | null;
|
|
12
|
+
rowError: string | null;
|
|
13
|
+
roleName: string;
|
|
14
|
+
setRoleName: (name: string) => void;
|
|
15
|
+
roleSaving: boolean;
|
|
16
|
+
roleError: string | null;
|
|
17
|
+
onCreateRole: (e: React.FormEvent) => Promise<void>;
|
|
18
|
+
policyName: string;
|
|
19
|
+
setPolicyName: (name: string) => void;
|
|
20
|
+
policyDescription: string;
|
|
21
|
+
setPolicyDescription: (description: string) => void;
|
|
22
|
+
policySaving: boolean;
|
|
23
|
+
policyError: string | null;
|
|
24
|
+
onCreatePolicy: (e: React.FormEvent) => Promise<void>;
|
|
25
|
+
editingRoleId: string | null;
|
|
26
|
+
setEditingRoleId: (roleId: string | null) => void;
|
|
27
|
+
editingRoleName: string;
|
|
28
|
+
setEditingRoleName: (name: string) => void;
|
|
29
|
+
startEditRole: (role: AdminRole) => void;
|
|
30
|
+
onSaveRole: (required: {
|
|
31
|
+
roleId: string;
|
|
32
|
+
}) => Promise<void>;
|
|
33
|
+
editingPolicyId: string | null;
|
|
34
|
+
setEditingPolicyId: (policyId: string | null) => void;
|
|
35
|
+
editingPolicyName: string;
|
|
36
|
+
setEditingPolicyName: (name: string) => void;
|
|
37
|
+
editingPolicyDescription: string;
|
|
38
|
+
setEditingPolicyDescription: (description: string) => void;
|
|
39
|
+
startEditPolicy: (policy: AdminPolicy) => void;
|
|
40
|
+
onSavePolicy: (required: {
|
|
41
|
+
policyId: string;
|
|
42
|
+
}) => Promise<void>;
|
|
43
|
+
rowSavingId: string | null;
|
|
44
|
+
permissionPolicyId: string | null;
|
|
45
|
+
permissionInput: string;
|
|
46
|
+
setPermissionInput: (permission: string) => void;
|
|
47
|
+
resourceTypeInput: string;
|
|
48
|
+
setResourceTypeInput: (resourceType: string) => void;
|
|
49
|
+
togglePermissionForm: (required: {
|
|
50
|
+
policyId: string;
|
|
51
|
+
}) => void;
|
|
52
|
+
onWritePermission: (required: {
|
|
53
|
+
policyId: string;
|
|
54
|
+
}) => Promise<void>;
|
|
55
|
+
permissionRows: AdminPolicyPermission[];
|
|
56
|
+
permissionsLoading: boolean;
|
|
57
|
+
removingPermissionId: string | null;
|
|
58
|
+
onRemovePermission: (required: {
|
|
59
|
+
policyId: string;
|
|
60
|
+
policyPermissionId: string;
|
|
61
|
+
}) => Promise<void>;
|
|
62
|
+
pendingPermissionRemove: PendingPermissionRemove | null;
|
|
63
|
+
setPendingPermissionRemove: (target: PendingPermissionRemove | null) => void;
|
|
64
|
+
onConfirmRemovePermission: () => Promise<void>;
|
|
65
|
+
pendingRoleDelete: AdminRole | null;
|
|
66
|
+
setPendingRoleDelete: (role: AdminRole | null) => void;
|
|
67
|
+
onDeleteRole: () => Promise<void>;
|
|
68
|
+
pendingPolicyDelete: AdminPolicy | null;
|
|
69
|
+
setPendingPolicyDelete: (policy: AdminPolicy | null) => void;
|
|
70
|
+
onDeletePolicy: () => Promise<void>;
|
|
71
|
+
t: (key: string) => string;
|
|
72
|
+
translate: Translate;
|
|
73
|
+
}
|
|
74
|
+
export interface RolesDependencies {
|
|
75
|
+
port: RolesPort;
|
|
76
|
+
translate: Translate;
|
|
77
|
+
queryScope: string;
|
|
78
|
+
}
|
|
79
|
+
export declare function useRoles(deps: RolesDependencies): RolesController;
|
|
80
|
+
//# sourceMappingURL=use-roles.hooks.d.ts.map
|
|
@@ -0,0 +1,303 @@
|
|
|
1
|
+
import { useRef, useState } from "react";
|
|
2
|
+
import {} from "../../models.js";
|
|
3
|
+
import { useFetchMutation, useFetchQuery } from "@jini-ai/ui/panel-kit";
|
|
4
|
+
import { describeApiError as describeFeatureError, KEYS } from "../rules.js";
|
|
5
|
+
async function runRowDelete(id, mutate, setRowSavingId, setRowError, clearPending, describeError) {
|
|
6
|
+
setRowSavingId(id);
|
|
7
|
+
setRowError(null);
|
|
8
|
+
try {
|
|
9
|
+
await mutate(id);
|
|
10
|
+
}
|
|
11
|
+
catch (e) {
|
|
12
|
+
setRowError(describeError(e));
|
|
13
|
+
}
|
|
14
|
+
finally {
|
|
15
|
+
setRowSavingId((current) => (current === id ? null : current));
|
|
16
|
+
clearPending();
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
export function useRoles(deps) {
|
|
20
|
+
const { port, translate, queryScope } = deps;
|
|
21
|
+
const describeApiError = (error, fallback) => describeFeatureError({ error, fallback, translate });
|
|
22
|
+
const t = translate;
|
|
23
|
+
const boundT = translate;
|
|
24
|
+
const list = useFetchQuery({
|
|
25
|
+
key: [queryScope, ...KEYS.list],
|
|
26
|
+
fetch: async () => {
|
|
27
|
+
const [r, p] = await Promise.all([port.listRoles({}), port.listPolicies({})]);
|
|
28
|
+
return { roles: r.roles, policies: p.policies };
|
|
29
|
+
},
|
|
30
|
+
});
|
|
31
|
+
const roles = list.data?.roles ?? null;
|
|
32
|
+
const policies = list.data?.policies ?? null;
|
|
33
|
+
const error = list.error ? describeApiError(list.error, t("failed to load roles/policies")) : null;
|
|
34
|
+
const [rowError, setRowError] = useState(null);
|
|
35
|
+
const [roleName, setRoleName] = useState("");
|
|
36
|
+
const [policyName, setPolicyName] = useState("");
|
|
37
|
+
const [policyDescription, setPolicyDescription] = useState("");
|
|
38
|
+
const [editingRoleId, setEditingRoleId] = useState(null);
|
|
39
|
+
const [editingRoleName, setEditingRoleName] = useState("");
|
|
40
|
+
const [editingPolicyId, setEditingPolicyId] = useState(null);
|
|
41
|
+
const [editingPolicyName, setEditingPolicyName] = useState("");
|
|
42
|
+
const [editingPolicyDescription, setEditingPolicyDescription] = useState("");
|
|
43
|
+
const [rowSavingId, setRowSavingId] = useState(null);
|
|
44
|
+
const [permissionPolicyId, setPermissionPolicyId] = useState(null);
|
|
45
|
+
const [permissionInput, setPermissionInput] = useState("");
|
|
46
|
+
const [resourceTypeInput, setResourceTypeInput] = useState("");
|
|
47
|
+
const [permissionRows, setPermissionRows] = useState([]);
|
|
48
|
+
const [permissionsLoading, setPermissionsLoading] = useState(false);
|
|
49
|
+
const [removingPermissionId, setRemovingPermissionId] = useState(null);
|
|
50
|
+
const permissionsGenerationRef = useRef(0);
|
|
51
|
+
const permissionPolicyIdRef = useRef(null);
|
|
52
|
+
const [pendingRoleDelete, setPendingRoleDelete] = useState(null);
|
|
53
|
+
const [pendingPolicyDelete, setPendingPolicyDelete] = useState(null);
|
|
54
|
+
const [pendingPermissionRemove, setPendingPermissionRemove] = useState(null);
|
|
55
|
+
const createRoleMutation = useFetchMutation({
|
|
56
|
+
run: ({ input: name }) => port.createRole({ name: name })
|
|
57
|
+
}, {
|
|
58
|
+
invalidates: [[queryScope, ...KEYS.list]],
|
|
59
|
+
});
|
|
60
|
+
const createPolicyMutation = useFetchMutation({
|
|
61
|
+
run: ({ input }) => port.createPolicy({ name: input.name }, { description: input.description })
|
|
62
|
+
}, {
|
|
63
|
+
invalidates: [[queryScope, ...KEYS.list]],
|
|
64
|
+
});
|
|
65
|
+
const saveRoleMutation = useFetchMutation({
|
|
66
|
+
run: ({ input }) => port.updateRole(input)
|
|
67
|
+
}, {
|
|
68
|
+
invalidates: [[queryScope, ...KEYS.list]],
|
|
69
|
+
});
|
|
70
|
+
const deleteRoleMutation = useFetchMutation({
|
|
71
|
+
run: ({ input: id }) => port.deleteRole({ roleId: id })
|
|
72
|
+
}, {
|
|
73
|
+
invalidates: [[queryScope, ...KEYS.list]],
|
|
74
|
+
});
|
|
75
|
+
const savePolicyMutation = useFetchMutation({
|
|
76
|
+
run: ({ input }) => port.updatePolicy({ policyId: input.policyId }, { name: input.name, description: input.description })
|
|
77
|
+
}, {
|
|
78
|
+
invalidates: [[queryScope, ...KEYS.list]],
|
|
79
|
+
});
|
|
80
|
+
const deletePolicyMutation = useFetchMutation({
|
|
81
|
+
run: ({ input: id }) => port.deletePolicy({ policyId: id })
|
|
82
|
+
}, {
|
|
83
|
+
invalidates: [[queryScope, ...KEYS.list]],
|
|
84
|
+
});
|
|
85
|
+
const writePermissionMutation = useFetchMutation({
|
|
86
|
+
run: ({ input }) => port.writePolicyPermission({ policyId: input.policyId, permission: input.permission }, { resourceType: input.resourceType }),
|
|
87
|
+
});
|
|
88
|
+
const removePermissionMutation = useFetchMutation({
|
|
89
|
+
run: ({ input }) => port.removePolicyPermission(input),
|
|
90
|
+
});
|
|
91
|
+
async function onCreateRole(e) {
|
|
92
|
+
e.preventDefault();
|
|
93
|
+
try {
|
|
94
|
+
await createRoleMutation.mutate({ input: roleName });
|
|
95
|
+
setRoleName("");
|
|
96
|
+
}
|
|
97
|
+
catch {
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
async function onCreatePolicy(e) {
|
|
101
|
+
e.preventDefault();
|
|
102
|
+
try {
|
|
103
|
+
await createPolicyMutation.mutate({ input: { name: policyName, description: policyDescription || undefined } });
|
|
104
|
+
setPolicyName("");
|
|
105
|
+
setPolicyDescription("");
|
|
106
|
+
}
|
|
107
|
+
catch {
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
function startEditRole(role) {
|
|
111
|
+
setRowError(null);
|
|
112
|
+
setEditingRoleId(role.id);
|
|
113
|
+
setEditingRoleName(role.name);
|
|
114
|
+
}
|
|
115
|
+
async function onSaveRole({ roleId }) {
|
|
116
|
+
setRowSavingId(roleId);
|
|
117
|
+
setRowError(null);
|
|
118
|
+
try {
|
|
119
|
+
await saveRoleMutation.mutate({ input: { roleId, name: editingRoleName } });
|
|
120
|
+
setEditingRoleId((current) => (current === roleId ? null : current));
|
|
121
|
+
}
|
|
122
|
+
catch (e) {
|
|
123
|
+
setRowError(describeApiError(e, t("failed to rename role")));
|
|
124
|
+
}
|
|
125
|
+
finally {
|
|
126
|
+
setRowSavingId((current) => (current === roleId ? null : current));
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
async function onDeleteRole() {
|
|
130
|
+
if (!pendingRoleDelete)
|
|
131
|
+
return;
|
|
132
|
+
const role = pendingRoleDelete;
|
|
133
|
+
await runRowDelete(role.id, (id) => deleteRoleMutation.mutate({ input: id }), setRowSavingId, setRowError, () => setPendingRoleDelete((current) => (current?.id === role.id ? null : current)), (e) => describeApiError(e, t("failed to delete role")));
|
|
134
|
+
}
|
|
135
|
+
function startEditPolicy(policy) {
|
|
136
|
+
setRowError(null);
|
|
137
|
+
setEditingPolicyId(policy.id);
|
|
138
|
+
setEditingPolicyName(policy.name);
|
|
139
|
+
setEditingPolicyDescription(policy.description ?? "");
|
|
140
|
+
}
|
|
141
|
+
async function onSavePolicy({ policyId }) {
|
|
142
|
+
setRowSavingId(policyId);
|
|
143
|
+
setRowError(null);
|
|
144
|
+
try {
|
|
145
|
+
await savePolicyMutation.mutate({ input: { policyId, name: editingPolicyName, description: editingPolicyDescription } });
|
|
146
|
+
setEditingPolicyId((current) => (current === policyId ? null : current));
|
|
147
|
+
}
|
|
148
|
+
catch (e) {
|
|
149
|
+
setRowError(describeApiError(e, t("failed to update policy")));
|
|
150
|
+
}
|
|
151
|
+
finally {
|
|
152
|
+
setRowSavingId((current) => (current === policyId ? null : current));
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
async function onDeletePolicy() {
|
|
156
|
+
if (!pendingPolicyDelete)
|
|
157
|
+
return;
|
|
158
|
+
const policy = pendingPolicyDelete;
|
|
159
|
+
await runRowDelete(policy.id, (id) => deletePolicyMutation.mutate({ input: id }), setRowSavingId, setRowError, () => setPendingPolicyDelete((current) => (current?.id === policy.id ? null : current)), (e) => describeApiError(e, t("failed to delete policy")));
|
|
160
|
+
}
|
|
161
|
+
async function loadPermissions(policyId) {
|
|
162
|
+
const generation = ++permissionsGenerationRef.current;
|
|
163
|
+
setPermissionsLoading(true);
|
|
164
|
+
try {
|
|
165
|
+
const { policyPermissions } = await port.listPolicyPermissions({ policyId: policyId });
|
|
166
|
+
if (permissionsGenerationRef.current !== generation)
|
|
167
|
+
return;
|
|
168
|
+
setPermissionRows(policyPermissions);
|
|
169
|
+
}
|
|
170
|
+
catch (e) {
|
|
171
|
+
if (permissionsGenerationRef.current !== generation)
|
|
172
|
+
return;
|
|
173
|
+
setPermissionRows([]);
|
|
174
|
+
setRowError(describeApiError(e, t("failed to load permissions")));
|
|
175
|
+
}
|
|
176
|
+
finally {
|
|
177
|
+
if (permissionsGenerationRef.current !== generation)
|
|
178
|
+
return;
|
|
179
|
+
setPermissionsLoading(false);
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
function togglePermissionForm({ policyId }) {
|
|
183
|
+
setRowError(null);
|
|
184
|
+
setPermissionInput("");
|
|
185
|
+
setResourceTypeInput("");
|
|
186
|
+
const closing = permissionPolicyIdRef.current === policyId;
|
|
187
|
+
permissionPolicyIdRef.current = closing ? null : policyId;
|
|
188
|
+
setPermissionPolicyId(permissionPolicyIdRef.current);
|
|
189
|
+
setPermissionRows([]);
|
|
190
|
+
if (!closing)
|
|
191
|
+
void loadPermissions(policyId);
|
|
192
|
+
}
|
|
193
|
+
async function onRemovePermission({ policyId, policyPermissionId }) {
|
|
194
|
+
setRemovingPermissionId(policyPermissionId);
|
|
195
|
+
setRowError(null);
|
|
196
|
+
try {
|
|
197
|
+
await removePermissionMutation.mutate({ input: { policyId, policyPermissionId } });
|
|
198
|
+
if (permissionPolicyIdRef.current === policyId)
|
|
199
|
+
await loadPermissions(policyId);
|
|
200
|
+
}
|
|
201
|
+
catch (e) {
|
|
202
|
+
setRowError(describeApiError(e, t("failed to remove permission")));
|
|
203
|
+
}
|
|
204
|
+
finally {
|
|
205
|
+
setRemovingPermissionId((current) => (current === policyPermissionId ? null : current));
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
async function onConfirmRemovePermission() {
|
|
209
|
+
if (!pendingPermissionRemove)
|
|
210
|
+
return;
|
|
211
|
+
const target = pendingPermissionRemove;
|
|
212
|
+
try {
|
|
213
|
+
await onRemovePermission({ policyId: target.policyId, policyPermissionId: target.row.id });
|
|
214
|
+
}
|
|
215
|
+
finally {
|
|
216
|
+
setPendingPermissionRemove((current) => (current?.row.id === target.row.id ? null : current));
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
async function onWritePermission({ policyId }) {
|
|
220
|
+
if (!permissionInput)
|
|
221
|
+
return;
|
|
222
|
+
const generationAtStart = permissionsGenerationRef.current;
|
|
223
|
+
setRowSavingId(policyId);
|
|
224
|
+
setRowError(null);
|
|
225
|
+
try {
|
|
226
|
+
await writePermissionMutation.mutate({ input: { policyId, permission: permissionInput, resourceType: resourceTypeInput || undefined } });
|
|
227
|
+
if (permissionsGenerationRef.current === generationAtStart) {
|
|
228
|
+
setPermissionInput("");
|
|
229
|
+
setResourceTypeInput("");
|
|
230
|
+
}
|
|
231
|
+
if (permissionPolicyIdRef.current === policyId)
|
|
232
|
+
await loadPermissions(policyId);
|
|
233
|
+
}
|
|
234
|
+
catch (e) {
|
|
235
|
+
setRowError(describeApiError(e, t("failed to add permission")));
|
|
236
|
+
}
|
|
237
|
+
finally {
|
|
238
|
+
setRowSavingId((current) => (current === policyId ? null : current));
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
const roleSaving = createRoleMutation.status === "pending";
|
|
242
|
+
const roleError = createRoleMutation.error ? describeApiError(createRoleMutation.error, t("failed to create role")) : null;
|
|
243
|
+
const policySaving = createPolicyMutation.status === "pending";
|
|
244
|
+
const policyError = createPolicyMutation.error
|
|
245
|
+
? describeApiError(createPolicyMutation.error, t("failed to create policy"))
|
|
246
|
+
: null;
|
|
247
|
+
return {
|
|
248
|
+
roles,
|
|
249
|
+
policies,
|
|
250
|
+
error,
|
|
251
|
+
rowError,
|
|
252
|
+
roleName,
|
|
253
|
+
setRoleName,
|
|
254
|
+
roleSaving,
|
|
255
|
+
roleError,
|
|
256
|
+
onCreateRole,
|
|
257
|
+
policyName,
|
|
258
|
+
setPolicyName,
|
|
259
|
+
policyDescription,
|
|
260
|
+
setPolicyDescription,
|
|
261
|
+
policySaving,
|
|
262
|
+
policyError,
|
|
263
|
+
onCreatePolicy,
|
|
264
|
+
editingRoleId,
|
|
265
|
+
setEditingRoleId,
|
|
266
|
+
editingRoleName,
|
|
267
|
+
setEditingRoleName,
|
|
268
|
+
startEditRole,
|
|
269
|
+
onSaveRole,
|
|
270
|
+
editingPolicyId,
|
|
271
|
+
setEditingPolicyId,
|
|
272
|
+
editingPolicyName,
|
|
273
|
+
setEditingPolicyName,
|
|
274
|
+
editingPolicyDescription,
|
|
275
|
+
setEditingPolicyDescription,
|
|
276
|
+
startEditPolicy,
|
|
277
|
+
onSavePolicy,
|
|
278
|
+
rowSavingId,
|
|
279
|
+
permissionPolicyId,
|
|
280
|
+
permissionInput,
|
|
281
|
+
setPermissionInput,
|
|
282
|
+
resourceTypeInput,
|
|
283
|
+
setResourceTypeInput,
|
|
284
|
+
togglePermissionForm,
|
|
285
|
+
onWritePermission,
|
|
286
|
+
permissionRows,
|
|
287
|
+
permissionsLoading,
|
|
288
|
+
removingPermissionId,
|
|
289
|
+
onRemovePermission,
|
|
290
|
+
pendingPermissionRemove,
|
|
291
|
+
setPendingPermissionRemove,
|
|
292
|
+
onConfirmRemovePermission,
|
|
293
|
+
pendingRoleDelete,
|
|
294
|
+
setPendingRoleDelete,
|
|
295
|
+
onDeleteRole,
|
|
296
|
+
pendingPolicyDelete,
|
|
297
|
+
setPendingPolicyDelete,
|
|
298
|
+
onDeletePolicy,
|
|
299
|
+
t: boundT,
|
|
300
|
+
translate,
|
|
301
|
+
};
|
|
302
|
+
}
|
|
303
|
+
//# sourceMappingURL=use-roles.hooks.js.map
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import type { AdminRole, AdminPolicy } from "../models.js";
|
|
2
|
+
import type { Translate } from "@jini-ai/ui/panel-kit";
|
|
3
|
+
import type { RowMenuItem } from "@jini-ai/admin/react";
|
|
4
|
+
import type { QueryKey } from "@jini-ai/ui/panel-kit";
|
|
5
|
+
export declare const KEYS: {
|
|
6
|
+
list: QueryKey;
|
|
7
|
+
};
|
|
8
|
+
export declare function describeApiError({ error, fallback, translate }: {
|
|
9
|
+
error: unknown;
|
|
10
|
+
fallback: string;
|
|
11
|
+
translate: Translate;
|
|
12
|
+
}): string;
|
|
13
|
+
export interface RoleRowMenuHandlers {
|
|
14
|
+
onRename: (role: AdminRole) => void;
|
|
15
|
+
onDelete: (role: AdminRole) => void;
|
|
16
|
+
}
|
|
17
|
+
export declare function roleMenuItems({ role, handlers, translate }: {
|
|
18
|
+
role: AdminRole;
|
|
19
|
+
handlers: RoleRowMenuHandlers;
|
|
20
|
+
translate: Translate;
|
|
21
|
+
}): RowMenuItem[];
|
|
22
|
+
export interface PolicyRowMenuHandlers {
|
|
23
|
+
onRename: (policy: AdminPolicy) => void;
|
|
24
|
+
onTogglePermissionForm: (required: {
|
|
25
|
+
policyId: string;
|
|
26
|
+
}) => void;
|
|
27
|
+
onDelete: (policy: AdminPolicy) => void;
|
|
28
|
+
}
|
|
29
|
+
export declare function policyMenuItems({ policy, permissionPolicyId, handlers, translate }: {
|
|
30
|
+
policy: AdminPolicy;
|
|
31
|
+
permissionPolicyId: string | null;
|
|
32
|
+
handlers: PolicyRowMenuHandlers;
|
|
33
|
+
translate: Translate;
|
|
34
|
+
}): RowMenuItem[];
|
|
35
|
+
//# sourceMappingURL=rules.d.ts.map
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { describeIdentityError } from "../errors.js";
|
|
2
|
+
export const KEYS = {
|
|
3
|
+
list: ["roles-and-policies"],
|
|
4
|
+
};
|
|
5
|
+
const STATIC_ERROR_MESSAGES = {
|
|
6
|
+
FORBIDDEN: "You do not have permission to do that.",
|
|
7
|
+
RESOURCE_CONFLICT: "It is still in use — remove that assignment/attachment first.",
|
|
8
|
+
PERMISSION_UNKNOWN: "That permission is not recognized.",
|
|
9
|
+
GRANT_EXCEEDS_ISSUER: "You cannot grant a permission you do not hold.",
|
|
10
|
+
};
|
|
11
|
+
export function describeApiError({ error, fallback, translate }) {
|
|
12
|
+
return describeIdentityError({ error, fallback, translate }, { messages: STATIC_ERROR_MESSAGES });
|
|
13
|
+
}
|
|
14
|
+
export function roleMenuItems({ role, handlers, translate }) {
|
|
15
|
+
return [
|
|
16
|
+
{ key: "rename", label: translate("Rename"), onSelect: () => handlers.onRename(role) },
|
|
17
|
+
{ key: "delete", label: translate("Delete"), destructive: true, onSelect: () => handlers.onDelete(role) },
|
|
18
|
+
];
|
|
19
|
+
}
|
|
20
|
+
export function policyMenuItems({ policy, permissionPolicyId, handlers, translate }) {
|
|
21
|
+
return [
|
|
22
|
+
{ key: "rename", label: translate("Rename"), onSelect: () => handlers.onRename(policy) },
|
|
23
|
+
{
|
|
24
|
+
key: "permission",
|
|
25
|
+
label: permissionPolicyId === policy.id ? translate("Close") : translate("Add permission"),
|
|
26
|
+
onSelect: () => handlers.onTogglePermissionForm({ policyId: policy.id }),
|
|
27
|
+
},
|
|
28
|
+
{ key: "delete", label: translate("Delete"), destructive: true, onSelect: () => handlers.onDelete(policy) },
|
|
29
|
+
];
|
|
30
|
+
}
|
|
31
|
+
//# sourceMappingURL=rules.js.map
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { type Translate } from "@jini-ai/ui/panel-kit";
|
|
2
|
+
export declare function rolesDescriptionParts({ translate }: {
|
|
3
|
+
translate: Translate;
|
|
4
|
+
}): {
|
|
5
|
+
prefix: string;
|
|
6
|
+
suffix: string;
|
|
7
|
+
linkLabel: string;
|
|
8
|
+
};
|
|
9
|
+
export declare function roleDeleteBodyParts({ translate }: {
|
|
10
|
+
translate: Translate;
|
|
11
|
+
}): {
|
|
12
|
+
prefix: string;
|
|
13
|
+
suffix: string;
|
|
14
|
+
};
|
|
15
|
+
export declare function policyDeleteBodyParts({ translate }: {
|
|
16
|
+
translate: Translate;
|
|
17
|
+
}): {
|
|
18
|
+
prefix: string;
|
|
19
|
+
suffix: string;
|
|
20
|
+
};
|
|
21
|
+
export declare function permissionRemoveBodyParts({ translate }: {
|
|
22
|
+
translate: Translate;
|
|
23
|
+
}): {
|
|
24
|
+
prefix: string;
|
|
25
|
+
suffix: string;
|
|
26
|
+
};
|
|
27
|
+
//# sourceMappingURL=text.d.ts.map
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { splitOnPlaceholders } from "@jini-ai/ui/panel-kit";
|
|
2
|
+
export function rolesDescriptionParts({ translate }) {
|
|
3
|
+
const [prefix = "", suffix = ""] = splitOnPlaceholders({ template: translate("Roles and policies grant access to operator users. Assign a role or policy to a specific user from the {users} screen."), tokens: ["{users}"] });
|
|
4
|
+
return { prefix, suffix, linkLabel: translate("Users") };
|
|
5
|
+
}
|
|
6
|
+
function nameParts(translate, template) {
|
|
7
|
+
const [prefix = "", suffix = ""] = splitOnPlaceholders({ template: translate(template), tokens: ["{name}"] });
|
|
8
|
+
return { prefix, suffix };
|
|
9
|
+
}
|
|
10
|
+
export function roleDeleteBodyParts({ translate }) { return nameParts(translate, 'Delete role "{name}"?'); }
|
|
11
|
+
export function policyDeleteBodyParts({ translate }) { return nameParts(translate, 'Delete policy "{name}"?'); }
|
|
12
|
+
export function permissionRemoveBodyParts({ translate }) { return nameParts(translate, 'Remove "{name}" from this policy? Anyone with this policy loses it.'); }
|
|
13
|
+
//# sourceMappingURL=text.js.map
|