User Journey: Know what I may do

Table of Contents

This page documents a user journey: A member sees the roles they hold and the permissions those roles carry.

A user journey describes what one person is trying to do, before anything is built. It is a design document that outlives any sprint, so it names the screens, the entity fields each screen shows, and the operations and messages each step needs.

1. Goal

"I want to see what I am allowed to do, and where it comes from." The member reads the roles on their account and the permissions those roles carry. The journey is done when the member can name every role they hold, and answer "may I do this?" for any action the system offers.

2. Actor and trigger

The member: an account of type user, signed in, reading their own record. The trigger is a refusal — a screen answers "permission denied" — or curiosity from the account menu. It starts when the member opens My access, and ends when they close it. Nothing is written, so the journey keeps no server-side state.

This is the read half of the access topic. An administrator changes the same records for somebody else — see Change someone's access.

3. Steps

  1. Open my access. The member chooses My access. The system reads the roles assigned to the caller's account and lists them with their descriptions.
  2. Read the roles. Each row shows the role name and what the role is for. The member's identity in the header comes from the session established at sign-in, not from a second read.
  3. Read the permissions. The system reads the effective permissions of the account and lists the codes. Each code follows component::resource:action, so the member reads the component and the action from the code alone.
  4. Follow a permission back. The member picks a permission and asks which role grants it. The system reads the permissions of the held roles and answers.
  5. Leave. The member closes the screen. No change has been made.

4. Screens and wireframes

One screen, read-only. Three panels: the caller's identity, the roles they hold, and the permissions those roles carry. The member thinks of the three as one answer to one question, so they are panels of one screen rather than separate screens.

wireframe_know_what_i_may_do.png

Figure 1: My access: who I am, the roles I hold, and the permissions they carry.

5. Entities composed

Entity What it contributes Model
account The signed-in identity in the header: username and full name ores.iam.account
role The roles the account holds: name and description ores.iam.role
permission The atomic codes the roles bundle: code and description ores.iam.permission
account_role The junction row that links the account to each role no entity model — junction table
role_permission The junction row that links each role to its permissions no entity model — junction table

The two junctions have no entity model of their own: they are many-to-many links, and the joined shape is a query result rather than an entity. There is also no module entity and no module table. The component segment of the code — iam in iam::accounts:read — is what plays the part a module would play.

No credential field appears on this screen. password_hash, password_salt, service_password_hash, totp_secret and cleartext passwords stay on the server, and no read in this journey returns them.

6. Operations and messages

Step Operation Subject Status
Open my access Read the roles I hold, with the permissions each grants and its assignment record iam.v1.ops.get_my_roles exists
Follow a permission back Read the permissions one role bundles iam.v1.ops.get_role_permissions exists
Another account's access, which this journey does not ask for Read the same shape for an account the caller names iam.v1.ops.get_account_roles exists

The reads are defined in ores.iam.authorization_messages, and Authorised reads states the rule they follow. iam.v1.ops.get_my_roles takes no account id, so it cannot name anybody else: the account is the authenticated caller's, and the read needs no permission beyond a session. The privileged read still takes an id and now requires iam::roles:read before it looks the account up.

7. What is missing

The screen runs in ores.web as My access: the roles held with who gave each one and why, a Can I…? search that names the role behind a yes, and the permissions by area. A role that grants everything says so instead of listing every code (Implement the access journeys in ores.web).

Nothing. All three gaps this journey recorded have closed, and the build that closed them is recorded on Implement the access journeys in ores.web.

iam.v1.ops.get_my_roles answers one composed shape: each role the caller holds, the permission codes that role grants, and the account_role junction's own record of who granted it, when and why. So the three gaps close together rather than in turn. The read attributes a permission to the role it arrives through, which the flat list could not; it carries the assignment tail, which no subject returned; and it is scoped to the caller by construction rather than by a comparison that has to be right.

A refusal is worth stating separately, because it is what the composed read needed before it could be honest. iam.v1.ops.get_role_permissions-by-account answered a flat list and nothing else, so an empty list meant both "you may read nothing" and "this account holds nothing". The read that replaced it carries a result, and a caller who may not have what it asked for is told denied with the permission that was required. An account that holds no roles answers ok with no rows, which is a different answer.

8. Related journeys

9. Related knowledge

10. See also

Emacs 29.3 (Org mode 9.6.15)