2022-03-30 11:42:47 +03:00
|
|
|
// Copyright 2022 The Gitea Authors. All rights reserved.
|
2022-11-27 21:20:29 +03:00
|
|
|
// SPDX-License-Identifier: MIT
|
2022-03-30 11:42:47 +03:00
|
|
|
|
|
|
|
package packages
|
|
|
|
|
|
|
|
import (
|
|
|
|
"net/http"
|
|
|
|
"regexp"
|
|
|
|
"strings"
|
|
|
|
|
2023-04-27 03:24:03 +03:00
|
|
|
auth_model "code.gitea.io/gitea/models/auth"
|
2022-03-30 11:42:47 +03:00
|
|
|
"code.gitea.io/gitea/models/perm"
|
feat(quota): Quota enforcement
The previous commit laid out the foundation of the quota engine, this
one builds on top of it, and implements the actual enforcement.
Enforcement happens at the route decoration level, whenever possible. In
case of the API, when over quota, a 413 error is returned, with an
appropriate JSON payload. In case of web routes, a 413 HTML page is
rendered with similar information.
This implementation is for a **soft quota**: quota usage is checked
before an operation is to be performed, and the operation is *only*
denied if the user is already over quota. This makes it possible to go
over quota, but has the significant advantage of being practically
implementable within the current Forgejo architecture.
The goal of enforcement is to deny actions that can make the user go
over quota, and allow the rest. As such, deleting things should - in
almost all cases - be possible. A prime exemption is deleting files via
the web ui: that creates a new commit, which in turn increases repo
size, thus, is denied if the user is over quota.
Limitations
-----------
Because we generally work at a route decorator level, and rarely
look *into* the operation itself, `size:repos:public` and
`size:repos:private` are not enforced at this level, the engine enforces
against `size:repos:all`. This will be improved in the future.
AGit does not play very well with this system, because AGit PRs count
toward the repo they're opened against, while in the GitHub-style fork +
pull model, it counts against the fork. This too, can be improved in the
future.
There's very little done on the UI side to guard against going over
quota. What this patch implements, is enforcement, not prevention. The
UI will still let you *try* operations that *will* result in a denial.
Signed-off-by: Gergely Nagy <forgejo@gergo.csillger.hu>
2024-07-06 11:30:16 +03:00
|
|
|
quota_model "code.gitea.io/gitea/models/quota"
|
refactor auth interface to return error when verify failure (#22119)
This PR changed the Auth interface signature from
`Verify(http *http.Request, w http.ResponseWriter, store DataStore, sess
SessionStore) *user_model.User`
to
`Verify(http *http.Request, w http.ResponseWriter, store DataStore, sess
SessionStore) (*user_model.User, error)`.
There is a new return argument `error` which means the verification
condition matched but verify process failed, we should stop the auth
process.
Before this PR, when return a `nil` user, we don't know the reason why
it returned `nil`. If the match condition is not satisfied or it
verified failure? For these two different results, we should have
different handler. If the match condition is not satisfied, we should
try next auth method and if there is no more auth method, it's an
anonymous user. If the condition matched but verify failed, the auth
process should be stop and return immediately.
This will fix #20563
Co-authored-by: KN4CK3R <admin@oldschoolhack.me>
Co-authored-by: Jason Song <i@wolfogre.com>
2022-12-28 08:53:28 +03:00
|
|
|
"code.gitea.io/gitea/modules/log"
|
2022-03-30 11:42:47 +03:00
|
|
|
"code.gitea.io/gitea/modules/setting"
|
|
|
|
"code.gitea.io/gitea/modules/web"
|
2023-05-12 20:27:50 +03:00
|
|
|
"code.gitea.io/gitea/routers/api/packages/alpine"
|
2024-08-04 09:16:29 +03:00
|
|
|
"code.gitea.io/gitea/routers/api/packages/arch"
|
2023-02-05 13:12:31 +03:00
|
|
|
"code.gitea.io/gitea/routers/api/packages/cargo"
|
2023-02-06 04:49:21 +03:00
|
|
|
"code.gitea.io/gitea/routers/api/packages/chef"
|
2022-03-30 11:42:47 +03:00
|
|
|
"code.gitea.io/gitea/routers/api/packages/composer"
|
|
|
|
"code.gitea.io/gitea/routers/api/packages/conan"
|
2023-02-01 21:30:39 +03:00
|
|
|
"code.gitea.io/gitea/routers/api/packages/conda"
|
2022-03-30 11:42:47 +03:00
|
|
|
"code.gitea.io/gitea/routers/api/packages/container"
|
2023-05-22 05:57:49 +03:00
|
|
|
"code.gitea.io/gitea/routers/api/packages/cran"
|
2023-05-02 19:31:35 +03:00
|
|
|
"code.gitea.io/gitea/routers/api/packages/debian"
|
2022-03-30 11:42:47 +03:00
|
|
|
"code.gitea.io/gitea/routers/api/packages/generic"
|
2023-05-14 18:38:40 +03:00
|
|
|
"code.gitea.io/gitea/routers/api/packages/goproxy"
|
2022-04-19 19:55:35 +03:00
|
|
|
"code.gitea.io/gitea/routers/api/packages/helm"
|
2022-03-30 11:42:47 +03:00
|
|
|
"code.gitea.io/gitea/routers/api/packages/maven"
|
|
|
|
"code.gitea.io/gitea/routers/api/packages/npm"
|
|
|
|
"code.gitea.io/gitea/routers/api/packages/nuget"
|
2022-08-07 13:09:54 +03:00
|
|
|
"code.gitea.io/gitea/routers/api/packages/pub"
|
2022-03-30 11:42:47 +03:00
|
|
|
"code.gitea.io/gitea/routers/api/packages/pypi"
|
2023-05-05 23:33:37 +03:00
|
|
|
"code.gitea.io/gitea/routers/api/packages/rpm"
|
2022-03-30 11:42:47 +03:00
|
|
|
"code.gitea.io/gitea/routers/api/packages/rubygems"
|
2023-03-13 23:28:39 +03:00
|
|
|
"code.gitea.io/gitea/routers/api/packages/swift"
|
2022-08-29 10:04:45 +03:00
|
|
|
"code.gitea.io/gitea/routers/api/packages/vagrant"
|
2022-03-30 11:42:47 +03:00
|
|
|
"code.gitea.io/gitea/services/auth"
|
2024-02-27 10:12:22 +03:00
|
|
|
"code.gitea.io/gitea/services/context"
|
2022-03-30 11:42:47 +03:00
|
|
|
)
|
|
|
|
|
|
|
|
func reqPackageAccess(accessMode perm.AccessMode) func(ctx *context.Context) {
|
|
|
|
return func(ctx *context.Context) {
|
2023-04-27 03:24:03 +03:00
|
|
|
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
|
|
|
|
}
|
2024-10-08 12:51:09 +03:00
|
|
|
|
|
|
|
// check if scope only applies to public resources
|
|
|
|
publicOnly, err := scope.PublicOnly()
|
|
|
|
if err != nil {
|
|
|
|
ctx.Error(http.StatusForbidden, "tokenRequiresScope", "parsing public resource scope failed: "+err.Error())
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
if publicOnly {
|
|
|
|
if ctx.Package != nil && ctx.Package.Owner.Visibility.IsPrivate() {
|
|
|
|
ctx.Error(http.StatusForbidden, "reqToken", "token scope is limited to public packages")
|
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
2023-04-27 03:24:03 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-03-30 11:42:47 +03:00
|
|
|
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")
|
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
feat(quota): Quota enforcement
The previous commit laid out the foundation of the quota engine, this
one builds on top of it, and implements the actual enforcement.
Enforcement happens at the route decoration level, whenever possible. In
case of the API, when over quota, a 413 error is returned, with an
appropriate JSON payload. In case of web routes, a 413 HTML page is
rendered with similar information.
This implementation is for a **soft quota**: quota usage is checked
before an operation is to be performed, and the operation is *only*
denied if the user is already over quota. This makes it possible to go
over quota, but has the significant advantage of being practically
implementable within the current Forgejo architecture.
The goal of enforcement is to deny actions that can make the user go
over quota, and allow the rest. As such, deleting things should - in
almost all cases - be possible. A prime exemption is deleting files via
the web ui: that creates a new commit, which in turn increases repo
size, thus, is denied if the user is over quota.
Limitations
-----------
Because we generally work at a route decorator level, and rarely
look *into* the operation itself, `size:repos:public` and
`size:repos:private` are not enforced at this level, the engine enforces
against `size:repos:all`. This will be improved in the future.
AGit does not play very well with this system, because AGit PRs count
toward the repo they're opened against, while in the GitHub-style fork +
pull model, it counts against the fork. This too, can be improved in the
future.
There's very little done on the UI side to guard against going over
quota. What this patch implements, is enforcement, not prevention. The
UI will still let you *try* operations that *will* result in a denial.
Signed-off-by: Gergely Nagy <forgejo@gergo.csillger.hu>
2024-07-06 11:30:16 +03:00
|
|
|
func enforcePackagesQuota() func(ctx *context.Context) {
|
|
|
|
return func(ctx *context.Context) {
|
|
|
|
ok, err := quota_model.EvaluateForUser(ctx, ctx.Doer.ID, quota_model.LimitSubjectSizeAssetsPackagesAll)
|
|
|
|
if err != nil {
|
|
|
|
log.Error("quota_model.EvaluateForUser: %v", err)
|
|
|
|
ctx.Error(http.StatusInternalServerError, "Error checking quota")
|
|
|
|
return
|
|
|
|
}
|
|
|
|
if !ok {
|
|
|
|
ctx.Error(http.StatusRequestEntityTooLarge, "enforcePackagesQuota", "quota exceeded")
|
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-04-10 10:21:03 +03:00
|
|
|
func verifyAuth(r *web.Route, authMethods []auth.Method) {
|
2022-03-30 11:42:47 +03:00
|
|
|
if setting.Service.EnableReverseProxyAuth {
|
|
|
|
authMethods = append(authMethods, &auth.ReverseProxy{})
|
|
|
|
}
|
|
|
|
authGroup := auth.NewGroup(authMethods...)
|
2023-04-10 10:21:03 +03:00
|
|
|
|
2022-03-30 11:42:47 +03:00
|
|
|
r.Use(func(ctx *context.Context) {
|
refactor auth interface to return error when verify failure (#22119)
This PR changed the Auth interface signature from
`Verify(http *http.Request, w http.ResponseWriter, store DataStore, sess
SessionStore) *user_model.User`
to
`Verify(http *http.Request, w http.ResponseWriter, store DataStore, sess
SessionStore) (*user_model.User, error)`.
There is a new return argument `error` which means the verification
condition matched but verify process failed, we should stop the auth
process.
Before this PR, when return a `nil` user, we don't know the reason why
it returned `nil`. If the match condition is not satisfied or it
verified failure? For these two different results, we should have
different handler. If the match condition is not satisfied, we should
try next auth method and if there is no more auth method, it's an
anonymous user. If the condition matched but verify failed, the auth
process should be stop and return immediately.
This will fix #20563
Co-authored-by: KN4CK3R <admin@oldschoolhack.me>
Co-authored-by: Jason Song <i@wolfogre.com>
2022-12-28 08:53:28 +03:00
|
|
|
var err error
|
|
|
|
ctx.Doer, err = authGroup.Verify(ctx.Req, ctx.Resp, ctx, ctx.Session)
|
|
|
|
if err != nil {
|
2023-04-10 10:21:03 +03:00
|
|
|
log.Error("Failed to verify user: %v", err)
|
refactor auth interface to return error when verify failure (#22119)
This PR changed the Auth interface signature from
`Verify(http *http.Request, w http.ResponseWriter, store DataStore, sess
SessionStore) *user_model.User`
to
`Verify(http *http.Request, w http.ResponseWriter, store DataStore, sess
SessionStore) (*user_model.User, error)`.
There is a new return argument `error` which means the verification
condition matched but verify process failed, we should stop the auth
process.
Before this PR, when return a `nil` user, we don't know the reason why
it returned `nil`. If the match condition is not satisfied or it
verified failure? For these two different results, we should have
different handler. If the match condition is not satisfied, we should
try next auth method and if there is no more auth method, it's an
anonymous user. If the condition matched but verify failed, the auth
process should be stop and return immediately.
This will fix #20563
Co-authored-by: KN4CK3R <admin@oldschoolhack.me>
Co-authored-by: Jason Song <i@wolfogre.com>
2022-12-28 08:53:28 +03:00
|
|
|
ctx.Error(http.StatusUnauthorized, "authGroup.Verify")
|
|
|
|
return
|
|
|
|
}
|
2022-10-24 22:23:25 +03:00
|
|
|
ctx.IsSigned = ctx.Doer != nil
|
2022-03-30 11:42:47 +03:00
|
|
|
})
|
2023-04-10 10:21:03 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
// CommonRoutes provide endpoints for most package managers (except containers - see below)
|
|
|
|
// These are mounted on `/api/packages` (not `/api/v1/packages`)
|
2023-06-18 10:59:09 +03:00
|
|
|
func CommonRoutes() *web.Route {
|
2023-04-10 10:21:03 +03:00
|
|
|
r := web.NewRoute()
|
|
|
|
|
2023-05-21 04:50:53 +03:00
|
|
|
r.Use(context.PackageContexter())
|
2023-04-10 10:21:03 +03:00
|
|
|
|
|
|
|
verifyAuth(r, []auth.Method{
|
|
|
|
&auth.OAuth2{},
|
|
|
|
&auth.Basic{},
|
|
|
|
&nuget.Auth{},
|
|
|
|
&conan.Auth{},
|
|
|
|
&chef.Auth{},
|
|
|
|
})
|
2022-03-30 11:42:47 +03:00
|
|
|
|
|
|
|
r.Group("/{username}", func() {
|
2023-05-12 20:27:50 +03:00
|
|
|
r.Group("/alpine", func() {
|
|
|
|
r.Get("/key", alpine.GetRepositoryKey)
|
|
|
|
r.Group("/{branch}/{repository}", func() {
|
feat(quota): Quota enforcement
The previous commit laid out the foundation of the quota engine, this
one builds on top of it, and implements the actual enforcement.
Enforcement happens at the route decoration level, whenever possible. In
case of the API, when over quota, a 413 error is returned, with an
appropriate JSON payload. In case of web routes, a 413 HTML page is
rendered with similar information.
This implementation is for a **soft quota**: quota usage is checked
before an operation is to be performed, and the operation is *only*
denied if the user is already over quota. This makes it possible to go
over quota, but has the significant advantage of being practically
implementable within the current Forgejo architecture.
The goal of enforcement is to deny actions that can make the user go
over quota, and allow the rest. As such, deleting things should - in
almost all cases - be possible. A prime exemption is deleting files via
the web ui: that creates a new commit, which in turn increases repo
size, thus, is denied if the user is over quota.
Limitations
-----------
Because we generally work at a route decorator level, and rarely
look *into* the operation itself, `size:repos:public` and
`size:repos:private` are not enforced at this level, the engine enforces
against `size:repos:all`. This will be improved in the future.
AGit does not play very well with this system, because AGit PRs count
toward the repo they're opened against, while in the GitHub-style fork +
pull model, it counts against the fork. This too, can be improved in the
future.
There's very little done on the UI side to guard against going over
quota. What this patch implements, is enforcement, not prevention. The
UI will still let you *try* operations that *will* result in a denial.
Signed-off-by: Gergely Nagy <forgejo@gergo.csillger.hu>
2024-07-06 11:30:16 +03:00
|
|
|
r.Put("", reqPackageAccess(perm.AccessModeWrite), enforcePackagesQuota(), alpine.UploadPackageFile)
|
2023-05-12 20:27:50 +03:00
|
|
|
r.Group("/{architecture}", func() {
|
|
|
|
r.Get("/APKINDEX.tar.gz", alpine.GetRepositoryFile)
|
|
|
|
r.Group("/{filename}", func() {
|
|
|
|
r.Get("", alpine.DownloadPackageFile)
|
|
|
|
r.Delete("", reqPackageAccess(perm.AccessModeWrite), alpine.DeletePackageFile)
|
|
|
|
})
|
|
|
|
})
|
|
|
|
})
|
|
|
|
}, reqPackageAccess(perm.AccessModeRead))
|
2024-08-04 09:16:29 +03:00
|
|
|
r.Group("/arch", func() {
|
|
|
|
r.Group("/repository.key", func() {
|
|
|
|
r.Head("", arch.GetRepositoryKey)
|
|
|
|
r.Get("", arch.GetRepositoryKey)
|
|
|
|
})
|
2024-08-11 13:35:11 +03:00
|
|
|
|
|
|
|
r.Methods("HEAD,GET,PUT,DELETE", "*", func(ctx *context.Context) {
|
|
|
|
pathGroups := strings.Split(strings.Trim(ctx.Params("*"), "/"), "/")
|
|
|
|
groupLen := len(pathGroups)
|
|
|
|
isGetHead := ctx.Req.Method == "HEAD" || ctx.Req.Method == "GET"
|
|
|
|
isPut := ctx.Req.Method == "PUT"
|
|
|
|
isDelete := ctx.Req.Method == "DELETE"
|
|
|
|
if isGetHead {
|
|
|
|
if groupLen < 2 {
|
|
|
|
ctx.Status(http.StatusNotFound)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
if groupLen == 2 {
|
|
|
|
ctx.SetParams("group", "")
|
|
|
|
ctx.SetParams("arch", pathGroups[0])
|
|
|
|
ctx.SetParams("file", pathGroups[1])
|
|
|
|
} else {
|
|
|
|
ctx.SetParams("group", strings.Join(pathGroups[:groupLen-2], "/"))
|
|
|
|
ctx.SetParams("arch", pathGroups[groupLen-2])
|
|
|
|
ctx.SetParams("file", pathGroups[groupLen-1])
|
|
|
|
}
|
|
|
|
arch.GetPackageOrDB(ctx)
|
|
|
|
return
|
|
|
|
} else if isPut {
|
|
|
|
ctx.SetParams("group", strings.Join(pathGroups, "/"))
|
|
|
|
reqPackageAccess(perm.AccessModeWrite)(ctx)
|
|
|
|
if ctx.Written() {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
arch.PushPackage(ctx)
|
|
|
|
return
|
|
|
|
} else if isDelete {
|
2024-09-27 11:21:22 +03:00
|
|
|
if groupLen < 3 {
|
2024-08-11 13:35:11 +03:00
|
|
|
ctx.Status(http.StatusBadRequest)
|
|
|
|
return
|
|
|
|
}
|
2024-09-27 11:21:22 +03:00
|
|
|
if groupLen == 3 {
|
2024-08-11 13:35:11 +03:00
|
|
|
ctx.SetParams("group", "")
|
|
|
|
ctx.SetParams("package", pathGroups[0])
|
|
|
|
ctx.SetParams("version", pathGroups[1])
|
2024-09-27 11:21:22 +03:00
|
|
|
ctx.SetParams("arch", pathGroups[2])
|
2024-08-11 13:35:11 +03:00
|
|
|
} else {
|
2024-09-27 11:21:22 +03:00
|
|
|
ctx.SetParams("group", strings.Join(pathGroups[:groupLen-3], "/"))
|
|
|
|
ctx.SetParams("package", pathGroups[groupLen-3])
|
|
|
|
ctx.SetParams("version", pathGroups[groupLen-2])
|
|
|
|
ctx.SetParams("arch", pathGroups[groupLen-1])
|
2024-08-11 13:35:11 +03:00
|
|
|
}
|
|
|
|
reqPackageAccess(perm.AccessModeWrite)(ctx)
|
|
|
|
if ctx.Written() {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
arch.RemovePackage(ctx)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
ctx.Status(http.StatusNotFound)
|
2024-08-04 09:16:29 +03:00
|
|
|
})
|
|
|
|
}, reqPackageAccess(perm.AccessModeRead))
|
2023-02-05 13:12:31 +03:00
|
|
|
r.Group("/cargo", func() {
|
|
|
|
r.Group("/api/v1/crates", func() {
|
|
|
|
r.Get("", cargo.SearchPackages)
|
feat(quota): Quota enforcement
The previous commit laid out the foundation of the quota engine, this
one builds on top of it, and implements the actual enforcement.
Enforcement happens at the route decoration level, whenever possible. In
case of the API, when over quota, a 413 error is returned, with an
appropriate JSON payload. In case of web routes, a 413 HTML page is
rendered with similar information.
This implementation is for a **soft quota**: quota usage is checked
before an operation is to be performed, and the operation is *only*
denied if the user is already over quota. This makes it possible to go
over quota, but has the significant advantage of being practically
implementable within the current Forgejo architecture.
The goal of enforcement is to deny actions that can make the user go
over quota, and allow the rest. As such, deleting things should - in
almost all cases - be possible. A prime exemption is deleting files via
the web ui: that creates a new commit, which in turn increases repo
size, thus, is denied if the user is over quota.
Limitations
-----------
Because we generally work at a route decorator level, and rarely
look *into* the operation itself, `size:repos:public` and
`size:repos:private` are not enforced at this level, the engine enforces
against `size:repos:all`. This will be improved in the future.
AGit does not play very well with this system, because AGit PRs count
toward the repo they're opened against, while in the GitHub-style fork +
pull model, it counts against the fork. This too, can be improved in the
future.
There's very little done on the UI side to guard against going over
quota. What this patch implements, is enforcement, not prevention. The
UI will still let you *try* operations that *will* result in a denial.
Signed-off-by: Gergely Nagy <forgejo@gergo.csillger.hu>
2024-07-06 11:30:16 +03:00
|
|
|
r.Put("/new", reqPackageAccess(perm.AccessModeWrite), enforcePackagesQuota(), cargo.UploadPackage)
|
2023-02-05 13:12:31 +03:00
|
|
|
r.Group("/{package}", func() {
|
|
|
|
r.Group("/{version}", func() {
|
|
|
|
r.Get("/download", cargo.DownloadPackageFile)
|
|
|
|
r.Delete("/yank", reqPackageAccess(perm.AccessModeWrite), cargo.YankPackage)
|
feat(quota): Quota enforcement
The previous commit laid out the foundation of the quota engine, this
one builds on top of it, and implements the actual enforcement.
Enforcement happens at the route decoration level, whenever possible. In
case of the API, when over quota, a 413 error is returned, with an
appropriate JSON payload. In case of web routes, a 413 HTML page is
rendered with similar information.
This implementation is for a **soft quota**: quota usage is checked
before an operation is to be performed, and the operation is *only*
denied if the user is already over quota. This makes it possible to go
over quota, but has the significant advantage of being practically
implementable within the current Forgejo architecture.
The goal of enforcement is to deny actions that can make the user go
over quota, and allow the rest. As such, deleting things should - in
almost all cases - be possible. A prime exemption is deleting files via
the web ui: that creates a new commit, which in turn increases repo
size, thus, is denied if the user is over quota.
Limitations
-----------
Because we generally work at a route decorator level, and rarely
look *into* the operation itself, `size:repos:public` and
`size:repos:private` are not enforced at this level, the engine enforces
against `size:repos:all`. This will be improved in the future.
AGit does not play very well with this system, because AGit PRs count
toward the repo they're opened against, while in the GitHub-style fork +
pull model, it counts against the fork. This too, can be improved in the
future.
There's very little done on the UI side to guard against going over
quota. What this patch implements, is enforcement, not prevention. The
UI will still let you *try* operations that *will* result in a denial.
Signed-off-by: Gergely Nagy <forgejo@gergo.csillger.hu>
2024-07-06 11:30:16 +03:00
|
|
|
r.Put("/unyank", reqPackageAccess(perm.AccessModeWrite), enforcePackagesQuota(), cargo.UnyankPackage)
|
2023-02-05 13:12:31 +03:00
|
|
|
})
|
|
|
|
r.Get("/owners", cargo.ListOwners)
|
|
|
|
})
|
|
|
|
})
|
2023-05-03 23:58:43 +03:00
|
|
|
r.Get("/config.json", cargo.RepositoryConfig)
|
|
|
|
r.Get("/1/{package}", cargo.EnumeratePackageVersions)
|
|
|
|
r.Get("/2/{package}", cargo.EnumeratePackageVersions)
|
|
|
|
// Use dummy placeholders because these parts are not of interest
|
|
|
|
r.Get("/3/{_}/{package}", cargo.EnumeratePackageVersions)
|
|
|
|
r.Get("/{_}/{__}/{package}", cargo.EnumeratePackageVersions)
|
2023-02-05 13:12:31 +03:00
|
|
|
}, reqPackageAccess(perm.AccessModeRead))
|
2023-02-06 04:49:21 +03:00
|
|
|
r.Group("/chef", func() {
|
|
|
|
r.Group("/api/v1", func() {
|
|
|
|
r.Get("/universe", chef.PackagesUniverse)
|
|
|
|
r.Get("/search", chef.EnumeratePackages)
|
|
|
|
r.Group("/cookbooks", func() {
|
|
|
|
r.Get("", chef.EnumeratePackages)
|
feat(quota): Quota enforcement
The previous commit laid out the foundation of the quota engine, this
one builds on top of it, and implements the actual enforcement.
Enforcement happens at the route decoration level, whenever possible. In
case of the API, when over quota, a 413 error is returned, with an
appropriate JSON payload. In case of web routes, a 413 HTML page is
rendered with similar information.
This implementation is for a **soft quota**: quota usage is checked
before an operation is to be performed, and the operation is *only*
denied if the user is already over quota. This makes it possible to go
over quota, but has the significant advantage of being practically
implementable within the current Forgejo architecture.
The goal of enforcement is to deny actions that can make the user go
over quota, and allow the rest. As such, deleting things should - in
almost all cases - be possible. A prime exemption is deleting files via
the web ui: that creates a new commit, which in turn increases repo
size, thus, is denied if the user is over quota.
Limitations
-----------
Because we generally work at a route decorator level, and rarely
look *into* the operation itself, `size:repos:public` and
`size:repos:private` are not enforced at this level, the engine enforces
against `size:repos:all`. This will be improved in the future.
AGit does not play very well with this system, because AGit PRs count
toward the repo they're opened against, while in the GitHub-style fork +
pull model, it counts against the fork. This too, can be improved in the
future.
There's very little done on the UI side to guard against going over
quota. What this patch implements, is enforcement, not prevention. The
UI will still let you *try* operations that *will* result in a denial.
Signed-off-by: Gergely Nagy <forgejo@gergo.csillger.hu>
2024-07-06 11:30:16 +03:00
|
|
|
r.Post("", reqPackageAccess(perm.AccessModeWrite), enforcePackagesQuota(), chef.UploadPackage)
|
2023-02-06 04:49:21 +03:00
|
|
|
r.Group("/{name}", func() {
|
|
|
|
r.Get("", chef.PackageMetadata)
|
|
|
|
r.Group("/versions/{version}", func() {
|
|
|
|
r.Get("", chef.PackageVersionMetadata)
|
|
|
|
r.Delete("", reqPackageAccess(perm.AccessModeWrite), chef.DeletePackageVersion)
|
|
|
|
r.Get("/download", chef.DownloadPackage)
|
|
|
|
})
|
|
|
|
r.Delete("", reqPackageAccess(perm.AccessModeWrite), chef.DeletePackage)
|
|
|
|
})
|
|
|
|
})
|
|
|
|
})
|
|
|
|
}, reqPackageAccess(perm.AccessModeRead))
|
2022-03-30 11:42:47 +03:00
|
|
|
r.Group("/composer", func() {
|
|
|
|
r.Get("/packages.json", composer.ServiceIndex)
|
|
|
|
r.Get("/search.json", composer.SearchPackages)
|
|
|
|
r.Get("/list.json", composer.EnumeratePackages)
|
|
|
|
r.Get("/p2/{vendorname}/{projectname}~dev.json", composer.PackageMetadata)
|
|
|
|
r.Get("/p2/{vendorname}/{projectname}.json", composer.PackageMetadata)
|
|
|
|
r.Get("/files/{package}/{version}/{filename}", composer.DownloadPackageFile)
|
feat(quota): Quota enforcement
The previous commit laid out the foundation of the quota engine, this
one builds on top of it, and implements the actual enforcement.
Enforcement happens at the route decoration level, whenever possible. In
case of the API, when over quota, a 413 error is returned, with an
appropriate JSON payload. In case of web routes, a 413 HTML page is
rendered with similar information.
This implementation is for a **soft quota**: quota usage is checked
before an operation is to be performed, and the operation is *only*
denied if the user is already over quota. This makes it possible to go
over quota, but has the significant advantage of being practically
implementable within the current Forgejo architecture.
The goal of enforcement is to deny actions that can make the user go
over quota, and allow the rest. As such, deleting things should - in
almost all cases - be possible. A prime exemption is deleting files via
the web ui: that creates a new commit, which in turn increases repo
size, thus, is denied if the user is over quota.
Limitations
-----------
Because we generally work at a route decorator level, and rarely
look *into* the operation itself, `size:repos:public` and
`size:repos:private` are not enforced at this level, the engine enforces
against `size:repos:all`. This will be improved in the future.
AGit does not play very well with this system, because AGit PRs count
toward the repo they're opened against, while in the GitHub-style fork +
pull model, it counts against the fork. This too, can be improved in the
future.
There's very little done on the UI side to guard against going over
quota. What this patch implements, is enforcement, not prevention. The
UI will still let you *try* operations that *will* result in a denial.
Signed-off-by: Gergely Nagy <forgejo@gergo.csillger.hu>
2024-07-06 11:30:16 +03:00
|
|
|
r.Put("", reqPackageAccess(perm.AccessModeWrite), enforcePackagesQuota(), composer.UploadPackage)
|
2022-09-24 18:17:08 +03:00
|
|
|
}, reqPackageAccess(perm.AccessModeRead))
|
2022-03-30 11:42:47 +03:00
|
|
|
r.Group("/conan", func() {
|
|
|
|
r.Group("/v1", func() {
|
|
|
|
r.Get("/ping", conan.Ping)
|
|
|
|
r.Group("/users", func() {
|
|
|
|
r.Get("/authenticate", conan.Authenticate)
|
|
|
|
r.Get("/check_credentials", conan.CheckCredentials)
|
|
|
|
})
|
|
|
|
r.Group("/conans", func() {
|
|
|
|
r.Get("/search", conan.SearchRecipes)
|
|
|
|
r.Group("/{name}/{version}/{user}/{channel}", func() {
|
|
|
|
r.Get("", conan.RecipeSnapshot)
|
|
|
|
r.Delete("", reqPackageAccess(perm.AccessModeWrite), conan.DeleteRecipeV1)
|
|
|
|
r.Get("/search", conan.SearchPackagesV1)
|
|
|
|
r.Get("/digest", conan.RecipeDownloadURLs)
|
feat(quota): Quota enforcement
The previous commit laid out the foundation of the quota engine, this
one builds on top of it, and implements the actual enforcement.
Enforcement happens at the route decoration level, whenever possible. In
case of the API, when over quota, a 413 error is returned, with an
appropriate JSON payload. In case of web routes, a 413 HTML page is
rendered with similar information.
This implementation is for a **soft quota**: quota usage is checked
before an operation is to be performed, and the operation is *only*
denied if the user is already over quota. This makes it possible to go
over quota, but has the significant advantage of being practically
implementable within the current Forgejo architecture.
The goal of enforcement is to deny actions that can make the user go
over quota, and allow the rest. As such, deleting things should - in
almost all cases - be possible. A prime exemption is deleting files via
the web ui: that creates a new commit, which in turn increases repo
size, thus, is denied if the user is over quota.
Limitations
-----------
Because we generally work at a route decorator level, and rarely
look *into* the operation itself, `size:repos:public` and
`size:repos:private` are not enforced at this level, the engine enforces
against `size:repos:all`. This will be improved in the future.
AGit does not play very well with this system, because AGit PRs count
toward the repo they're opened against, while in the GitHub-style fork +
pull model, it counts against the fork. This too, can be improved in the
future.
There's very little done on the UI side to guard against going over
quota. What this patch implements, is enforcement, not prevention. The
UI will still let you *try* operations that *will* result in a denial.
Signed-off-by: Gergely Nagy <forgejo@gergo.csillger.hu>
2024-07-06 11:30:16 +03:00
|
|
|
r.Post("/upload_urls", reqPackageAccess(perm.AccessModeWrite), enforcePackagesQuota(), conan.RecipeUploadURLs)
|
2022-03-30 11:42:47 +03:00
|
|
|
r.Get("/download_urls", conan.RecipeDownloadURLs)
|
|
|
|
r.Group("/packages", func() {
|
|
|
|
r.Post("/delete", reqPackageAccess(perm.AccessModeWrite), conan.DeletePackageV1)
|
|
|
|
r.Group("/{package_reference}", func() {
|
|
|
|
r.Get("", conan.PackageSnapshot)
|
|
|
|
r.Get("/digest", conan.PackageDownloadURLs)
|
feat(quota): Quota enforcement
The previous commit laid out the foundation of the quota engine, this
one builds on top of it, and implements the actual enforcement.
Enforcement happens at the route decoration level, whenever possible. In
case of the API, when over quota, a 413 error is returned, with an
appropriate JSON payload. In case of web routes, a 413 HTML page is
rendered with similar information.
This implementation is for a **soft quota**: quota usage is checked
before an operation is to be performed, and the operation is *only*
denied if the user is already over quota. This makes it possible to go
over quota, but has the significant advantage of being practically
implementable within the current Forgejo architecture.
The goal of enforcement is to deny actions that can make the user go
over quota, and allow the rest. As such, deleting things should - in
almost all cases - be possible. A prime exemption is deleting files via
the web ui: that creates a new commit, which in turn increases repo
size, thus, is denied if the user is over quota.
Limitations
-----------
Because we generally work at a route decorator level, and rarely
look *into* the operation itself, `size:repos:public` and
`size:repos:private` are not enforced at this level, the engine enforces
against `size:repos:all`. This will be improved in the future.
AGit does not play very well with this system, because AGit PRs count
toward the repo they're opened against, while in the GitHub-style fork +
pull model, it counts against the fork. This too, can be improved in the
future.
There's very little done on the UI side to guard against going over
quota. What this patch implements, is enforcement, not prevention. The
UI will still let you *try* operations that *will* result in a denial.
Signed-off-by: Gergely Nagy <forgejo@gergo.csillger.hu>
2024-07-06 11:30:16 +03:00
|
|
|
r.Post("/upload_urls", reqPackageAccess(perm.AccessModeWrite), enforcePackagesQuota(), conan.PackageUploadURLs)
|
2022-03-30 11:42:47 +03:00
|
|
|
r.Get("/download_urls", conan.PackageDownloadURLs)
|
|
|
|
})
|
|
|
|
})
|
|
|
|
}, conan.ExtractPathParameters)
|
|
|
|
})
|
|
|
|
r.Group("/files/{name}/{version}/{user}/{channel}/{recipe_revision}", func() {
|
|
|
|
r.Group("/recipe/{filename}", func() {
|
|
|
|
r.Get("", conan.DownloadRecipeFile)
|
feat(quota): Quota enforcement
The previous commit laid out the foundation of the quota engine, this
one builds on top of it, and implements the actual enforcement.
Enforcement happens at the route decoration level, whenever possible. In
case of the API, when over quota, a 413 error is returned, with an
appropriate JSON payload. In case of web routes, a 413 HTML page is
rendered with similar information.
This implementation is for a **soft quota**: quota usage is checked
before an operation is to be performed, and the operation is *only*
denied if the user is already over quota. This makes it possible to go
over quota, but has the significant advantage of being practically
implementable within the current Forgejo architecture.
The goal of enforcement is to deny actions that can make the user go
over quota, and allow the rest. As such, deleting things should - in
almost all cases - be possible. A prime exemption is deleting files via
the web ui: that creates a new commit, which in turn increases repo
size, thus, is denied if the user is over quota.
Limitations
-----------
Because we generally work at a route decorator level, and rarely
look *into* the operation itself, `size:repos:public` and
`size:repos:private` are not enforced at this level, the engine enforces
against `size:repos:all`. This will be improved in the future.
AGit does not play very well with this system, because AGit PRs count
toward the repo they're opened against, while in the GitHub-style fork +
pull model, it counts against the fork. This too, can be improved in the
future.
There's very little done on the UI side to guard against going over
quota. What this patch implements, is enforcement, not prevention. The
UI will still let you *try* operations that *will* result in a denial.
Signed-off-by: Gergely Nagy <forgejo@gergo.csillger.hu>
2024-07-06 11:30:16 +03:00
|
|
|
r.Put("", reqPackageAccess(perm.AccessModeWrite), enforcePackagesQuota(), conan.UploadRecipeFile)
|
2022-03-30 11:42:47 +03:00
|
|
|
})
|
|
|
|
r.Group("/package/{package_reference}/{package_revision}/{filename}", func() {
|
|
|
|
r.Get("", conan.DownloadPackageFile)
|
feat(quota): Quota enforcement
The previous commit laid out the foundation of the quota engine, this
one builds on top of it, and implements the actual enforcement.
Enforcement happens at the route decoration level, whenever possible. In
case of the API, when over quota, a 413 error is returned, with an
appropriate JSON payload. In case of web routes, a 413 HTML page is
rendered with similar information.
This implementation is for a **soft quota**: quota usage is checked
before an operation is to be performed, and the operation is *only*
denied if the user is already over quota. This makes it possible to go
over quota, but has the significant advantage of being practically
implementable within the current Forgejo architecture.
The goal of enforcement is to deny actions that can make the user go
over quota, and allow the rest. As such, deleting things should - in
almost all cases - be possible. A prime exemption is deleting files via
the web ui: that creates a new commit, which in turn increases repo
size, thus, is denied if the user is over quota.
Limitations
-----------
Because we generally work at a route decorator level, and rarely
look *into* the operation itself, `size:repos:public` and
`size:repos:private` are not enforced at this level, the engine enforces
against `size:repos:all`. This will be improved in the future.
AGit does not play very well with this system, because AGit PRs count
toward the repo they're opened against, while in the GitHub-style fork +
pull model, it counts against the fork. This too, can be improved in the
future.
There's very little done on the UI side to guard against going over
quota. What this patch implements, is enforcement, not prevention. The
UI will still let you *try* operations that *will* result in a denial.
Signed-off-by: Gergely Nagy <forgejo@gergo.csillger.hu>
2024-07-06 11:30:16 +03:00
|
|
|
r.Put("", reqPackageAccess(perm.AccessModeWrite), enforcePackagesQuota(), conan.UploadPackageFile)
|
2022-03-30 11:42:47 +03:00
|
|
|
})
|
|
|
|
}, conan.ExtractPathParameters)
|
|
|
|
})
|
|
|
|
r.Group("/v2", func() {
|
|
|
|
r.Get("/ping", conan.Ping)
|
|
|
|
r.Group("/users", func() {
|
|
|
|
r.Get("/authenticate", conan.Authenticate)
|
|
|
|
r.Get("/check_credentials", conan.CheckCredentials)
|
|
|
|
})
|
|
|
|
r.Group("/conans", func() {
|
|
|
|
r.Get("/search", conan.SearchRecipes)
|
|
|
|
r.Group("/{name}/{version}/{user}/{channel}", func() {
|
|
|
|
r.Delete("", reqPackageAccess(perm.AccessModeWrite), conan.DeleteRecipeV2)
|
|
|
|
r.Get("/search", conan.SearchPackagesV2)
|
|
|
|
r.Get("/latest", conan.LatestRecipeRevision)
|
|
|
|
r.Group("/revisions", func() {
|
|
|
|
r.Get("", conan.ListRecipeRevisions)
|
|
|
|
r.Group("/{recipe_revision}", func() {
|
|
|
|
r.Delete("", reqPackageAccess(perm.AccessModeWrite), conan.DeleteRecipeV2)
|
|
|
|
r.Get("/search", conan.SearchPackagesV2)
|
|
|
|
r.Group("/files", func() {
|
|
|
|
r.Get("", conan.ListRecipeRevisionFiles)
|
|
|
|
r.Group("/{filename}", func() {
|
|
|
|
r.Get("", conan.DownloadRecipeFile)
|
feat(quota): Quota enforcement
The previous commit laid out the foundation of the quota engine, this
one builds on top of it, and implements the actual enforcement.
Enforcement happens at the route decoration level, whenever possible. In
case of the API, when over quota, a 413 error is returned, with an
appropriate JSON payload. In case of web routes, a 413 HTML page is
rendered with similar information.
This implementation is for a **soft quota**: quota usage is checked
before an operation is to be performed, and the operation is *only*
denied if the user is already over quota. This makes it possible to go
over quota, but has the significant advantage of being practically
implementable within the current Forgejo architecture.
The goal of enforcement is to deny actions that can make the user go
over quota, and allow the rest. As such, deleting things should - in
almost all cases - be possible. A prime exemption is deleting files via
the web ui: that creates a new commit, which in turn increases repo
size, thus, is denied if the user is over quota.
Limitations
-----------
Because we generally work at a route decorator level, and rarely
look *into* the operation itself, `size:repos:public` and
`size:repos:private` are not enforced at this level, the engine enforces
against `size:repos:all`. This will be improved in the future.
AGit does not play very well with this system, because AGit PRs count
toward the repo they're opened against, while in the GitHub-style fork +
pull model, it counts against the fork. This too, can be improved in the
future.
There's very little done on the UI side to guard against going over
quota. What this patch implements, is enforcement, not prevention. The
UI will still let you *try* operations that *will* result in a denial.
Signed-off-by: Gergely Nagy <forgejo@gergo.csillger.hu>
2024-07-06 11:30:16 +03:00
|
|
|
r.Put("", reqPackageAccess(perm.AccessModeWrite), enforcePackagesQuota(), conan.UploadRecipeFile)
|
2022-03-30 11:42:47 +03:00
|
|
|
})
|
|
|
|
})
|
|
|
|
r.Group("/packages", func() {
|
|
|
|
r.Delete("", reqPackageAccess(perm.AccessModeWrite), conan.DeletePackageV2)
|
|
|
|
r.Group("/{package_reference}", func() {
|
|
|
|
r.Delete("", reqPackageAccess(perm.AccessModeWrite), conan.DeletePackageV2)
|
|
|
|
r.Get("/latest", conan.LatestPackageRevision)
|
|
|
|
r.Group("/revisions", func() {
|
|
|
|
r.Get("", conan.ListPackageRevisions)
|
|
|
|
r.Group("/{package_revision}", func() {
|
|
|
|
r.Delete("", reqPackageAccess(perm.AccessModeWrite), conan.DeletePackageV2)
|
|
|
|
r.Group("/files", func() {
|
|
|
|
r.Get("", conan.ListPackageRevisionFiles)
|
|
|
|
r.Group("/{filename}", func() {
|
|
|
|
r.Get("", conan.DownloadPackageFile)
|
feat(quota): Quota enforcement
The previous commit laid out the foundation of the quota engine, this
one builds on top of it, and implements the actual enforcement.
Enforcement happens at the route decoration level, whenever possible. In
case of the API, when over quota, a 413 error is returned, with an
appropriate JSON payload. In case of web routes, a 413 HTML page is
rendered with similar information.
This implementation is for a **soft quota**: quota usage is checked
before an operation is to be performed, and the operation is *only*
denied if the user is already over quota. This makes it possible to go
over quota, but has the significant advantage of being practically
implementable within the current Forgejo architecture.
The goal of enforcement is to deny actions that can make the user go
over quota, and allow the rest. As such, deleting things should - in
almost all cases - be possible. A prime exemption is deleting files via
the web ui: that creates a new commit, which in turn increases repo
size, thus, is denied if the user is over quota.
Limitations
-----------
Because we generally work at a route decorator level, and rarely
look *into* the operation itself, `size:repos:public` and
`size:repos:private` are not enforced at this level, the engine enforces
against `size:repos:all`. This will be improved in the future.
AGit does not play very well with this system, because AGit PRs count
toward the repo they're opened against, while in the GitHub-style fork +
pull model, it counts against the fork. This too, can be improved in the
future.
There's very little done on the UI side to guard against going over
quota. What this patch implements, is enforcement, not prevention. The
UI will still let you *try* operations that *will* result in a denial.
Signed-off-by: Gergely Nagy <forgejo@gergo.csillger.hu>
2024-07-06 11:30:16 +03:00
|
|
|
r.Put("", reqPackageAccess(perm.AccessModeWrite), enforcePackagesQuota(), conan.UploadPackageFile)
|
2022-03-30 11:42:47 +03:00
|
|
|
})
|
|
|
|
})
|
|
|
|
})
|
|
|
|
})
|
|
|
|
})
|
|
|
|
})
|
|
|
|
})
|
|
|
|
})
|
|
|
|
}, conan.ExtractPathParameters)
|
|
|
|
})
|
|
|
|
})
|
2022-09-24 18:17:08 +03:00
|
|
|
}, reqPackageAccess(perm.AccessModeRead))
|
2023-02-01 21:30:39 +03:00
|
|
|
r.Group("/conda", func() {
|
|
|
|
var (
|
|
|
|
downloadPattern = regexp.MustCompile(`\A(.+/)?(.+)/((?:[^/]+(?:\.tar\.bz2|\.conda))|(?:current_)?repodata\.json(?:\.bz2)?)\z`)
|
|
|
|
uploadPattern = regexp.MustCompile(`\A(.+/)?([^/]+(?:\.tar\.bz2|\.conda))\z`)
|
|
|
|
)
|
|
|
|
|
|
|
|
r.Get("/*", func(ctx *context.Context) {
|
|
|
|
m := downloadPattern.FindStringSubmatch(ctx.Params("*"))
|
|
|
|
if len(m) == 0 {
|
|
|
|
ctx.Status(http.StatusNotFound)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
ctx.SetParams("channel", strings.TrimSuffix(m[1], "/"))
|
|
|
|
ctx.SetParams("architecture", m[2])
|
|
|
|
ctx.SetParams("filename", m[3])
|
|
|
|
|
|
|
|
switch m[3] {
|
|
|
|
case "repodata.json", "repodata.json.bz2", "current_repodata.json", "current_repodata.json.bz2":
|
|
|
|
conda.EnumeratePackages(ctx)
|
|
|
|
default:
|
|
|
|
conda.DownloadPackageFile(ctx)
|
|
|
|
}
|
|
|
|
})
|
feat(quota): Quota enforcement
The previous commit laid out the foundation of the quota engine, this
one builds on top of it, and implements the actual enforcement.
Enforcement happens at the route decoration level, whenever possible. In
case of the API, when over quota, a 413 error is returned, with an
appropriate JSON payload. In case of web routes, a 413 HTML page is
rendered with similar information.
This implementation is for a **soft quota**: quota usage is checked
before an operation is to be performed, and the operation is *only*
denied if the user is already over quota. This makes it possible to go
over quota, but has the significant advantage of being practically
implementable within the current Forgejo architecture.
The goal of enforcement is to deny actions that can make the user go
over quota, and allow the rest. As such, deleting things should - in
almost all cases - be possible. A prime exemption is deleting files via
the web ui: that creates a new commit, which in turn increases repo
size, thus, is denied if the user is over quota.
Limitations
-----------
Because we generally work at a route decorator level, and rarely
look *into* the operation itself, `size:repos:public` and
`size:repos:private` are not enforced at this level, the engine enforces
against `size:repos:all`. This will be improved in the future.
AGit does not play very well with this system, because AGit PRs count
toward the repo they're opened against, while in the GitHub-style fork +
pull model, it counts against the fork. This too, can be improved in the
future.
There's very little done on the UI side to guard against going over
quota. What this patch implements, is enforcement, not prevention. The
UI will still let you *try* operations that *will* result in a denial.
Signed-off-by: Gergely Nagy <forgejo@gergo.csillger.hu>
2024-07-06 11:30:16 +03:00
|
|
|
r.Put("/*", reqPackageAccess(perm.AccessModeWrite), enforcePackagesQuota(), func(ctx *context.Context) {
|
2023-02-01 21:30:39 +03:00
|
|
|
m := uploadPattern.FindStringSubmatch(ctx.Params("*"))
|
|
|
|
if len(m) == 0 {
|
|
|
|
ctx.Status(http.StatusNotFound)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
ctx.SetParams("channel", strings.TrimSuffix(m[1], "/"))
|
|
|
|
ctx.SetParams("filename", m[2])
|
|
|
|
|
|
|
|
conda.UploadPackageFile(ctx)
|
|
|
|
})
|
|
|
|
}, reqPackageAccess(perm.AccessModeRead))
|
2023-05-22 05:57:49 +03:00
|
|
|
r.Group("/cran", func() {
|
|
|
|
r.Group("/src", func() {
|
|
|
|
r.Group("/contrib", func() {
|
|
|
|
r.Get("/PACKAGES", cran.EnumerateSourcePackages)
|
|
|
|
r.Get("/PACKAGES{format}", cran.EnumerateSourcePackages)
|
|
|
|
r.Get("/{filename}", cran.DownloadSourcePackageFile)
|
|
|
|
})
|
feat(quota): Quota enforcement
The previous commit laid out the foundation of the quota engine, this
one builds on top of it, and implements the actual enforcement.
Enforcement happens at the route decoration level, whenever possible. In
case of the API, when over quota, a 413 error is returned, with an
appropriate JSON payload. In case of web routes, a 413 HTML page is
rendered with similar information.
This implementation is for a **soft quota**: quota usage is checked
before an operation is to be performed, and the operation is *only*
denied if the user is already over quota. This makes it possible to go
over quota, but has the significant advantage of being practically
implementable within the current Forgejo architecture.
The goal of enforcement is to deny actions that can make the user go
over quota, and allow the rest. As such, deleting things should - in
almost all cases - be possible. A prime exemption is deleting files via
the web ui: that creates a new commit, which in turn increases repo
size, thus, is denied if the user is over quota.
Limitations
-----------
Because we generally work at a route decorator level, and rarely
look *into* the operation itself, `size:repos:public` and
`size:repos:private` are not enforced at this level, the engine enforces
against `size:repos:all`. This will be improved in the future.
AGit does not play very well with this system, because AGit PRs count
toward the repo they're opened against, while in the GitHub-style fork +
pull model, it counts against the fork. This too, can be improved in the
future.
There's very little done on the UI side to guard against going over
quota. What this patch implements, is enforcement, not prevention. The
UI will still let you *try* operations that *will* result in a denial.
Signed-off-by: Gergely Nagy <forgejo@gergo.csillger.hu>
2024-07-06 11:30:16 +03:00
|
|
|
r.Put("", reqPackageAccess(perm.AccessModeWrite), enforcePackagesQuota(), cran.UploadSourcePackageFile)
|
2023-05-22 05:57:49 +03:00
|
|
|
})
|
|
|
|
r.Group("/bin", func() {
|
|
|
|
r.Group("/{platform}/contrib/{rversion}", func() {
|
|
|
|
r.Get("/PACKAGES", cran.EnumerateBinaryPackages)
|
|
|
|
r.Get("/PACKAGES{format}", cran.EnumerateBinaryPackages)
|
|
|
|
r.Get("/{filename}", cran.DownloadBinaryPackageFile)
|
|
|
|
})
|
feat(quota): Quota enforcement
The previous commit laid out the foundation of the quota engine, this
one builds on top of it, and implements the actual enforcement.
Enforcement happens at the route decoration level, whenever possible. In
case of the API, when over quota, a 413 error is returned, with an
appropriate JSON payload. In case of web routes, a 413 HTML page is
rendered with similar information.
This implementation is for a **soft quota**: quota usage is checked
before an operation is to be performed, and the operation is *only*
denied if the user is already over quota. This makes it possible to go
over quota, but has the significant advantage of being practically
implementable within the current Forgejo architecture.
The goal of enforcement is to deny actions that can make the user go
over quota, and allow the rest. As such, deleting things should - in
almost all cases - be possible. A prime exemption is deleting files via
the web ui: that creates a new commit, which in turn increases repo
size, thus, is denied if the user is over quota.
Limitations
-----------
Because we generally work at a route decorator level, and rarely
look *into* the operation itself, `size:repos:public` and
`size:repos:private` are not enforced at this level, the engine enforces
against `size:repos:all`. This will be improved in the future.
AGit does not play very well with this system, because AGit PRs count
toward the repo they're opened against, while in the GitHub-style fork +
pull model, it counts against the fork. This too, can be improved in the
future.
There's very little done on the UI side to guard against going over
quota. What this patch implements, is enforcement, not prevention. The
UI will still let you *try* operations that *will* result in a denial.
Signed-off-by: Gergely Nagy <forgejo@gergo.csillger.hu>
2024-07-06 11:30:16 +03:00
|
|
|
r.Put("", reqPackageAccess(perm.AccessModeWrite), enforcePackagesQuota(), cran.UploadBinaryPackageFile)
|
2023-05-22 05:57:49 +03:00
|
|
|
})
|
|
|
|
}, reqPackageAccess(perm.AccessModeRead))
|
2023-05-02 19:31:35 +03:00
|
|
|
r.Group("/debian", func() {
|
|
|
|
r.Get("/repository.key", debian.GetRepositoryKey)
|
|
|
|
r.Group("/dists/{distribution}", func() {
|
|
|
|
r.Get("/{filename}", debian.GetRepositoryFile)
|
|
|
|
r.Get("/by-hash/{algorithm}/{hash}", debian.GetRepositoryFileByHash)
|
|
|
|
r.Group("/{component}/{architecture}", func() {
|
|
|
|
r.Get("/{filename}", debian.GetRepositoryFile)
|
|
|
|
r.Get("/by-hash/{algorithm}/{hash}", debian.GetRepositoryFileByHash)
|
|
|
|
})
|
|
|
|
})
|
|
|
|
r.Group("/pool/{distribution}/{component}", func() {
|
|
|
|
r.Get("/{name}_{version}_{architecture}.deb", debian.DownloadPackageFile)
|
|
|
|
r.Group("", func() {
|
feat(quota): Quota enforcement
The previous commit laid out the foundation of the quota engine, this
one builds on top of it, and implements the actual enforcement.
Enforcement happens at the route decoration level, whenever possible. In
case of the API, when over quota, a 413 error is returned, with an
appropriate JSON payload. In case of web routes, a 413 HTML page is
rendered with similar information.
This implementation is for a **soft quota**: quota usage is checked
before an operation is to be performed, and the operation is *only*
denied if the user is already over quota. This makes it possible to go
over quota, but has the significant advantage of being practically
implementable within the current Forgejo architecture.
The goal of enforcement is to deny actions that can make the user go
over quota, and allow the rest. As such, deleting things should - in
almost all cases - be possible. A prime exemption is deleting files via
the web ui: that creates a new commit, which in turn increases repo
size, thus, is denied if the user is over quota.
Limitations
-----------
Because we generally work at a route decorator level, and rarely
look *into* the operation itself, `size:repos:public` and
`size:repos:private` are not enforced at this level, the engine enforces
against `size:repos:all`. This will be improved in the future.
AGit does not play very well with this system, because AGit PRs count
toward the repo they're opened against, while in the GitHub-style fork +
pull model, it counts against the fork. This too, can be improved in the
future.
There's very little done on the UI side to guard against going over
quota. What this patch implements, is enforcement, not prevention. The
UI will still let you *try* operations that *will* result in a denial.
Signed-off-by: Gergely Nagy <forgejo@gergo.csillger.hu>
2024-07-06 11:30:16 +03:00
|
|
|
r.Put("/upload", enforcePackagesQuota(), debian.UploadPackageFile)
|
2023-05-02 19:31:35 +03:00
|
|
|
r.Delete("/{name}/{version}/{architecture}", debian.DeletePackageFile)
|
|
|
|
}, reqPackageAccess(perm.AccessModeWrite))
|
|
|
|
})
|
|
|
|
}, reqPackageAccess(perm.AccessModeRead))
|
2023-05-14 18:38:40 +03:00
|
|
|
r.Group("/go", func() {
|
feat(quota): Quota enforcement
The previous commit laid out the foundation of the quota engine, this
one builds on top of it, and implements the actual enforcement.
Enforcement happens at the route decoration level, whenever possible. In
case of the API, when over quota, a 413 error is returned, with an
appropriate JSON payload. In case of web routes, a 413 HTML page is
rendered with similar information.
This implementation is for a **soft quota**: quota usage is checked
before an operation is to be performed, and the operation is *only*
denied if the user is already over quota. This makes it possible to go
over quota, but has the significant advantage of being practically
implementable within the current Forgejo architecture.
The goal of enforcement is to deny actions that can make the user go
over quota, and allow the rest. As such, deleting things should - in
almost all cases - be possible. A prime exemption is deleting files via
the web ui: that creates a new commit, which in turn increases repo
size, thus, is denied if the user is over quota.
Limitations
-----------
Because we generally work at a route decorator level, and rarely
look *into* the operation itself, `size:repos:public` and
`size:repos:private` are not enforced at this level, the engine enforces
against `size:repos:all`. This will be improved in the future.
AGit does not play very well with this system, because AGit PRs count
toward the repo they're opened against, while in the GitHub-style fork +
pull model, it counts against the fork. This too, can be improved in the
future.
There's very little done on the UI side to guard against going over
quota. What this patch implements, is enforcement, not prevention. The
UI will still let you *try* operations that *will* result in a denial.
Signed-off-by: Gergely Nagy <forgejo@gergo.csillger.hu>
2024-07-06 11:30:16 +03:00
|
|
|
r.Put("/upload", reqPackageAccess(perm.AccessModeWrite), enforcePackagesQuota(), goproxy.UploadPackage)
|
2023-05-14 18:38:40 +03:00
|
|
|
r.Get("/sumdb/sum.golang.org/supported", func(ctx *context.Context) {
|
|
|
|
ctx.Status(http.StatusNotFound)
|
|
|
|
})
|
|
|
|
|
|
|
|
// Manual mapping of routes because the package name contains slashes which chi does not support
|
|
|
|
// https://go.dev/ref/mod#goproxy-protocol
|
|
|
|
r.Get("/*", func(ctx *context.Context) {
|
|
|
|
path := ctx.Params("*")
|
|
|
|
|
|
|
|
if strings.HasSuffix(path, "/@latest") {
|
|
|
|
ctx.SetParams("name", path[:len(path)-len("/@latest")])
|
|
|
|
ctx.SetParams("version", "latest")
|
|
|
|
|
|
|
|
goproxy.PackageVersionMetadata(ctx)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
parts := strings.SplitN(path, "/@v/", 2)
|
|
|
|
if len(parts) != 2 {
|
|
|
|
ctx.Status(http.StatusNotFound)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
ctx.SetParams("name", parts[0])
|
|
|
|
|
|
|
|
// <package/name>/@v/list
|
|
|
|
if parts[1] == "list" {
|
|
|
|
goproxy.EnumeratePackageVersions(ctx)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
// <package/name>/@v/<version>.zip
|
|
|
|
if strings.HasSuffix(parts[1], ".zip") {
|
|
|
|
ctx.SetParams("version", parts[1][:len(parts[1])-len(".zip")])
|
|
|
|
|
|
|
|
goproxy.DownloadPackageFile(ctx)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
// <package/name>/@v/<version>.info
|
|
|
|
if strings.HasSuffix(parts[1], ".info") {
|
|
|
|
ctx.SetParams("version", parts[1][:len(parts[1])-len(".info")])
|
|
|
|
|
|
|
|
goproxy.PackageVersionMetadata(ctx)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
// <package/name>/@v/<version>.mod
|
|
|
|
if strings.HasSuffix(parts[1], ".mod") {
|
|
|
|
ctx.SetParams("version", parts[1][:len(parts[1])-len(".mod")])
|
|
|
|
|
|
|
|
goproxy.PackageVersionGoModContent(ctx)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
ctx.Status(http.StatusNotFound)
|
|
|
|
})
|
|
|
|
}, reqPackageAccess(perm.AccessModeRead))
|
2022-03-30 11:42:47 +03:00
|
|
|
r.Group("/generic", func() {
|
2022-08-09 07:39:24 +03:00
|
|
|
r.Group("/{packagename}/{packageversion}", func() {
|
|
|
|
r.Delete("", reqPackageAccess(perm.AccessModeWrite), generic.DeletePackage)
|
|
|
|
r.Group("/{filename}", func() {
|
|
|
|
r.Get("", generic.DownloadPackageFile)
|
|
|
|
r.Group("", func() {
|
feat(quota): Quota enforcement
The previous commit laid out the foundation of the quota engine, this
one builds on top of it, and implements the actual enforcement.
Enforcement happens at the route decoration level, whenever possible. In
case of the API, when over quota, a 413 error is returned, with an
appropriate JSON payload. In case of web routes, a 413 HTML page is
rendered with similar information.
This implementation is for a **soft quota**: quota usage is checked
before an operation is to be performed, and the operation is *only*
denied if the user is already over quota. This makes it possible to go
over quota, but has the significant advantage of being practically
implementable within the current Forgejo architecture.
The goal of enforcement is to deny actions that can make the user go
over quota, and allow the rest. As such, deleting things should - in
almost all cases - be possible. A prime exemption is deleting files via
the web ui: that creates a new commit, which in turn increases repo
size, thus, is denied if the user is over quota.
Limitations
-----------
Because we generally work at a route decorator level, and rarely
look *into* the operation itself, `size:repos:public` and
`size:repos:private` are not enforced at this level, the engine enforces
against `size:repos:all`. This will be improved in the future.
AGit does not play very well with this system, because AGit PRs count
toward the repo they're opened against, while in the GitHub-style fork +
pull model, it counts against the fork. This too, can be improved in the
future.
There's very little done on the UI side to guard against going over
quota. What this patch implements, is enforcement, not prevention. The
UI will still let you *try* operations that *will* result in a denial.
Signed-off-by: Gergely Nagy <forgejo@gergo.csillger.hu>
2024-07-06 11:30:16 +03:00
|
|
|
r.Put("", enforcePackagesQuota(), generic.UploadPackage)
|
2022-08-09 07:39:24 +03:00
|
|
|
r.Delete("", generic.DeletePackageFile)
|
|
|
|
}, reqPackageAccess(perm.AccessModeWrite))
|
|
|
|
})
|
2022-03-30 11:42:47 +03:00
|
|
|
})
|
2022-09-24 18:17:08 +03:00
|
|
|
}, reqPackageAccess(perm.AccessModeRead))
|
2022-04-19 19:55:35 +03:00
|
|
|
r.Group("/helm", func() {
|
|
|
|
r.Get("/index.yaml", helm.Index)
|
|
|
|
r.Get("/{filename}", helm.DownloadPackageFile)
|
feat(quota): Quota enforcement
The previous commit laid out the foundation of the quota engine, this
one builds on top of it, and implements the actual enforcement.
Enforcement happens at the route decoration level, whenever possible. In
case of the API, when over quota, a 413 error is returned, with an
appropriate JSON payload. In case of web routes, a 413 HTML page is
rendered with similar information.
This implementation is for a **soft quota**: quota usage is checked
before an operation is to be performed, and the operation is *only*
denied if the user is already over quota. This makes it possible to go
over quota, but has the significant advantage of being practically
implementable within the current Forgejo architecture.
The goal of enforcement is to deny actions that can make the user go
over quota, and allow the rest. As such, deleting things should - in
almost all cases - be possible. A prime exemption is deleting files via
the web ui: that creates a new commit, which in turn increases repo
size, thus, is denied if the user is over quota.
Limitations
-----------
Because we generally work at a route decorator level, and rarely
look *into* the operation itself, `size:repos:public` and
`size:repos:private` are not enforced at this level, the engine enforces
against `size:repos:all`. This will be improved in the future.
AGit does not play very well with this system, because AGit PRs count
toward the repo they're opened against, while in the GitHub-style fork +
pull model, it counts against the fork. This too, can be improved in the
future.
There's very little done on the UI side to guard against going over
quota. What this patch implements, is enforcement, not prevention. The
UI will still let you *try* operations that *will* result in a denial.
Signed-off-by: Gergely Nagy <forgejo@gergo.csillger.hu>
2024-07-06 11:30:16 +03:00
|
|
|
r.Post("/api/charts", reqPackageAccess(perm.AccessModeWrite), enforcePackagesQuota(), helm.UploadPackage)
|
2022-09-24 18:17:08 +03:00
|
|
|
}, reqPackageAccess(perm.AccessModeRead))
|
2022-03-30 11:42:47 +03:00
|
|
|
r.Group("/maven", func() {
|
feat(quota): Quota enforcement
The previous commit laid out the foundation of the quota engine, this
one builds on top of it, and implements the actual enforcement.
Enforcement happens at the route decoration level, whenever possible. In
case of the API, when over quota, a 413 error is returned, with an
appropriate JSON payload. In case of web routes, a 413 HTML page is
rendered with similar information.
This implementation is for a **soft quota**: quota usage is checked
before an operation is to be performed, and the operation is *only*
denied if the user is already over quota. This makes it possible to go
over quota, but has the significant advantage of being practically
implementable within the current Forgejo architecture.
The goal of enforcement is to deny actions that can make the user go
over quota, and allow the rest. As such, deleting things should - in
almost all cases - be possible. A prime exemption is deleting files via
the web ui: that creates a new commit, which in turn increases repo
size, thus, is denied if the user is over quota.
Limitations
-----------
Because we generally work at a route decorator level, and rarely
look *into* the operation itself, `size:repos:public` and
`size:repos:private` are not enforced at this level, the engine enforces
against `size:repos:all`. This will be improved in the future.
AGit does not play very well with this system, because AGit PRs count
toward the repo they're opened against, while in the GitHub-style fork +
pull model, it counts against the fork. This too, can be improved in the
future.
There's very little done on the UI side to guard against going over
quota. What this patch implements, is enforcement, not prevention. The
UI will still let you *try* operations that *will* result in a denial.
Signed-off-by: Gergely Nagy <forgejo@gergo.csillger.hu>
2024-07-06 11:30:16 +03:00
|
|
|
r.Put("/*", reqPackageAccess(perm.AccessModeWrite), enforcePackagesQuota(), maven.UploadPackageFile)
|
2022-03-30 11:42:47 +03:00
|
|
|
r.Get("/*", maven.DownloadPackageFile)
|
2022-11-24 17:25:13 +03:00
|
|
|
r.Head("/*", maven.ProvidePackageFileHeader)
|
2022-09-24 18:17:08 +03:00
|
|
|
}, reqPackageAccess(perm.AccessModeRead))
|
2022-03-30 11:42:47 +03:00
|
|
|
r.Group("/nuget", func() {
|
2022-10-13 13:19:39 +03:00
|
|
|
r.Group("", func() { // Needs to be unauthenticated for the NuGet client.
|
|
|
|
r.Get("/", nuget.ServiceIndexV2)
|
|
|
|
r.Get("/index.json", nuget.ServiceIndexV3)
|
|
|
|
r.Get("/$metadata", nuget.FeedCapabilityResource)
|
|
|
|
})
|
2022-03-30 11:42:47 +03:00
|
|
|
r.Group("", func() {
|
2022-10-13 13:19:39 +03:00
|
|
|
r.Get("/query", nuget.SearchServiceV3)
|
2022-09-24 18:17:08 +03:00
|
|
|
r.Group("/registration/{id}", func() {
|
|
|
|
r.Get("/index.json", nuget.RegistrationIndex)
|
2022-10-13 13:19:39 +03:00
|
|
|
r.Get("/{version}", nuget.RegistrationLeafV3)
|
2022-09-24 18:17:08 +03:00
|
|
|
})
|
|
|
|
r.Group("/package/{id}", func() {
|
2022-10-13 13:19:39 +03:00
|
|
|
r.Get("/index.json", nuget.EnumeratePackageVersionsV3)
|
2022-09-24 18:17:08 +03:00
|
|
|
r.Get("/{version}/{filename}", nuget.DownloadPackageFile)
|
|
|
|
})
|
|
|
|
r.Group("", func() {
|
feat(quota): Quota enforcement
The previous commit laid out the foundation of the quota engine, this
one builds on top of it, and implements the actual enforcement.
Enforcement happens at the route decoration level, whenever possible. In
case of the API, when over quota, a 413 error is returned, with an
appropriate JSON payload. In case of web routes, a 413 HTML page is
rendered with similar information.
This implementation is for a **soft quota**: quota usage is checked
before an operation is to be performed, and the operation is *only*
denied if the user is already over quota. This makes it possible to go
over quota, but has the significant advantage of being practically
implementable within the current Forgejo architecture.
The goal of enforcement is to deny actions that can make the user go
over quota, and allow the rest. As such, deleting things should - in
almost all cases - be possible. A prime exemption is deleting files via
the web ui: that creates a new commit, which in turn increases repo
size, thus, is denied if the user is over quota.
Limitations
-----------
Because we generally work at a route decorator level, and rarely
look *into* the operation itself, `size:repos:public` and
`size:repos:private` are not enforced at this level, the engine enforces
against `size:repos:all`. This will be improved in the future.
AGit does not play very well with this system, because AGit PRs count
toward the repo they're opened against, while in the GitHub-style fork +
pull model, it counts against the fork. This too, can be improved in the
future.
There's very little done on the UI side to guard against going over
quota. What this patch implements, is enforcement, not prevention. The
UI will still let you *try* operations that *will* result in a denial.
Signed-off-by: Gergely Nagy <forgejo@gergo.csillger.hu>
2024-07-06 11:30:16 +03:00
|
|
|
r.Put("/", enforcePackagesQuota(), nuget.UploadPackage)
|
|
|
|
r.Put("/symbolpackage", enforcePackagesQuota(), nuget.UploadSymbolPackage)
|
2022-09-24 18:17:08 +03:00
|
|
|
r.Delete("/{id}/{version}", nuget.DeletePackage)
|
|
|
|
}, reqPackageAccess(perm.AccessModeWrite))
|
2022-10-12 09:53:56 +03:00
|
|
|
r.Get("/symbols/{filename}/{guid:[0-9a-fA-F]{32}[fF]{8}}/{filename2}", nuget.DownloadSymbolFile)
|
2022-10-13 13:19:39 +03:00
|
|
|
r.Get("/Packages(Id='{id:[^']+}',Version='{version:[^']+}')", nuget.RegistrationLeafV2)
|
2023-02-11 14:30:44 +03:00
|
|
|
r.Group("/Packages()", func() {
|
|
|
|
r.Get("", nuget.SearchServiceV2)
|
|
|
|
r.Get("/$count", nuget.SearchServiceV2Count)
|
|
|
|
})
|
|
|
|
r.Group("/FindPackagesById()", func() {
|
|
|
|
r.Get("", nuget.EnumeratePackageVersionsV2)
|
|
|
|
r.Get("/$count", nuget.EnumeratePackageVersionsV2Count)
|
|
|
|
})
|
|
|
|
r.Group("/Search()", func() {
|
|
|
|
r.Get("", nuget.SearchServiceV2)
|
|
|
|
r.Get("/$count", nuget.SearchServiceV2Count)
|
|
|
|
})
|
2022-09-24 18:17:08 +03:00
|
|
|
}, reqPackageAccess(perm.AccessModeRead))
|
2022-03-30 11:42:47 +03:00
|
|
|
})
|
|
|
|
r.Group("/npm", func() {
|
|
|
|
r.Group("/@{scope}/{id}", func() {
|
|
|
|
r.Get("", npm.PackageMetadata)
|
feat(quota): Quota enforcement
The previous commit laid out the foundation of the quota engine, this
one builds on top of it, and implements the actual enforcement.
Enforcement happens at the route decoration level, whenever possible. In
case of the API, when over quota, a 413 error is returned, with an
appropriate JSON payload. In case of web routes, a 413 HTML page is
rendered with similar information.
This implementation is for a **soft quota**: quota usage is checked
before an operation is to be performed, and the operation is *only*
denied if the user is already over quota. This makes it possible to go
over quota, but has the significant advantage of being practically
implementable within the current Forgejo architecture.
The goal of enforcement is to deny actions that can make the user go
over quota, and allow the rest. As such, deleting things should - in
almost all cases - be possible. A prime exemption is deleting files via
the web ui: that creates a new commit, which in turn increases repo
size, thus, is denied if the user is over quota.
Limitations
-----------
Because we generally work at a route decorator level, and rarely
look *into* the operation itself, `size:repos:public` and
`size:repos:private` are not enforced at this level, the engine enforces
against `size:repos:all`. This will be improved in the future.
AGit does not play very well with this system, because AGit PRs count
toward the repo they're opened against, while in the GitHub-style fork +
pull model, it counts against the fork. This too, can be improved in the
future.
There's very little done on the UI side to guard against going over
quota. What this patch implements, is enforcement, not prevention. The
UI will still let you *try* operations that *will* result in a denial.
Signed-off-by: Gergely Nagy <forgejo@gergo.csillger.hu>
2024-07-06 11:30:16 +03:00
|
|
|
r.Put("", reqPackageAccess(perm.AccessModeWrite), enforcePackagesQuota(), npm.UploadPackage)
|
2022-08-09 10:23:43 +03:00
|
|
|
r.Group("/-/{version}/{filename}", func() {
|
|
|
|
r.Get("", npm.DownloadPackageFile)
|
|
|
|
r.Delete("/-rev/{revision}", reqPackageAccess(perm.AccessModeWrite), npm.DeletePackageVersion)
|
|
|
|
})
|
2022-10-24 16:50:22 +03:00
|
|
|
r.Get("/-/{filename}", npm.DownloadPackageFileByName)
|
2022-08-09 10:23:43 +03:00
|
|
|
r.Group("/-rev/{revision}", func() {
|
|
|
|
r.Delete("", npm.DeletePackage)
|
|
|
|
r.Put("", npm.DeletePreview)
|
|
|
|
}, reqPackageAccess(perm.AccessModeWrite))
|
2022-03-30 11:42:47 +03:00
|
|
|
})
|
|
|
|
r.Group("/{id}", func() {
|
|
|
|
r.Get("", npm.PackageMetadata)
|
feat(quota): Quota enforcement
The previous commit laid out the foundation of the quota engine, this
one builds on top of it, and implements the actual enforcement.
Enforcement happens at the route decoration level, whenever possible. In
case of the API, when over quota, a 413 error is returned, with an
appropriate JSON payload. In case of web routes, a 413 HTML page is
rendered with similar information.
This implementation is for a **soft quota**: quota usage is checked
before an operation is to be performed, and the operation is *only*
denied if the user is already over quota. This makes it possible to go
over quota, but has the significant advantage of being practically
implementable within the current Forgejo architecture.
The goal of enforcement is to deny actions that can make the user go
over quota, and allow the rest. As such, deleting things should - in
almost all cases - be possible. A prime exemption is deleting files via
the web ui: that creates a new commit, which in turn increases repo
size, thus, is denied if the user is over quota.
Limitations
-----------
Because we generally work at a route decorator level, and rarely
look *into* the operation itself, `size:repos:public` and
`size:repos:private` are not enforced at this level, the engine enforces
against `size:repos:all`. This will be improved in the future.
AGit does not play very well with this system, because AGit PRs count
toward the repo they're opened against, while in the GitHub-style fork +
pull model, it counts against the fork. This too, can be improved in the
future.
There's very little done on the UI side to guard against going over
quota. What this patch implements, is enforcement, not prevention. The
UI will still let you *try* operations that *will* result in a denial.
Signed-off-by: Gergely Nagy <forgejo@gergo.csillger.hu>
2024-07-06 11:30:16 +03:00
|
|
|
r.Put("", reqPackageAccess(perm.AccessModeWrite), enforcePackagesQuota(), npm.UploadPackage)
|
2022-08-09 10:23:43 +03:00
|
|
|
r.Group("/-/{version}/{filename}", func() {
|
|
|
|
r.Get("", npm.DownloadPackageFile)
|
|
|
|
r.Delete("/-rev/{revision}", reqPackageAccess(perm.AccessModeWrite), npm.DeletePackageVersion)
|
|
|
|
})
|
2022-10-24 16:50:22 +03:00
|
|
|
r.Get("/-/{filename}", npm.DownloadPackageFileByName)
|
2022-08-09 10:23:43 +03:00
|
|
|
r.Group("/-rev/{revision}", func() {
|
|
|
|
r.Delete("", npm.DeletePackage)
|
|
|
|
r.Put("", npm.DeletePreview)
|
|
|
|
}, reqPackageAccess(perm.AccessModeWrite))
|
2022-03-30 11:42:47 +03:00
|
|
|
})
|
|
|
|
r.Group("/-/package/@{scope}/{id}/dist-tags", func() {
|
|
|
|
r.Get("", npm.ListPackageTags)
|
|
|
|
r.Group("/{tag}", func() {
|
|
|
|
r.Put("", npm.AddPackageTag)
|
|
|
|
r.Delete("", npm.DeletePackageTag)
|
|
|
|
}, reqPackageAccess(perm.AccessModeWrite))
|
|
|
|
})
|
|
|
|
r.Group("/-/package/{id}/dist-tags", func() {
|
|
|
|
r.Get("", npm.ListPackageTags)
|
|
|
|
r.Group("/{tag}", func() {
|
|
|
|
r.Put("", npm.AddPackageTag)
|
|
|
|
r.Delete("", npm.DeletePackageTag)
|
|
|
|
}, reqPackageAccess(perm.AccessModeWrite))
|
|
|
|
})
|
2022-09-24 14:24:33 +03:00
|
|
|
r.Group("/-/v1/search", func() {
|
|
|
|
r.Get("", npm.PackageSearch)
|
|
|
|
})
|
2022-09-24 18:17:08 +03:00
|
|
|
}, reqPackageAccess(perm.AccessModeRead))
|
2022-08-07 13:09:54 +03:00
|
|
|
r.Group("/pub", func() {
|
|
|
|
r.Group("/api/packages", func() {
|
|
|
|
r.Group("/versions/new", func() {
|
|
|
|
r.Get("", pub.RequestUpload)
|
feat(quota): Quota enforcement
The previous commit laid out the foundation of the quota engine, this
one builds on top of it, and implements the actual enforcement.
Enforcement happens at the route decoration level, whenever possible. In
case of the API, when over quota, a 413 error is returned, with an
appropriate JSON payload. In case of web routes, a 413 HTML page is
rendered with similar information.
This implementation is for a **soft quota**: quota usage is checked
before an operation is to be performed, and the operation is *only*
denied if the user is already over quota. This makes it possible to go
over quota, but has the significant advantage of being practically
implementable within the current Forgejo architecture.
The goal of enforcement is to deny actions that can make the user go
over quota, and allow the rest. As such, deleting things should - in
almost all cases - be possible. A prime exemption is deleting files via
the web ui: that creates a new commit, which in turn increases repo
size, thus, is denied if the user is over quota.
Limitations
-----------
Because we generally work at a route decorator level, and rarely
look *into* the operation itself, `size:repos:public` and
`size:repos:private` are not enforced at this level, the engine enforces
against `size:repos:all`. This will be improved in the future.
AGit does not play very well with this system, because AGit PRs count
toward the repo they're opened against, while in the GitHub-style fork +
pull model, it counts against the fork. This too, can be improved in the
future.
There's very little done on the UI side to guard against going over
quota. What this patch implements, is enforcement, not prevention. The
UI will still let you *try* operations that *will* result in a denial.
Signed-off-by: Gergely Nagy <forgejo@gergo.csillger.hu>
2024-07-06 11:30:16 +03:00
|
|
|
r.Post("/upload", enforcePackagesQuota(), pub.UploadPackageFile)
|
2022-08-07 13:09:54 +03:00
|
|
|
r.Get("/finalize/{id}/{version}", pub.FinalizePackage)
|
|
|
|
}, reqPackageAccess(perm.AccessModeWrite))
|
|
|
|
r.Group("/{id}", func() {
|
|
|
|
r.Get("", pub.EnumeratePackageVersions)
|
|
|
|
r.Get("/files/{version}", pub.DownloadPackageFile)
|
|
|
|
r.Get("/{version}", pub.PackageVersionMetadata)
|
|
|
|
})
|
|
|
|
})
|
2022-09-24 18:17:08 +03:00
|
|
|
}, reqPackageAccess(perm.AccessModeRead))
|
2022-03-30 11:42:47 +03:00
|
|
|
r.Group("/pypi", func() {
|
feat(quota): Quota enforcement
The previous commit laid out the foundation of the quota engine, this
one builds on top of it, and implements the actual enforcement.
Enforcement happens at the route decoration level, whenever possible. In
case of the API, when over quota, a 413 error is returned, with an
appropriate JSON payload. In case of web routes, a 413 HTML page is
rendered with similar information.
This implementation is for a **soft quota**: quota usage is checked
before an operation is to be performed, and the operation is *only*
denied if the user is already over quota. This makes it possible to go
over quota, but has the significant advantage of being practically
implementable within the current Forgejo architecture.
The goal of enforcement is to deny actions that can make the user go
over quota, and allow the rest. As such, deleting things should - in
almost all cases - be possible. A prime exemption is deleting files via
the web ui: that creates a new commit, which in turn increases repo
size, thus, is denied if the user is over quota.
Limitations
-----------
Because we generally work at a route decorator level, and rarely
look *into* the operation itself, `size:repos:public` and
`size:repos:private` are not enforced at this level, the engine enforces
against `size:repos:all`. This will be improved in the future.
AGit does not play very well with this system, because AGit PRs count
toward the repo they're opened against, while in the GitHub-style fork +
pull model, it counts against the fork. This too, can be improved in the
future.
There's very little done on the UI side to guard against going over
quota. What this patch implements, is enforcement, not prevention. The
UI will still let you *try* operations that *will* result in a denial.
Signed-off-by: Gergely Nagy <forgejo@gergo.csillger.hu>
2024-07-06 11:30:16 +03:00
|
|
|
r.Post("/", reqPackageAccess(perm.AccessModeWrite), enforcePackagesQuota(), pypi.UploadPackageFile)
|
2022-03-30 11:42:47 +03:00
|
|
|
r.Get("/files/{id}/{version}/{filename}", pypi.DownloadPackageFile)
|
|
|
|
r.Get("/simple/{id}", pypi.PackageMetadata)
|
2022-09-24 18:17:08 +03:00
|
|
|
}, reqPackageAccess(perm.AccessModeRead))
|
2024-01-19 14:37:10 +03:00
|
|
|
r.Group("/rpm", func() {
|
|
|
|
r.Group("/repository.key", func() {
|
|
|
|
r.Head("", rpm.GetRepositoryKey)
|
|
|
|
r.Get("", rpm.GetRepositoryKey)
|
|
|
|
})
|
|
|
|
|
|
|
|
var (
|
|
|
|
repoPattern = regexp.MustCompile(`\A(.*?)\.repo\z`)
|
|
|
|
uploadPattern = regexp.MustCompile(`\A(.*?)/upload\z`)
|
|
|
|
filePattern = regexp.MustCompile(`\A(.*?)/package/([^/]+)/([^/]+)/([^/]+)(?:/([^/]+\.rpm)|)\z`)
|
|
|
|
repoFilePattern = regexp.MustCompile(`\A(.*?)/repodata/([^/]+)\z`)
|
|
|
|
)
|
|
|
|
|
|
|
|
r.Methods("HEAD,GET,PUT,DELETE", "*", func(ctx *context.Context) {
|
|
|
|
path := ctx.Params("*")
|
|
|
|
isHead := ctx.Req.Method == "HEAD"
|
|
|
|
isGetHead := ctx.Req.Method == "HEAD" || ctx.Req.Method == "GET"
|
|
|
|
isPut := ctx.Req.Method == "PUT"
|
|
|
|
isDelete := ctx.Req.Method == "DELETE"
|
|
|
|
|
|
|
|
m := repoPattern.FindStringSubmatch(path)
|
|
|
|
if len(m) == 2 && isGetHead {
|
|
|
|
ctx.SetParams("group", strings.Trim(m[1], "/"))
|
|
|
|
rpm.GetRepositoryConfig(ctx)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
m = repoFilePattern.FindStringSubmatch(path)
|
|
|
|
if len(m) == 3 && isGetHead {
|
|
|
|
ctx.SetParams("group", strings.Trim(m[1], "/"))
|
|
|
|
ctx.SetParams("filename", m[2])
|
|
|
|
if isHead {
|
|
|
|
rpm.CheckRepositoryFileExistence(ctx)
|
|
|
|
} else {
|
|
|
|
rpm.GetRepositoryFile(ctx)
|
|
|
|
}
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
m = uploadPattern.FindStringSubmatch(path)
|
|
|
|
if len(m) == 2 && isPut {
|
|
|
|
reqPackageAccess(perm.AccessModeWrite)(ctx)
|
|
|
|
if ctx.Written() {
|
|
|
|
return
|
|
|
|
}
|
feat(quota): Quota enforcement
The previous commit laid out the foundation of the quota engine, this
one builds on top of it, and implements the actual enforcement.
Enforcement happens at the route decoration level, whenever possible. In
case of the API, when over quota, a 413 error is returned, with an
appropriate JSON payload. In case of web routes, a 413 HTML page is
rendered with similar information.
This implementation is for a **soft quota**: quota usage is checked
before an operation is to be performed, and the operation is *only*
denied if the user is already over quota. This makes it possible to go
over quota, but has the significant advantage of being practically
implementable within the current Forgejo architecture.
The goal of enforcement is to deny actions that can make the user go
over quota, and allow the rest. As such, deleting things should - in
almost all cases - be possible. A prime exemption is deleting files via
the web ui: that creates a new commit, which in turn increases repo
size, thus, is denied if the user is over quota.
Limitations
-----------
Because we generally work at a route decorator level, and rarely
look *into* the operation itself, `size:repos:public` and
`size:repos:private` are not enforced at this level, the engine enforces
against `size:repos:all`. This will be improved in the future.
AGit does not play very well with this system, because AGit PRs count
toward the repo they're opened against, while in the GitHub-style fork +
pull model, it counts against the fork. This too, can be improved in the
future.
There's very little done on the UI side to guard against going over
quota. What this patch implements, is enforcement, not prevention. The
UI will still let you *try* operations that *will* result in a denial.
Signed-off-by: Gergely Nagy <forgejo@gergo.csillger.hu>
2024-07-06 11:30:16 +03:00
|
|
|
enforcePackagesQuota()(ctx)
|
|
|
|
if ctx.Written() {
|
|
|
|
return
|
|
|
|
}
|
2024-01-19 14:37:10 +03:00
|
|
|
ctx.SetParams("group", strings.Trim(m[1], "/"))
|
|
|
|
rpm.UploadPackageFile(ctx)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
m = filePattern.FindStringSubmatch(path)
|
|
|
|
if len(m) == 6 && (isGetHead || isDelete) {
|
|
|
|
ctx.SetParams("group", strings.Trim(m[1], "/"))
|
|
|
|
ctx.SetParams("name", m[2])
|
|
|
|
ctx.SetParams("version", m[3])
|
|
|
|
ctx.SetParams("architecture", m[4])
|
|
|
|
if isGetHead {
|
|
|
|
rpm.DownloadPackageFile(ctx)
|
|
|
|
} else {
|
|
|
|
reqPackageAccess(perm.AccessModeWrite)(ctx)
|
|
|
|
if ctx.Written() {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
rpm.DeletePackageFile(ctx)
|
|
|
|
}
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
ctx.Status(http.StatusNotFound)
|
|
|
|
})
|
|
|
|
}, reqPackageAccess(perm.AccessModeRead))
|
2022-03-30 11:42:47 +03:00
|
|
|
r.Group("/rubygems", func() {
|
|
|
|
r.Get("/specs.4.8.gz", rubygems.EnumeratePackages)
|
|
|
|
r.Get("/latest_specs.4.8.gz", rubygems.EnumeratePackagesLatest)
|
|
|
|
r.Get("/prerelease_specs.4.8.gz", rubygems.EnumeratePackagesPreRelease)
|
2024-05-20 02:30:41 +03:00
|
|
|
r.Get("/info/{package}", rubygems.ServePackageInfo)
|
|
|
|
r.Get("/versions", rubygems.ServeVersionsFile)
|
2022-03-30 11:42:47 +03:00
|
|
|
r.Get("/quick/Marshal.4.8/{filename}", rubygems.ServePackageSpecification)
|
|
|
|
r.Get("/gems/{filename}", rubygems.DownloadPackageFile)
|
|
|
|
r.Group("/api/v1/gems", func() {
|
feat(quota): Quota enforcement
The previous commit laid out the foundation of the quota engine, this
one builds on top of it, and implements the actual enforcement.
Enforcement happens at the route decoration level, whenever possible. In
case of the API, when over quota, a 413 error is returned, with an
appropriate JSON payload. In case of web routes, a 413 HTML page is
rendered with similar information.
This implementation is for a **soft quota**: quota usage is checked
before an operation is to be performed, and the operation is *only*
denied if the user is already over quota. This makes it possible to go
over quota, but has the significant advantage of being practically
implementable within the current Forgejo architecture.
The goal of enforcement is to deny actions that can make the user go
over quota, and allow the rest. As such, deleting things should - in
almost all cases - be possible. A prime exemption is deleting files via
the web ui: that creates a new commit, which in turn increases repo
size, thus, is denied if the user is over quota.
Limitations
-----------
Because we generally work at a route decorator level, and rarely
look *into* the operation itself, `size:repos:public` and
`size:repos:private` are not enforced at this level, the engine enforces
against `size:repos:all`. This will be improved in the future.
AGit does not play very well with this system, because AGit PRs count
toward the repo they're opened against, while in the GitHub-style fork +
pull model, it counts against the fork. This too, can be improved in the
future.
There's very little done on the UI side to guard against going over
quota. What this patch implements, is enforcement, not prevention. The
UI will still let you *try* operations that *will* result in a denial.
Signed-off-by: Gergely Nagy <forgejo@gergo.csillger.hu>
2024-07-06 11:30:16 +03:00
|
|
|
r.Post("/", enforcePackagesQuota(), rubygems.UploadPackageFile)
|
2022-03-30 11:42:47 +03:00
|
|
|
r.Delete("/yank", rubygems.DeletePackage)
|
|
|
|
}, reqPackageAccess(perm.AccessModeWrite))
|
2022-09-24 18:17:08 +03:00
|
|
|
}, reqPackageAccess(perm.AccessModeRead))
|
2023-03-13 23:28:39 +03:00
|
|
|
r.Group("/swift", func() {
|
|
|
|
r.Group("/{scope}/{name}", func() {
|
|
|
|
r.Group("", func() {
|
|
|
|
r.Get("", swift.EnumeratePackageVersions)
|
|
|
|
r.Get(".json", swift.EnumeratePackageVersions)
|
|
|
|
}, swift.CheckAcceptMediaType(swift.AcceptJSON))
|
|
|
|
r.Group("/{version}", func() {
|
|
|
|
r.Get("/Package.swift", swift.CheckAcceptMediaType(swift.AcceptSwift), swift.DownloadManifest)
|
feat(quota): Quota enforcement
The previous commit laid out the foundation of the quota engine, this
one builds on top of it, and implements the actual enforcement.
Enforcement happens at the route decoration level, whenever possible. In
case of the API, when over quota, a 413 error is returned, with an
appropriate JSON payload. In case of web routes, a 413 HTML page is
rendered with similar information.
This implementation is for a **soft quota**: quota usage is checked
before an operation is to be performed, and the operation is *only*
denied if the user is already over quota. This makes it possible to go
over quota, but has the significant advantage of being practically
implementable within the current Forgejo architecture.
The goal of enforcement is to deny actions that can make the user go
over quota, and allow the rest. As such, deleting things should - in
almost all cases - be possible. A prime exemption is deleting files via
the web ui: that creates a new commit, which in turn increases repo
size, thus, is denied if the user is over quota.
Limitations
-----------
Because we generally work at a route decorator level, and rarely
look *into* the operation itself, `size:repos:public` and
`size:repos:private` are not enforced at this level, the engine enforces
against `size:repos:all`. This will be improved in the future.
AGit does not play very well with this system, because AGit PRs count
toward the repo they're opened against, while in the GitHub-style fork +
pull model, it counts against the fork. This too, can be improved in the
future.
There's very little done on the UI side to guard against going over
quota. What this patch implements, is enforcement, not prevention. The
UI will still let you *try* operations that *will* result in a denial.
Signed-off-by: Gergely Nagy <forgejo@gergo.csillger.hu>
2024-07-06 11:30:16 +03:00
|
|
|
r.Put("", reqPackageAccess(perm.AccessModeWrite), swift.CheckAcceptMediaType(swift.AcceptJSON), enforcePackagesQuota(), swift.UploadPackageFile)
|
2023-03-13 23:28:39 +03:00
|
|
|
r.Get("", func(ctx *context.Context) {
|
|
|
|
// Can't use normal routes here: https://github.com/go-chi/chi/issues/781
|
|
|
|
|
|
|
|
version := ctx.Params("version")
|
|
|
|
if strings.HasSuffix(version, ".zip") {
|
|
|
|
swift.CheckAcceptMediaType(swift.AcceptZip)(ctx)
|
|
|
|
if ctx.Written() {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
ctx.SetParams("version", version[:len(version)-4])
|
|
|
|
swift.DownloadPackageFile(ctx)
|
|
|
|
} else {
|
|
|
|
swift.CheckAcceptMediaType(swift.AcceptJSON)(ctx)
|
|
|
|
if ctx.Written() {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
if strings.HasSuffix(version, ".json") {
|
|
|
|
ctx.SetParams("version", version[:len(version)-5])
|
|
|
|
}
|
|
|
|
swift.PackageVersionMetadata(ctx)
|
|
|
|
}
|
|
|
|
})
|
|
|
|
})
|
|
|
|
})
|
|
|
|
r.Get("/identifiers", swift.CheckAcceptMediaType(swift.AcceptJSON), swift.LookupPackageIdentifiers)
|
|
|
|
}, reqPackageAccess(perm.AccessModeRead))
|
2022-08-29 10:04:45 +03:00
|
|
|
r.Group("/vagrant", func() {
|
|
|
|
r.Group("/authenticate", func() {
|
|
|
|
r.Get("", vagrant.CheckAuthenticate)
|
|
|
|
})
|
|
|
|
r.Group("/{name}", func() {
|
|
|
|
r.Head("", vagrant.CheckBoxAvailable)
|
|
|
|
r.Get("", vagrant.EnumeratePackageVersions)
|
|
|
|
r.Group("/{version}/{provider}", func() {
|
|
|
|
r.Get("", vagrant.DownloadPackageFile)
|
feat(quota): Quota enforcement
The previous commit laid out the foundation of the quota engine, this
one builds on top of it, and implements the actual enforcement.
Enforcement happens at the route decoration level, whenever possible. In
case of the API, when over quota, a 413 error is returned, with an
appropriate JSON payload. In case of web routes, a 413 HTML page is
rendered with similar information.
This implementation is for a **soft quota**: quota usage is checked
before an operation is to be performed, and the operation is *only*
denied if the user is already over quota. This makes it possible to go
over quota, but has the significant advantage of being practically
implementable within the current Forgejo architecture.
The goal of enforcement is to deny actions that can make the user go
over quota, and allow the rest. As such, deleting things should - in
almost all cases - be possible. A prime exemption is deleting files via
the web ui: that creates a new commit, which in turn increases repo
size, thus, is denied if the user is over quota.
Limitations
-----------
Because we generally work at a route decorator level, and rarely
look *into* the operation itself, `size:repos:public` and
`size:repos:private` are not enforced at this level, the engine enforces
against `size:repos:all`. This will be improved in the future.
AGit does not play very well with this system, because AGit PRs count
toward the repo they're opened against, while in the GitHub-style fork +
pull model, it counts against the fork. This too, can be improved in the
future.
There's very little done on the UI side to guard against going over
quota. What this patch implements, is enforcement, not prevention. The
UI will still let you *try* operations that *will* result in a denial.
Signed-off-by: Gergely Nagy <forgejo@gergo.csillger.hu>
2024-07-06 11:30:16 +03:00
|
|
|
r.Put("", reqPackageAccess(perm.AccessModeWrite), enforcePackagesQuota(), vagrant.UploadPackageFile)
|
2022-08-29 10:04:45 +03:00
|
|
|
})
|
|
|
|
})
|
2022-09-24 18:17:08 +03:00
|
|
|
}, reqPackageAccess(perm.AccessModeRead))
|
2024-02-27 10:12:22 +03:00
|
|
|
}, context.UserAssignmentWeb(), context.PackageAssignment())
|
2022-03-30 11:42:47 +03:00
|
|
|
|
|
|
|
return r
|
|
|
|
}
|
|
|
|
|
2022-11-12 21:59:15 +03:00
|
|
|
// ContainerRoutes provides endpoints that implement the OCI API to serve containers
|
|
|
|
// These have to be mounted on `/v2/...` to comply with the OCI spec:
|
|
|
|
// https://github.com/opencontainers/distribution-spec/blob/main/spec.md
|
2023-06-18 10:59:09 +03:00
|
|
|
func ContainerRoutes() *web.Route {
|
2022-03-30 11:42:47 +03:00
|
|
|
r := web.NewRoute()
|
|
|
|
|
2023-05-21 04:50:53 +03:00
|
|
|
r.Use(context.PackageContexter())
|
2022-03-30 11:42:47 +03:00
|
|
|
|
2023-04-10 10:21:03 +03:00
|
|
|
verifyAuth(r, []auth.Method{
|
2022-03-30 11:42:47 +03:00
|
|
|
&auth.Basic{},
|
|
|
|
&container.Auth{},
|
|
|
|
})
|
|
|
|
|
|
|
|
r.Get("", container.ReqContainerAccess, container.DetermineSupport)
|
2023-12-13 23:23:53 +03:00
|
|
|
r.Group("/token", func() {
|
|
|
|
r.Get("", container.Authenticate)
|
|
|
|
r.Post("", container.AuthenticateNotImplemented)
|
|
|
|
})
|
2022-07-28 06:59:39 +03:00
|
|
|
r.Get("/_catalog", container.ReqContainerAccess, container.GetRepositoryList)
|
2022-03-30 11:42:47 +03:00
|
|
|
r.Group("/{username}", func() {
|
|
|
|
r.Group("/{image}", func() {
|
|
|
|
r.Group("/blobs/uploads", func() {
|
|
|
|
r.Post("", container.InitiateUploadBlob)
|
|
|
|
r.Group("/{uuid}", func() {
|
2022-10-07 18:30:59 +03:00
|
|
|
r.Get("", container.GetUploadBlob)
|
2022-03-30 11:42:47 +03:00
|
|
|
r.Patch("", container.UploadBlob)
|
|
|
|
r.Put("", container.EndUploadBlob)
|
2022-10-07 18:30:59 +03:00
|
|
|
r.Delete("", container.CancelUploadBlob)
|
2022-03-30 11:42:47 +03:00
|
|
|
})
|
|
|
|
}, reqPackageAccess(perm.AccessModeWrite))
|
|
|
|
r.Group("/blobs/{digest}", func() {
|
|
|
|
r.Head("", container.HeadBlob)
|
|
|
|
r.Get("", container.GetBlob)
|
|
|
|
r.Delete("", reqPackageAccess(perm.AccessModeWrite), container.DeleteBlob)
|
|
|
|
})
|
|
|
|
r.Group("/manifests/{reference}", func() {
|
|
|
|
r.Put("", reqPackageAccess(perm.AccessModeWrite), container.UploadManifest)
|
|
|
|
r.Head("", container.HeadManifest)
|
|
|
|
r.Get("", container.GetManifest)
|
|
|
|
r.Delete("", reqPackageAccess(perm.AccessModeWrite), container.DeleteManifest)
|
|
|
|
})
|
|
|
|
r.Get("/tags/list", container.GetTagList)
|
|
|
|
}, container.VerifyImageName)
|
|
|
|
|
|
|
|
var (
|
|
|
|
blobsUploadsPattern = regexp.MustCompile(`\A(.+)/blobs/uploads/([a-zA-Z0-9-_.=]+)\z`)
|
|
|
|
blobsPattern = regexp.MustCompile(`\A(.+)/blobs/([^/]+)\z`)
|
|
|
|
manifestsPattern = regexp.MustCompile(`\A(.+)/manifests/([^/]+)\z`)
|
|
|
|
)
|
|
|
|
|
|
|
|
// Manual mapping of routes because {image} can contain slashes which chi does not support
|
2023-07-21 01:43:49 +03:00
|
|
|
r.Methods("HEAD,GET,POST,PUT,PATCH,DELETE", "/*", func(ctx *context.Context) {
|
2022-03-30 11:42:47 +03:00
|
|
|
path := ctx.Params("*")
|
|
|
|
isHead := ctx.Req.Method == "HEAD"
|
|
|
|
isGet := ctx.Req.Method == "GET"
|
|
|
|
isPost := ctx.Req.Method == "POST"
|
|
|
|
isPut := ctx.Req.Method == "PUT"
|
|
|
|
isPatch := ctx.Req.Method == "PATCH"
|
|
|
|
isDelete := ctx.Req.Method == "DELETE"
|
|
|
|
|
|
|
|
if isPost && strings.HasSuffix(path, "/blobs/uploads") {
|
|
|
|
reqPackageAccess(perm.AccessModeWrite)(ctx)
|
|
|
|
if ctx.Written() {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
ctx.SetParams("image", path[:len(path)-14])
|
|
|
|
container.VerifyImageName(ctx)
|
|
|
|
if ctx.Written() {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
container.InitiateUploadBlob(ctx)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
if isGet && strings.HasSuffix(path, "/tags/list") {
|
|
|
|
ctx.SetParams("image", path[:len(path)-10])
|
|
|
|
container.VerifyImageName(ctx)
|
|
|
|
if ctx.Written() {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
container.GetTagList(ctx)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
m := blobsUploadsPattern.FindStringSubmatch(path)
|
2022-10-07 18:30:59 +03:00
|
|
|
if len(m) == 3 && (isGet || isPut || isPatch || isDelete) {
|
2022-03-30 11:42:47 +03:00
|
|
|
reqPackageAccess(perm.AccessModeWrite)(ctx)
|
|
|
|
if ctx.Written() {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
ctx.SetParams("image", m[1])
|
|
|
|
container.VerifyImageName(ctx)
|
|
|
|
if ctx.Written() {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
ctx.SetParams("uuid", m[2])
|
|
|
|
|
2022-10-07 18:30:59 +03:00
|
|
|
if isGet {
|
|
|
|
container.GetUploadBlob(ctx)
|
|
|
|
} else if isPatch {
|
2022-03-30 11:42:47 +03:00
|
|
|
container.UploadBlob(ctx)
|
2022-10-07 18:30:59 +03:00
|
|
|
} else if isPut {
|
2022-03-30 11:42:47 +03:00
|
|
|
container.EndUploadBlob(ctx)
|
2022-10-07 18:30:59 +03:00
|
|
|
} else {
|
|
|
|
container.CancelUploadBlob(ctx)
|
2022-03-30 11:42:47 +03:00
|
|
|
}
|
|
|
|
return
|
|
|
|
}
|
|
|
|
m = blobsPattern.FindStringSubmatch(path)
|
|
|
|
if len(m) == 3 && (isHead || isGet || isDelete) {
|
|
|
|
ctx.SetParams("image", m[1])
|
|
|
|
container.VerifyImageName(ctx)
|
|
|
|
if ctx.Written() {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
ctx.SetParams("digest", m[2])
|
|
|
|
|
|
|
|
if isHead {
|
|
|
|
container.HeadBlob(ctx)
|
|
|
|
} else if isGet {
|
|
|
|
container.GetBlob(ctx)
|
|
|
|
} else {
|
|
|
|
reqPackageAccess(perm.AccessModeWrite)(ctx)
|
|
|
|
if ctx.Written() {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
container.DeleteBlob(ctx)
|
|
|
|
}
|
|
|
|
return
|
|
|
|
}
|
|
|
|
m = manifestsPattern.FindStringSubmatch(path)
|
|
|
|
if len(m) == 3 && (isHead || isGet || isPut || isDelete) {
|
|
|
|
ctx.SetParams("image", m[1])
|
|
|
|
container.VerifyImageName(ctx)
|
|
|
|
if ctx.Written() {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
ctx.SetParams("reference", m[2])
|
|
|
|
|
|
|
|
if isHead {
|
|
|
|
container.HeadManifest(ctx)
|
|
|
|
} else if isGet {
|
|
|
|
container.GetManifest(ctx)
|
|
|
|
} else {
|
|
|
|
reqPackageAccess(perm.AccessModeWrite)(ctx)
|
|
|
|
if ctx.Written() {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
if isPut {
|
|
|
|
container.UploadManifest(ctx)
|
|
|
|
} else {
|
|
|
|
container.DeleteManifest(ctx)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
ctx.Status(http.StatusNotFound)
|
|
|
|
})
|
2024-02-27 10:12:22 +03:00
|
|
|
}, container.ReqContainerAccess, context.UserAssignmentWeb(), context.PackageAssignment(), reqPackageAccess(perm.AccessModeRead))
|
2022-03-30 11:42:47 +03:00
|
|
|
|
|
|
|
return r
|
|
|
|
}
|