Automatically handle ownership transfers
We have received more ownership transfers, so now that we have more projects it's starting to make more sense to automate ownership transfers.
From a security POV:
- Both the past and future must owner agree on the transfer before the transfer takes place. An admin can override, e.g., if the current owner dies.
- We don't want to let attackers spam users with junk email. The obvious solution is to require the current owner to start the transfer, and not automatically notify the recipient ourselves (make the current owner contact the new owner separately).
- Only current owners, potential new owners, and admins should see a transfer - not even other editors. A transfer might happen because someone is leaving a company, and we don't want to tip that off if the current owner is trying to wait for a better time to announce it.
From a simplicity POV, I think a current owner should designate a single new potential owner UID.
The alternative, more functional capability would be to delegate trust to project admins on GitHub. Any admin would be allowed to edit the project they're admin of. Of course, that would only cover GitHub, but the vast majority of our projects are hosted there.
One way to do this is to have a separate table of "transfers" with "/transfers" as the API resource (and controller). This is a slight overkill, but it would make API clean.
- The "transfers" table could store id, created_at time (as usual), project#, old owner user id, new owner user id. The older owner user id is basically a check (like double-accounting) - if it doesn't match at transfer time, then something has gone wrong.
- "GET /transfers/new?..." would provide a form (allow filling in project #, old owner user id, new user id). Caching is disabled.
- "POST /transfers/new" would allow a current owner or admin create a new transfer entry for a proposed transfer from that owner to a new owner (the entry will be added if it's okay). That new transfer entry would have its own id. If there are existing transfers of that project, they are first deleted. This would cause an email to be sent to the current owner; the current owner or admin is the one doing this, so we're not worried about spam in this case - instead, the issue is that perhaps account credentials have been stolen and the attacker is trying to take over the badge entry.
- "GET /transfers/#" would show a transfer numbered #. This is only allowed for the current owner, new (potential) owner), and admin (for privacy). Caching is disabled.
- "DELETE /transfers/#" would destroy a transfer numbered #, allowed by current owner, new potential owner, and admin.
- "POST /transfers/#?accept=true" would cause the transfer to occur. Only the new potential owner or admin can do this. It checks if the transfer is in time. If all okay, the transfer occurs (modifying the owner of the project), the transfer record is removed, and the old and new owner are each sent separate emails noting that the transfer is complete. We might require a cooling-off time (say 4-24 hours) after a transfer has begun before it can be accepted by normal user, in case the transfer was started by an attacker who took over the original owner's account (e.g., via a stolen password). The project should also have a new entry allowing the old owner to edit (the new owner can remove this later if desired).
- "GET /transfers/" - show the pending transfers to/from the current logged-in user. Optional "?user=#" allows seeing the page of that user, only admins can do this. If "?all=true" (admin-only), shows whole table (paged) instead of just one user. For each entry if authorized you can "approve" (admin or potential new owner) or "cancel" (==delete, admin or old owner) which links to POST or DELETE.
Transfers would only be valid for a period of time, say 21 days after they are created (they are deleted after that).
An alternative approach would be to store the "potential new user id" in the projects record, along with the time it was sent. In the projects edit page, there'd be a new advanced field for "Transfer ownership to user id:" that only shows to owners & admins. ONLY the current owner or admins can edit this field. If edited, it can be a user id or empty (if you delete it, that stops a transfer).
The alternative, more functional capability would be to delegate trust to project admins on GitHub. Any admin would be allowed to edit the project they're admin of. Of course, that would only cover GitHub, but the vast majority of our projects are hosted there.
We already try to allow any admin to edit the project they're an admin of. However, GitHub does not reliably tell us when someone is an admin (!). See: https://github.com/coreinfrastructure/best-practices-badge/issues/1254
And as you noted, not everyone is on GitHub.
Sorry, that issue doesn't illustrate what the problem is with relying on GitHub.
The issue is that in the badging project we always want a single final owner, and we need to confirm carefully any transfers. Not all is on GitHub, and GitHub doesn't provide reliable answers last we tried.
The alternative would be to say that we're fine with multiple owners of a project, and we're happy to outsource tracking of that ownership to GitHub (when the project decides to). Then, we'd need to figure out whether we can reliably get a list of all valid admins of a project from GitHub when someone attempts a change.
That covers 0% of projects. It won't work on GitHub (we can't reliably get the info & we've tried in the past), and it won't work for projects not on GitHub :-). The current setup assumes exactly 1 owner; that can change, but would be more work. I was hoping for something easy-to-implement to automate a relatively rare situation :-).
It's the "can't reliably get the info" that I'm questioning. But, I'm not volunteering to do the work to show that GitHub is reliable, so please go ahead with your approach.
It's tricky to do the research, because things seem to work for some users & not for others when we tested this a while back. We never did figure out a pattern. I think GitHub is intentionally hiding some info, possibly due to privacy concerns. Put another way, I don't think that GitHub's database is corrupt; I just think that GitHub doesn't always tell us all it knows, for reasons we don't understand.
If we do transfers the way first proposed, then transfers will "just work" no matter what. I like "just works" :-).