@@ -13,18 +13,32 @@ import (
13
13
14
14
"code.gitea.io/gitea/models/auth"
15
15
repo_model "code.gitea.io/gitea/models/repo"
16
- "code.gitea.io/gitea/modules/cache"
16
+ "code.gitea.io/gitea/models/unit"
17
+ user_model "code.gitea.io/gitea/models/user"
18
+ mc "code.gitea.io/gitea/modules/cache"
17
19
"code.gitea.io/gitea/modules/git"
18
20
"code.gitea.io/gitea/modules/httpcache"
19
21
"code.gitea.io/gitea/modules/log"
20
22
"code.gitea.io/gitea/modules/setting"
21
- "code.gitea.io/gitea/modules/web/middleware"
23
+
24
+ "gitea.com/go-chi/cache"
22
25
)
23
26
24
27
// APIContext is a specific context for API service
25
28
type APIContext struct {
26
- * Context
27
- Org * APIOrganization
29
+ * Base
30
+
31
+ Cache cache.Cache
32
+
33
+ Doer * user_model.User // current signed-in user
34
+ IsSigned bool
35
+ IsBasicAuth bool
36
+
37
+ ContextUser * user_model.User // the user which is being visited, in most cases it differs from Doer
38
+
39
+ Repo * Repository
40
+ Org * APIOrganization
41
+ Package * Package
28
42
}
29
43
30
44
// Currently, we have the following common fields in error response:
@@ -128,11 +142,6 @@ type apiContextKeyType struct{}
128
142
129
143
var apiContextKey = apiContextKeyType {}
130
144
131
- // WithAPIContext set up api context in request
132
- func WithAPIContext (req * http.Request , ctx * APIContext ) * http.Request {
133
- return req .WithContext (context .WithValue (req .Context (), apiContextKey , ctx ))
134
- }
135
-
136
145
// GetAPIContext returns a context for API routes
137
146
func GetAPIContext (req * http.Request ) * APIContext {
138
147
return req .Context ().Value (apiContextKey ).(* APIContext )
@@ -195,21 +204,21 @@ func (ctx *APIContext) CheckForOTP() {
195
204
}
196
205
197
206
otpHeader := ctx .Req .Header .Get ("X-Gitea-OTP" )
198
- twofa , err := auth .GetTwoFactorByUID (ctx .Context . Doer .ID )
207
+ twofa , err := auth .GetTwoFactorByUID (ctx .Doer .ID )
199
208
if err != nil {
200
209
if auth .IsErrTwoFactorNotEnrolled (err ) {
201
210
return // No 2FA enrollment for this user
202
211
}
203
- ctx .Context . Error (http .StatusInternalServerError )
212
+ ctx .Error (http .StatusInternalServerError , "GetTwoFactorByUID" , err )
204
213
return
205
214
}
206
215
ok , err := twofa .ValidateTOTP (otpHeader )
207
216
if err != nil {
208
- ctx .Context . Error (http .StatusInternalServerError )
217
+ ctx .Error (http .StatusInternalServerError , "ValidateTOTP" , err )
209
218
return
210
219
}
211
220
if ! ok {
212
- ctx .Context . Error (http .StatusUnauthorized )
221
+ ctx .Error (http .StatusUnauthorized , "" , nil )
213
222
return
214
223
}
215
224
}
@@ -218,23 +227,17 @@ func (ctx *APIContext) CheckForOTP() {
218
227
func APIContexter () func (http.Handler ) http.Handler {
219
228
return func (next http.Handler ) http.Handler {
220
229
return http .HandlerFunc (func (w http.ResponseWriter , req * http.Request ) {
221
- locale := middleware .Locale (w , req )
222
- ctx := APIContext {
223
- Context : & Context {
224
- Resp : NewResponse (w ),
225
- Data : middleware .GetContextData (req .Context ()),
226
- Locale : locale ,
227
- Cache : cache .GetCache (),
228
- Repo : & Repository {
229
- PullRequest : & PullRequest {},
230
- },
231
- Org : & Organization {},
232
- },
233
- Org : & APIOrganization {},
230
+ base , baseCleanUp := NewBaseContext (w , req )
231
+ ctx := & APIContext {
232
+ Base : base ,
233
+ Cache : mc .GetCache (),
234
+ Repo : & Repository {PullRequest : & PullRequest {}},
235
+ Org : & APIOrganization {},
234
236
}
235
- defer ctx . Close ()
237
+ defer baseCleanUp ()
236
238
237
- ctx .Req = WithAPIContext (WithContext (req , ctx .Context ), & ctx )
239
+ ctx .Base .AppendContextValue (apiContextKey , ctx )
240
+ ctx .Base .AppendContextValueFunc (git .RepositoryContextKey , func () any { return ctx .Repo .GitRepo })
238
241
239
242
// If request sends files, parse them here otherwise the Query() can't be parsed and the CsrfToken will be invalid.
240
243
if ctx .Req .Method == "POST" && strings .Contains (ctx .Req .Header .Get ("Content-Type" ), "multipart/form-data" ) {
@@ -247,8 +250,6 @@ func APIContexter() func(http.Handler) http.Handler {
247
250
httpcache .SetCacheControlInHeader (ctx .Resp .Header (), 0 , "no-transform" )
248
251
ctx .Resp .Header ().Set (`X-Frame-Options` , setting .CORSConfig .XFrameOptions )
249
252
250
- ctx .Data ["Context" ] = & ctx
251
-
252
253
next .ServeHTTP (ctx .Resp , ctx .Req )
253
254
})
254
255
}
@@ -301,7 +302,7 @@ func ReferencesGitRepo(allowEmpty ...bool) func(ctx *APIContext) (cancel context
301
302
return func () {
302
303
// If it's been set to nil then assume someone else has closed it.
303
304
if ctx .Repo .GitRepo != nil {
304
- ctx .Repo .GitRepo .Close ()
305
+ _ = ctx .Repo .GitRepo .Close ()
305
306
}
306
307
}
307
308
}
@@ -337,7 +338,7 @@ func RepoRefForAPI(next http.Handler) http.Handler {
337
338
}
338
339
339
340
var err error
340
- refName := getRefName (ctx .Context , RepoRefAny )
341
+ refName := getRefName (ctx .Base , ctx . Repo , RepoRefAny )
341
342
342
343
if ctx .Repo .GitRepo .IsBranchExist (refName ) {
343
344
ctx .Repo .Commit , err = ctx .Repo .GitRepo .GetBranchCommit (refName )
@@ -368,3 +369,53 @@ func RepoRefForAPI(next http.Handler) http.Handler {
368
369
next .ServeHTTP (w , req )
369
370
})
370
371
}
372
+
373
+ // HasAPIError returns true if error occurs in form validation.
374
+ func (ctx * APIContext ) HasAPIError () bool {
375
+ hasErr , ok := ctx .Data ["HasError" ]
376
+ if ! ok {
377
+ return false
378
+ }
379
+ return hasErr .(bool )
380
+ }
381
+
382
+ // GetErrMsg returns error message in form validation.
383
+ func (ctx * APIContext ) GetErrMsg () string {
384
+ msg , _ := ctx .Data ["ErrorMsg" ].(string )
385
+ if msg == "" {
386
+ msg = "invalid form data"
387
+ }
388
+ return msg
389
+ }
390
+
391
+ // NotFoundOrServerError use error check function to determine if the error
392
+ // is about not found. It responds with 404 status code for not found error,
393
+ // or error context description for logging purpose of 500 server error.
394
+ func (ctx * APIContext ) NotFoundOrServerError (logMsg string , errCheck func (error ) bool , logErr error ) {
395
+ if errCheck (logErr ) {
396
+ ctx .JSON (http .StatusNotFound , nil )
397
+ return
398
+ }
399
+ ctx .Error (http .StatusInternalServerError , "NotFoundOrServerError" , logMsg )
400
+ }
401
+
402
+ // IsUserSiteAdmin returns true if current user is a site admin
403
+ func (ctx * APIContext ) IsUserSiteAdmin () bool {
404
+ return ctx .IsSigned && ctx .Doer .IsAdmin
405
+ }
406
+
407
+ // IsUserRepoAdmin returns true if current user is admin in current repo
408
+ func (ctx * APIContext ) IsUserRepoAdmin () bool {
409
+ return ctx .Repo .IsAdmin ()
410
+ }
411
+
412
+ // IsUserRepoWriter returns true if current user has write privilege in current repo
413
+ func (ctx * APIContext ) IsUserRepoWriter (unitTypes []unit.Type ) bool {
414
+ for _ , unitType := range unitTypes {
415
+ if ctx .Repo .CanWrite (unitType ) {
416
+ return true
417
+ }
418
+ }
419
+
420
+ return false
421
+ }
0 commit comments