@@ -6,6 +6,7 @@ package web
6
6
import (
7
7
gocontext "context"
8
8
"net/http"
9
+ "strings"
9
10
10
11
"code.gitea.io/gitea/models/perm"
11
12
"code.gitea.io/gitea/models/unit"
@@ -19,6 +20,7 @@ import (
19
20
"code.gitea.io/gitea/modules/templates"
20
21
"code.gitea.io/gitea/modules/validation"
21
22
"code.gitea.io/gitea/modules/web"
23
+ "code.gitea.io/gitea/modules/web/middleware"
22
24
"code.gitea.io/gitea/modules/web/routing"
23
25
"code.gitea.io/gitea/routers/common"
24
26
"code.gitea.io/gitea/routers/web/admin"
@@ -46,7 +48,7 @@ import (
46
48
47
49
"gitea.com/go-chi/captcha"
48
50
"github.com/NYTimes/gziphandler"
49
- "github.com/go-chi/chi/v5/middleware"
51
+ chi_middleware "github.com/go-chi/chi/v5/middleware"
50
52
"github.com/go-chi/cors"
51
53
"github.com/prometheus/client_golang/prometheus"
52
54
)
@@ -95,6 +97,109 @@ func buildAuthGroup() *auth_service.Group {
95
97
return group
96
98
}
97
99
100
+ func webAuth (authMethod auth_service.Method ) func (* context.Context ) {
101
+ return func (ctx * context.Context ) {
102
+ ar , err := common .AuthShared (ctx .Base , ctx .Session , authMethod )
103
+ if err != nil {
104
+ log .Error ("Failed to verify user: %v" , err )
105
+ ctx .Error (http .StatusUnauthorized , "Verify" )
106
+ return
107
+ }
108
+ ctx .Doer = ar .Doer
109
+ ctx .IsSigned = ar .Doer != nil
110
+ ctx .IsBasicAuth = ar .IsBasicAuth
111
+ if ctx .Doer == nil {
112
+ // ensure the session uid is deleted
113
+ _ = ctx .Session .Delete ("uid" )
114
+ }
115
+ }
116
+ }
117
+
118
+ // verifyAuthWithOptions checks authentication according to options
119
+ func verifyAuthWithOptions (options * common.VerifyOptions ) func (ctx * context.Context ) {
120
+ return func (ctx * context.Context ) {
121
+ // Check prohibit login users.
122
+ if ctx .IsSigned {
123
+ if ! ctx .Doer .IsActive && setting .Service .RegisterEmailConfirm {
124
+ ctx .Data ["Title" ] = ctx .Tr ("auth.active_your_account" )
125
+ ctx .HTML (http .StatusOK , "user/auth/activate" )
126
+ return
127
+ }
128
+ if ! ctx .Doer .IsActive || ctx .Doer .ProhibitLogin {
129
+ log .Info ("Failed authentication attempt for %s from %s" , ctx .Doer .Name , ctx .RemoteAddr ())
130
+ ctx .Data ["Title" ] = ctx .Tr ("auth.prohibit_login" )
131
+ ctx .HTML (http .StatusOK , "user/auth/prohibit_login" )
132
+ return
133
+ }
134
+
135
+ if ctx .Doer .MustChangePassword {
136
+ if ctx .Req .URL .Path != "/user/settings/change_password" {
137
+ if strings .HasPrefix (ctx .Req .UserAgent (), "git" ) {
138
+ ctx .Error (http .StatusUnauthorized , ctx .Tr ("auth.must_change_password" ))
139
+ return
140
+ }
141
+ ctx .Data ["Title" ] = ctx .Tr ("auth.must_change_password" )
142
+ ctx .Data ["ChangePasscodeLink" ] = setting .AppSubURL + "/user/change_password"
143
+ if ctx .Req .URL .Path != "/user/events" {
144
+ middleware .SetRedirectToCookie (ctx .Resp , setting .AppSubURL + ctx .Req .URL .RequestURI ())
145
+ }
146
+ ctx .Redirect (setting .AppSubURL + "/user/settings/change_password" )
147
+ return
148
+ }
149
+ } else if ctx .Req .URL .Path == "/user/settings/change_password" {
150
+ // make sure that the form cannot be accessed by users who don't need this
151
+ ctx .Redirect (setting .AppSubURL + "/" )
152
+ return
153
+ }
154
+ }
155
+
156
+ // Redirect to dashboard (or alternate location) if user tries to visit any non-login page.
157
+ if options .SignOutRequired && ctx .IsSigned && ctx .Req .URL .RequestURI () != "/" {
158
+ ctx .RedirectToFirst (ctx .FormString ("redirect_to" ))
159
+ return
160
+ }
161
+
162
+ if ! options .SignOutRequired && ! options .DisableCSRF && ctx .Req .Method == "POST" {
163
+ ctx .Csrf .Validate (ctx )
164
+ if ctx .Written () {
165
+ return
166
+ }
167
+ }
168
+
169
+ if options .SignInRequired {
170
+ if ! ctx .IsSigned {
171
+ if ctx .Req .URL .Path != "/user/events" {
172
+ middleware .SetRedirectToCookie (ctx .Resp , setting .AppSubURL + ctx .Req .URL .RequestURI ())
173
+ }
174
+ ctx .Redirect (setting .AppSubURL + "/user/login" )
175
+ return
176
+ } else if ! ctx .Doer .IsActive && setting .Service .RegisterEmailConfirm {
177
+ ctx .Data ["Title" ] = ctx .Tr ("auth.active_your_account" )
178
+ ctx .HTML (http .StatusOK , "user/auth/activate" )
179
+ return
180
+ }
181
+ }
182
+
183
+ // Redirect to log in page if auto-signin info is provided and has not signed in.
184
+ if ! options .SignOutRequired && ! ctx .IsSigned &&
185
+ len (ctx .GetSiteCookie (setting .CookieUserName )) > 0 {
186
+ if ctx .Req .URL .Path != "/user/events" {
187
+ middleware .SetRedirectToCookie (ctx .Resp , setting .AppSubURL + ctx .Req .URL .RequestURI ())
188
+ }
189
+ ctx .Redirect (setting .AppSubURL + "/user/login" )
190
+ return
191
+ }
192
+
193
+ if options .AdminRequired {
194
+ if ! ctx .Doer .IsAdmin {
195
+ ctx .Error (http .StatusForbidden )
196
+ return
197
+ }
198
+ ctx .Data ["PageIsAdmin" ] = true
199
+ }
200
+ }
201
+ }
202
+
98
203
func ctxDataSet (args ... any ) func (ctx * context.Context ) {
99
204
return func (ctx * context.Context ) {
100
205
for i := 0 ; i < len (args ); i += 2 {
@@ -144,10 +249,10 @@ func Routes() *web.Route {
144
249
mid = append (mid , common .Sessioner (), context .Contexter ())
145
250
146
251
// Get user from session if logged in.
147
- mid = append (mid , auth_service . Auth (buildAuthGroup ()))
252
+ mid = append (mid , webAuth (buildAuthGroup ()))
148
253
149
254
// GetHead allows a HEAD request redirect to GET if HEAD method is not defined for that route
150
- mid = append (mid , middleware .GetHead )
255
+ mid = append (mid , chi_middleware .GetHead )
151
256
152
257
if setting .API .EnableSwagger {
153
258
// Note: The route is here but no in API routes because it renders a web page
@@ -168,12 +273,12 @@ func Routes() *web.Route {
168
273
169
274
// registerRoutes register routes
170
275
func registerRoutes (m * web.Route ) {
171
- reqSignIn := auth_service . VerifyAuthWithOptions ( & auth_service .VerifyOptions {SignInRequired : true })
172
- reqSignOut := auth_service . VerifyAuthWithOptions ( & auth_service .VerifyOptions {SignOutRequired : true })
276
+ reqSignIn := verifyAuthWithOptions ( & common .VerifyOptions {SignInRequired : true })
277
+ reqSignOut := verifyAuthWithOptions ( & common .VerifyOptions {SignOutRequired : true })
173
278
// TODO: rename them to "optSignIn", which means that the "sign-in" could be optional, depends on the VerifyOptions (RequireSignInView)
174
- ignSignIn := auth_service . VerifyAuthWithOptions ( & auth_service .VerifyOptions {SignInRequired : setting .Service .RequireSignInView })
175
- ignExploreSignIn := auth_service . VerifyAuthWithOptions ( & auth_service .VerifyOptions {SignInRequired : setting .Service .RequireSignInView || setting .Service .Explore .RequireSigninView })
176
- ignSignInAndCsrf := auth_service . VerifyAuthWithOptions ( & auth_service .VerifyOptions {DisableCSRF : true })
279
+ ignSignIn := verifyAuthWithOptions ( & common .VerifyOptions {SignInRequired : setting .Service .RequireSignInView })
280
+ ignExploreSignIn := verifyAuthWithOptions ( & common .VerifyOptions {SignInRequired : setting .Service .RequireSignInView || setting .Service .Explore .RequireSigninView })
281
+ ignSignInAndCsrf := verifyAuthWithOptions ( & common .VerifyOptions {DisableCSRF : true })
177
282
validation .AddBindingRules ()
178
283
179
284
linkAccountEnabled := func (ctx * context.Context ) {
@@ -543,7 +648,7 @@ func registerRoutes(m *web.Route) {
543
648
544
649
m .Get ("/avatar/{hash}" , user .AvatarByEmailHash )
545
650
546
- adminReq := auth_service . VerifyAuthWithOptions ( & auth_service .VerifyOptions {SignInRequired : true , AdminRequired : true })
651
+ adminReq := verifyAuthWithOptions ( & common .VerifyOptions {SignInRequired : true , AdminRequired : true })
547
652
548
653
// ***** START: Admin *****
549
654
m .Group ("/admin" , func () {
0 commit comments