4.3 KiB
Plan: Role-as-Password as the Default Authorization Model
Status: Draft — ready for review
Problem
The signer currently defaults to requiring interactive approval for every request, even for the default "main" role. This contradicts the intended design where knowing the role name is sufficient authorization (role-as-password). Users running v0.1.21 are prompted to approve requests when they should be authorized automatically.
Root cause
Three places in the code set requires_approval = 1 by default:
| Location | Context | Current value |
|---|---|---|
src/main.c:3816 |
Default "main" role (non-interactive mode) | requires_approval = 1 |
src/main.c:2374 |
Wizard role creation prompt | Default 1 (Y/n) |
src/main.c:2223 |
Wizard OTP role creation prompt | Default 1 (Y/n) |
Changes required
1. Default "main" role — non-interactive mode
File: src/main.c:3816
Change:
role.requires_approval = 1;
To:
role.requires_approval = 0; /* role-as-password: knowing the role name is sufficient */
This is the most critical fix — it affects every user running in non-interactive mode (e.g., --listen unix, --listen tcp, --listen qrexec).
2. Wizard role creation — remove approval prompt
File: src/main.c:2359-2377
Currently the wizard asks:
Require interactive approval for each request? [Y/n]:
With role-as-password as the default, this prompt should be removed entirely. The role is created with requires_approval = 0. If a user wants approval, they can use --preapprove or manually edit the role after creation.
Remove the prompt block (lines 2359-2377) and set:
int requires_approval = 0; /* role-as-password by default */
3. Wizard OTP role creation — remove approval prompt
File: src/main.c:2208-2226
Same change as #2. Remove the approval prompt for OTP roles and default to requires_approval = 0.
4. Policy table default — no change needed
File: src/policy.c:1097-1113
The current default policy is * → PROMPT_EVERY_REQUEST. This is correct because:
- For roles with
requires_approval = 0,policy_check_with_role()returnsPOLICY_ALLOWbefore consulting the policy table. - For roles with
requires_approval = 1, the policy table prompt still fires as expected.
No change needed here.
5. --allow-all flag — retain as-is
File: src/main.c:3678
The --allow-all flag sets g_prompt_always_allow, which bypasses prompts for algorithm-based verbs (sign, verify, encapsulate, etc.) that don't go through the role system. This is still useful for testing and non-interactive scenarios. No change needed.
Authorization flow after changes
flowchart TD
A[Client sends request with role name] --> B{Role found?}
B -- No --> C[Reject: unknown_role]
B -- Yes --> D{requires_approval?}
D -- No --> E[Authorize immediately - role-as-password]
D -- Yes --> F[Check policy table]
F --> G{Policy match?}
G -- Allow --> H[Authorize]
G -- Prompt --> I[Show interactive prompt]
G -- Deny --> J[Reject: policy_denied]
I --> K{User choice}
K -- y --> H
K -- a/e --> L[Add session grant] --> H
K -- n --> J
Test impact
tests/test_n_signer_client.sh— may need updates if tests relied on the old approval-required defaulttests/test_integration.c— verify no tests break from the default change- The
NSIGNER_TEST_FORCE_PROMPTenv var (src/main.c:3063) can be used to force prompts in tests that need to exercise the approval path
Files to modify
| File | Lines | Change |
|---|---|---|
src/main.c |
3816 | requires_approval = 1 → 0 |
src/main.c |
2359-2377 | Remove approval prompt, default to 0 |
src/main.c |
2208-2226 | Remove approval prompt, default to 0 |
Summary
Three one-line changes (plus removing two prompt blocks) to make role-as-password the default. The mechanism already exists in the code — it's just not the default.