mirror of
https://github.com/gogs/gogs.git
synced 2026-02-27 16:50:58 +01:00
Continue flamego migration - fix Context methods and route handlers.
Co-authored-by: unknwon <2946214+unknwon@users.noreply.github.com>
This commit is contained in:
@@ -15,6 +15,14 @@ import (
|
||||
"gogs.io/gogs/internal/lfsutil"
|
||||
)
|
||||
|
||||
// writeError writes an HTTP error response.
|
||||
func writeError(w http.ResponseWriter, status int, text string) {
|
||||
w.WriteHeader(status)
|
||||
if text != "" {
|
||||
w.Write([]byte(text))
|
||||
}
|
||||
}
|
||||
|
||||
// RegisterRoutes registers LFS routes using given router, and inherits all
|
||||
// groups and middleware.
|
||||
func RegisterRoutes(r flamego.Router) {
|
||||
@@ -66,7 +74,7 @@ func authenticate(store Store) flamego.Handler {
|
||||
}
|
||||
|
||||
if err == nil && store.IsTwoFactorEnabled(c.Request().Context(), user.ID) {
|
||||
c.Error(http.StatusBadRequest, "Users with 2FA enabled are not allowed to authenticate via username and password.")
|
||||
writeError(c.ResponseWriter(), http.StatusBadRequest, "Users with 2FA enabled are not allowed to authenticate via username and password.")
|
||||
return
|
||||
}
|
||||
|
||||
@@ -85,7 +93,7 @@ func authenticate(store Store) flamego.Handler {
|
||||
if database.IsErrAccessTokenNotExist(err) {
|
||||
askCredentials(c.ResponseWriter())
|
||||
} else {
|
||||
c.Status(http.StatusInternalServerError)
|
||||
c.ResponseWriter().WriteHeader(http.StatusInternalServerError)
|
||||
log.Error("Failed to authenticate by access token via password: %v", err)
|
||||
}
|
||||
return
|
||||
@@ -108,7 +116,7 @@ func authorize(store Store, mode database.AccessMode) flamego.Handler {
|
||||
owner, err := store.GetUserByUsername(c.Request().Context(), username)
|
||||
if err != nil {
|
||||
if database.IsErrUserNotExist(err) {
|
||||
c.Status(http.StatusNotFound)
|
||||
c.ResponseWriter().WriteHeader(http.StatusNotFound)
|
||||
} else {
|
||||
internalServerError(c.ResponseWriter())
|
||||
log.Error("Failed to get user [name: %s]: %v", username, err)
|
||||
@@ -119,7 +127,7 @@ func authorize(store Store, mode database.AccessMode) flamego.Handler {
|
||||
repo, err := store.GetRepositoryByName(c.Request().Context(), owner.ID, reponame)
|
||||
if err != nil {
|
||||
if database.IsErrRepoNotExist(err) {
|
||||
c.Status(http.StatusNotFound)
|
||||
c.ResponseWriter().WriteHeader(http.StatusNotFound)
|
||||
} else {
|
||||
internalServerError(c.ResponseWriter())
|
||||
log.Error("Failed to get repository [owner_id: %d, name: %s]: %v", owner.ID, reponame, err)
|
||||
@@ -133,7 +141,7 @@ func authorize(store Store, mode database.AccessMode) flamego.Handler {
|
||||
Private: repo.IsPrivate,
|
||||
},
|
||||
) {
|
||||
c.Status(http.StatusNotFound)
|
||||
c.ResponseWriter().WriteHeader(http.StatusNotFound)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -156,7 +164,7 @@ func verifyHeader(key, value string, failCode int) flamego.Handler {
|
||||
}
|
||||
|
||||
log.Trace("[LFS] HTTP header %q does not contain value %q", key, value)
|
||||
c.Status(failCode)
|
||||
c.ResponseWriter().WriteHeader(failCode)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -34,22 +34,30 @@ type HTTPContext struct {
|
||||
AuthUser *database.User
|
||||
}
|
||||
|
||||
// writeError writes an HTTP error response.
|
||||
func writeError(w http.ResponseWriter, status int, text string) {
|
||||
w.WriteHeader(status)
|
||||
if text != "" {
|
||||
w.Write([]byte(text))
|
||||
}
|
||||
}
|
||||
|
||||
// askCredentials responses HTTP header and status which informs client to provide credentials.
|
||||
func askCredentials(c flamego.Context, status int, text string) {
|
||||
c.Header().Set("WWW-Authenticate", "Basic realm=\".\"")
|
||||
c.Error(status, text)
|
||||
c.ResponseWriter().Header().Set("WWW-Authenticate", "Basic realm=\".\"")
|
||||
writeError(c.ResponseWriter(), status, text)
|
||||
}
|
||||
|
||||
func HTTPContexter(store Store) flamego.Handler {
|
||||
return func(c flamego.Context) {
|
||||
if len(conf.HTTP.AccessControlAllowOrigin) > 0 {
|
||||
// Set CORS headers for browser-based git clients
|
||||
c.Header().Set("Access-Control-Allow-Origin", conf.HTTP.AccessControlAllowOrigin)
|
||||
c.Header().Set("Access-Control-Allow-Headers", "Content-Type, Authorization, User-Agent")
|
||||
c.ResponseWriter().Header().Set("Access-Control-Allow-Origin", conf.HTTP.AccessControlAllowOrigin)
|
||||
c.ResponseWriter().Header().Set("Access-Control-Allow-Headers", "Content-Type, Authorization, User-Agent")
|
||||
|
||||
// Handle preflight OPTIONS request
|
||||
if c.Req.Method == "OPTIONS" {
|
||||
c.Status(http.StatusOK)
|
||||
if c.Request().Method == "OPTIONS" {
|
||||
c.ResponseWriter().WriteHeader(http.StatusOK)
|
||||
return
|
||||
}
|
||||
}
|
||||
@@ -59,26 +67,26 @@ func HTTPContexter(store Store) flamego.Handler {
|
||||
repoName = strings.TrimSuffix(repoName, ".wiki")
|
||||
|
||||
isPull := c.Query("service") == "git-upload-pack" ||
|
||||
strings.HasSuffix(c.Req.URL.Path, "git-upload-pack") ||
|
||||
c.Req.Method == "GET"
|
||||
strings.HasSuffix(c.Request().URL.Path, "git-upload-pack") ||
|
||||
c.Request().Method == "GET"
|
||||
|
||||
owner, err := store.GetUserByUsername(c.Req.Context(), ownerName)
|
||||
owner, err := store.GetUserByUsername(c.Request().Context(), ownerName)
|
||||
if err != nil {
|
||||
if database.IsErrUserNotExist(err) {
|
||||
c.Status(http.StatusNotFound)
|
||||
c.ResponseWriter().WriteHeader(http.StatusNotFound)
|
||||
} else {
|
||||
c.Status(http.StatusInternalServerError)
|
||||
c.ResponseWriter().WriteHeader(http.StatusInternalServerError)
|
||||
log.Error("Failed to get user [name: %s]: %v", ownerName, err)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
repo, err := store.GetRepositoryByName(c.Req.Context(), owner.ID, repoName)
|
||||
repo, err := store.GetRepositoryByName(c.Request().Context(), owner.ID, repoName)
|
||||
if err != nil {
|
||||
if database.IsErrRepoNotExist(err) {
|
||||
c.Status(http.StatusNotFound)
|
||||
c.ResponseWriter().WriteHeader(http.StatusNotFound)
|
||||
} else {
|
||||
c.Status(http.StatusInternalServerError)
|
||||
c.ResponseWriter().WriteHeader(http.StatusInternalServerError)
|
||||
log.Error("Failed to get repository [owner_id: %d, name: %s]: %v", owner.ID, repoName, err)
|
||||
}
|
||||
return
|
||||
@@ -98,12 +106,12 @@ func HTTPContexter(store Store) flamego.Handler {
|
||||
!strings.Contains(action, "info/") &&
|
||||
!strings.Contains(action, "HEAD") &&
|
||||
!strings.Contains(action, "objects/") {
|
||||
c.Error(http.StatusBadRequest, fmt.Sprintf("Unrecognized action %q", action))
|
||||
writeError(c.ResponseWriter(), http.StatusBadRequest, fmt.Sprintf("Unrecognized action %q", action))
|
||||
return
|
||||
}
|
||||
|
||||
// Handle HTTP Basic Authentication
|
||||
authHead := c.Req.Header.Get("Authorization")
|
||||
authHead := c.Request().Header.Get("Authorization")
|
||||
if authHead == "" {
|
||||
askCredentials(c, http.StatusUnauthorized, "")
|
||||
return
|
||||
@@ -120,9 +128,9 @@ func HTTPContexter(store Store) flamego.Handler {
|
||||
return
|
||||
}
|
||||
|
||||
authUser, err := store.AuthenticateUser(c.Req.Context(), authUsername, authPassword, -1)
|
||||
authUser, err := store.AuthenticateUser(c.Request().Context(), authUsername, authPassword, -1)
|
||||
if err != nil && !auth.IsErrBadCredentials(err) {
|
||||
c.Status(http.StatusInternalServerError)
|
||||
c.ResponseWriter().WriteHeader(http.StatusInternalServerError)
|
||||
log.Error("Failed to authenticate user [name: %s]: %v", authUsername, err)
|
||||
return
|
||||
}
|
||||
@@ -130,25 +138,25 @@ func HTTPContexter(store Store) flamego.Handler {
|
||||
// If username and password combination failed, try again using either username
|
||||
// or password as the token.
|
||||
if authUser == nil {
|
||||
authUser, err = context.AuthenticateByToken(store, c.Req.Context(), authUsername)
|
||||
authUser, err = context.AuthenticateByToken(store, c.Request().Context(), authUsername)
|
||||
if err != nil && !database.IsErrAccessTokenNotExist(err) {
|
||||
c.Status(http.StatusInternalServerError)
|
||||
c.ResponseWriter().WriteHeader(http.StatusInternalServerError)
|
||||
log.Error("Failed to authenticate by access token via username: %v", err)
|
||||
return
|
||||
} else if database.IsErrAccessTokenNotExist(err) {
|
||||
// Try again using the password field as the token.
|
||||
authUser, err = context.AuthenticateByToken(store, c.Req.Context(), authPassword)
|
||||
authUser, err = context.AuthenticateByToken(store, c.Request().Context(), authPassword)
|
||||
if err != nil {
|
||||
if database.IsErrAccessTokenNotExist(err) {
|
||||
askCredentials(c, http.StatusUnauthorized, "")
|
||||
} else {
|
||||
c.Status(http.StatusInternalServerError)
|
||||
c.ResponseWriter().WriteHeader(http.StatusInternalServerError)
|
||||
log.Error("Failed to authenticate by access token via password: %v", err)
|
||||
}
|
||||
return
|
||||
}
|
||||
}
|
||||
} else if store.IsTwoFactorEnabled(c.Req.Context(), authUser.ID) {
|
||||
} else if store.IsTwoFactorEnabled(c.Request().Context(), authUser.ID) {
|
||||
askCredentials(c, http.StatusUnauthorized, `User with two-factor authentication enabled cannot perform HTTP/HTTPS operations via plain username and password
|
||||
Please create and use personal access token on user settings page`)
|
||||
return
|
||||
@@ -160,7 +168,7 @@ Please create and use personal access token on user settings page`)
|
||||
if isPull {
|
||||
mode = database.AccessModeRead
|
||||
}
|
||||
if !database.Handle.Permissions().Authorize(c.Req.Context(), authUser.ID, repo.ID, mode,
|
||||
if !database.Handle.Permissions().Authorize(c.Request().Context(), authUser.ID, repo.ID, mode,
|
||||
database.AccessModeOptions{
|
||||
OwnerID: repo.OwnerID,
|
||||
Private: repo.IsPrivate,
|
||||
@@ -171,7 +179,7 @@ Please create and use personal access token on user settings page`)
|
||||
}
|
||||
|
||||
if !isPull && repo.IsMirror {
|
||||
c.Error(http.StatusForbidden, "Mirror repository is read-only")
|
||||
writeError(c.ResponseWriter(), http.StatusForbidden, "Mirror repository is read-only")
|
||||
return
|
||||
}
|
||||
|
||||
@@ -388,7 +396,7 @@ func getGitRepoPath(dir string) (string, error) {
|
||||
|
||||
func HTTP(c *HTTPContext) {
|
||||
for _, route := range routes {
|
||||
reqPath := strings.ToLower(c.Req.URL.Path)
|
||||
reqPath := strings.ToLower(c.Request().URL.Path)
|
||||
m := route.re.FindStringSubmatch(reqPath)
|
||||
if m == nil {
|
||||
continue
|
||||
@@ -398,19 +406,19 @@ func HTTP(c *HTTPContext) {
|
||||
// but we only want to output this message only if user is really trying to access
|
||||
// Git HTTP endpoints.
|
||||
if conf.Repository.DisableHTTPGit {
|
||||
c.Error(http.StatusForbidden, "Interacting with repositories by HTTP protocol is disabled")
|
||||
writeError(c.ResponseWriter(), http.StatusForbidden, "Interacting with repositories by HTTP protocol is disabled")
|
||||
return
|
||||
}
|
||||
|
||||
if route.method != c.Req.Method {
|
||||
c.Error(http.StatusNotFound)
|
||||
if route.method != c.Request().Method {
|
||||
writeError(c.ResponseWriter(), http.StatusNotFound)
|
||||
return
|
||||
}
|
||||
|
||||
// 🚨 SECURITY: Prevent path traversal.
|
||||
cleaned := pathutil.Clean(m[1])
|
||||
if m[1] != "/"+cleaned {
|
||||
c.Error(http.StatusBadRequest, "Request path contains suspicious characters")
|
||||
writeError(c.ResponseWriter(), http.StatusBadRequest, "Request path contains suspicious characters")
|
||||
return
|
||||
}
|
||||
|
||||
@@ -418,13 +426,13 @@ func HTTP(c *HTTPContext) {
|
||||
dir, err := getGitRepoPath(cleaned)
|
||||
if err != nil {
|
||||
log.Warn("HTTP.getGitRepoPath: %v", err)
|
||||
c.Error(http.StatusNotFound)
|
||||
writeError(c.ResponseWriter(), http.StatusNotFound)
|
||||
return
|
||||
}
|
||||
|
||||
route.handler(serviceHandler{
|
||||
w: c.Resp,
|
||||
r: c.Req.Request,
|
||||
r: c.Request().Request,
|
||||
dir: dir,
|
||||
file: file,
|
||||
|
||||
@@ -437,5 +445,5 @@ func HTTP(c *HTTPContext) {
|
||||
return
|
||||
}
|
||||
|
||||
c.Error(http.StatusNotFound)
|
||||
writeError(c.ResponseWriter(), http.StatusNotFound)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user