feat: share repos with people, and invite people who have no account yet
Closes the last piece of the product picture: give one person access to some
of your repos and not others.
- POST /v1/grants add|remove|list -- runs on the CALLER'S OWN token. Verified
against the live forge that a repo owner's scoped token adds and removes
collaborators (204), so sharing needs no elevated rights anywhere.
- POST /v1/invite -- records a promise against a VERIFIED email and creates
nothing until it is redeemed. Applied on first link.
- client: share / shared / invite.
Three properties the tests pin:
* the FORGE decides who may share (listing collaborators requires repo
admin, so its 200 is the authorisation answer, not ours);
* an unverified email collects nothing, and its invite stays pending rather
than being consumed;
* a failed grant never blocks a sign-in -- nobody is locked out of their own
account because a repo they were promised has since been deleted.
Applying an invite uses the admin credential deliberately: the inviter
authorised it at invite time and their session is long gone by redemption.
198 tests (was 184).
This commit is contained in:
@@ -412,7 +412,7 @@ deleted it again, `DELETE …/tokens/{id}` returning 204 under basic auth):
|
||||
|
||||
## Tests
|
||||
|
||||
* `python3 -m unittest discover -s tests` — 184 tests. The v1.2 additions
|
||||
* `python3 -m unittest discover -s tests` — 198 tests. The v1.2 additions
|
||||
cover: a snapshot capturing uncommitted work while HEAD, the index and the
|
||||
working tree stay byte-identical; snapshots landing outside `refs/heads`;
|
||||
an unchanged tree not being re-pushed; a diverged folder still being backed
|
||||
@@ -541,7 +541,48 @@ vault keys must exist, with the `shre-cred request` line to supply them.
|
||||
Per-repo `mode` in the manifest overrides the default, so a documents folder
|
||||
can be declared `mirror` while everything else stays on the safe `snapshot`.
|
||||
|
||||
## Next phase — invites and per-repo access (designed, not built)
|
||||
## Sharing: grants and invites
|
||||
|
||||
Two commands, because there are two situations.
|
||||
|
||||
**They already have an account** — share directly:
|
||||
|
||||
granthi-sync share bob --repo notes # write by default
|
||||
granthi-sync share bob --repo notes --permission read
|
||||
granthi-sync share bob --repo notes --revoke
|
||||
granthi-sync shared --repo notes # who can see it
|
||||
|
||||
**They do not have an account yet** — invite them:
|
||||
|
||||
granthi-sync invite carol@example.com --repo notes --repo reports
|
||||
|
||||
An invite creates **nothing**: no account, no token, no collaborator row. The
|
||||
grant is held against their email and applied the first time they run
|
||||
`granthi-sync link`. An invite that is never accepted leaves nothing behind.
|
||||
|
||||
Three properties worth keeping:
|
||||
|
||||
* **The forge decides who may share.** Before recording anything, the service
|
||||
asks Gitea whether the caller can administer that repo (listing
|
||||
collaborators requires repo admin, so a 200 is Gitea's own answer). Deciding
|
||||
it here from the repo name would be a second opinion about someone else's
|
||||
authorisation — and the wrong one the first time a repo is transferred.
|
||||
* **An unverified email collects nothing.** The address is the only thing
|
||||
tying a promise to a person, so an invite is applied only when the IdP says
|
||||
the address is verified. The invite stays pending rather than being consumed.
|
||||
* **A failed grant never blocks a sign-in.** If a promised repo has since been
|
||||
deleted, the person still links successfully and the failure is audited.
|
||||
Someone must not be locked out of their own account by somebody else's
|
||||
stale invite.
|
||||
|
||||
Applying an invite uses the admin credential deliberately: the inviter
|
||||
authorised it when they issued it, and their session is long gone by the time
|
||||
it is redeemed. Everything else — sharing, unsharing, listing — runs on the
|
||||
caller's own token, which is why granting needs no elevated rights at all
|
||||
(verified against the live forge: a repo owner's scoped token adds and removes
|
||||
collaborators, HTTP 204).
|
||||
|
||||
## Superseded design note — invites and per-repo access
|
||||
|
||||
Today `/v1/link` creates an account and every folder becomes a private repo
|
||||
under it. What is missing is the multi-person case: an existing account
|
||||
|
||||
Reference in New Issue
Block a user