@dooer/dooer-test-env 1.15.0 → 1.16.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,246 @@
1
+ # Logins and user management
2
+
3
+ These commands talk to an environment through its **public GraphQL endpoint** — the same one the frontends
4
+ use. No VPN, no kubectl, no cluster credentials: just an ordinary HTTPS call and a session.
5
+
6
+ | environment | endpoint | `--env` |
7
+ | --- | --- | --- |
8
+ | your local stack | `http://localhost:4000/graphql` | `local` (the default) |
9
+ | staging | `https://api.s-e032.com/graphql` | `staging`, or `dooer-staging` / `s-e032` |
10
+ | live | `https://api.dooer.com/graphql` | `production`, or `live` / `dooer-production` |
11
+
12
+ ---
13
+
14
+ ## 1. The three kinds of user
15
+
16
+ One person normally has **several user rows** — commonly `you@dooer.com` as a customer and
17
+ `you+hi@dooer.com` as a professional. They are separate accounts with separate passwords, and *which one
18
+ you sign in as* decides what you can see and do. This is the single most important thing on this page: most
19
+ confusing "why can't I…" moments are a session that belongs to the wrong one of your accounts.
20
+
21
+ | | who uses it | what it can see | how to log in |
22
+ | --- | --- | --- | --- |
23
+ | **customer** | end users | only the organizations attached to that user | email **or** BankID |
24
+ | **partner** (`hi`) | accountants | every organization belonging to their partner | email **or** BankID |
25
+ | **admin** | internal staff | everything, and can create users | email **only** |
26
+
27
+ **BankID cannot reach an admin account.** The general BankID route (`loginWithBankIdV2`) is pinned to
28
+ `user_type: 'customer'` in service-accounts, and the partner route resolves your `hi` account — so an admin
29
+ session always comes from an email + password login.
30
+
31
+ ### Partners worth knowing
32
+
33
+ A partner is an accounting firm; its `domain` is how it is addressed on the command line.
34
+
35
+ | partner | domain | what it actually is |
36
+ | --- | --- | --- |
37
+ | Dooer | `dooer` | **Voitto** — the in-house accounting firm |
38
+ | Dooer Sales | `dooersales` | **Dooer free customers** |
39
+
40
+ `dooer-test-env partner list` shows the rest for whichever environment you point it at.
41
+
42
+ ---
43
+
44
+ ## 2. Logging in
45
+
46
+ The session is stored in your macOS keychain, one per environment, and every other command picks it up.
47
+
48
+ ### As a customer user
49
+
50
+ ```bash
51
+ # email + password (you are prompted for the password; it is not echoed)
52
+ dooer-test-env remote-environment login --env staging --email you@dooer.com
53
+
54
+ # BankID — --customer selects the customer route
55
+ dooer-test-env remote-environment login --env staging --bankid --customer
56
+ ```
57
+
58
+ ### As a partner user (accountant)
59
+
60
+ ```bash
61
+ # BankID — this is the default, and is exactly what HQ does
62
+ dooer-test-env remote-environment login --env staging
63
+
64
+ # email + password, using your professional account's address
65
+ dooer-test-env remote-environment login --env staging --email you+hi@dooer.com
66
+ ```
67
+
68
+ The BankID flow never asks which partner you are signing in to — like HQ, it submits an empty domain and
69
+ lets the server resolve it from your BankID identity.
70
+
71
+ ### As an admin
72
+
73
+ ```bash
74
+ dooer-test-env remote-environment login --env staging --email you+admin@dooer.com
75
+ ```
76
+
77
+ Email only, for the reason in §1.
78
+
79
+ ### What a BankID login looks like
80
+
81
+ An animated QR is drawn in the terminal and refreshed once a second (the payload is time-based, so a frozen
82
+ QR stops scanning). Open the BankID app, scan, confirm — the command prints who you ended up as:
83
+
84
+ ```
85
+ logged in to staging (s-e032) as Jimmy Bergman <jimmy.bergman+hi@dooer.com>
86
+ user id 203b9010-abb9-4f7c-9a5e-8bbf6f75ca7b
87
+ user type hi
88
+ session stored in keychain, valid until 2026-09-11T09:31:17.000Z
89
+ ```
90
+
91
+ Always read the **user type** line. It is the quickest way to catch "I meant to be my other account".
92
+
93
+ ---
94
+
95
+ ## 3. Becoming a partner user from an admin session
96
+
97
+ Signed in as an admin, you can assume a partner user without their BankID — the same thing back-office's
98
+ **“Become user in X”** button does:
99
+
100
+ ```bash
101
+ dooer-test-env remote-environment become someone+hi@dooer.com --env staging
102
+ dooer-test-env remote-environment become someone+hi@dooer.com --env staging --partner "Dooer Sales"
103
+ ```
104
+
105
+ `--partner` is only needed when they belong to more than one; otherwise it resolves on its own.
106
+
107
+ Becoming replaces the stored session, so the admin token that authorised it is stashed first:
108
+
109
+ ```bash
110
+ dooer-test-env remote-environment become --revert --env staging
111
+ ```
112
+
113
+ ---
114
+
115
+ ## 4. Who am I, and where
116
+
117
+ ```bash
118
+ dooer-test-env remote-environment status --env staging
119
+ dooer-test-env remote-environment status --all # every environment at once
120
+ dooer-test-env remote-environment list # endpoints + whether you are logged in
121
+ dooer-test-env remote-environment logout --env staging
122
+ ```
123
+
124
+ ---
125
+
126
+ ## 5. Using the token from scripts
127
+
128
+ `remote-environment token` writes **only** the token to stdout — no colour, no label, no trailing text — so
129
+ it drops straight into a variable or a pipe. Errors go to stderr, and stdout stays empty when they do.
130
+
131
+ ```bash
132
+ dooer-test-env remote-environment token # local
133
+ dooer-test-env remote-environment token --env staging
134
+ dooer-test-env remote-environment token --env staging | pbcopy
135
+ ```
136
+
137
+ Calling the API by hand with it:
138
+
139
+ ```bash
140
+ TOKEN=$(dooer-test-env remote-environment token --env staging)
141
+
142
+ curl -s https://api.s-e032.com/graphql \
143
+ -H "content-type: application/json" \
144
+ -H "authorization: Bearer $TOKEN" \
145
+ -d '{"query":"{ currentUser { id email userType } }"}'
146
+ ```
147
+
148
+ The same works against the local stack on `http://localhost:4000/graphql`. The local environment needs no
149
+ login at all — with no stored session it mints a service token from the shared dev keypair, so
150
+ `token --env local` always returns something usable.
151
+
152
+ > The token is a bearer credential for whichever account you logged in as. Treat it like a password: it is
153
+ > fine in a shell variable, not in a commit, a ticket or a chat message.
154
+
155
+ ---
156
+
157
+ ## 6. Managing users
158
+
159
+ ```bash
160
+ dooer-test-env user search # the most recent users
161
+ dooer-test-env user search anna # email, name, personnummer or id
162
+ dooer-test-env user search --role customer # only customers
163
+ dooer-test-env user get anna@dooer.com # details + partner memberships
164
+ dooer-test-env user partners anna@dooer.com # just the memberships, with levels
165
+ ```
166
+
167
+ Users are addressable by **id or email** everywhere.
168
+
169
+ ### Creating a user
170
+
171
+ ```bash
172
+ # password generated and printed once
173
+ dooer-test-env user create --email anna@dooer.com --execute
174
+
175
+ # a customer, with your own password and a personnummer
176
+ dooer-test-env user create --email kim@example.com \
177
+ --role customer --password 'hunter2hunter2' --personnummer 19900101-1239 --execute
178
+
179
+ # a professional placed straight into a partner
180
+ dooer-test-env user create --email anna@dooer.com --partner "Dooer Sales" --level admin --execute
181
+ ```
182
+
183
+ This calls `users.create`, the same mutation back-office's `/users/new` uses, so the service hashes the
184
+ password and applies its own validation. Notes:
185
+
186
+ - **Needs an admin session** in any remote environment.
187
+ - `--role` defaults to `hi`; the others are `customer` and `admin`.
188
+ - The password is shown **once** and cannot be read back. It works on the ordinary email+password login.
189
+ - First and last name default to something derived from the address; override with `--first-name` /
190
+ `--last-name`.
191
+ - The personnummer must be a **real** one — the API checks the Luhn digit, so an invented number is
192
+ rejected. The CLI checks it first and tells you which rule failed.
193
+ - Writes are **dry-run until `--execute`**.
194
+
195
+ ---
196
+
197
+ ## 7. Partner membership
198
+
199
+ ```bash
200
+ dooer-test-env partner list # every partner in the environment
201
+ dooer-test-env partner users "Dooer Sales" # its users and their level
202
+
203
+ dooer-test-env partner assign anna@dooer.com dooersales --level admin --execute
204
+ dooer-test-env partner unassign anna@dooer.com dooersales --execute
205
+ ```
206
+
207
+ Partners are addressable by **id, name or domain**.
208
+
209
+ `--level` (`member` or `admin`) is the level *within* the partner — not the same axis as the user's
210
+ `--role` at Dooer. Only **`hi`** users can belong to a partner; the CLI applies the same
211
+ `invalid-user-type` rule the API does, so it will not write a row HQ would trip over.
212
+
213
+ ---
214
+
215
+ ## 8. What each session may do
216
+
217
+ | operation | required session |
218
+ | --- | --- |
219
+ | `user search`, `user get`, `user partners` | any |
220
+ | `partner assign`, `partner unassign`, `partner users` | partner |
221
+ | `partner list` | admin |
222
+ | `user create` | admin |
223
+
224
+ When a session lacks the level, the CLI names the one that is required and who you are currently signed in
225
+ as, instead of surfacing a raw `Forbidden`:
226
+
227
+ ```
228
+ creating a user needs an "admin" session. You are signed in to staging as jimmy.bergman+hi@dooer.com (hi).
229
+
230
+ Log in as an account that has it:
231
+ dooer-test-env remote-environment login --env staging --email <your-admin-email>
232
+ ```
233
+
234
+ ---
235
+
236
+ ## Troubleshooting
237
+
238
+ | Symptom | Cause |
239
+ | --- | --- |
240
+ | `not logged in to <env>` | No session for that environment — log in (§2) |
241
+ | `your <env> session expired …` | Sessions are short-lived; log in again |
242
+ | `needs an "admin" session` | Wrong one of your accounts — see §1 and §8 |
243
+ | `only "hi" users can belong to a partner` | You aimed a partner command at a customer/admin account |
244
+ | `"x@y.com" matches N users` | That address exists once per user type; address the user by id instead |
245
+ | `personnummer … has an invalid check digit` | Not a real personnummer — the API validates the Luhn digit |
246
+ | BankID QR never completes on **local** | The local stack lost its BankID certs; `dooer-test-env up` re-injects them |
@@ -204,6 +204,17 @@ module.exports = {
204
204
  console.log(` Revert with: dooer-test-env remote-environment become --revert --env ${env.name}\n`)
205
205
  },
