You are here: start » en » Internal Documentation » DokuWiki extensions (WvdS) » Roles and permissions » Role matrix

Role matrix

The complete account of what each role in this wiki allows and what it does not, layer by layer. This page is the one source for permission values; every other page links here rather than restating them.

Scope

Layer Grants Stored in Evaluated by
1 DokuWiki ACL numbers 0–255 per page or namespace conf/acl.auth.php DokuWiki core
2 Session assurance AUTHENTICATED, MAIL_OTP, TOTP, MFA_ANY conf/wkidentity-policy.json wkidentity
3 Project role none, reader, contributor, maintainer, admin groups in the user manager wkdocore
4 Component tiers differs per component ACL or a policy file of its own the component itself

The layers are asked in this order, and each may only refuse. A role on layer 3 cannot grant what layer 1 has denied.

Layer 1: the DokuWiki ACL

The base ladder. It applies to every page and every namespace and is the only layer that works without any component of this house.

The ladder

Value Name Allows Does not allow
0 No access nothing; the page behaves as though it did not exist any read, including attachments and feeds
1 Read view the page, history and diffs, export, subscribe any change, including creating a subpage
2 Edit additionally: change existing pages, drafts, revert creating new pages, uploading files
4 Create additionally: create new pages in the namespace uploading files, deleting files
8 Upload additionally: put files into the media area deleting uploaded files again
16 Delete additionally: delete files in the media area configuration, user manager, extensions
255 Administration everything, bypassing every ACL rule
The values 4, 8 and 16 apply to namespace rules only. On a single page 2 is the highest meaningful value; higher numbers on a page rule behave like 2.

How a value is arrived at

DokuWiki evaluates the most specific rule, not the highest. In order:

  1. A rule for this account on this page.
  2. A rule for a group of this account on this page — where several apply, the highest wins.
  3. The same two steps in the next namespace up, all the way to the root.
  4. Nothing matches: no access.

An account rule beats every group rule at the same level, even a lower one. That is how a single account is kept out of an area its group has open.

The groups of this wiki

Group Who is in it Meaning
@ALL every visitor, signed in or not the base value; at 0 the area is not public
@user every signed-in account “being signed in is enough”
@admin the superuser group ($conf['superuser']) bypasses layer 1 entirely
@wvds editorial contributors write access on the language and content namespaces
@wvdse technical maintenance of the source mounts full access to the mounted repositories
@dwdo_<project>_readers readers of a project see layer 3
@dwdo_<project>_contributors contributors of a project see layer 3
@dwdo_<project>_maintainers maintainers of a project see layer 3

Layer 2: session assurance

This layer does not ask who you are but how well this one session has shown it. It is the most common reason for being signed in and turned away all the same.

Level Means Reached by Not enough for
AUTHENTICATED signed in, password is enough ordinary sign-in areas that require a factor
MAIL_OTP one-time code by e-mail confirmed entering the code from the e-mail areas that explicitly require TOTP
TOTP authenticator app confirmed six-digit code from the app
MFA_ANY some second factor confirmed MAIL_OTP or TOTP

What a requirement can be bound to

A requirement is bound to a scope. Eight kinds exist:

  • namespace — every page under a prefix.
  • plugin, module, view — a component, a module within it, a single view.
  • command, adminAction — a command, or an administrative action.
  • group, user — bound to the requester rather than to the target: “this group always needs TOTP”.

Where several apply, the highest holds. A session that has proved MAIL_OTP satisfies MFA_ANY and MAIL_OTP, but not TOTP.

The three older lists

List Effect Blocks?
required_users the profile page and a site banner nag until enrolment no, a prompt only
email_users may use the e-mail code even where it is generally off no, only widens
required_namespaces read and write are refused until a factor is enrolled yes

email_users can only add a method, never take one away. A missing or unreadable policy file can therefore never lock anybody out.

Layer 3: the project role

Inside a project a ladder of its own applies. It decides which hubs appear in the navigation and who may change a project's membership.

Role Level Allows Does not allow
none 0 nothing any project area
reader 10 view project areas and hubs change content, see members
contributor 20 additionally: create and change the project's content manage members, change settings
maintainer 30 additionally: manage members, project settings appoint maintainers, global configuration
admin 40 everything
admin is the global superuser of the wiki, not a project-scoped role. There is no project-scoped administration right: the highest role obtainable within a project is maintainer. Maintainers are appointed by the global administration.

Where the role comes from

The group overlay first, then the ACL. First match wins:

  1. Superuser → admin.
  2. Member of @dwdo_<project>_maintainersmaintainer.
  3. Member of @dwdo_<project>_contributorscontributor.
  4. Member of @dwdo_<project>_readersreader.
  5. Otherwise: translate the ACL value on the project root — from 16 → maintainer, from 2 → contributor, from 1 → reader, else none.

The prefix dwdo_ is configurable (roleprefix). The role is always determined at the project root, never at the page you happen to be viewing — it is a property of the project, not of the request.

Step 5 is why a project works with no overlay groups at all: it then runs purely off the namespace ACL. It is equally why an ACL value of 16 on a project root makes somebody a maintainer without any group saying so visibly.

What the navigation hides

Hubs your role does not reach are hidden from the navigation rail. That is a display decision, not a boundary: the target page checks its own permission. A missing menu entry therefore means “there is nothing here for you”, not “something is hidden here”.

Layer 4: component tiers

Some components additionally run a ladder of their own, because their subject is not a wiki page.

