mirror of
https://codeberg.org/forgejo/forgejo.git
synced 2026-05-12 22:10:25 +00:00
feat: add APIContext.Reducer computed from access token
This commit is contained in:
parent
44c18465b5
commit
635f13a07e
10 changed files with 426 additions and 1 deletions
|
|
@ -14,7 +14,6 @@ forgejo.org/models
|
|||
IsErrMergeDivergingFastForwardOnly
|
||||
|
||||
forgejo.org/models/auth
|
||||
GetRepositoriesAccessibleWithToken
|
||||
WebAuthnCredentials
|
||||
|
||||
forgejo.org/models/db
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ import (
|
|||
"forgejo.org/modules/setting"
|
||||
"forgejo.org/routers/common"
|
||||
"forgejo.org/services/auth"
|
||||
"forgejo.org/services/authz"
|
||||
"forgejo.org/services/context"
|
||||
|
||||
"github.com/go-chi/cors"
|
||||
|
|
@ -64,6 +65,10 @@ func apiAuth(authMethod auth.Method) func(*context.APIContext) {
|
|||
ctx.Doer = ar.Doer
|
||||
ctx.IsSigned = ar.Doer != nil
|
||||
ctx.IsBasicAuth = ar.IsBasicAuth
|
||||
if ctx.Reducer == nil {
|
||||
// Ensure ctx.Reducer isn't nil, but has no impact:
|
||||
ctx.Reducer = &authz.AllAccessAuthorizationReducer{}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
68
routers/api/shared/middleware_test.go
Normal file
68
routers/api/shared/middleware_test.go
Normal file
|
|
@ -0,0 +1,68 @@
|
|||
// Copyright 2026 The Forgejo Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
package shared
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
|
||||
"forgejo.org/modules/json"
|
||||
"forgejo.org/modules/web"
|
||||
"forgejo.org/routers/common"
|
||||
"forgejo.org/services/authz"
|
||||
"forgejo.org/services/context"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestReducer(t *testing.T) {
|
||||
makeRecorder := func() *httptest.ResponseRecorder {
|
||||
buff := bytes.NewBufferString("")
|
||||
recorder := httptest.NewRecorder()
|
||||
recorder.Body = buff
|
||||
return recorder
|
||||
}
|
||||
|
||||
r := web.NewRoute()
|
||||
r.Use(common.ProtocolMiddlewares()...)
|
||||
r.Use(Middlewares()...)
|
||||
|
||||
type ReducerInfo struct {
|
||||
IsSigned bool
|
||||
IsNil bool
|
||||
IsAllAccess bool
|
||||
}
|
||||
|
||||
r.Get("/api/test", func(ctx *context.APIContext) {
|
||||
retval := ReducerInfo{
|
||||
IsSigned: ctx.IsSigned,
|
||||
IsNil: ctx.Reducer == nil,
|
||||
}
|
||||
|
||||
_, isAllAccess := ctx.Reducer.(*authz.AllAccessAuthorizationReducer)
|
||||
retval.IsAllAccess = isAllAccess
|
||||
|
||||
ctx.JSON(http.StatusOK, retval)
|
||||
})
|
||||
|
||||
// shared middleware ensures that `APIContext.Reducer` is not nil, and so that's the only test required in this
|
||||
// package's scope -- an anonymous request and `Reducer` is not nil:
|
||||
t.Run("anonymous", func(t *testing.T) {
|
||||
recorder := makeRecorder()
|
||||
req, err := http.NewRequest("GET", "http://localhost:8000/api/test", nil)
|
||||
require.NoError(t, err)
|
||||
r.ServeHTTP(recorder, req)
|
||||
assert.Equal(t, http.StatusOK, recorder.Code)
|
||||
|
||||
var reducerInfo ReducerInfo
|
||||
require.NoError(t, json.Unmarshal(recorder.Body.Bytes(), &reducerInfo))
|
||||
|
||||
assert.False(t, reducerInfo.IsSigned)
|
||||
assert.False(t, reducerInfo.IsNil)
|
||||
assert.True(t, reducerInfo.IsAllAccess)
|
||||
})
|
||||
}
|
||||
38
routers/api/v1/TestTokenRequiresScopes/access_token.yml
Normal file
38
routers/api/v1/TestTokenRequiresScopes/access_token.yml
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
-
|
||||
id: 6
|
||||
uid: 2
|
||||
name: Unrestricted Token
|
||||
# token: 4a0c970da8bf58408a8c22264b2ac1ff47dadcce
|
||||
token_hash: e26d1456df98b47394c9e8750023e690426e0209de1f719edd4b852651fb69fb349e15f6d42912a0899d055dc7e595be077a
|
||||
token_salt: XKbbVcQbz-ahT8LQfXB5C_
|
||||
token_last_eight: 47dadcce
|
||||
created_unix: 946687980
|
||||
updated_unix: 946687980
|
||||
scope: write:repository
|
||||
resource_all_repos: true
|
||||
|
||||
-
|
||||
id: 7
|
||||
uid: 2
|
||||
name: Public Resources Only Token
|
||||
# token: 83909b5b978acc5620ae0c7b0e55b548da2e26b5
|
||||
token_hash: 211bc49d6c48a1f553d5f4400d903f20280ec3851a06f0e300253c042beb608191a8bcd52cb5d230a9096bbb9494b9d4cabf
|
||||
token_salt: AD89WTwe8hCHGEUF7AG8BO
|
||||
token_last_eight: da2e26b5
|
||||
created_unix: 946687980
|
||||
updated_unix: 946687980
|
||||
scope: public-only,write:repository
|
||||
resource_all_repos: true
|
||||
|
||||
-
|
||||
id: 8
|
||||
uid: 2
|
||||
name: Specific Repos Only Token
|
||||
# token: 46088605ec804b43ebd15cef1b3f210c31b066dd
|
||||
token_hash: cd2739ac5894f25f0fd0e4b93e92b382d7bde82b09b791fa8aed6a514f8ffa9fda90fdb943c56f7b2eac78c9fd484ab3ecdf
|
||||
token_salt: IhB81EQbYWHsVJGEGx587X
|
||||
token_last_eight: 31b066dd
|
||||
created_unix: 946687980
|
||||
updated_unix: 946687980
|
||||
scope: write:repository
|
||||
resource_all_repos: false
|
||||
|
|
@ -86,6 +86,7 @@ import (
|
|||
"forgejo.org/routers/api/v1/user"
|
||||
"forgejo.org/services/actions"
|
||||
"forgejo.org/services/auth"
|
||||
"forgejo.org/services/authz"
|
||||
"forgejo.org/services/context"
|
||||
"forgejo.org/services/forms"
|
||||
redirect_service "forgejo.org/services/redirect"
|
||||
|
|
@ -365,6 +366,20 @@ func tokenRequiresScopes(requiredScopeCategories ...auth_model.AccessTokenScopeC
|
|||
|
||||
// assign to true so that those searching should only filter public repositories/users/organizations
|
||||
ctx.PublicOnly = publicOnly
|
||||
|
||||
reducer, ok := ctx.Data["ApiTokenReducer"].(authz.AuthorizationReducer)
|
||||
if ok {
|
||||
ctx.Reducer = reducer
|
||||
} else {
|
||||
// No "ApiTokenReducer" will be populated if the auth method wasn't an PAT. In this case, we populate
|
||||
// `ctx.Reducer` so no nil checks are needed, and we respect the scope `PublicOnly()` so that it it's safe
|
||||
// to just rely on `ctx.Reducer` to account for public-only access:
|
||||
if ctx.PublicOnly {
|
||||
ctx.Reducer = &authz.PublicReposAuthorizationReducer{}
|
||||
} else {
|
||||
ctx.Reducer = &authz.AllAccessAuthorizationReducer{}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
266
routers/api/v1/api_test.go
Normal file
266
routers/api/v1/api_test.go
Normal file
|
|
@ -0,0 +1,266 @@
|
|||
// Copyright 2026 The Forgejo Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
package v1
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
auth_model "forgejo.org/models/auth"
|
||||
"forgejo.org/models/db"
|
||||
"forgejo.org/models/unittest"
|
||||
"forgejo.org/modules/json"
|
||||
"forgejo.org/modules/jwtx"
|
||||
"forgejo.org/modules/test"
|
||||
"forgejo.org/modules/web"
|
||||
"forgejo.org/routers/api/shared"
|
||||
"forgejo.org/routers/common"
|
||||
"forgejo.org/services/auth/source/oauth2"
|
||||
"forgejo.org/services/authz"
|
||||
"forgejo.org/services/context"
|
||||
|
||||
"github.com/golang-jwt/jwt/v5"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestTokenRequiresScopes(t *testing.T) {
|
||||
defer unittest.OverrideFixtures("routers/api/v1/TestTokenRequiresScopes")()
|
||||
require.NoError(t, unittest.PrepareTestDatabase())
|
||||
|
||||
makeRecorder := func() *httptest.ResponseRecorder {
|
||||
buff := bytes.NewBufferString("")
|
||||
recorder := httptest.NewRecorder()
|
||||
recorder.Body = buff
|
||||
return recorder
|
||||
}
|
||||
|
||||
r := web.NewRoute()
|
||||
r.Use(common.ProtocolMiddlewares()...)
|
||||
r.Use(shared.Middlewares()...)
|
||||
|
||||
type ReducerInfo struct {
|
||||
IsSigned bool
|
||||
IsNil bool
|
||||
IsAllAccess bool
|
||||
IsPublicAccess bool
|
||||
IsSpecificAccess bool
|
||||
}
|
||||
|
||||
r.Get("/api/test", tokenRequiresScopes(auth_model.AccessTokenScopeCategoryRepository), func(ctx *context.APIContext) {
|
||||
retval := ReducerInfo{
|
||||
IsSigned: ctx.IsSigned,
|
||||
IsNil: ctx.Reducer == nil,
|
||||
}
|
||||
|
||||
_, isAllAccess := ctx.Reducer.(*authz.AllAccessAuthorizationReducer)
|
||||
retval.IsAllAccess = isAllAccess
|
||||
|
||||
_, isPublicAccess := ctx.Reducer.(*authz.PublicReposAuthorizationReducer)
|
||||
retval.IsPublicAccess = isPublicAccess
|
||||
|
||||
_, isSpecificAccess := ctx.Reducer.(*authz.SpecificReposAuthorizationReducer)
|
||||
retval.IsSpecificAccess = isSpecificAccess
|
||||
|
||||
ctx.JSON(http.StatusOK, retval)
|
||||
})
|
||||
|
||||
t.Run("Basic Auth w/ PAT", func(t *testing.T) {
|
||||
t.Run("unrestricted access token", func(t *testing.T) {
|
||||
recorder := makeRecorder()
|
||||
req, err := http.NewRequest("GET", "http://localhost:8000/api/test", nil)
|
||||
req.SetBasicAuth("token", "4a0c970da8bf58408a8c22264b2ac1ff47dadcce")
|
||||
require.NoError(t, err)
|
||||
r.ServeHTTP(recorder, req)
|
||||
assert.Equal(t, http.StatusOK, recorder.Code)
|
||||
|
||||
var reducerInfo ReducerInfo
|
||||
require.NoError(t, json.Unmarshal(recorder.Body.Bytes(), &reducerInfo))
|
||||
|
||||
assert.True(t, reducerInfo.IsSigned)
|
||||
assert.False(t, reducerInfo.IsNil)
|
||||
assert.True(t, reducerInfo.IsAllAccess)
|
||||
assert.False(t, reducerInfo.IsPublicAccess)
|
||||
assert.False(t, reducerInfo.IsSpecificAccess)
|
||||
})
|
||||
|
||||
t.Run("public-only access token", func(t *testing.T) {
|
||||
recorder := makeRecorder()
|
||||
req, err := http.NewRequest("GET", "http://localhost:8000/api/test", nil)
|
||||
req.SetBasicAuth("token", "83909b5b978acc5620ae0c7b0e55b548da2e26b5")
|
||||
require.NoError(t, err)
|
||||
r.ServeHTTP(recorder, req)
|
||||
assert.Equal(t, http.StatusOK, recorder.Code)
|
||||
|
||||
var reducerInfo ReducerInfo
|
||||
require.NoError(t, json.Unmarshal(recorder.Body.Bytes(), &reducerInfo))
|
||||
|
||||
assert.True(t, reducerInfo.IsSigned)
|
||||
assert.False(t, reducerInfo.IsNil)
|
||||
assert.False(t, reducerInfo.IsAllAccess)
|
||||
assert.True(t, reducerInfo.IsPublicAccess)
|
||||
assert.False(t, reducerInfo.IsSpecificAccess)
|
||||
})
|
||||
|
||||
t.Run("specific-repo access token", func(t *testing.T) {
|
||||
recorder := makeRecorder()
|
||||
req, err := http.NewRequest("GET", "http://localhost:8000/api/test", nil)
|
||||
req.SetBasicAuth("token", "46088605ec804b43ebd15cef1b3f210c31b066dd")
|
||||
require.NoError(t, err)
|
||||
r.ServeHTTP(recorder, req)
|
||||
assert.Equal(t, http.StatusOK, recorder.Code)
|
||||
|
||||
var reducerInfo ReducerInfo
|
||||
require.NoError(t, json.Unmarshal(recorder.Body.Bytes(), &reducerInfo))
|
||||
|
||||
assert.True(t, reducerInfo.IsSigned)
|
||||
assert.False(t, reducerInfo.IsNil)
|
||||
assert.False(t, reducerInfo.IsAllAccess)
|
||||
assert.False(t, reducerInfo.IsPublicAccess)
|
||||
assert.True(t, reducerInfo.IsSpecificAccess)
|
||||
})
|
||||
})
|
||||
|
||||
t.Run("Token Auth", func(t *testing.T) {
|
||||
t.Run("unrestricted access token", func(t *testing.T) {
|
||||
recorder := makeRecorder()
|
||||
req, err := http.NewRequest("GET", "http://localhost:8000/api/test", nil)
|
||||
req.Header.Set("Authorization", "token 4a0c970da8bf58408a8c22264b2ac1ff47dadcce")
|
||||
require.NoError(t, err)
|
||||
r.ServeHTTP(recorder, req)
|
||||
assert.Equal(t, http.StatusOK, recorder.Code)
|
||||
|
||||
var reducerInfo ReducerInfo
|
||||
require.NoError(t, json.Unmarshal(recorder.Body.Bytes(), &reducerInfo))
|
||||
|
||||
assert.True(t, reducerInfo.IsSigned)
|
||||
assert.False(t, reducerInfo.IsNil)
|
||||
assert.True(t, reducerInfo.IsAllAccess)
|
||||
assert.False(t, reducerInfo.IsPublicAccess)
|
||||
assert.False(t, reducerInfo.IsSpecificAccess)
|
||||
})
|
||||
|
||||
t.Run("public-only access token", func(t *testing.T) {
|
||||
recorder := makeRecorder()
|
||||
req, err := http.NewRequest("GET", "http://localhost:8000/api/test", nil)
|
||||
req.Header.Set("Authorization", "token 83909b5b978acc5620ae0c7b0e55b548da2e26b5")
|
||||
require.NoError(t, err)
|
||||
r.ServeHTTP(recorder, req)
|
||||
assert.Equal(t, http.StatusOK, recorder.Code)
|
||||
|
||||
var reducerInfo ReducerInfo
|
||||
require.NoError(t, json.Unmarshal(recorder.Body.Bytes(), &reducerInfo))
|
||||
|
||||
assert.True(t, reducerInfo.IsSigned)
|
||||
assert.False(t, reducerInfo.IsNil)
|
||||
assert.False(t, reducerInfo.IsAllAccess)
|
||||
assert.True(t, reducerInfo.IsPublicAccess)
|
||||
assert.False(t, reducerInfo.IsSpecificAccess)
|
||||
})
|
||||
|
||||
t.Run("specific-repo access token", func(t *testing.T) {
|
||||
recorder := makeRecorder()
|
||||
req, err := http.NewRequest("GET", "http://localhost:8000/api/test", nil)
|
||||
req.Header.Set("Authorization", "token 46088605ec804b43ebd15cef1b3f210c31b066dd")
|
||||
require.NoError(t, err)
|
||||
r.ServeHTTP(recorder, req)
|
||||
assert.Equal(t, http.StatusOK, recorder.Code)
|
||||
|
||||
var reducerInfo ReducerInfo
|
||||
require.NoError(t, json.Unmarshal(recorder.Body.Bytes(), &reducerInfo))
|
||||
|
||||
assert.True(t, reducerInfo.IsSigned)
|
||||
assert.False(t, reducerInfo.IsNil)
|
||||
assert.False(t, reducerInfo.IsAllAccess)
|
||||
assert.False(t, reducerInfo.IsPublicAccess)
|
||||
assert.True(t, reducerInfo.IsSpecificAccess)
|
||||
})
|
||||
})
|
||||
|
||||
t.Run("OAuth", func(t *testing.T) {
|
||||
signingKey, err := jwtx.CreateSigningKey("HS256", make([]byte, 32))
|
||||
require.NoError(t, err)
|
||||
defer test.MockVariableValue(&oauth2.DefaultSigningKey, signingKey)()
|
||||
|
||||
t.Run("unrestricted grant", func(t *testing.T) {
|
||||
grant := &auth_model.OAuth2Grant{
|
||||
UserID: 2,
|
||||
ApplicationID: 100, // fake, but required here for unique constraint
|
||||
Scope: "write:repository",
|
||||
}
|
||||
_, err = db.GetEngine(t.Context()).Insert(grant)
|
||||
require.NoError(t, err)
|
||||
|
||||
token := oauth2.Token{
|
||||
GrantID: grant.ID,
|
||||
Type: oauth2.TypeAccessToken,
|
||||
Counter: 100,
|
||||
RegisteredClaims: jwt.RegisteredClaims{
|
||||
IssuedAt: jwt.NewNumericDate(time.Now().Add(-1 * time.Hour)),
|
||||
ExpiresAt: jwt.NewNumericDate(time.Now().Add(1 * time.Hour)),
|
||||
},
|
||||
}
|
||||
signed, err := token.SignToken(oauth2.DefaultSigningKey)
|
||||
require.NoError(t, err)
|
||||
|
||||
recorder := makeRecorder()
|
||||
req, err := http.NewRequest("GET", "http://localhost:8000/api/test", nil)
|
||||
req.Header.Set("Authorization", fmt.Sprintf("bearer %s", signed))
|
||||
require.NoError(t, err)
|
||||
r.ServeHTTP(recorder, req)
|
||||
assert.Equal(t, http.StatusOK, recorder.Code)
|
||||
|
||||
var reducerInfo ReducerInfo
|
||||
require.NoError(t, json.Unmarshal(recorder.Body.Bytes(), &reducerInfo))
|
||||
|
||||
assert.True(t, reducerInfo.IsSigned)
|
||||
assert.False(t, reducerInfo.IsNil)
|
||||
assert.True(t, reducerInfo.IsAllAccess)
|
||||
assert.False(t, reducerInfo.IsPublicAccess)
|
||||
assert.False(t, reducerInfo.IsSpecificAccess)
|
||||
})
|
||||
|
||||
t.Run("public-only grant", func(t *testing.T) {
|
||||
grant := &auth_model.OAuth2Grant{
|
||||
UserID: 2,
|
||||
ApplicationID: 101, // fake, but required here for unique constraint
|
||||
Scope: "write:repository public-only",
|
||||
}
|
||||
_, err = db.GetEngine(t.Context()).Insert(grant)
|
||||
require.NoError(t, err)
|
||||
|
||||
token := oauth2.Token{
|
||||
GrantID: grant.ID,
|
||||
Type: oauth2.TypeAccessToken,
|
||||
Counter: 100,
|
||||
RegisteredClaims: jwt.RegisteredClaims{
|
||||
IssuedAt: jwt.NewNumericDate(time.Now().Add(-1 * time.Hour)),
|
||||
ExpiresAt: jwt.NewNumericDate(time.Now().Add(1 * time.Hour)),
|
||||
},
|
||||
}
|
||||
signed, err := token.SignToken(oauth2.DefaultSigningKey)
|
||||
require.NoError(t, err)
|
||||
|
||||
recorder := makeRecorder()
|
||||
req, err := http.NewRequest("GET", "http://localhost:8000/api/test", nil)
|
||||
req.Header.Set("Authorization", fmt.Sprintf("bearer %s", signed))
|
||||
require.NoError(t, err)
|
||||
r.ServeHTTP(recorder, req)
|
||||
assert.Equal(t, http.StatusOK, recorder.Code)
|
||||
|
||||
var reducerInfo ReducerInfo
|
||||
require.NoError(t, json.Unmarshal(recorder.Body.Bytes(), &reducerInfo))
|
||||
|
||||
assert.True(t, reducerInfo.IsSigned)
|
||||
assert.False(t, reducerInfo.IsNil)
|
||||
assert.False(t, reducerInfo.IsAllAccess)
|
||||
assert.True(t, reducerInfo.IsPublicAccess)
|
||||
assert.False(t, reducerInfo.IsSpecificAccess)
|
||||
})
|
||||
})
|
||||
}
|
||||
14
routers/api/v1/main_test.go
Normal file
14
routers/api/v1/main_test.go
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
// Copyright 2026 The Forgejo Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
package v1
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"forgejo.org/models/unittest"
|
||||
)
|
||||
|
||||
func TestMain(m *testing.M) {
|
||||
unittest.MainTest(m)
|
||||
}
|
||||
|
|
@ -17,6 +17,7 @@ import (
|
|||
"forgejo.org/modules/setting"
|
||||
"forgejo.org/modules/util"
|
||||
"forgejo.org/modules/web/middleware"
|
||||
"forgejo.org/services/authz"
|
||||
)
|
||||
|
||||
// Ensure the struct implements the interface.
|
||||
|
|
@ -102,6 +103,14 @@ func (b *Basic) Verify(req *http.Request, w http.ResponseWriter, store DataStore
|
|||
|
||||
store.GetData()["IsApiToken"] = true
|
||||
store.GetData()["ApiTokenScope"] = token.Scope
|
||||
|
||||
reducer, err := authz.GetAuthorizationReducerForAccessToken(req.Context(), token)
|
||||
if err != nil {
|
||||
log.Error("authz.GetAuthorizationReducerForAccessToken: %v", err)
|
||||
return nil, err
|
||||
}
|
||||
store.GetData()["ApiTokenReducer"] = reducer
|
||||
|
||||
return u, nil
|
||||
} else if !auth_model.IsErrAccessTokenNotExist(err) && !auth_model.IsErrAccessTokenEmpty(err) {
|
||||
log.Error("GetAccessTokenBySha: %v", err)
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@ import (
|
|||
"forgejo.org/modules/web/middleware"
|
||||
"forgejo.org/services/actions"
|
||||
"forgejo.org/services/auth/source/oauth2"
|
||||
"forgejo.org/services/authz"
|
||||
)
|
||||
|
||||
// Ensure the struct implements the interface.
|
||||
|
|
@ -200,6 +201,14 @@ func (o *OAuth2) userIDFromToken(ctx context.Context, tokenSHA string, store Dat
|
|||
}
|
||||
store.GetData()["IsApiToken"] = true
|
||||
store.GetData()["ApiTokenScope"] = t.Scope
|
||||
|
||||
reducer, err := authz.GetAuthorizationReducerForAccessToken(ctx, t)
|
||||
if err != nil {
|
||||
log.Error("authz.GetAuthorizationReducerForAccessToken: %v", err)
|
||||
return 0, err
|
||||
}
|
||||
store.GetData()["ApiTokenReducer"] = reducer
|
||||
|
||||
return t.UID, nil
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -24,6 +24,7 @@ import (
|
|||
"forgejo.org/modules/setting"
|
||||
"forgejo.org/modules/web"
|
||||
web_types "forgejo.org/modules/web/types"
|
||||
"forgejo.org/services/authz"
|
||||
|
||||
"code.forgejo.org/go-chi/cache"
|
||||
)
|
||||
|
|
@ -47,6 +48,7 @@ type APIContext struct {
|
|||
QuotaGroup *quota_model.Group
|
||||
QuotaRule *quota_model.Rule
|
||||
PublicOnly bool // Whether the request is for a public endpoint
|
||||
Reducer authz.AuthorizationReducer
|
||||
}
|
||||
|
||||
func init() {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue