Permissions are a fixed list from plugin code; grant them on Users and Groups
This commit is contained in:
@@ -2,7 +2,7 @@
|
||||
Group admin detail / membership page: the group-detail body in the app shell.
|
||||
%><%
|
||||
const nav = include("partials/nav-tree", { nodes: chrome.nav });
|
||||
const body = include("partials/group-detail-body", { add: model.add, csrfToken: model.csrfToken, del: model.delete, error: model.error, group: model.group, members: model.members });
|
||||
const body = include("partials/group-detail-body", { add: model.add, csrfToken: model.csrfToken, del: model.delete, error: model.error, group: model.group, members: model.members, permissions: model.permissions });
|
||||
-%>
|
||||
<%- include("partials/shell", {
|
||||
body,
|
||||
|
||||
@@ -36,6 +36,9 @@
|
||||
<p class="cell-muted"><%= t("admin.groups.allMembers") %></p>
|
||||
<% } -%>
|
||||
</section>
|
||||
<% if (locals.permissions) { -%>
|
||||
<%- include("partials/permission-picker", { csrfToken: csrf, permissions: locals.permissions }) %>
|
||||
<% } -%>
|
||||
<section class="form-card admin-actions" aria-label="<%= t("admin.groups.actions") %>">
|
||||
<a class="btn btn-danger" href="<%= localeHref(del.action) %>"><svg class="ico ico-sm" aria-hidden="true"><use href="#i-trash"/></svg><%= t("admin.groups.delete") %></a>
|
||||
</section>
|
||||
|
||||
@@ -1,57 +0,0 @@
|
||||
<%#
|
||||
Admin permission detail body, captured into the shell content slot. Config:
|
||||
permission { name }
|
||||
members { action, rows: { kind:"group"|"identity", label, subject }[] } action = revoke endpoint
|
||||
effective { label }[] users who hold the permission (expand)
|
||||
add { action, options: {label,value}[] } action = assign endpoint
|
||||
del { action } delete the whole permission
|
||||
csrfToken, error?
|
||||
%><%
|
||||
const permission = locals.permission;
|
||||
const members = locals.members;
|
||||
const effective = locals.effective;
|
||||
const add = locals.add;
|
||||
const del = locals.del;
|
||||
const csrf = locals.csrfToken;
|
||||
-%>
|
||||
<div class="form-page">
|
||||
<% if (locals.error) { -%>
|
||||
<%- include("partials/alert", { text: locals.error, tone: "neg" }) %>
|
||||
<% } -%>
|
||||
<section class="form-card" aria-labelledby="members-h">
|
||||
<h2 class="card-title" id="members-h"><%= t("admin.permissions.assignedTo") %></h2>
|
||||
<% if (members.rows.length) { -%>
|
||||
<div class="table-wrap"><table class="table"><caption class="sr-only"><%= t("admin.groups.membersOf", { name: permission.name }) %></caption><thead><tr><th scope="col"><%= t("admin.common.member") %></th><th scope="col"><%= t("admin.common.type") %></th><th class="col-actions" scope="col"><span class="sr-only"><%= t("table.actions") %></span></th></tr></thead><tbody>
|
||||
<% members.rows.forEach((m) => { -%>
|
||||
<tr><th scope="row"><span class="cell-strong"><%= m.label %></span></th><td><span class="badge info"><span class="dot"></span><%= m.kind === "group" ? t("admin.common.group") : t("admin.common.user") %></span></td><td class="col-actions"><form method="post" action="<%= localeHref(members.action) %>"><input type="hidden" name="_csrf" value="<%= csrf %>"><input type="hidden" name="member" value="<%= m.subject %>"><button class="btn" type="submit"><svg class="ico ico-sm" aria-hidden="true"><use href="#i-x"/></svg><%= t("admin.permissions.revoke") %></button></form></td></tr>
|
||||
<% }) -%>
|
||||
</tbody></table></div>
|
||||
<% } else { -%>
|
||||
<p class="cell-muted"><%= t("admin.permissions.noMembers") %></p>
|
||||
<% } -%>
|
||||
</section>
|
||||
<section class="form-card" aria-labelledby="effective-h">
|
||||
<h2 class="card-title" id="effective-h"><%= t("admin.permissions.effective") %></h2>
|
||||
<p class="field-hint"><%= t("admin.permissions.effectiveHint") %></p>
|
||||
<% if (effective.length) { -%>
|
||||
<ul class="plain-list">
|
||||
<% effective.forEach((u) => { -%>
|
||||
<li><span class="cell-strong"><%= u.label %></span></li>
|
||||
<% }) -%>
|
||||
</ul>
|
||||
<% } else { -%>
|
||||
<p class="cell-muted"><%= t("admin.permissions.noEffective") %></p>
|
||||
<% } -%>
|
||||
</section>
|
||||
<section class="form-card" aria-labelledby="add-h">
|
||||
<h2 class="card-title" id="add-h"><%= t("admin.permissions.assign") %></h2>
|
||||
<% if (add.options.length) { -%>
|
||||
<form class="inline-form" method="post" action="<%= localeHref(add.action) %>"><input type="hidden" name="_csrf" value="<%= csrf %>"><label class="sr-only" for="add-member"><%= t("admin.common.member") %></label><span class="select"><select id="add-member" name="member" required><option value="" disabled selected><%= t("admin.common.chooseMember") %></option><% add.options.forEach((o) => { %><option value="<%= o.value %>"><%= o.label %></option><% }) %></select></span><button class="btn btn-primary" type="submit"><svg class="ico ico-sm" aria-hidden="true"><use href="#i-plus"/></svg><%= t("admin.permissions.assignAction") %></button></form>
|
||||
<% } else { -%>
|
||||
<p class="cell-muted"><%= t("admin.permissions.allAssigned") %></p>
|
||||
<% } -%>
|
||||
</section>
|
||||
<section class="form-card admin-actions" aria-label="<%= t("admin.permissions.actions") %>">
|
||||
<a class="btn btn-danger" href="<%= localeHref(del.action) %>"><svg class="ico ico-sm" aria-hidden="true"><use href="#i-trash"/></svg><%= t("admin.permissions.delete") %></a>
|
||||
</section>
|
||||
</div>
|
||||
@@ -1,26 +0,0 @@
|
||||
<%#
|
||||
Admin permission create form body, captured into the shell content slot. Config:
|
||||
form { action, csrfToken, submitLabel, cancelHref, nameField: field.ejs config,
|
||||
memberOptions: {label,value}[], selectedMember }
|
||||
error? string shown when a write was rejected
|
||||
%><%
|
||||
const form = locals.form;
|
||||
-%>
|
||||
<div class="form-page">
|
||||
<% if (locals.error) { -%>
|
||||
<%- include("partials/alert", { text: locals.error, tone: "neg" }) %>
|
||||
<% } -%>
|
||||
<form class="form-card" method="post" action="<%= localeHref(form.action) %>">
|
||||
<input type="hidden" name="_csrf" value="<%= form.csrfToken %>">
|
||||
<%- include("partials/field", form.nameField) %>
|
||||
<div class="field">
|
||||
<label for="member"><%= t("admin.permissions.assignTo") %></label>
|
||||
<span class="select"><select id="member" name="member" required><option value="" disabled<% if (!form.selectedMember) { %> selected<% } %>><%= t("admin.common.chooseMember") %></option><% form.memberOptions.forEach((o) => { %><option value="<%= o.value %>"<% if (form.selectedMember === o.value) { %> selected<% } %>><%= o.label %></option><% }) %></select></span>
|
||||
<span class="field-hint">A permission exists once assigned; add more users or groups after creating it.</span>
|
||||
</div>
|
||||
<div class="form-actions">
|
||||
<a class="btn" href="<%= localeHref(form.cancelHref) %>"><%= t("common.cancel") %></a>
|
||||
<button class="btn btn-primary" type="submit"><%= form.submitLabel %></button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
@@ -0,0 +1,26 @@
|
||||
<%#
|
||||
The permission picker, shared by the user-edit and group-detail pages. A fieldset of checkboxes —
|
||||
one per permission the installed plugins declare — ticked where this user/group already holds it.
|
||||
The whole set posts back, so what is submitted IS the desired state (see admin-grants.ts).
|
||||
Locals: csrfToken, permissions ({ action, choices, empty, field, legend, submit }).
|
||||
%>
|
||||
<section class="form-card" aria-labelledby="permissions-h">
|
||||
<h2 class="card-title" id="permissions-h"><%= permissions.legend %></h2>
|
||||
<% if (permissions.empty) { -%>
|
||||
<p class="cell-muted"><%= permissions.empty %></p>
|
||||
<% } else { -%>
|
||||
<form method="post" action="<%= localeHref(permissions.action) %>">
|
||||
<input type="hidden" name="_csrf" value="<%= csrfToken %>">
|
||||
<fieldset class="check-list">
|
||||
<legend class="sr-only"><%= permissions.legend %></legend>
|
||||
<% permissions.choices.forEach((c, i) => { -%>
|
||||
<div class="check-row">
|
||||
<input type="checkbox" id="perm-<%= i %>" name="<%= permissions.field %>" value="<%= c.name %>"<%= c.checked ? " checked" : "" %>>
|
||||
<label for="perm-<%= i %>"><span class="cell-strong"><%= c.name %></span><% if (c.description) { %><span class="cell-muted"><%= c.description %></span><% } %></label>
|
||||
</div>
|
||||
<% }) -%>
|
||||
</fieldset>
|
||||
<button class="btn btn-primary" type="submit"><svg class="ico ico-sm" aria-hidden="true"><use href="#i-check-circle"/></svg><%= permissions.submit %></button>
|
||||
</form>
|
||||
<% } -%>
|
||||
</section>
|
||||
@@ -26,6 +26,9 @@
|
||||
<button class="btn btn-primary" type="submit"><%= form.submitLabel %></button>
|
||||
</div>
|
||||
</form>
|
||||
<% if (edit && locals.permissions) { -%>
|
||||
<%- include("partials/permission-picker", { csrfToken: form.csrfToken, permissions: locals.permissions }) %>
|
||||
<% } -%>
|
||||
<% if (edit) { -%>
|
||||
<section class="form-card admin-actions" aria-label="<%= t("admin.users.actions") %>">
|
||||
<form method="post" action="<%= localeHref(edit.recoveryAction) %>"><input type="hidden" name="_csrf" value="<%= form.csrfToken %>"><button class="btn" type="submit"><svg class="ico ico-sm" aria-hidden="true"><use href="#i-mail"/></svg><%= t("admin.users.recovery.generate") %></button></form>
|
||||
|
||||
@@ -1,16 +0,0 @@
|
||||
<%#
|
||||
Permission admin detail page: the permission-detail body (members · effective access) in the shell.
|
||||
%><%
|
||||
const nav = include("partials/nav-tree", { nodes: chrome.nav });
|
||||
const body = include("partials/permission-detail-body", { add: model.add, csrfToken: model.csrfToken, del: model.delete, effective: model.effective, error: model.error, members: model.members, permission: model.permission });
|
||||
-%>
|
||||
<%- include("partials/shell", {
|
||||
body,
|
||||
brand: chrome.brand,
|
||||
breadcrumbs: model.breadcrumbs,
|
||||
csrfToken: chrome.csrfToken,
|
||||
nav,
|
||||
theme: chrome.theme,
|
||||
title: model.title,
|
||||
user: chrome.user,
|
||||
}) %>
|
||||
@@ -1,16 +0,0 @@
|
||||
<%#
|
||||
Permission admin create page: the permission-form body captured into the app shell.
|
||||
%><%
|
||||
const nav = include("partials/nav-tree", { nodes: chrome.nav });
|
||||
const body = include("partials/permission-form-body", { error: model.error, form: model.form });
|
||||
-%>
|
||||
<%- include("partials/shell", {
|
||||
body,
|
||||
brand: chrome.brand,
|
||||
breadcrumbs: model.breadcrumbs,
|
||||
csrfToken: chrome.csrfToken,
|
||||
nav,
|
||||
theme: chrome.theme,
|
||||
title: model.title,
|
||||
user: chrome.user,
|
||||
}) %>
|
||||
@@ -1,21 +0,0 @@
|
||||
<%#
|
||||
Permissions admin list: the same building blocks as the Groups screen, around the shell, backed
|
||||
by live Keto Permission subject sets (admin-permissions.ts). Filter/sort/page round-trip the URL.
|
||||
%><%
|
||||
const nav = include("partials/nav-tree", { nodes: chrome.nav });
|
||||
const filters = include("partials/filter-bar", model.filterBar);
|
||||
const table = include("partials/data-table", model.table);
|
||||
const pager = include("partials/pagination", model.pagination);
|
||||
const actions = '<a class="btn btn-primary" href="' + localeHref("/admin/permissions/new") + '"><svg class="ico ico-sm" aria-hidden="true"><use href="#i-plus"/></svg>' + t("admin.permissions.new") + '</a>';
|
||||
-%>
|
||||
<%- include("partials/shell", {
|
||||
actions,
|
||||
body: filters + table + pager,
|
||||
brand: chrome.brand,
|
||||
breadcrumbs: model.breadcrumbs,
|
||||
csrfToken: chrome.csrfToken,
|
||||
nav,
|
||||
theme: chrome.theme,
|
||||
title: model.title,
|
||||
user: chrome.user,
|
||||
}) %>
|
||||
@@ -2,7 +2,7 @@
|
||||
Users admin create/edit page: the user-form body captured into the app shell.
|
||||
%><%
|
||||
const nav = include("partials/nav-tree", { nodes: chrome.nav });
|
||||
const body = include("partials/user-form-body", { edit: model.edit, error: model.error, form: model.form, recovery: model.recovery });
|
||||
const body = include("partials/user-form-body", { edit: model.edit, error: model.error, form: model.form, permissions: model.permissions, recovery: model.recovery });
|
||||
-%>
|
||||
<%- include("partials/shell", {
|
||||
body,
|
||||
|
||||
Reference in New Issue
Block a user