Completely redesign access system #12155

Open
opened 2025-11-02 10:00:29 -06:00 by GiteaMirror · 0 comments
Owner

Originally created by @delvh on GitHub (Dec 6, 2023).

Problem

At the moment, most access checks happen through the user ID, and if necessary, by checking if any of the teams of a user have the corresponding permission.

However, this has some shortcomings:

  1. it is quite hard to implement correctly (see the corresponding go files. They are not nice to look at)
  2. it is quite hard to ensure that everything uses the correct permission, i.e. site admins are often forgotten (see i.e. the various issues where site admins cannot do something that normal users can)
  3. it has become effectively unmaintainable. No one wants to change anything about it, simply due to its complexity. However, it is in dire need of maintenance as a better permission system would fix quite a few issues already.
  4. it is not possible to change the permissions of multiple entities simultaneously, unless we are already

Proposal

I thus propose the following alternative approach:
Instead of doing the permission check manually, let's add a new authorization layer in between:
An internal team is nothing else than a set of user IDs (members) with a corresponding set of permissions.
There are different kinds of permissions, i.e. managing the site, reading a repo, writing to a repo, …
Every user is a member of n internal teams, including one where only they are a member of (the "new user ID").
There is a system-wide public and a system-wide limited team that mimic the permissions of the current public and limited visibilities, for anonymous visitors/any logged-in user.
Site admins are also a member of an admin team.
Once we implement this, the permission system boils down to max(permissions(teams(doer))[type]) for any action type and user doer, which can be implemented as a "simple" SQL query.

Pre-defined teams

  • global: Everyone, including anonymous users
  • internal: Everyone who is logged in
  • admin: Everyone who is a site-admin
  • <user-team>: Created for every user as soon as the user is created. New proxy for the user ID. Has only one member, the user themselves

Permission assignment

  • on repo creation: creator receives owner rights on the repository
  • on org creation: creator receives owner rights on the org
  • teams are mapped directly onto internal teams
  • on addition to team: joiner gets added to the team
  • on PAT creation: a new team is created whose member is the PAT
  • on adding a collaborator: invited user gets the permission indicated by the collaboration type

Types of permissions

  • team can see repo
  • repeat for concept in [code, issue, pr, projects, packages, actions, releases, wiki, settings]:
    • team can see <concept> in repo
    • team can modify <concept> in repo
  • and don't forget the current PAT scopes [api-activitypub, api-misc, api-notification, api-user]
  • team can access global admin panel

Problems

  • Changes almost the complete database, and migrating from previous instances is going to be tedious, so the implementation would be a do-everything-at-once-or-never problem.
    In particular, such a PR would need to be merged as quickly as possible, as again, it touches the whole codebase.
  • DB will use much more memory (every permission would be stored instead of computed for every team, so i.e. every org team will store exactly which repos it has access to, same for every PAT). We could think of allowing teams to have parent teams to reduce the number of entries, if we are okay with having recursive joins instead.
Originally created by @delvh on GitHub (Dec 6, 2023). ## Problem At the moment, most access checks happen through the user ID, and if necessary, by checking if any of the teams of a user have the corresponding permission. However, this has some shortcomings: 1. it is quite hard to implement correctly (see the corresponding go files. They are not nice to look at) 2. it is quite hard to ensure that everything uses the correct permission, i.e. site admins are often forgotten (see i.e. the various issues where site admins cannot do something that normal users can) 3. it has become effectively unmaintainable. No one wants to change anything about it, simply due to its complexity. However, it is in dire need of maintenance as a better permission system would fix quite a few issues already. 4. it is not possible to change the permissions of multiple entities simultaneously, unless we are already ## Proposal I thus propose the following alternative approach: Instead of doing the permission check manually, let's add a new authorization layer in between: An `internal team` is nothing else than a set of user IDs (members) with a corresponding set of permissions. There are different kinds of permissions, i.e. managing the site, reading a repo, writing to a repo, … Every user is a member of $n$ `internal teams`, including one where only they are a member of (the "new user ID"). There is a system-wide public and a system-wide limited team that mimic the permissions of the current public and limited visibilities, for anonymous visitors/any logged-in user. Site admins are also a member of an `admin` team. Once we implement this, the permission system boils down to $max(permissions(teams(doer))[type])$ for any action $type$ and user $doer$, which can be implemented as a "simple" SQL query. ## Pre-defined teams - `global`: Everyone, including anonymous users - `internal`: Everyone who is logged in - `admin`: Everyone who is a site-admin - `<user-team>`: Created for every user as soon as the user is created. New proxy for the user ID. Has only one member, the user themselves ## Permission assignment - on repo creation: creator receives `owner` rights on the repository - on org creation: creator receives `owner` rights on the org - teams are mapped directly onto internal teams - on addition to team: joiner gets added to the team - on PAT creation: a new team is created whose member is the PAT - on adding a collaborator: invited user gets the permission indicated by the collaboration type ## Types of permissions - `team can see repo` - repeat for `concept` in `[code, issue, pr, projects, packages, actions, releases, wiki, settings]`: - `team can see <concept> in repo` - `team can modify <concept> in repo` - and don't forget the current PAT scopes `[api-activitypub, api-misc, api-notification, api-user]` - `team can access global admin panel` ## Problems - Changes almost the complete database, and migrating from previous instances is going to be tedious, so the implementation would be a do-everything-at-once-or-never problem. In particular, such a PR would need to be merged as quickly as possible, as again, it touches the whole codebase. - DB will use much more memory (every permission would be stored instead of computed for every team, so i.e. every org team will store exactly which repos it has access to, same for every PAT). We could think of allowing teams to have parent teams to reduce the number of entries, if we are okay with having recursive joins instead.
GiteaMirror added the type/proposal label 2025-11-02 10:00:29 -06:00
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: github-starred/gitea#12155