|
|
|
@@ -32,47 +32,69 @@ GITEA=https://git.example # your Gitea base URL
|
|
|
|
|
EXP=$(date -u -d '+90 days' +%Y-%m-%dT00:00:00Z) # Vikunja token expiry
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
Write `EXP` down. It goes in the business file as each Vikunja token's
|
|
|
|
|
`expires`.
|
|
|
|
|
Write down the date part of `EXP`. It goes in the business file as
|
|
|
|
|
each Vikunja token's `expires`, in `YYYY-MM-DD` form. The broker treats
|
|
|
|
|
00:00Z on that date as the expiry, the same instant `EXP` names.
|
|
|
|
|
|
|
|
|
|
The commands need curl 7.76 or later, for `--fail-with-body`.
|
|
|
|
|
|
|
|
|
|
## 1. Gitea: four bot users and their tokens
|
|
|
|
|
|
|
|
|
|
Gitea tokens don't expire, and the HTTP route for creating one needs a
|
|
|
|
|
password, so this part uses the web UI.
|
|
|
|
|
|
|
|
|
|
1. As a site admin, create the users `pm-bot`, `cto-bot`, `coder-bot`
|
|
|
|
|
and `reviewer-bot` (Site Administration, User Accounts, Create). Give
|
|
|
|
|
1. As a site admin, create the users `mosaic-stack-pm-bot`,
|
|
|
|
|
`mosaic-stack-cto-bot`, `mosaic-stack-coder-bot` and
|
|
|
|
|
`mosaic-stack-reviewer-bot` (Site Administration, User Accounts,
|
|
|
|
|
Create). Gitea usernames are global to the instance, so the business
|
|
|
|
|
id in the name keeps one business's bots, and revoking them, apart
|
|
|
|
|
from another's. Give
|
|
|
|
|
each a long random password, which you won't need again after step 3.
|
|
|
|
|
None of them is an admin.
|
|
|
|
|
2. In `mosaicstack/stack`, add each bot as a collaborator:
|
|
|
|
|
|
|
|
|
|
| Bot | Repository access |
|
|
|
|
|
|---|---|
|
|
|
|
|
| pm-bot | Write (labels, assignees and closing need it) |
|
|
|
|
|
| cto-bot | Write |
|
|
|
|
|
| coder-bot | Write |
|
|
|
|
|
| reviewer-bot | Read |
|
|
|
|
|
| mosaic-stack-pm-bot | Write (labels, assignees and closing need it) |
|
|
|
|
|
| mosaic-stack-cto-bot | Write |
|
|
|
|
|
| mosaic-stack-coder-bot | Write |
|
|
|
|
|
| mosaic-stack-reviewer-bot | Read |
|
|
|
|
|
|
|
|
|
|
No bot is on a protected branch's push or merge allowlist. Pushing to
|
|
|
|
|
a protected branch is a gated action, and it stays Jason's.
|
|
|
|
|
No bot is on a protected branch's push, merge or approvals allowlist.
|
|
|
|
|
Pushing to a protected branch is a gated action, and merges stay
|
|
|
|
|
Jason's. Reviewer-bot's approvals in Gitea are advisory and don't
|
|
|
|
|
count toward required approvals. The review verdict that gates a row
|
|
|
|
|
lives in the queue and the bus.
|
|
|
|
|
3. Log in as each bot, go to Settings, Applications, and generate one
|
|
|
|
|
token named `mosaic-stack-<role>-<date>` with these scopes:
|
|
|
|
|
|
|
|
|
|
| Bot | Scopes |
|
|
|
|
|
|---|---|
|
|
|
|
|
| pm-bot | `write:issue`, `read:repository`, `read:user` |
|
|
|
|
|
| cto-bot | `write:issue`, `write:repository`, `read:user` |
|
|
|
|
|
| coder-bot | `write:issue`, `write:repository`, `read:user` |
|
|
|
|
|
| reviewer-bot | `write:issue`, `write:repository`, `read:user` |
|
|
|
|
|
| mosaic-stack-pm-bot | `write:issue`, `read:repository`, `read:user` |
|
|
|
|
|
| mosaic-stack-cto-bot | `write:issue`, `write:repository`, `read:user` |
|
|
|
|
|
| mosaic-stack-coder-bot | `write:issue`, `write:repository`, `read:user` |
|
|
|
|
|
| mosaic-stack-reviewer-bot | `write:issue`, `write:repository`, `read:user` |
|
|
|
|
|
|
|
|
|
|
Leave every other scope unset, and set none to "public only".
|
|
|
|
|
Reviewer-bot's `write:repository` is there because Gitea files pull
|
|
|
|
|
request reviews under the repository scope. Its Read collaborator
|
|
|
|
|
access is what stops it pushing. Row SR's review checks that before
|
|
|
|
|
anyone relies on it.
|
|
|
|
|
4. Copy each token into its file with an editor, never `echo`:
|
|
|
|
|
`$S/pm-gitea.token`, `$S/cto-gitea.token`, `$S/coder-gitea.token`,
|
|
|
|
|
`$S/reviewer-gitea.token`. One line, the token only.
|
|
|
|
|
Reviewer-bot's `write:repository` is there because Gitea checks pull
|
|
|
|
|
request reviews against the repository scope. Its Read collaborator
|
|
|
|
|
access stops pushes to branches and tags. It does not stop an AGit
|
|
|
|
|
push to `refs/for/<branch>`, which opens a pull request. That's
|
|
|
|
|
acceptable only because the broker holds the token and offers the
|
|
|
|
|
reviewer no push action.
|
|
|
|
|
4. Write each token to its file from a silent prompt, in the same
|
|
|
|
|
`umask 077` shell. Paste the token at the prompt and press Enter:
|
|
|
|
|
|
|
|
|
|
```sh
|
|
|
|
|
for r in pm cto coder reviewer; do
|
|
|
|
|
printf '%s token: ' "$r"; read -rs t; echo
|
|
|
|
|
printf %s "$t" > "$S/$r-gitea.token"; unset t
|
|
|
|
|
done
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
`read` and `printf` are shell builtins, so the value never reaches
|
|
|
|
|
`ps` or the history. Don't use an editor, which can leave a swap or
|
|
|
|
|
backup copy behind.
|
|
|
|
|
5. Log out of each bot account. Record today's date as each Gitea
|
|
|
|
|
token's `rotateBy` base. Section 5 has the rotation schedule.
|
|
|
|
|
|
|
|
|
@@ -110,9 +132,13 @@ docker exec -it mosaic-vikunja /app/vikunja/vikunja user create -u jason -e you@
|
|
|
|
|
VK=http://127.0.0.1:3456
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
`user create` asks for the password when you leave out `-p`. If your
|
|
|
|
|
build doesn't prompt, stop and set it some other way. Don't put the
|
|
|
|
|
password on the command line.
|
|
|
|
|
`user create` asks for the password when you leave out `-p`. Keep `-it`,
|
|
|
|
|
because without a terminal the command exits instead of prompting. Never
|
|
|
|
|
put the password on the command line.
|
|
|
|
|
|
|
|
|
|
Both mounts are required. The container runs as your uid, and
|
|
|
|
|
`/app/vikunja` isn't writable to it, so Vikunja needs the files mount
|
|
|
|
|
for its startup write check and the db mount for its database.
|
|
|
|
|
|
|
|
|
|
### Owner login for this guide
|
|
|
|
|
|
|
|
|
@@ -135,8 +161,8 @@ print("owner header written")
|
|
|
|
|
PY
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
The probes logged in at `/api/v2/login`. The `token` field name is
|
|
|
|
|
assumed from v1, so the script stops if it's missing. Section 4 deletes
|
|
|
|
|
The `token` field is confirmed in the v2.7.0 source (`auth_login.go`).
|
|
|
|
|
The script still stops if it's missing. Section 4 deletes
|
|
|
|
|
the header file.
|
|
|
|
|
|
|
|
|
|
## 3. Vikunja: the project, the board and the bots
|
|
|
|
@@ -150,9 +176,9 @@ In the web UI, as the owner:
|
|
|
|
|
`blocked`. Each title appears once.
|
|
|
|
|
3. In the view's settings, check that the done bucket is `done` and the
|
|
|
|
|
default bucket is `todo`. Leave bucket configuration on manual.
|
|
|
|
|
4. Create the labels the business file template lists, and note each id.
|
|
|
|
|
The business file names labels by id until row S3 settles which
|
|
|
|
|
labels a bot can see.
|
|
|
|
|
4. Slice 1 needs no labels, since the requirement id lives in the task,
|
|
|
|
|
not in a label. If you create any, note each id. The business file
|
|
|
|
|
names labels by id until row S3 settles which labels a bot can see.
|
|
|
|
|
|
|
|
|
|
```sh
|
|
|
|
|
P=<project id>
|
|
|
|
@@ -213,8 +239,10 @@ mint() { # mint ROLE BOT_ID SCOPES_FILE
|
|
|
|
|
local r=$1 id=$2 sc=$3 resp="$S/.mint-$1.json"
|
|
|
|
|
jq -n --arg t "$BIZ-$r-$(date +%F)" --argjson o "$id" --arg e "$EXP" --slurpfile p "$sc" \
|
|
|
|
|
'{title:$t, owner_id:$o, expires_at:$e, permissions:$p[0]}' |
|
|
|
|
|
api -X POST "$VK/api/v2/tokens" --data-binary @- -o "$resp" &&
|
|
|
|
|
jq -j .token "$resp" > "$S/$r-vikunja.token" &&
|
|
|
|
|
curl -s --fail-with-body -H @"$S/vikunja-owner.hdr" -H 'Content-Type: application/json' \
|
|
|
|
|
-X POST "$VK/api/v2/tokens" --data-binary @- -o "$resp" ||
|
|
|
|
|
{ jq -c '{code, message}' "$resp"; rm -f "$resp"; return 1; }
|
|
|
|
|
jq -je '.token | strings' "$resp" > "$S/$r-vikunja.token" &&
|
|
|
|
|
jq -c '{id, owner_id, expires_at, starts_tk: (.token | startswith("tk_"))}' "$resp"
|
|
|
|
|
rm -f "$resp"
|
|
|
|
|
}
|
|
|
|
@@ -226,9 +254,11 @@ mint sync <sync id> "$S/scopes-sync.json"
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
Check that each line shows `starts_tk: true` and the `expires_at` you
|
|
|
|
|
set. Vikunja accepts a past expiry without complaint, so read it. A 400
|
|
|
|
|
with code 14002 means a scope name is wrong. Fix the scope file and mint
|
|
|
|
|
again.
|
|
|
|
|
set. Vikunja accepts a past expiry without complaint, so read it. On an
|
|
|
|
|
error, `mint` prints the response's `code` and `message`, which carry no
|
|
|
|
|
token. Code 14002 means a scope name is wrong. Fix the scope file and
|
|
|
|
|
mint again. If the response has no `token` field, `mint` writes nothing
|
|
|
|
|
usable and stops; delete the empty file before you retry.
|
|
|
|
|
|
|
|
|
|
## 4. Check and clean up
|
|
|
|
|
|
|
|
|
@@ -261,14 +291,17 @@ role. The stack never writes this file.
|
|
|
|
|
|
|
|
|
|
## 5. Rotation
|
|
|
|
|
|
|
|
|
|
- **Vikunja**, before `expires`: log in as the owner (section 2), mint a
|
|
|
|
|
new token for the same bot with `mint`, update `expires` in the
|
|
|
|
|
business file, and restart the broker. Then revoke the old token:
|
|
|
|
|
- **Vikunja**, before `expires`: in a new shell, rerun section 0, the
|
|
|
|
|
owner login in section 2, and the `api` and `mint` definitions in
|
|
|
|
|
section 3. Mint a new token for the same bot, update `expires` in the
|
|
|
|
|
business file, restart the broker, and finish with section 4's
|
|
|
|
|
`rm -f`. Then revoke the old token:
|
|
|
|
|
`api -X DELETE "$VK/api/v2/tokens/<old id>"`. The broker refuses to
|
|
|
|
|
start with a token past its `expires`, and it treats any 401 as a
|
|
|
|
|
refusal, never a retry.
|
|
|
|
|
- **Gitea**, every 90 days or at once if a token may have leaked: log in
|
|
|
|
|
as the bot, generate the replacement, write it to the file, restart
|
|
|
|
|
as the bot, generate the replacement, write it to the file with the
|
|
|
|
|
`read -rs` loop from section 1, restart
|
|
|
|
|
the broker, then delete the old token in the same settings page.
|
|
|
|
|
Update `rotateBy`. Each rotation gets one line in
|
|
|
|
|
`docs/SESSIONS.md`: date, who, which identities, and no values.
|
|
|
|
|