206
206
  })
207
+ .command({
208
+ command: 'token',
209
+ describe: 'print the stored token for an environment — bare, for scripts',
210
+ builder: envOption,
211
+ handler: (argv) => {
212
+ const env = remote.resolveEnv(argv.env)
213
+ // Deliberately the ONLY thing on stdout: no colour, no label, no trailing prose, so
214
+ // `$(… token)` and `| pbcopy` both do the obvious thing. Failures go to stderr via cli.js.
215
+ process.stdout.write(remote.tokenFor(env.name) + '\n')
216
+ },
217
+ })
207
218
  .command({
208
219
  command: 'status',
209
220
  describe: 'who you are logged in as, and until when',
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dooer/dooer-test-env",
3
- "version": "1.15.0",
3
+ "version": "1.16.0",
4
4
  "description": "Run the whole Dooer backend locally (staging DB minus customers), copy/purge customers between environments, and shred — one CLI.",
5
5
  "license": "UNLICENSED",
6
6
  "repository": "Dooer/cli-dooer-test-env",
package/readme.md CHANGED
@@ -21,6 +21,9 @@ logins and the BankID cert all come from there.
21
21
  certificate, and verify both actually work.
22
22
  - **[Issuing access](./docs/issuing-access.md)** — admin: sign someone's certificate, bind their
23
23
  permissions, and revoke them again.
24
+ - **[Logins and user management](./docs/user-management.md)** — signing in to an environment as a customer,
25
+ partner or admin user; creating users; partner membership; using the token from scripts. Needs no VPN or
26
+ kubectl at all.
24
27
 
25
28
  Already have both? Straight to the quick start.
26
29
 
@@ -53,7 +56,7 @@ db roles (re)create per-service DB role
53
56
  db snapshot <name> | db rollback <name> local restore points
54
57
  customer new "<name>" --owner <userId> --execute empty functional account: company + Owner + subscriptions + partner dooer
55
58
  customer copy | customer purge copy / delete one org between environments (emails anonymized)
56
- remote-environment login|become|status|logout|list sign in to an environment; session → keychain
59
+ remote-environment login|become|token|status|logout sign in to an environment; session → keychain
57
60
  user search | get | create | partners find/inspect/create users (local env unless --env)
58
61
  partner list | users | assign | unassign partners and who belongs to them, at which level
59
62
  shred anonymize the LOCAL db (localhost only)
@@ -167,57 +170,47 @@ user's `--role` at Dooer. Only **`hi`** users can belong to a partner; the CLI a
167
170
 
168
171
  ### Look at (or fix) users in a real environment
169
172
 
170
- The `user` and `partner` commands talk to an environment's **public GraphQL endpoint** — the same one the
171
- frontends use — so reaching staging or live needs no VPN, no kubectl and no cluster credentials. It does
172
- need a session. Sign in once per environment; the token is kept in your keychain:
173
+ The `user` and `partner` commands reach an environment through its **public GraphQL endpoint** — the same
174
+ one the frontends use — so staging and live need no VPN, no kubectl and no cluster credentials. They do
175
+ need a session:
173
176
 
174
177
  ```bash
175
- # BankID — exactly what HQ does, so you get your professional (hi) account
178
+ # BankID — what HQ does, so you get your professional (hi) account
176
179
  npx @dooer/dooer-test-env@latest remote-environment login --env staging
177
180
 
178
- # email + password (prompted, hidden) — the only way to reach an ADMIN account
181
+ # email + password — the only route to an ADMIN account (BankID cannot reach one)
179
182
  npx @dooer/dooer-test-env@latest remote-environment login --env staging --email you+admin@dooer.com
180
183
 
181
- npx @dooer/dooer-test-env@latest remote-environment status --all # who you are, in every environment
182
- npx @dooer/dooer-test-env@latest remote-environment logout --env staging
184
+ npx @dooer/dooer-test-env@latest remote-environment status --all
183
185
  ```
184
186
 
185
- Then point any command at it with `--env` (`local` is the default; `staging`, `production`, and aliases
186
- like `dooer-staging` / `live` all resolve):
187
+ Then point any command at it with `--env` (`local` is the default; `staging` / `production` and aliases
188
+ like `dooer-staging` / `live` resolve too):
187
189
 
188
190
  ```bash
189
191
  npx @dooer/dooer-test-env@latest user search someone@dooer.com --env staging
190
192
  npx @dooer/dooer-test-env@latest partner list --env production
191
193
  ```
192
194
 
193
- **Which account you sign in as decides what you can do**, because one person has several user rows:
195
+ **Which of your accounts you sign in as decides what you can do** reads work from any session, partner
196
+ membership needs a partner session, and creating a user needs an admin one. See
197
+ **[Logins and user management](./docs/user-management.md)** for the full picture: the three kinds of user,
198
+ `become`, and the permission matrix.
194
199
 
195
- | operation | needs | how to get it |
196
- | --- | --- | --- |
197
- | reads (`user search`, `user get`) | any session | either login |
198
- | `partner assign` / `unassign` | a **partner** session | BankID login, or `become` from an admin session |
199
- | `user create` | an **admin** session | password login as your admin account |
200
+ ### Use the session token in your own scripts
200
201
 
201
- BankID cannot reach an admin account at all: `loginWithBankIdV2` is pinned to `user_type: 'customer'`
202
- server-side, and HQ's partner flow resolves your `hi` user. When a session lacks the level, the CLI says
203
- which one is required and who you are currently signed in as, rather than surfacing a raw `Forbidden`.
204
-
205
- Signed in as an admin, you can assume a partner user without BankID — the same thing back-office's
206
- **“Become user in X”** button does (`partner.user.become`, itself admin-gated):
202
+ `remote-environment token` prints the token and nothing else no colour, no label — so it drops straight
203
+ into a variable or a pipe:
207
204
 
208
205
  ```bash
209
- npx @dooer/dooer-test-env@latest remote-environment become someone@dooer.com --env staging
210
- # …--partner "Dooer Devteam" if they belong to more than one
206
+ TOKEN=$(npx @dooer/dooer-test-env@latest remote-environment token --env staging)
211
207
 
212
- npx @dooer/dooer-test-env@latest remote-environment become --revert --env staging
208
+ curl -s https://api.s-e032.com/graphql \
209
+ -H "content-type: application/json" -H "authorization: Bearer $TOKEN" \
210
+ -d '{"query":"{ currentUser { id email userType } }"}'
213
211
  ```
214
212
 
215
- Becoming replaces the stored session, so the admin token that authorised it is stashed first and
216
- `--revert` puts it back — otherwise you would have to log in again just to get where you were.
217
-
218
- The local env needs no login — it is signed with the shared dev keypair this CLI already holds.
219
-
220
- Writes are dry-run until `--execute`.
213
+ The local env needs no login for this with no stored session it mints one from the shared dev keypair.
221
214
 
222
215
  ### Copy a real customer into the local env
223
216