Give the paging loop, Link-header parser and FieldConfig one home each, and delete a group from its parents #235
+4
-2
@@ -32,8 +32,10 @@ and answers with the error page. It used to leave some grants gone and the membe
|
||||
|
||||
### The admin example removes a deleted group from the groups it was nested in
|
||||
|
||||
Deleting a group now also deletes the tuples that made it a member of another group, in the same
|
||||
transaction. It used to leave them, so a group re-created with the same name was back in every parent.
|
||||
Deleting a group now also removes it from every group it was a member of, in the same Keto
|
||||
transaction as the rest. It used to stay listed there, so a group re-created with the same name was
|
||||
back in every parent. A parent whose only member was the deleted group is left with no members, and
|
||||
so no longer exists as a group.
|
||||
|
||||
### The admin example keeps a user's permissions section when it refuses a self-deactivate or self-delete
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
// A group is a Keto subject set `Group:<name>#members`; a member is a user or a nested group (see
|
||||
// parseSubject). Writes go only to Keto (README "stateless"). Keto has no "create object" — a group
|
||||
// exists exactly while it has ≥1 member, so create writes its first-member tuple and delete removes
|
||||
// every member tuple. Pure builders turn tuples + the request URL into view models; below them are thin
|
||||
// every tuple that makes it a group or puts it in another. Pure builders turn tuples + the request URL into view models; below them are thin
|
||||
// per-route handlers (keyed on ctx.params) over a shared `withGroups` gate — admin-only, CSRF-guarded,
|
||||
// each returning a RouteResult.
|
||||
|
||||
@@ -428,8 +428,10 @@ export const groupsDelete = withGroupName(async (deps, name) => {
|
||||
const lost = await lostOnDelete(deps, name);
|
||||
if (lost.length > 0) return refuseSelfLockout(deps, name, "delete", lost);
|
||||
// A Keto set exists only through its tuples, so leaving the grants or the parents' membership
|
||||
// tuples behind would resurrect every permission and nesting the moment someone re-created a
|
||||
// group with the same name.
|
||||
// tuples behind would resurrect every permission and parent membership the moment someone
|
||||
// re-created a group with the same name. A parent that had only this group as a member is left
|
||||
// with none and so ceases to exist, as when its last member is removed by hand; its own grants
|
||||
// and parent memberships stay (todo.md).
|
||||
const subject = groupSubject(name);
|
||||
const held = await heldPermissions(keto, subject);
|
||||
const grants = held.map((permission) => grantTuple(permission, subject));
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
|
||||
`Bar = 1`
|
||||
|
||||
`Next ID = 72`
|
||||
`Next ID = 73`
|
||||
|
||||
## Items
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
| 19 | 0.6.0 | | question | **Run the documented Docker commands on macOS and fix whatever misbehaves.** | 2 | 2 | 1 | 3 | 3 | 1 by 3 | 3.3 |
|
||||
| 16 | 0.6.0 | | question | **Decide whether the single Keto `Resource` namespace splits into per-domain namespaces.** | 3 | 2 | 3 | 6 | 6 | 7 by 2 | 0.5 |
|
||||
| 65 | 0.6.0 | | | **Export `KetoError` from `@plainpages/plugin-api`, so a plugin can tell a Keto failure apart with `instanceof`.** | 1 | 2 | 1 | 2 | 1 | 7 by 3, 1 by 1 | 6.7 |
|
||||
| 72 | 0.6.0 | | | **Delete a group's grants and parent memberships when its last member leaves, by group delete or by removing the member, so re-creating the name restores none.** | 2 | 2 | 2 | 4 | 3 | 2 by 4 | 4.2 |
|
||||
| 67 | 0.6.0 | | | **Group `src/auth`'s modules into one subfolder per concern, with `bootstrap.ts` staying at `src/auth/bootstrap.ts`.** | 3 | 1 | 2 | 4 | 6 | code health by 4 | 4.0 |
|
||||
| 14 | 0.6.0 | | | **Drop a double-clicked duplicate submit, with no client-side JavaScript.** | 4 | 2 | 3 | 7 | 7 | 2 by 7, 1 by 4 | 1.4 |
|
||||
|
||||
@@ -43,6 +44,28 @@ surface, so this adds to the plugin API: it ships in a minor, and `HOST_API_VERS
|
||||
`0.6.0` when 0.6.0 is tagged (AGENTS.md → Rules). Adding is safe for installed plugins; removing it
|
||||
later would take a major.
|
||||
|
||||
### 72. Delete a group's grants and parent memberships when its last member leaves, by group delete or by removing the member, so re-creating the name restores none.
|
||||
|
||||
In `examples/plugins/auth-admin/admin-groups.ts` a group exists exactly while it has a
|
||||
`Group:<name>#members` tuple. Two paths empty a group but keep its other tuples, so re-creating the
|
||||
name silently hands every new member the old permissions and parent memberships:
|
||||
|
||||
1. `groupsDelete` of a group that is a parent's only member: the parent ceases to exist but keeps
|
||||
its `Permission:*#granted@Group:<parent>#members` grants and its memberships in grandparents.
|
||||
2. `groupsRemoveMember` removing a group's last member, which today is a lone `deleteTuple`.
|
||||
|
||||
Fix both in one `transactTuples` with the removal: when the removal leaves a group with no members,
|
||||
add its grants and the tuples naming it as a member of other groups to the delete. The cascade is
|
||||
transitive: a grandparent emptied by its parent vanishing is the same bug one level up. The walk
|
||||
stops at a group that keeps any member, so a nesting cycle (`groupsAddMember` refuses only a
|
||||
self-nest) ends it. An emptied group grants nobody, so the cascade cannot change what
|
||||
`lostToSelf` computes. Test it with nesting two levels deep and with a cycle.
|
||||
|
||||
When it ships, rewrite the comment at `groupsDelete` that points here, and the CHANGELOG entry "The
|
||||
admin example removes a deleted group from the groups it was nested in", which then says a parent
|
||||
left with no members no longer exists as a group. `lostToSelf`, in this file, is a place the seats
|
||||
found hard; if the change touches it, make it easier to read.
|
||||
|
||||
### 67. Group `src/auth`'s modules into one subfolder per concern, with `bootstrap.ts` staying at `src/auth/bootstrap.ts`.
|
||||
|
||||
`src/auth` holds 19 modules over several concerns: the per-request session path (`jwt*`, `jwks`,
|
||||
|
||||
Reference in New Issue
Block a user