Skip to content

Commit a51cc6d

Browse files
authored
Fix access log (#14475)
Fix #14121, #14478. The `AccessLog` middleware has to be after `Contexter` or `APIContexter` so that we can get `LoginUserName` if possible. And also there is a **BREAK** change that it removed internal API access log.
1 parent 4c6e029 commit a51cc6d

File tree

10 files changed

+129
-72
lines changed

10 files changed

+129
-72
lines changed

modules/auth/sso/oauth2.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import (
1212

1313
"code.gitea.io/gitea/models"
1414
"code.gitea.io/gitea/modules/log"
15+
"code.gitea.io/gitea/modules/middlewares"
1516
"code.gitea.io/gitea/modules/timeutil"
1617
)
1718

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

124-
if isInternalPath(req) || !isAPIPath(req) && !isAttachmentDownload(req) {
125+
if middlewares.IsInternalPath(req) || !middlewares.IsAPIPath(req) && !isAttachmentDownload(req) {
125126
return nil
126127
}
127128

modules/auth/sso/sso.go

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -94,16 +94,6 @@ func SessionUser(sess SessionStore) *models.User {
9494
return user
9595
}
9696

97-
// isAPIPath returns true if the specified URL is an API path
98-
func isAPIPath(req *http.Request) bool {
99-
return strings.HasPrefix(req.URL.Path, "/api/")
100-
}
101-
102-
// isInternalPath returns true if the specified URL is an internal API path
103-
func isInternalPath(req *http.Request) bool {
104-
return strings.HasPrefix(req.URL.Path, "/api/internal/")
105-
}
106-
10797
// isAttachmentDownload check if request is a file download (GET) with URL to an attachment
10898
func isAttachmentDownload(req *http.Request) bool {
10999
return strings.HasPrefix(req.URL.Path, "/attachments/") && req.Method == "GET"

modules/auth/sso/sspi_windows.go

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import (
1212
"code.gitea.io/gitea/models"
1313
"code.gitea.io/gitea/modules/base"
1414
"code.gitea.io/gitea/modules/log"
15+
"code.gitea.io/gitea/modules/middlewares"
1516
"code.gitea.io/gitea/modules/setting"
1617
"code.gitea.io/gitea/modules/templates"
1718

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

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

@@ -166,9 +167,9 @@ func (s *SSPI) shouldAuthenticate(req *http.Request) (shouldAuth bool) {
166167
} else if req.FormValue("auth_with_sspi") == "1" {
167168
shouldAuth = true
168169
}
169-
} else if isInternalPath(req) {
170+
} else if middlewares.IsInternalPath(req) {
170171
shouldAuth = false
171-
} else if isAPIPath(req) || isAttachmentDownload(req) {
172+
} else if middlewares.IsAPIPath(req) || isAttachmentDownload(req) {
172173
shouldAuth = true
173174
}
174175
return

modules/context/access_log.go

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
// Copyright 2020 The Gitea Authors. All rights reserved.
2+
// Use of this source code is governed by a MIT-style
3+
// license that can be found in the LICENSE file.
4+
5+
package context
6+
7+
import (
8+
"bytes"
9+
"html/template"
10+
"net/http"
11+
"time"
12+
13+
"code.gitea.io/gitea/modules/log"
14+
"code.gitea.io/gitea/modules/setting"
15+
)
16+
17+
type routerLoggerOptions struct {
18+
req *http.Request
19+
Identity *string
20+
Start *time.Time
21+
ResponseWriter http.ResponseWriter
22+
Ctx map[string]interface{}
23+
}
24+
25+
// AccessLogger returns a middleware to log access logger
26+
func AccessLogger() func(http.Handler) http.Handler {
27+
logger := log.GetLogger("access")
28+
logTemplate, _ := template.New("log").Parse(setting.AccessLogTemplate)
29+
return func(next http.Handler) http.Handler {
30+
return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
31+
start := time.Now()
32+
next.ServeHTTP(w, req)
33+
identity := "-"
34+
if val := SignedUserName(req); val != "" {
35+
identity = val
36+
}
37+
rw := w.(ResponseWriter)
38+
39+
buf := bytes.NewBuffer([]byte{})
40+
err := logTemplate.Execute(buf, routerLoggerOptions{
41+
req: req,
42+
Identity: &identity,
43+
Start: &start,
44+
ResponseWriter: rw,
45+
Ctx: map[string]interface{}{
46+
"RemoteAddr": req.RemoteAddr,
47+
"Req": req,
48+
},
49+
})
50+
if err != nil {
51+
log.Error("Could not set up chi access logger: %v", err.Error())
52+
}
53+
54+
err = logger.SendLog(log.INFO, "", "", 0, buf.String(), "")
55+
if err != nil {
56+
log.Error("Could not set up chi access logger: %v", err.Error())
57+
}
58+
})
59+
}
60+
}

modules/context/context.go

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -485,6 +485,31 @@ func GetContext(req *http.Request) *Context {
485485
return req.Context().Value(contextKey).(*Context)
486486
}
487487

488+
// SignedUserName returns signed user's name via context
489+
func SignedUserName(req *http.Request) string {
490+
if middlewares.IsInternalPath(req) {
491+
return ""
492+
}
493+
if middlewares.IsAPIPath(req) {
494+
ctx, ok := req.Context().Value(apiContextKey).(*APIContext)
495+
if ok {
496+
v := ctx.Data["SignedUserName"]
497+
if res, ok := v.(string); ok {
498+
return res
499+
}
500+
}
501+
} else {
502+
ctx, ok := req.Context().Value(contextKey).(*Context)
503+
if ok {
504+
v := ctx.Data["SignedUserName"]
505+
if res, ok := v.(string); ok {
506+
return res
507+
}
508+
}
509+
}
510+
return ""
511+
}
512+
488513
func getCsrfOpts() CsrfOptions {
489514
return CsrfOptions{
490515
Secret: setting.SecretKey,

modules/context/response.go

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ type ResponseWriter interface {
1212
Flush()
1313
Status() int
1414
Before(func(ResponseWriter))
15+
Size() int
1516
}
1617

1718
var (
@@ -21,11 +22,17 @@ var (
2122
// Response represents a response
2223
type Response struct {
2324
http.ResponseWriter
25+
written int
2426
status int
2527
befores []func(ResponseWriter)
2628
beforeExecuted bool
2729
}
2830

31+
// Size return written size
32+
func (r *Response) Size() int {
33+
return r.written
34+
}
35+
2936
// Write writes bytes to HTTP endpoint
3037
func (r *Response) Write(bs []byte) (int, error) {
3138
if !r.beforeExecuted {
@@ -35,8 +42,9 @@ func (r *Response) Write(bs []byte) (int, error) {
3542
r.beforeExecuted = true
3643
}
3744
size, err := r.ResponseWriter.Write(bs)
45+
r.written += size
3846
if err != nil {
39-
return 0, err
47+
return size, err
4048
}
4149
if r.status == 0 {
4250
r.WriteHeader(200)

modules/middlewares/request.go

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
// Copyright 2020 The Gitea Authors. All rights reserved.
2+
// Use of this source code is governed by a MIT-style
3+
// license that can be found in the LICENSE file.
4+
5+
package middlewares
6+
7+
import (
8+
"net/http"
9+
"strings"
10+
)
11+
12+
// IsAPIPath returns true if the specified URL is an API path
13+
func IsAPIPath(req *http.Request) bool {
14+
return strings.HasPrefix(req.URL.Path, "/api/")
15+
}
16+
17+
// IsInternalPath returns true if the specified URL is an internal API path
18+
func IsInternalPath(req *http.Request) bool {
19+
return strings.HasPrefix(req.URL.Path, "/api/internal/")
20+
}

routers/api/v1/api.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -553,6 +553,11 @@ func Routes() *web.Route {
553553
}))
554554
}
555555
m.Use(context.APIContexter())
556+
557+
if setting.EnableAccessLog {
558+
m.Use(context.AccessLogger())
559+
}
560+
556561
m.Use(context.ToggleAPI(&context.ToggleOptions{
557562
SignInRequired: setting.Service.RequireSignInView,
558563
}))

routers/routes/base.go

Lines changed: 0 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -5,15 +5,13 @@
55
package routes
66

77
import (
8-
"bytes"
98
"errors"
109
"fmt"
1110
"io"
1211
"net/http"
1312
"os"
1413
"path"
1514
"strings"
16-
"text/template"
1715
"time"
1816

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

31-
type routerLoggerOptions struct {
32-
req *http.Request
33-
Identity *string
34-
Start *time.Time
35-
ResponseWriter http.ResponseWriter
36-
}
37-
38-
// SignedUserName returns signed user's name via context
39-
func SignedUserName(req *http.Request) string {
40-
ctx := context.GetContext(req)
41-
if ctx != nil {
42-
v := ctx.Data["SignedUserName"]
43-
if res, ok := v.(string); ok {
44-
return res
45-
}
46-
}
47-
return ""
48-
}
49-
50-
func accessLogger() func(http.Handler) http.Handler {
51-
logger := log.GetLogger("access")
52-
logTemplate, _ := template.New("log").Parse(setting.AccessLogTemplate)
53-
return func(next http.Handler) http.Handler {
54-
return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
55-
start := time.Now()
56-
next.ServeHTTP(w, req)
57-
identity := "-"
58-
if val := SignedUserName(req); val != "" {
59-
identity = val
60-
}
61-
rw := w
62-
63-
buf := bytes.NewBuffer([]byte{})
64-
err := logTemplate.Execute(buf, routerLoggerOptions{
65-
req: req,
66-
Identity: &identity,
67-
Start: &start,
68-
ResponseWriter: rw,
69-
})
70-
if err != nil {
71-
log.Error("Could not set up chi access logger: %v", err.Error())
72-
}
73-
74-
err = logger.SendLog(log.INFO, "", "", 0, buf.String(), "")
75-
if err != nil {
76-
log.Error("Could not set up chi access logger: %v", err.Error())
77-
}
78-
})
79-
}
80-
}
81-
8229
// LoggerHandler is a handler that will log the routing to the default gitea log
8330
func LoggerHandler(level log.Level) func(next http.Handler) http.Handler {
8431
return func(next http.Handler) http.Handler {

routers/routes/web.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -88,10 +88,6 @@ func commonMiddlewares() []func(http.Handler) http.Handler {
8888
next.ServeHTTP(resp, req)
8989
})
9090
})
91-
92-
if setting.EnableAccessLog {
93-
handlers = append(handlers, accessLogger())
94-
}
9591
return handlers
9692
}
9793

@@ -168,6 +164,10 @@ func WebRoutes() *web.Route {
168164
r.Use(context.Contexter())
169165
// Removed: SetAutoHead allow a get request redirect to head if get method is not exist
170166

167+
if setting.EnableAccessLog {
168+
r.Use(context.AccessLogger())
169+
}
170+
171171
r.Use(user.GetNotificationCount)
172172
r.Use(repo.GetActiveStopwatch)
173173
r.Use(func(ctx *context.Context) {

0 commit comments

Comments
 (0)