Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix access log #14475

Merged
merged 5 commits into from
Jan 27, 2021
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion modules/auth/sso/oauth2.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import (

"code.gitea.io/gitea/models"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/middlewares"
lunny marked this conversation as resolved.
Show resolved Hide resolved
"code.gitea.io/gitea/modules/timeutil"
)

Expand Down Expand Up @@ -121,7 +122,7 @@ func (o *OAuth2) VerifyAuthData(req *http.Request, w http.ResponseWriter, store
return nil
}

if isInternalPath(req) || !isAPIPath(req) && !isAttachmentDownload(req) {
if middlewares.IsInternalPath(req) || !middlewares.IsAPIPath(req) && !isAttachmentDownload(req) {
return nil
}

Expand Down
10 changes: 0 additions & 10 deletions modules/auth/sso/sso.go
Original file line number Diff line number Diff line change
Expand Up @@ -94,16 +94,6 @@ func SessionUser(sess SessionStore) *models.User {
return user
}

// isAPIPath returns true if the specified URL is an API path
func isAPIPath(req *http.Request) bool {
return strings.HasPrefix(req.URL.Path, "/api/")
}

// isInternalPath returns true if the specified URL is an internal API path
func isInternalPath(req *http.Request) bool {
return strings.HasPrefix(req.URL.Path, "/api/internal/")
}

// isAttachmentDownload check if request is a file download (GET) with URL to an attachment
func isAttachmentDownload(req *http.Request) bool {
return strings.HasPrefix(req.URL.Path, "/attachments/") && req.Method == "GET"
Expand Down
7 changes: 4 additions & 3 deletions modules/auth/sso/sspi_windows.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import (
"code.gitea.io/gitea/models"
"code.gitea.io/gitea/modules/base"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/middlewares"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/templates"

Expand Down Expand Up @@ -135,7 +136,7 @@ func (s *SSPI) VerifyAuthData(req *http.Request, w http.ResponseWriter, store Da
}

// Make sure requests to API paths and PWA resources do not create a new session
if !isAPIPath(req) && !isAttachmentDownload(req) {
if !middlewares.IsAPIPath(req) && !isAttachmentDownload(req) {
handleSignIn(w, req, sess, user)
}

Expand Down Expand Up @@ -166,9 +167,9 @@ func (s *SSPI) shouldAuthenticate(req *http.Request) (shouldAuth bool) {
} else if req.FormValue("auth_with_sspi") == "1" {
shouldAuth = true
}
} else if isInternalPath(req) {
} else if middlewares.IsInternalPath(req) {
shouldAuth = false
} else if isAPIPath(req) || isAttachmentDownload(req) {
} else if middlewares.IsAPIPath(req) || isAttachmentDownload(req) {
shouldAuth = true
}
return
Expand Down
60 changes: 60 additions & 0 deletions modules/context/access_log.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
// Copyright 2020 The Gitea Authors. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.

package context

import (
"bytes"
"html/template"
"net/http"
"time"

"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/setting"
)

type routerLoggerOptions struct {
req *http.Request
Identity *string
Start *time.Time
ResponseWriter http.ResponseWriter
Ctx map[string]interface{}
}

// AccessLogger returns a middleware to log access logger
func AccessLogger() func(http.Handler) http.Handler {
logger := log.GetLogger("access")
logTemplate, _ := template.New("log").Parse(setting.AccessLogTemplate)
return func(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
start := time.Now()
next.ServeHTTP(w, req)
identity := "-"
if val := SignedUserName(req); val != "" {
identity = val
}
rw := w.(ResponseWriter)

buf := bytes.NewBuffer([]byte{})
err := logTemplate.Execute(buf, routerLoggerOptions{
req: req,
Identity: &identity,
Start: &start,
ResponseWriter: rw,
Ctx: map[string]interface{}{
"RemoteAddr": req.RemoteAddr,
"Req": req,
},
})
if err != nil {
log.Error("Could not set up chi access logger: %v", err.Error())
}

err = logger.SendLog(log.INFO, "", "", 0, buf.String(), "")
if err != nil {
log.Error("Could not set up chi access logger: %v", err.Error())
}
})
}
}
25 changes: 25 additions & 0 deletions modules/context/context.go
Original file line number Diff line number Diff line change
Expand Up @@ -484,6 +484,31 @@ func GetContext(req *http.Request) *Context {
return req.Context().Value(contextKey).(*Context)
}

// SignedUserName returns signed user's name via context
func SignedUserName(req *http.Request) string {
if middlewares.IsInternalPath(req) {
return ""
}
if middlewares.IsAPIPath(req) {
ctx, ok := req.Context().Value(apiContextKey).(*APIContext)
if ok {
v := ctx.Data["SignedUserName"]
if res, ok := v.(string); ok {
return res
}
}
} else {
ctx, ok := req.Context().Value(contextKey).(*Context)
if ok {
v := ctx.Data["SignedUserName"]
if res, ok := v.(string); ok {
return res
}
}
}
return ""
}

func getCsrfOpts() CsrfOptions {
return CsrfOptions{
Secret: setting.SecretKey,
Expand Down
10 changes: 9 additions & 1 deletion modules/context/response.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ type ResponseWriter interface {
Flush()
Status() int
Before(func(ResponseWriter))
Size() int
}

var (
Expand All @@ -21,11 +22,17 @@ var (
// Response represents a response
type Response struct {
http.ResponseWriter
written int
status int
befores []func(ResponseWriter)
beforeExecuted bool
}

// Size return written size
func (r *Response) Size() int {
return r.written
}

// Write writes bytes to HTTP endpoint
func (r *Response) Write(bs []byte) (int, error) {
if !r.beforeExecuted {
Expand All @@ -35,8 +42,9 @@ func (r *Response) Write(bs []byte) (int, error) {
r.beforeExecuted = true
}
size, err := r.ResponseWriter.Write(bs)
r.written += size
if err != nil {
return 0, err
return size, err
}
if r.status == 0 {
r.WriteHeader(200)
Expand Down
20 changes: 20 additions & 0 deletions modules/middlewares/request.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
// Copyright 2020 The Gitea Authors. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.

package middlewares

import (
"net/http"
"strings"
)

// IsAPIPath returns true if the specified URL is an API path
func IsAPIPath(req *http.Request) bool {
return strings.HasPrefix(req.URL.Path, "/api/")
}

// IsInternalPath returns true if the specified URL is an internal API path
func IsInternalPath(req *http.Request) bool {
return strings.HasPrefix(req.URL.Path, "/api/internal/")
}
5 changes: 5 additions & 0 deletions routers/api/v1/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -553,6 +553,11 @@ func Routes() *web.Route {
}))
}
m.Use(context.APIContexter())

if setting.EnableAccessLog {
m.Use(context.AccessLogger())
}

m.Use(context.ToggleAPI(&context.ToggleOptions{
SignInRequired: setting.Service.RequireSignInView,
}))
Expand Down
53 changes: 0 additions & 53 deletions routers/routes/base.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,13 @@
package routes

import (
"bytes"
"errors"
"fmt"
"io"
"net/http"
"os"
"path"
"strings"
"text/template"
"time"

"code.gitea.io/gitea/modules/auth/sso"
Expand All @@ -28,57 +26,6 @@ import (
"gitea.com/go-chi/session"
)

type routerLoggerOptions struct {
req *http.Request
Identity *string
Start *time.Time
ResponseWriter http.ResponseWriter
}

// SignedUserName returns signed user's name via context
func SignedUserName(req *http.Request) string {
ctx := context.GetContext(req)
if ctx != nil {
v := ctx.Data["SignedUserName"]
if res, ok := v.(string); ok {
return res
}
}
return ""
}

func accessLogger() func(http.Handler) http.Handler {
logger := log.GetLogger("access")
logTemplate, _ := template.New("log").Parse(setting.AccessLogTemplate)
return func(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
start := time.Now()
next.ServeHTTP(w, req)
identity := "-"
if val := SignedUserName(req); val != "" {
identity = val
}
rw := w

buf := bytes.NewBuffer([]byte{})
err := logTemplate.Execute(buf, routerLoggerOptions{
req: req,
Identity: &identity,
Start: &start,
ResponseWriter: rw,
})
if err != nil {
log.Error("Could not set up chi access logger: %v", err.Error())
}

err = logger.SendLog(log.INFO, "", "", 0, buf.String(), "")
if err != nil {
log.Error("Could not set up chi access logger: %v", err.Error())
}
})
}
}

// LoggerHandler is a handler that will log the routing to the default gitea log
func LoggerHandler(level log.Level) func(next http.Handler) http.Handler {
return func(next http.Handler) http.Handler {
Expand Down
8 changes: 4 additions & 4 deletions routers/routes/web.go
Original file line number Diff line number Diff line change
Expand Up @@ -88,10 +88,6 @@ func commonMiddlewares() []func(http.Handler) http.Handler {
next.ServeHTTP(resp, req)
})
})

if setting.EnableAccessLog {
handlers = append(handlers, accessLogger())
}
return handlers
}

Expand Down Expand Up @@ -166,6 +162,10 @@ func WebRoutes() *web.Route {
r.Use(context.Contexter())
// Removed: SetAutoHead allow a get request redirect to head if get method is not exist

if setting.EnableAccessLog {
r.Use(context.AccessLogger())
}

r.Use(user.GetNotificationCount)
r.Use(repo.GetActiveStopwatch)
r.Use(func(ctx *context.Context) {
Expand Down