Skip to content
Closed
Show file tree
Hide file tree
Changes from 3 commits
Commits
Show all changes
94 commits
Select commit Hold shift + click to select a range
4885397
oauth2 additional scopes
marcellmars Oct 2, 2024
33da8f1
no setting requirement for additional grant scopes
marcellmars Oct 10, 2024
667c8cc
Merge branch 'main' into oauth2-granular-scopes
marcellmars Oct 25, 2024
2c56714
Refactor the DB migration system slightly (#32344)
wxiaoguang Oct 27, 2024
4497ac1
Fix db engine (#32351)
wxiaoguang Oct 27, 2024
960869e
Migrate vue components to setup (#32329)
anbraten Oct 28, 2024
990fd45
Suggestions for issues (#32327)
anbraten Oct 29, 2024
8cb7aae
Optimize branch protection rule loading (#32280)
6543 Oct 29, 2024
2706557
Fix clean tmp dir (#32360)
lunny Oct 29, 2024
3041406
remove unused call to $.HeadRepo in view_title template (#32317)
bohde Oct 30, 2024
79d63f7
Add new [lfs_client].BATCH_SIZE and [server].LFS_MAX_BATCH_SIZE confi…
rremer Oct 30, 2024
baec377
Fix undefined errors on Activity page (#32378)
cloudchamb3r Oct 30, 2024
62ce7fc
Fix absolute-date (#32375)
wxiaoguang Oct 30, 2024
440cba6
Respect UI.ExploreDefaultSort setting again (#32357)
6543 Oct 30, 2024
d0de783
Fix toAbsoluteLocaleDate and add more tests (#32387)
wxiaoguang Oct 30, 2024
ed3d87e
Fix the missing menu in organization project view page (#32313)
yp05327 Oct 30, 2024
abcac26
refactor: remove redundant err declarations (#32381)
alexandear Oct 30, 2024
1d5e81c
Fix suggestions for issues (#32380)
wxiaoguang Oct 30, 2024
65db92f
Update JS and PY dependencies (#32388)
silverwind Oct 31, 2024
b3efee9
Update go dependencies (#32389)
wxiaoguang Oct 31, 2024
19beedc
Fix a number of typescript issues (#32308)
silverwind Oct 31, 2024
70c85dd
Fix `missing signature key` error when pulling Docker images with `SE…
Zettat123 Oct 31, 2024
baad896
Add artifacts test fixture (#30300)
kdumontnu Nov 1, 2024
7b578b8
Refactor tests to prevent from unnecessary preparations (#32398)
wxiaoguang Nov 1, 2024
68ebf96
improve performance of diffs (#32393)
bohde Nov 2, 2024
89c5736
Replace DateTime with DateUtils (#32383)
wxiaoguang Nov 2, 2024
b4e08e0
Fix created_unix for mirroring (#32342)
lunny Nov 2, 2024
784f464
Fix git error handling (#32401)
wxiaoguang Nov 2, 2024
05e7a65
Replace DateTime with proper functions (#32402)
wxiaoguang Nov 2, 2024
a1b0385
Refactor repo legacy (#32404)
wxiaoguang Nov 3, 2024
2ac493d
Make LFS http_client parallel within a batch. (#32369)
rremer Nov 4, 2024
5f33651
Add some handy markdown editor features (#32400)
wxiaoguang Nov 4, 2024
a5f8bd8
Refactor markup package (#32399)
wxiaoguang Nov 4, 2024
8212728
Refactor DateUtils and merge TimeSince (#32409)
wxiaoguang Nov 4, 2024
13e319f
Refactor template ctx and render utils (#32422)
wxiaoguang Nov 5, 2024
61b44f9
Refactor RepoRefByType (#32413)
wxiaoguang Nov 5, 2024
4b90f99
Only query team tables if repository is under org when getting assign…
lunny Nov 5, 2024
347fffc
Fix milestone deadline and date related problems (#32339)
lunny Nov 5, 2024
2d8f5e6
Use 8 as default value for git lfs concurrency (#32421)
wxiaoguang Nov 5, 2024
1169162
Correctly query the primary button in a form (#32438)
wxiaoguang Nov 6, 2024
4aa0c3c
Updated tokenizer to better matching when search for code snippets (#…
bsofiato Nov 6, 2024
e61ba7b
Include file extension checks in attachment API (#32151)
kemzeb Nov 6, 2024
c14dc65
Add new index for action to resolve the performance problem (#32333)
lunny Nov 6, 2024
7f55aad
Move AddCollabrator and CreateRepositoryByExample to service layer (#…
lunny Nov 7, 2024
a2e9ed3
Support quote selected comments to reply (#32431)
wxiaoguang Nov 7, 2024
42a5d6d
Add new event commit status creation and webhook implementation (#27151)
lunny Nov 7, 2024
295bcfc
Only provide the commit summary for Discord webhook push events (#32432)
kemzeb Nov 7, 2024
60fbe04
Split issue sidebar into small templates (#32444)
wxiaoguang Nov 8, 2024
773d040
Refactor issue page info (#32445)
wxiaoguang Nov 8, 2024
3030905
Fix broken releases when re-pushing tags (#32435)
Zettat123 Nov 8, 2024
473ab5a
Refactor language menu and dom utils (#32450)
wxiaoguang Nov 8, 2024
1c656ff
Fix issue sidebar (#32455)
wxiaoguang Nov 8, 2024
0c42d88
Add reviewers selection to new pull request (#32403)
CalK16 Nov 9, 2024
d406da7
Fix mermaid diagram height when initially hidden (#32457)
silverwind Nov 9, 2024
b1ae78e
Refactor sidebar label selector (#32460)
wxiaoguang Nov 10, 2024
75152db
Refactor sidebar assignee&milestone&project selectors (#32465)
wxiaoguang Nov 10, 2024
ca8ca8c
Add a doctor check to disable the "Actions" unit for mirrors (#32424)
Zettat123 Nov 10, 2024
172e2a4
Add `DEFAULT_MIRROR_REPO_UNITS` and `DEFAULT_TEMPLATE_REPO_UNITS` opt…
Zettat123 Nov 11, 2024
0afff76
Calculate `PublicOnly` for org membership only once (#32234)
6543 Nov 11, 2024
f7cbb80
Move some functions from issue.go to standalone files (#32468)
lunny Nov 11, 2024
185e1f4
Harden runner updateTask and updateLog api (#32462)
ChristopherHX Nov 11, 2024
9a3cdb3
Fix a number of typescript issues (#32459)
silverwind Nov 11, 2024
afea8b7
Refactor LFS SSH and internal routers (#32473)
wxiaoguang Nov 12, 2024
cf73089
cargo registry - respect renamed dependencies (#32430)
usbalbin Nov 12, 2024
9b95175
Limit org member view of restricted users (#32211)
6543 Nov 12, 2024
b2aecb1
Fix test fixtures for user2/lfs.git (#32477)
wxiaoguang Nov 12, 2024
fc0771b
Update `github.com/meilisearch/meilisearch-go` (#32484)
silverwind Nov 12, 2024
d43c2e2
Update JS and PY dependencies (#32482)
silverwind Nov 12, 2024
b01fa60
Disable Oauth check if oauth disabled (#32368)
lunny Nov 12, 2024
cb817e1
Fix LFS route mock, realm, middleware names (#32488)
wxiaoguang Nov 13, 2024
c155d0f
Perf: add extra index to notification table (#32395)
BoYanZh Nov 13, 2024
d852a8a
Bump CI,Flake and Snap to Node 22 (#32487)
silverwind Nov 13, 2024
d280204
Fix nil panic if repo doesn't exist (#32501)
wxiaoguang Nov 14, 2024
4160316
Refactor render system (#32492)
wxiaoguang Nov 14, 2024
a7a8f4a
Reimplement GetUserOrgsList to make it simple and clear (#32486)
lunny Nov 14, 2024
62869b8
Trim title before insert/update to database to match the size require…
lunny Nov 14, 2024
44c6a26
Remove jQuery import from some files (#32512)
wxiaoguang Nov 14, 2024
ebaf67f
Reduce integration test overhead (#32475)
bohde Nov 14, 2024
7902c01
Add avif image file support (#32508)
wxiaoguang Nov 15, 2024
d1574da
Fix incorrect project page CSS class (#32510)
wxiaoguang Nov 15, 2024
9a60268
Fix oauth2 error handle not return immediately (#32514)
lunny Nov 15, 2024
29aabe4
Remove transaction for archive download (#32186)
lunny Nov 15, 2024
8666feb
Fix `recentupdate` sorting bugs (#32505)
Zettat123 Nov 15, 2024
7341c75
Improve testing and try to fix MySQL hanging (#32515)
wxiaoguang Nov 15, 2024
742a255
Fix large image overflow in comment page (#31740)
charles7668 Nov 15, 2024
193ac86
Fix and refactor markdown rendering (#32522)
wxiaoguang Nov 16, 2024
fd5badc
Fix basic auth with webauthn (#32531)
lunny Nov 16, 2024
d3af46a
Use better name for userinfo structure (#32544)
lunny Nov 18, 2024
b66e71a
Refactor find forks and fix possible bugs that weak permissions check…
lunny Nov 18, 2024
a8a6841
Refactor markup render system (#32533)
wxiaoguang Nov 18, 2024
1f89ae7
Refactor push mirror find and add check for updating push mirror (#32…
lunny Nov 18, 2024
c53f38c
Fix some places which doesn't repsect org full name setting (#32243)
lunny Nov 18, 2024
e6fab4c
oauth2 additional scopes
marcellmars Oct 2, 2024
e2a0187
onlyPublicGroups compiled from form & in db.Find
marcellmars Nov 18, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions options/locale/locale_en-US.ini
Original file line number Diff line number Diff line change
Expand Up @@ -453,6 +453,7 @@ authorize_application = Authorize Application
authorize_redirect_notice = You will be redirected to %s if you authorize this application.
authorize_application_created_by = This application was created by %s.
authorize_application_description = If you grant the access, it will be able to access and write to all your account information, including private repos and organisations.
authorize_application_with_scopes = With scopes: %s
authorize_title = Authorize "%s" to access your account?
authorization_failed = Authorization failed
authorization_failed_desc = The authorization failed because we detected an invalid request. Please contact the maintainer of the app you have tried to authorize.
Expand Down
12 changes: 10 additions & 2 deletions routers/web/auth/oauth2_provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ type userInfoResponse struct {
Username string `json:"preferred_username"`
Email string `json:"email"`
Picture string `json:"picture"`
Groups []string `json:"groups"`
Groups []string `json:"groups,omitempty"`
}

// InfoOAuth manages request for userinfo endpoint
Expand All @@ -104,7 +104,8 @@ func InfoOAuth(ctx *context.Context) {
Picture: ctx.Doer.AvatarLink(ctx),
}

groups, err := oauth2_provider.GetOAuthGroupsForUser(ctx, ctx.Doer)
onlyPublicGroups := ctx.IsSigned && (ctx.FormString("private") == "" || ctx.FormBool("private"))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ctx.FormString("private") == "" || ctx.FormBool("private")
It seems it's not right here? Maybe ctx.FormString("private") == "" || !ctx.FormBool("private").

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

that was verbatim line for the private i copied from routers/web/repo/repo.go#L573

but meanwhile there were some changes with the new public only approach introduced recently.

after those changes oauth2_provider.GetOAuthGroupsForUser had the search into the database so the private can be done via an option without the need to traverse orgs as i originally did.

i changed my code into this:

	form := web.GetForm(ctx).(*forms.AuthorizationForm)
	onlyPublicGroups, _ := oauth2_provider.GrantAdditionalScopes(form.Scope).PublicOnly()
	groups, err := oauth2_provider.GetOAuthGroupsForUser(ctx, ctx.Doer, onlyPublicGroups)
	if err != nil {
		ctx.ServerError("Oauth groups for user", err)
		return
	}
	response.Groups = groups

but dancing around too many repos i ended up in a major mess with my pull request so i think i will just do another pull request which would try to clean it up.

groups, err := oauth2_provider.GetOAuthGroupsForUser(ctx, ctx.Doer, onlyPublicGroups)
if err != nil {
ctx.ServerError("Oauth groups for user", err)
return
Expand Down Expand Up @@ -304,6 +305,13 @@ func AuthorizeOAuth(ctx *context.Context) {
return
}

// check if additional scopes
if oauth2_provider.GrantAdditionalScopes(form.Scope) == auth.AccessTokenScopeAll {
ctx.Data["AdditionalScopes"] = false
} else {
ctx.Data["AdditionalScopes"] = true
}

// show authorize page to grant access
ctx.Data["Application"] = app
ctx.Data["RedirectURI"] = form.RedirectURI
Expand Down
2 changes: 1 addition & 1 deletion services/auth/basic.go
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ func (b *Basic) Verify(req *http.Request, w http.ResponseWriter, store DataStore
}

// check oauth2 token
uid := CheckOAuthAccessToken(req.Context(), authToken)
uid, _ := CheckOAuthAccessToken(req.Context(), authToken)
if uid != 0 {
log.Trace("Basic Authorization: Valid OAuthAccessToken for user[%d]", uid)

Expand Down
24 changes: 14 additions & 10 deletions services/auth/oauth2.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ import (
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/timeutil"
"code.gitea.io/gitea/modules/web/middleware"
"code.gitea.io/gitea/services/oauth2_provider"
oauth2_provider "code.gitea.io/gitea/services/oauth2_provider"
)

// Ensure the struct implements the interface.
Expand All @@ -26,27 +26,30 @@ var (
)

// CheckOAuthAccessToken returns uid of user from oauth token
func CheckOAuthAccessToken(ctx context.Context, accessToken string) int64 {
// + non default openid scopes requested
func CheckOAuthAccessToken(ctx context.Context, accessToken string) (int64, auth_model.AccessTokenScope) {
var accessTokenScope auth_model.AccessTokenScope
// JWT tokens require a "."
if !strings.Contains(accessToken, ".") {
return 0
return 0, accessTokenScope
}
token, err := oauth2_provider.ParseToken(accessToken, oauth2_provider.DefaultSigningKey)
if err != nil {
log.Trace("oauth2.ParseToken: %v", err)
return 0
return 0, accessTokenScope
}
var grant *auth_model.OAuth2Grant
if grant, err = auth_model.GetOAuth2GrantByID(ctx, token.GrantID); err != nil || grant == nil {
return 0
return 0, accessTokenScope
}
if token.Kind != oauth2_provider.KindAccessToken {
return 0
return 0, accessTokenScope
}
if token.ExpiresAt.Before(time.Now()) || token.IssuedAt.After(time.Now()) {
return 0
return 0, accessTokenScope
}
return grant.UserID
accessTokenScope = oauth2_provider.GrantAdditionalScopes(grant.Scope)
return grant.UserID, accessTokenScope
}

// OAuth2 implements the Auth interface and authenticates requests
Expand Down Expand Up @@ -92,10 +95,11 @@ func parseToken(req *http.Request) (string, bool) {
func (o *OAuth2) userIDFromToken(ctx context.Context, tokenSHA string, store DataStore) int64 {
// Let's see if token is valid.
if strings.Contains(tokenSHA, ".") {
uid := CheckOAuthAccessToken(ctx, tokenSHA)
uid, accessTokenScope := CheckOAuthAccessToken(ctx, tokenSHA)

if uid != 0 {
store.GetData()["IsApiToken"] = true
store.GetData()["ApiTokenScope"] = auth_model.AccessTokenScopeAll // fallback to all
store.GetData()["ApiTokenScope"] = accessTokenScope
}
return uid
}
Expand Down
41 changes: 39 additions & 2 deletions services/oauth2_provider/access_token.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ package oauth2_provider //nolint
import (
"context"
"fmt"
"slices"
"strings"

auth "code.gitea.io/gitea/models/auth"
org_model "code.gitea.io/gitea/models/organization"
Expand Down Expand Up @@ -68,6 +70,30 @@ type AccessTokenResponse struct {
IDToken string `json:"id_token,omitempty"`
}

// GrantAdditionalScopes returns valid scopes coming from grant
func GrantAdditionalScopes(grantScopes string) auth.AccessTokenScope {
// scopes_supported from templates/user/auth/oidc_wellknown.tmpl
scopesSupported := []string{
"openid",
"profile",
"email",
"groups",
}

var tokenScopes []string
for _, tokenScope := range strings.Split(grantScopes, " ") {
if slices.Index(scopesSupported, tokenScope) == -1 {
tokenScopes = append(tokenScopes, tokenScope)
}
}

accessTokenScope := auth.AccessTokenScope(strings.Join(tokenScopes, ","))
if accessTokenWithAdditionalScopes, err := accessTokenScope.Normalize(); err == nil && len(tokenScopes) > 0 {
return accessTokenWithAdditionalScopes
}
return auth.AccessTokenScopeAll
}

func NewAccessTokenResponse(ctx context.Context, grant *auth.OAuth2Grant, serverKey, clientKey JWTSigningKey) (*AccessTokenResponse, *AccessTokenError) {
if setting.OAuth2.InvalidateRefreshTokens {
if err := grant.IncreaseCounter(ctx); err != nil {
Expand Down Expand Up @@ -160,7 +186,10 @@ func NewAccessTokenResponse(ctx context.Context, grant *auth.OAuth2Grant, server
idToken.EmailVerified = user.IsActive
}
if grant.ScopeContains("groups") {
groups, err := GetOAuthGroupsForUser(ctx, user)
accessTokenScope := GrantAdditionalScopes(grant.Scope)
onlyPublicGroups, _ := accessTokenScope.PublicOnly()

groups, err := GetOAuthGroupsForUser(ctx, user, onlyPublicGroups)
if err != nil {
log.Error("Error getting groups: %v", err)
return nil, &AccessTokenError{
Expand Down Expand Up @@ -191,14 +220,22 @@ func NewAccessTokenResponse(ctx context.Context, grant *auth.OAuth2Grant, server

// returns a list of "org" and "org:team" strings,
// that the given user is a part of.
func GetOAuthGroupsForUser(ctx context.Context, user *user_model.User) ([]string, error) {
func GetOAuthGroupsForUser(ctx context.Context, user *user_model.User, onlyPublicGroups bool) ([]string, error) {
orgs, err := org_model.GetUserOrgsList(ctx, user)
if err != nil {
return nil, fmt.Errorf("GetUserOrgList: %w", err)
}

var groups []string
for _, org := range orgs {
if onlyPublicGroups {
if public, err := org_model.IsPublicMembership(ctx, org.ID, user.ID); err == nil {
if !public || !org.Visibility.IsPublic() {
continue
}
}
}

groups = append(groups, org.Name)
teams, err := org.LoadTeams(ctx)
if err != nil {
Expand Down
35 changes: 35 additions & 0 deletions services/oauth2_provider/additional_scopes_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
// Copyright 2024 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT

package oauth2_provider //nolint

import (
"testing"

"github.com/stretchr/testify/assert"
)

func TestGrantAdditionalScopes(t *testing.T) {
tests := []struct {
grantScopes string
expectedScopes string
}{
{"openid profile email", "all"},
{"openid profile email groups", "all"},
{"openid profile email all", "all"},
{"openid profile email read:user all", "all"},
{"openid profile email groups read:user", "read:user"},
{"read:user read:repository", "read:repository,read:user"},
{"read:user write:issue public-only", "public-only,write:issue,read:user"},
{"openid profile email read:user", "read:user"},
{"read:invalid_scope", "all"},
{"read:invalid_scope,write:scope_invalid,just-plain-wrong", "all"},
}

for _, test := range tests {
t.Run(test.grantScopes, func(t *testing.T) {
result := GrantAdditionalScopes(test.grantScopes)
assert.Equal(t, test.expectedScopes, string(result))
})
}
}
3 changes: 3 additions & 0 deletions templates/user/auth/grant.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,11 @@
<div class="ui attached segment">
{{template "base/alert" .}}
<p>
{{if not .AdditionalScopes}}
<b>{{ctx.Locale.Tr "auth.authorize_application_description"}}</b><br>
{{end}}
{{ctx.Locale.Tr "auth.authorize_application_created_by" .ApplicationCreatorLinkHTML}}
<p>{{ctx.Locale.Tr "auth.authorize_application_with_scopes" .Scope}}</p>
</p>
</div>
<div class="ui attached segment">
Expand Down
28 changes: 28 additions & 0 deletions tests/integration/integration_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import (
"net/url"
"os"
"path/filepath"
"strconv"
"strings"
"sync/atomic"
"testing"
Expand Down Expand Up @@ -502,3 +503,30 @@ func GetAnonymousCSRFToken(t testing.TB, session *TestSession) string {
require.NotEmpty(t, csrfToken)
return csrfToken
}

func loginUserWithPasswordRemember(t testing.TB, userName, password string, rememberMe bool) *TestSession {
t.Helper()
req := NewRequest(t, "GET", "/user/login")
resp := MakeRequest(t, req, http.StatusOK)

doc := NewHTMLParser(t, resp.Body)
req = NewRequestWithValues(t, "POST", "/user/login", map[string]string{
"_csrf": doc.GetCSRF(),
"user_name": userName,
"password": password,
"remember": strconv.FormatBool(rememberMe),
})
resp = MakeRequest(t, req, http.StatusSeeOther)

ch := http.Header{}
ch.Add("Cookie", strings.Join(resp.Header()["Set-Cookie"], ";"))
cr := http.Request{Header: ch}

session := emptyTestSession(t)

baseURL, err := url.Parse(setting.AppURL)
require.NoError(t, err)
session.jar.SetCookies(baseURL, cr.Cookies())

return session
}
Loading
Loading