aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--modules/context/permission.go33
-rw-r--r--routers/api/packages/api.go27
-rw-r--r--routers/web/repo/attachment.go5
-rw-r--r--routers/web/repo/http.go11
-rw-r--r--services/auth/basic.go1
-rw-r--r--services/lfs/locks.go20
-rw-r--r--services/lfs/server.go15
-rw-r--r--tests/integration/api_packages_npm_test.go3
-rw-r--r--tests/integration/api_packages_nuget_test.go3
-rw-r--r--tests/integration/api_packages_pub_test.go3
-rw-r--r--tests/integration/api_packages_vagrant_test.go3
11 files changed, 117 insertions, 7 deletions
diff --git a/modules/context/permission.go b/modules/context/permission.go
index 8cb5d09eb9..cc53fb99ed 100644
--- a/modules/context/permission.go
+++ b/modules/context/permission.go
@@ -4,6 +4,10 @@
package context
import (
+ "net/http"
+
+ auth_model "code.gitea.io/gitea/models/auth"
+ repo_model "code.gitea.io/gitea/models/repo"
"code.gitea.io/gitea/models/unit"
"code.gitea.io/gitea/modules/log"
)
@@ -106,3 +110,32 @@ func RequireRepoReaderOr(unitTypes ...unit.Type) func(ctx *Context) {
ctx.NotFound(ctx.Req.URL.RequestURI(), nil)
}
}
+
+// RequireRepoScopedToken check whether personal access token has repo scope
+func CheckRepoScopedToken(ctx *Context, repo *repo_model.Repository) {
+ if !ctx.IsBasicAuth || ctx.Data["IsApiToken"] != true {
+ return
+ }
+
+ var err error
+ scope, ok := ctx.Data["ApiTokenScope"].(auth_model.AccessTokenScope)
+ if ok { // it's a personal access token but not oauth2 token
+ var scopeMatched bool
+ scopeMatched, err = scope.HasScope(auth_model.AccessTokenScopeRepo)
+ if err != nil {
+ ctx.ServerError("HasScope", err)
+ return
+ }
+ if !scopeMatched && !repo.IsPrivate {
+ scopeMatched, err = scope.HasScope(auth_model.AccessTokenScopePublicRepo)
+ if err != nil {
+ ctx.ServerError("HasScope", err)
+ return
+ }
+ }
+ if !scopeMatched {
+ ctx.Error(http.StatusForbidden)
+ return
+ }
+ }
+}
diff --git a/routers/api/packages/api.go b/routers/api/packages/api.go
index 8bf5dbab35..d5acd3d261 100644
--- a/routers/api/packages/api.go
+++ b/routers/api/packages/api.go
@@ -9,6 +9,7 @@ import (
"regexp"
"strings"
+ auth_model "code.gitea.io/gitea/models/auth"
"code.gitea.io/gitea/models/perm"
"code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/log"
@@ -36,6 +37,32 @@ import (
func reqPackageAccess(accessMode perm.AccessMode) func(ctx *context.Context) {
return func(ctx *context.Context) {
+ if ctx.Data["IsApiToken"] == true {
+ scope, ok := ctx.Data["ApiTokenScope"].(auth_model.AccessTokenScope)
+ if ok { // it's a personal access token but not oauth2 token
+ scopeMatched := false
+ var err error
+ if accessMode == perm.AccessModeRead {
+ scopeMatched, err = scope.HasScope(auth_model.AccessTokenScopeReadPackage)
+ if err != nil {
+ ctx.Error(http.StatusInternalServerError, "HasScope", err.Error())
+ return
+ }
+ } else if accessMode == perm.AccessModeWrite {
+ scopeMatched, err = scope.HasScope(auth_model.AccessTokenScopeWritePackage)
+ if err != nil {
+ ctx.Error(http.StatusInternalServerError, "HasScope", err.Error())
+ return
+ }
+ }
+ if !scopeMatched {
+ ctx.Resp.Header().Set("WWW-Authenticate", `Basic realm="Gitea Package API"`)
+ ctx.Error(http.StatusUnauthorized, "reqPackageAccess", "user should have specific permission or be a site admin")
+ return
+ }
+ }
+ }
+
if ctx.Package.AccessMode < accessMode && !ctx.IsUserSiteAdmin() {
ctx.Resp.Header().Set("WWW-Authenticate", `Basic realm="Gitea Package API"`)
ctx.Error(http.StatusUnauthorized, "reqPackageAccess", "user should have specific permission or be a site admin")
diff --git a/routers/web/repo/attachment.go b/routers/web/repo/attachment.go
index 9fb9cb00bf..c6ea4e3cdb 100644
--- a/routers/web/repo/attachment.go
+++ b/routers/web/repo/attachment.go
@@ -110,6 +110,11 @@ func ServeAttachment(ctx *context.Context, uuid string) {
return
}
} else { // If we have the repository we check access
+ context.CheckRepoScopedToken(ctx, repository)
+ if ctx.Written() {
+ return
+ }
+
perm, err := access_model.GetUserRepoPermission(ctx, repository, ctx.Doer)
if err != nil {
ctx.Error(http.StatusInternalServerError, "GetUserRepoPermission", err.Error())
diff --git a/routers/web/repo/http.go b/routers/web/repo/http.go
index a01bb4f28e..4e45a9b6e2 100644
--- a/routers/web/repo/http.go
+++ b/routers/web/repo/http.go
@@ -19,7 +19,7 @@ import (
"time"
actions_model "code.gitea.io/gitea/models/actions"
- "code.gitea.io/gitea/models/auth"
+ auth_model "code.gitea.io/gitea/models/auth"
"code.gitea.io/gitea/models/perm"
access_model "code.gitea.io/gitea/models/perm/access"
repo_model "code.gitea.io/gitea/models/repo"
@@ -152,13 +152,18 @@ func httpBase(ctx *context.Context) (h *serviceHandler) {
return
}
+ context.CheckRepoScopedToken(ctx, repo)
+ if ctx.Written() {
+ return
+ }
+
if ctx.IsBasicAuth && ctx.Data["IsApiToken"] != true && ctx.Data["IsActionsToken"] != true {
- _, err = auth.GetTwoFactorByUID(ctx.Doer.ID)
+ _, err = auth_model.GetTwoFactorByUID(ctx.Doer.ID)
if err == nil {
// TODO: This response should be changed to "invalid credentials" for security reasons once the expectation behind it (creating an app token to authenticate) is properly documented
ctx.PlainText(http.StatusUnauthorized, "Users with two-factor authentication enabled cannot perform HTTP/HTTPS operations via plain username and password. Please create and use a personal access token on the user settings page")
return
- } else if !auth.IsErrTwoFactorNotEnrolled(err) {
+ } else if !auth_model.IsErrTwoFactorNotEnrolled(err) {
ctx.ServerError("IsErrTwoFactorNotEnrolled", err)
return
}
diff --git a/services/auth/basic.go b/services/auth/basic.go
index dc03780905..36480568ff 100644
--- a/services/auth/basic.go
+++ b/services/auth/basic.go
@@ -102,6 +102,7 @@ func (b *Basic) Verify(req *http.Request, w http.ResponseWriter, store DataStore
}
store.GetData()["IsApiToken"] = true
+ store.GetData()["ApiTokenScope"] = token.Scope
return u, nil
} else if !auth_model.IsErrAccessTokenNotExist(err) && !auth_model.IsErrAccessTokenEmpty(err) {
log.Error("GetAccessTokenBySha: %v", err)
diff --git a/services/lfs/locks.go b/services/lfs/locks.go
index d963d9ab57..1e5db6bd20 100644
--- a/services/lfs/locks.go
+++ b/services/lfs/locks.go
@@ -58,6 +58,11 @@ func GetListLockHandler(ctx *context.Context) {
}
repository.MustOwner(ctx)
+ context.CheckRepoScopedToken(ctx, repository)
+ if ctx.Written() {
+ return
+ }
+
authenticated := authenticate(ctx, repository, rv.Authorization, true, false)
if !authenticated {
ctx.Resp.Header().Set("WWW-Authenticate", "Basic realm=gitea-lfs")
@@ -145,6 +150,11 @@ func PostLockHandler(ctx *context.Context) {
}
repository.MustOwner(ctx)
+ context.CheckRepoScopedToken(ctx, repository)
+ if ctx.Written() {
+ return
+ }
+
authenticated := authenticate(ctx, repository, authorization, true, true)
if !authenticated {
ctx.Resp.Header().Set("WWW-Authenticate", "Basic realm=gitea-lfs")
@@ -212,6 +222,11 @@ func VerifyLockHandler(ctx *context.Context) {
}
repository.MustOwner(ctx)
+ context.CheckRepoScopedToken(ctx, repository)
+ if ctx.Written() {
+ return
+ }
+
authenticated := authenticate(ctx, repository, authorization, true, true)
if !authenticated {
ctx.Resp.Header().Set("WWW-Authenticate", "Basic realm=gitea-lfs")
@@ -278,6 +293,11 @@ func UnLockHandler(ctx *context.Context) {
}
repository.MustOwner(ctx)
+ context.CheckRepoScopedToken(ctx, repository)
+ if ctx.Written() {
+ return
+ }
+
authenticated := authenticate(ctx, repository, authorization, true, true)
if !authenticated {
ctx.Resp.Header().Set("WWW-Authenticate", "Basic realm=gitea-lfs")
diff --git a/services/lfs/server.go b/services/lfs/server.go
index 44de9ba74f..4c69e47512 100644
--- a/services/lfs/server.go
+++ b/services/lfs/server.go
@@ -86,6 +86,11 @@ func DownloadHandler(ctx *context.Context) {
return
}
+ repository := getAuthenticatedRepository(ctx, rc, true)
+ if repository == nil {
+ return
+ }
+
// Support resume download using Range header
var fromByte, toByte int64
toByte = meta.Size - 1
@@ -360,6 +365,11 @@ func VerifyHandler(ctx *context.Context) {
return
}
+ repository := getAuthenticatedRepository(ctx, rc, true)
+ if repository == nil {
+ return
+ }
+
contentStore := lfs_module.NewContentStore()
ok, err := contentStore.Verify(meta.Pointer)
@@ -423,6 +433,11 @@ func getAuthenticatedRepository(ctx *context.Context, rc *requestContext, requir
return nil
}
+ context.CheckRepoScopedToken(ctx, repository)
+ if ctx.Written() {
+ return nil
+ }
+
return repository
}
diff --git a/tests/integration/api_packages_npm_test.go b/tests/integration/api_packages_npm_test.go
index 28c14fb3b8..78389b5740 100644
--- a/tests/integration/api_packages_npm_test.go
+++ b/tests/integration/api_packages_npm_test.go
@@ -11,6 +11,7 @@ import (
"strings"
"testing"
+ auth_model "code.gitea.io/gitea/models/auth"
"code.gitea.io/gitea/models/db"
"code.gitea.io/gitea/models/packages"
"code.gitea.io/gitea/models/unittest"
@@ -27,7 +28,7 @@ func TestPackageNpm(t *testing.T) {
user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})
- token := fmt.Sprintf("Bearer %s", getTokenForLoggedInUser(t, loginUser(t, user.Name)))
+ token := fmt.Sprintf("Bearer %s", getTokenForLoggedInUser(t, loginUser(t, user.Name), auth_model.AccessTokenScopePackage))
packageName := "@scope/test-package"
packageVersion := "1.0.1-pre"
diff --git a/tests/integration/api_packages_nuget_test.go b/tests/integration/api_packages_nuget_test.go
index a74d696f03..2240d2a5d4 100644
--- a/tests/integration/api_packages_nuget_test.go
+++ b/tests/integration/api_packages_nuget_test.go
@@ -16,6 +16,7 @@ import (
"testing"
"time"
+ auth_model "code.gitea.io/gitea/models/auth"
"code.gitea.io/gitea/models/db"
"code.gitea.io/gitea/models/packages"
"code.gitea.io/gitea/models/unittest"
@@ -74,7 +75,7 @@ func TestPackageNuGet(t *testing.T) {
}
user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})
- token := getUserToken(t, user.Name)
+ token := getUserToken(t, user.Name, auth_model.AccessTokenScopePackage)
packageName := "test.package"
packageVersion := "1.0.3"
diff --git a/tests/integration/api_packages_pub_test.go b/tests/integration/api_packages_pub_test.go
index 4d4ce12402..5c1cc6052f 100644
--- a/tests/integration/api_packages_pub_test.go
+++ b/tests/integration/api_packages_pub_test.go
@@ -15,6 +15,7 @@ import (
"testing"
"time"
+ auth_model "code.gitea.io/gitea/models/auth"
"code.gitea.io/gitea/models/db"
"code.gitea.io/gitea/models/packages"
"code.gitea.io/gitea/models/unittest"
@@ -30,7 +31,7 @@ func TestPackagePub(t *testing.T) {
user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})
- token := "Bearer " + getUserToken(t, user.Name)
+ token := "Bearer " + getUserToken(t, user.Name, auth_model.AccessTokenScopePackage)
packageName := "test_package"
packageVersion := "1.0.1"
diff --git a/tests/integration/api_packages_vagrant_test.go b/tests/integration/api_packages_vagrant_test.go
index b4f04b0c89..b28bfca6f0 100644
--- a/tests/integration/api_packages_vagrant_test.go
+++ b/tests/integration/api_packages_vagrant_test.go
@@ -12,6 +12,7 @@ import (
"strings"
"testing"
+ auth_model "code.gitea.io/gitea/models/auth"
"code.gitea.io/gitea/models/db"
"code.gitea.io/gitea/models/packages"
"code.gitea.io/gitea/models/unittest"
@@ -27,7 +28,7 @@ func TestPackageVagrant(t *testing.T) {
defer tests.PrepareTestEnv(t)()
user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})
- token := "Bearer " + getUserToken(t, user.Name)
+ token := "Bearer " + getUserToken(t, user.Name, auth_model.AccessTokenScopePackage)
packageName := "test_package"
packageVersion := "1.0.1"