Mounted source repositories

Four rungs of one ladder, expressed in the same numbers as layer 1 — so there is no second permission store, only one ACL line per group on the mount point.

Capability ACL value Allows Does not allow
view 1 browse and view inside the wiki clone, download
fetch 2 additionally: clone and download an archive push changes of your own
propose 4 additionally: push under your own ref prefix write to other people's refs
write 8 additionally: push anywhere the repository allows administer mount points

New mount points come with four presets:

Tier Who sees it Who writes
private nobody but the technical maintenance technical maintenance
readonly every signed-in account (fetch) technical maintenance
public everybody, signed in or not (fetch) technical maintenance
team the project's readers (view) contributors fetch, maintainers write

team is the only tier whose groups depend on the project. It is the right choice as soon as several closed projects sit side by side: the other three know only “nobody”, “everybody signed in” and “everybody”.

SQLite Data Studio

A capability matrix rather than a ladder, because the six capabilities really are independent.

Capability Allows
use open the Studio at all
query run reading queries
write change data
design change the schema
export take results out
admin manage connections and the matrix itself

Resolution, first match wins:

  1. Superuser → always allowed.
  2. With dbadmin_readonly at 1, write and design are refused — even where the matrix grants them.
  3. A rule for the connection beats a global one; within one scope an account rule beats every group rule. Group rules at the same scope are unioned.
  4. No matching rule → refused.

Actions that require a factor

Several components additionally require a session assurance per layer 2 for individual actions, whatever permission the account holds.

Component Action Requires
Vault read AUTHENTICATED
Vault create, rotate, delete, rekey MFA_ANY
SQLite Data Studio code-write, connection-admin, db-destructive MFA_ANY
Resources create, update AUTHENTICATED
Resources delete MFA_ANY
Azure DevOps bridge secret-write, remove MFA_ANY
Source mounts token-issue AUTHENTICATED
Source mounts mount-admin MFA_ANY
Blog content-admin MFA_ANY

Who maintains what

Layer Screen Who may go there
1 ACL ?do=adminAccess control superuser
1 an account's groups ?do=adminUser manager superuser
2 session assurance ?do=adminIdentity superuser
3 project members project page → Members the project's maintainers
4 mount tiers ?do=adminSource mounts superuser
4 capability matrix ?do=adminSQLite Data StudioPermissions whoever holds admin in the Studio

A project's membership screen is the only one that needs no global administration. That is why it is the destination an access request about a project is routed to — see Where a request goes.

The contract for components

Your own component hangs its refusal on the same infrastructure instead of writing a sentence of its own. Two calls are enough.

A refusal about a missing permission:

$req = plugin_load('helper', 'wkrequest');
if (is_object($req) && method_exists($req, 'accessPanel')) {
    echo $req->accessPanel([
        'resourceLabel' => $title,
        'required'      => $req->permissionLabel(AUTH_READ),
        'current'       => $req->currentPermissionLabel($pageId),
        'draft'         => [
            'type'     => 'ACCESS_REQUEST',
            'owner'    => 'myplugin',
            'handler'  => 'myplugin:settings',
            'resource' => $pageId,
        ],
        'alternatives'  => [['label' => $otherRoute, 'href' => $url]],
    ]);
}

A refusal about a missing second factor:

echo $req->stepUpPanel([
    'requirement' => 'MFA_ANY',
    'origin'      => 'myplugin:delete',
    'back'        => $backUrl,
]);

Sign-in, “my open requests” and the way back are appended without the call site having to remember them; each reports itself unavailable in the situation it does not fit and is then left out.

So that a request reaches the right place, the component registers its administrative screen:

$controller->register_hook('WKREQUEST_REGISTER_HANDLERS', 'BEFORE', $this, 'registerRequestHandlers');
// ...
$event->data['handlers'][] = [
    'owner' => 'myplugin',
    'id'    => 'settings',
    'label' => $this->getLang('menu'),
    'url'   => wl('', ['do' => 'admin', 'page' => 'myplugin'], true, '&'),
    'types' => ['ACCESS_REQUEST', 'ADMIN_ACTION_REQUEST'],
];

Where a component knows the closed area better than the core does, it sharpens the panel instead of drawing a second one beside it:

$controller->register_hook('WKREQUEST_REFINE_DENIED', 'BEFORE', $this, 'refineDeniedRequest');

$event->data then carries id, resourceLabel, required, current, facts, alternatives and draft. Returned values are taken back one by one; an unusable value is discarded and the generic answer stands.

Known gaps

Places where the model today does less than the rest of this page suggests. They are listed because a gap nobody names looks like a mistake by the reader.

Gap Consequence Workaround
Messages after an attempted action are still one-line notices, not panels. This affects the Git view, pipelines, work items and the source view among others. After a failed click it says “you may not do that” with no route to a request. Reload the page: the closed page carries the panel.
Only a request that names a project finds the people responsible on its own. Everything else goes to the globally configured address — and none is set on this installation. A request with no project reference notifies nobody. It is stored and visible in the inbox, but nobody learns of it. Set an address under notify_mail, or open the inbox regularly.
@wvds and @wvdse appear in the ACL rules but are currently assigned to no account. Rules that open something only to these groups open it to nobody. Check the assignment in the user manager.
The project index that lists your role per project exists in German only so far. Anybody using the interface in another language finds their roles one at a time, on each project page. Project index (in German)
en/wiki/dwe/permissions/matrix.txt · Last modified: by 0.0.0.0