Skip to content

Conversation

@marcellmars
Copy link

@marcellmars marcellmars commented Sep 30, 2024

This pull request is combination of two pull requests from Forgejo "compressed" into two commits.

Don't show private repositories and organizations if called via access token with public-only scope

During testing of the "public-only" scope for API tokens, I discovered that the initial implementation didn't guard private repositories and organizations as intended.

I noticed that private repositories and organizations were still visible when using a token with the public-only scope.

This PR addresses this issue and ensures that the public-only scope functions correctly.

During my attempts to fix this I realized that the logic was not propagating the public-only flag deep enough. I think that the initial check for the public-only scope was happening too early in the request lifecycle as if it can be resolved per request where the actual logic should be implemented at the moment when the particular repository or organization is being traversed (so one could check if it is actually private).

The implementation in this PR introduces the publicOnlyToken function to check if access token carries the public-only scope. It then updates related functions where the traversal of repos and orgs are done.

Related functions are:

  • repoAssignment in routers/api/v1/api.go called via /api/v1/repos/{username}/{reponame}
  • listMyOrgs in routers/api/v1/org/org.go called via /api/v1/user/orgs, /users/{username}/orgs and /admin/users/{username}/orgs
  • GetAll in routers/api/v1/org/org.go called via /api/v1/orgs
  • Get in routers/api/v1/org/org.go called via /api/v1/orgs/{org}
  • Search in routers/api/v1/repo/repo.go called via /api/v1/repos/search
  • GetByID in routers/api/v1/repo/repo.go called via /api/v1/repositories/{id}
  • ListUserRepos in routers/api/v1/user/repo.go called via /api/v1/users/{username}/repos
  • ListMyRepos in routers/api/v1/user/repo.go called via /api/v1/users/repos
  • ListOrgRepos in routers/api/v1/user/repo.go called via /api/v1/orgs/{org}/repos

Middleware tokenRequiresScopes takes care of required scopes but also set if public-only scope comes with the request. There were few routes which missed the required scopes or did the wrong order with the middleware coming after the call to function returning the results.

Tests added:

  • TestAPIPublicOnlyTokenRepos verifies that tokens with public-only scope only show public repositories
  • TestAPIPublicOnlyTokenOrgs ensures that tokens with public-only scope only display public organizations

Enhancing Gitea OAuth2 Provider with Granular Scopes for Resource Access

This PR was initiated following my personal research to find the lightest possible Single Sign-On solution for self-hosted setups. The existing solutions often seemed too enterprise-oriented, involving many moving parts and services, demanding significant resources while promising planetary-scale capabilities. Others were adequate in supporting basic OAuth2 flows but lacked proper user management features, such as a change password UI.

Forgejo hits the sweet spot for me, provided it supports more granular access permissions for resources under users who accept the OAuth2 application.

This PR aims to introduce this capability as nonintrusively and simply as possible. It acknowledges that the default scope is all and begins to process only additional scopes typical for OIDC/OAuth2: openid, profile, email, and groups. These then should be the ones already established with personal tokens.

Personal tokens define scopes around specific resources: user info, repositories, issues, packages, organizations, notifications, miscellaneous, admin, and activitypub, with access delineated by read and/or write permissions.

The initial case I wanted to address was to have Forgejo act as an OAuth2 Identity Provider. To achieve that, with this PR, I would only add read:user to provide basic user information. The rest of the scopes should be provided as it is now, combining the existing Forgejo OAuth2 scopes: openid, profile, email, and groups.

Another example: if a third party wanted to interact solely with Issues, it would need to add read:user (for authorization) and read:issue/write:issue to manage Issues.

Another scenario could involve adding read:user and public-only, preventing the third party from accessing anything the user has kept private.

My approach is based on my understanding of how scopes can be utilized, supported by examples like Sample Use Cases: Scopes and Claims on auth0.com.

I added an additional check to CheckOAuthAccessToken that returns extra grant scopes, which are then added to store.GetData()["ApiTokenScope"] in userIDFromToken to be checked against the accepted/stored grant scopes. The main difference is the opportunity to reduce the permissions from all, as is currently the case, to what is provided by the additional scopes described above.

The intent of this PR is to ensure that if nothing is added, or if scopes are added to openid, profile, email, groups but not in AccessTokenScope, everything continues to work as it currently does.

@marcellmars marcellmars requested a review from yp05327 October 1, 2024 08:57
@lunny
Copy link
Owner

lunny commented Oct 2, 2024

Please send to the main repository since base branch merged.

@marcellmars marcellmars changed the base branch from lunny/clean_oauth2 to main October 2, 2024 05:14
@marcellmars marcellmars changed the base branch from main to lunny/clean_oauth2 October 2, 2024 05:15
@marcellmars
Copy link
Author

Please send to the main repository since base branch merged.

is this PR what i was supposed to do?

@marcellmars marcellmars closed this Oct 2, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants