code.gitea.io/gitea@v1.22.3/routers/api/v1/api.go (about) 1 // Copyright 2015 The Gogs Authors. All rights reserved. 2 // Copyright 2016 The Gitea Authors. All rights reserved. 3 // SPDX-License-Identifier: MIT 4 5 // Package v1 Gitea API 6 // 7 // This documentation describes the Gitea API. 8 // 9 // Schemes: https, http 10 // BasePath: /api/v1 11 // Version: {{AppVer | JSEscape}} 12 // License: MIT http://opensource.org/licenses/MIT 13 // 14 // Consumes: 15 // - application/json 16 // - text/plain 17 // 18 // Produces: 19 // - application/json 20 // - text/html 21 // 22 // Security: 23 // - BasicAuth : 24 // - Token : 25 // - AccessToken : 26 // - AuthorizationHeaderToken : 27 // - SudoParam : 28 // - SudoHeader : 29 // - TOTPHeader : 30 // 31 // SecurityDefinitions: 32 // BasicAuth: 33 // type: basic 34 // Token: 35 // type: apiKey 36 // name: token 37 // in: query 38 // description: This authentication option is deprecated for removal in Gitea 1.23. Please use AuthorizationHeaderToken instead. 39 // AccessToken: 40 // type: apiKey 41 // name: access_token 42 // in: query 43 // description: This authentication option is deprecated for removal in Gitea 1.23. Please use AuthorizationHeaderToken instead. 44 // AuthorizationHeaderToken: 45 // type: apiKey 46 // name: Authorization 47 // in: header 48 // description: API tokens must be prepended with "token" followed by a space. 49 // SudoParam: 50 // type: apiKey 51 // name: sudo 52 // in: query 53 // description: Sudo API request as the user provided as the key. Admin privileges are required. 54 // SudoHeader: 55 // type: apiKey 56 // name: Sudo 57 // in: header 58 // description: Sudo API request as the user provided as the key. Admin privileges are required. 59 // TOTPHeader: 60 // type: apiKey 61 // name: X-GITEA-OTP 62 // in: header 63 // description: Must be used in combination with BasicAuth if two-factor authentication is enabled. 64 // 65 // swagger:meta 66 package v1 67 68 import ( 69 "fmt" 70 "net/http" 71 "strings" 72 73 actions_model "code.gitea.io/gitea/models/actions" 74 auth_model "code.gitea.io/gitea/models/auth" 75 "code.gitea.io/gitea/models/db" 76 "code.gitea.io/gitea/models/organization" 77 "code.gitea.io/gitea/models/perm" 78 access_model "code.gitea.io/gitea/models/perm/access" 79 repo_model "code.gitea.io/gitea/models/repo" 80 "code.gitea.io/gitea/models/unit" 81 user_model "code.gitea.io/gitea/models/user" 82 "code.gitea.io/gitea/modules/log" 83 "code.gitea.io/gitea/modules/setting" 84 api "code.gitea.io/gitea/modules/structs" 85 "code.gitea.io/gitea/modules/web" 86 "code.gitea.io/gitea/routers/api/v1/activitypub" 87 "code.gitea.io/gitea/routers/api/v1/admin" 88 "code.gitea.io/gitea/routers/api/v1/misc" 89 "code.gitea.io/gitea/routers/api/v1/notify" 90 "code.gitea.io/gitea/routers/api/v1/org" 91 "code.gitea.io/gitea/routers/api/v1/packages" 92 "code.gitea.io/gitea/routers/api/v1/repo" 93 "code.gitea.io/gitea/routers/api/v1/settings" 94 "code.gitea.io/gitea/routers/api/v1/user" 95 "code.gitea.io/gitea/routers/common" 96 "code.gitea.io/gitea/services/actions" 97 "code.gitea.io/gitea/services/auth" 98 "code.gitea.io/gitea/services/context" 99 "code.gitea.io/gitea/services/forms" 100 101 _ "code.gitea.io/gitea/routers/api/v1/swagger" // for swagger generation 102 103 "gitea.com/go-chi/binding" 104 "github.com/go-chi/cors" 105 ) 106 107 func sudo() func(ctx *context.APIContext) { 108 return func(ctx *context.APIContext) { 109 sudo := ctx.FormString("sudo") 110 if len(sudo) == 0 { 111 sudo = ctx.Req.Header.Get("Sudo") 112 } 113 114 if len(sudo) > 0 { 115 if ctx.IsSigned && ctx.Doer.IsAdmin { 116 user, err := user_model.GetUserByName(ctx, sudo) 117 if err != nil { 118 if user_model.IsErrUserNotExist(err) { 119 ctx.NotFound() 120 } else { 121 ctx.Error(http.StatusInternalServerError, "GetUserByName", err) 122 } 123 return 124 } 125 log.Trace("Sudo from (%s) to: %s", ctx.Doer.Name, user.Name) 126 ctx.Doer = user 127 } else { 128 ctx.JSON(http.StatusForbidden, map[string]string{ 129 "message": "Only administrators allowed to sudo.", 130 }) 131 return 132 } 133 } 134 } 135 } 136 137 func repoAssignment() func(ctx *context.APIContext) { 138 return func(ctx *context.APIContext) { 139 userName := ctx.Params("username") 140 repoName := ctx.Params("reponame") 141 142 var ( 143 owner *user_model.User 144 err error 145 ) 146 147 // Check if the user is the same as the repository owner. 148 if ctx.IsSigned && ctx.Doer.LowerName == strings.ToLower(userName) { 149 owner = ctx.Doer 150 } else { 151 owner, err = user_model.GetUserByName(ctx, userName) 152 if err != nil { 153 if user_model.IsErrUserNotExist(err) { 154 if redirectUserID, err := user_model.LookupUserRedirect(ctx, userName); err == nil { 155 context.RedirectToUser(ctx.Base, userName, redirectUserID) 156 } else if user_model.IsErrUserRedirectNotExist(err) { 157 ctx.NotFound("GetUserByName", err) 158 } else { 159 ctx.Error(http.StatusInternalServerError, "LookupUserRedirect", err) 160 } 161 } else { 162 ctx.Error(http.StatusInternalServerError, "GetUserByName", err) 163 } 164 return 165 } 166 } 167 ctx.Repo.Owner = owner 168 ctx.ContextUser = owner 169 170 // Get repository. 171 repo, err := repo_model.GetRepositoryByName(ctx, owner.ID, repoName) 172 if err != nil { 173 if repo_model.IsErrRepoNotExist(err) { 174 redirectRepoID, err := repo_model.LookupRedirect(ctx, owner.ID, repoName) 175 if err == nil { 176 context.RedirectToRepo(ctx.Base, redirectRepoID) 177 } else if repo_model.IsErrRedirectNotExist(err) { 178 ctx.NotFound() 179 } else { 180 ctx.Error(http.StatusInternalServerError, "LookupRepoRedirect", err) 181 } 182 } else { 183 ctx.Error(http.StatusInternalServerError, "GetRepositoryByName", err) 184 } 185 return 186 } 187 188 repo.Owner = owner 189 ctx.Repo.Repository = repo 190 191 if ctx.Doer != nil && ctx.Doer.ID == user_model.ActionsUserID { 192 taskID := ctx.Data["ActionsTaskID"].(int64) 193 task, err := actions_model.GetTaskByID(ctx, taskID) 194 if err != nil { 195 ctx.Error(http.StatusInternalServerError, "actions_model.GetTaskByID", err) 196 return 197 } 198 if task.RepoID != repo.ID { 199 ctx.NotFound() 200 return 201 } 202 203 if task.IsForkPullRequest { 204 ctx.Repo.Permission.AccessMode = perm.AccessModeRead 205 } else { 206 ctx.Repo.Permission.AccessMode = perm.AccessModeWrite 207 } 208 209 if err := ctx.Repo.Repository.LoadUnits(ctx); err != nil { 210 ctx.Error(http.StatusInternalServerError, "LoadUnits", err) 211 return 212 } 213 ctx.Repo.Permission.SetUnitsWithDefaultAccessMode(ctx.Repo.Repository.Units, ctx.Repo.Permission.AccessMode) 214 } else { 215 ctx.Repo.Permission, err = access_model.GetUserRepoPermission(ctx, repo, ctx.Doer) 216 if err != nil { 217 ctx.Error(http.StatusInternalServerError, "GetUserRepoPermission", err) 218 return 219 } 220 } 221 222 if !ctx.Repo.Permission.HasAnyUnitAccess() { 223 ctx.NotFound() 224 return 225 } 226 } 227 } 228 229 func reqPackageAccess(accessMode perm.AccessMode) func(ctx *context.APIContext) { 230 return func(ctx *context.APIContext) { 231 if ctx.Package.AccessMode < accessMode && !ctx.IsUserSiteAdmin() { 232 ctx.Error(http.StatusForbidden, "reqPackageAccess", "user should have specific permission or be a site admin") 233 return 234 } 235 } 236 } 237 238 func checkTokenPublicOnly() func(ctx *context.APIContext) { 239 return func(ctx *context.APIContext) { 240 if !ctx.PublicOnly { 241 return 242 } 243 244 requiredScopeCategories, ok := ctx.Data["requiredScopeCategories"].([]auth_model.AccessTokenScopeCategory) 245 if !ok || len(requiredScopeCategories) == 0 { 246 return 247 } 248 249 // public Only permission check 250 switch { 251 case auth_model.ContainsCategory(requiredScopeCategories, auth_model.AccessTokenScopeCategoryRepository): 252 if ctx.Repo.Repository != nil && ctx.Repo.Repository.IsPrivate { 253 ctx.Error(http.StatusForbidden, "reqToken", "token scope is limited to public repos") 254 return 255 } 256 case auth_model.ContainsCategory(requiredScopeCategories, auth_model.AccessTokenScopeCategoryIssue): 257 if ctx.Repo.Repository != nil && ctx.Repo.Repository.IsPrivate { 258 ctx.Error(http.StatusForbidden, "reqToken", "token scope is limited to public issues") 259 return 260 } 261 case auth_model.ContainsCategory(requiredScopeCategories, auth_model.AccessTokenScopeCategoryOrganization): 262 if ctx.Org.Organization != nil && ctx.Org.Organization.Visibility != api.VisibleTypePublic { 263 ctx.Error(http.StatusForbidden, "reqToken", "token scope is limited to public orgs") 264 return 265 } 266 if ctx.ContextUser != nil && ctx.ContextUser.IsOrganization() && ctx.ContextUser.Visibility != api.VisibleTypePublic { 267 ctx.Error(http.StatusForbidden, "reqToken", "token scope is limited to public orgs") 268 return 269 } 270 case auth_model.ContainsCategory(requiredScopeCategories, auth_model.AccessTokenScopeCategoryUser): 271 if ctx.ContextUser != nil && ctx.ContextUser.IsUser() && ctx.ContextUser.Visibility != api.VisibleTypePublic { 272 ctx.Error(http.StatusForbidden, "reqToken", "token scope is limited to public users") 273 return 274 } 275 case auth_model.ContainsCategory(requiredScopeCategories, auth_model.AccessTokenScopeCategoryActivityPub): 276 if ctx.ContextUser != nil && ctx.ContextUser.IsUser() && ctx.ContextUser.Visibility != api.VisibleTypePublic { 277 ctx.Error(http.StatusForbidden, "reqToken", "token scope is limited to public activitypub") 278 return 279 } 280 case auth_model.ContainsCategory(requiredScopeCategories, auth_model.AccessTokenScopeCategoryNotification): 281 if ctx.Repo.Repository != nil && ctx.Repo.Repository.IsPrivate { 282 ctx.Error(http.StatusForbidden, "reqToken", "token scope is limited to public notifications") 283 return 284 } 285 case auth_model.ContainsCategory(requiredScopeCategories, auth_model.AccessTokenScopeCategoryPackage): 286 if ctx.Package != nil && ctx.Package.Owner.Visibility.IsPrivate() { 287 ctx.Error(http.StatusForbidden, "reqToken", "token scope is limited to public packages") 288 return 289 } 290 } 291 } 292 } 293 294 // if a token is being used for auth, we check that it contains the required scope 295 // if a token is not being used, reqToken will enforce other sign in methods 296 func tokenRequiresScopes(requiredScopeCategories ...auth_model.AccessTokenScopeCategory) func(ctx *context.APIContext) { 297 return func(ctx *context.APIContext) { 298 // no scope required 299 if len(requiredScopeCategories) == 0 { 300 return 301 } 302 303 // Need OAuth2 token to be present. 304 scope, scopeExists := ctx.Data["ApiTokenScope"].(auth_model.AccessTokenScope) 305 if ctx.Data["IsApiToken"] != true || !scopeExists { 306 return 307 } 308 309 // use the http method to determine the access level 310 requiredScopeLevel := auth_model.Read 311 if ctx.Req.Method == "POST" || ctx.Req.Method == "PUT" || ctx.Req.Method == "PATCH" || ctx.Req.Method == "DELETE" { 312 requiredScopeLevel = auth_model.Write 313 } 314 315 // get the required scope for the given access level and category 316 requiredScopes := auth_model.GetRequiredScopes(requiredScopeLevel, requiredScopeCategories...) 317 allow, err := scope.HasScope(requiredScopes...) 318 if err != nil { 319 ctx.Error(http.StatusForbidden, "tokenRequiresScope", "checking scope failed: "+err.Error()) 320 return 321 } 322 323 if !allow { 324 ctx.Error(http.StatusForbidden, "tokenRequiresScope", fmt.Sprintf("token does not have at least one of required scope(s): %v", requiredScopes)) 325 return 326 } 327 328 ctx.Data["requiredScopeCategories"] = requiredScopeCategories 329 330 // check if scope only applies to public resources 331 publicOnly, err := scope.PublicOnly() 332 if err != nil { 333 ctx.Error(http.StatusForbidden, "tokenRequiresScope", "parsing public resource scope failed: "+err.Error()) 334 return 335 } 336 337 // assign to true so that those searching should only filter public repositories/users/organizations 338 ctx.PublicOnly = publicOnly 339 } 340 } 341 342 // Contexter middleware already checks token for user sign in process. 343 func reqToken() func(ctx *context.APIContext) { 344 return func(ctx *context.APIContext) { 345 // If actions token is present 346 if true == ctx.Data["IsActionsToken"] { 347 return 348 } 349 350 if ctx.IsSigned { 351 return 352 } 353 ctx.Error(http.StatusUnauthorized, "reqToken", "token is required") 354 } 355 } 356 357 func reqExploreSignIn() func(ctx *context.APIContext) { 358 return func(ctx *context.APIContext) { 359 if setting.Service.Explore.RequireSigninView && !ctx.IsSigned { 360 ctx.Error(http.StatusUnauthorized, "reqExploreSignIn", "you must be signed in to search for users") 361 } 362 } 363 } 364 365 func reqBasicOrRevProxyAuth() func(ctx *context.APIContext) { 366 return func(ctx *context.APIContext) { 367 if ctx.IsSigned && setting.Service.EnableReverseProxyAuthAPI && ctx.Data["AuthedMethod"].(string) == auth.ReverseProxyMethodName { 368 return 369 } 370 if !ctx.IsBasicAuth { 371 ctx.Error(http.StatusUnauthorized, "reqBasicAuth", "auth required") 372 return 373 } 374 } 375 } 376 377 // reqSiteAdmin user should be the site admin 378 func reqSiteAdmin() func(ctx *context.APIContext) { 379 return func(ctx *context.APIContext) { 380 if !ctx.IsUserSiteAdmin() { 381 ctx.Error(http.StatusForbidden, "reqSiteAdmin", "user should be the site admin") 382 return 383 } 384 } 385 } 386 387 // reqOwner user should be the owner of the repo or site admin. 388 func reqOwner() func(ctx *context.APIContext) { 389 return func(ctx *context.APIContext) { 390 if !ctx.Repo.IsOwner() && !ctx.IsUserSiteAdmin() { 391 ctx.Error(http.StatusForbidden, "reqOwner", "user should be the owner of the repo") 392 return 393 } 394 } 395 } 396 397 // reqSelfOrAdmin doer should be the same as the contextUser or site admin 398 func reqSelfOrAdmin() func(ctx *context.APIContext) { 399 return func(ctx *context.APIContext) { 400 if !ctx.IsUserSiteAdmin() && ctx.ContextUser != ctx.Doer { 401 ctx.Error(http.StatusForbidden, "reqSelfOrAdmin", "doer should be the site admin or be same as the contextUser") 402 return 403 } 404 } 405 } 406 407 // reqAdmin user should be an owner or a collaborator with admin write of a repository, or site admin 408 func reqAdmin() func(ctx *context.APIContext) { 409 return func(ctx *context.APIContext) { 410 if !ctx.IsUserRepoAdmin() && !ctx.IsUserSiteAdmin() { 411 ctx.Error(http.StatusForbidden, "reqAdmin", "user should be an owner or a collaborator with admin write of a repository") 412 return 413 } 414 } 415 } 416 417 // reqRepoWriter user should have a permission to write to a repo, or be a site admin 418 func reqRepoWriter(unitTypes ...unit.Type) func(ctx *context.APIContext) { 419 return func(ctx *context.APIContext) { 420 if !ctx.IsUserRepoWriter(unitTypes) && !ctx.IsUserRepoAdmin() && !ctx.IsUserSiteAdmin() { 421 ctx.Error(http.StatusForbidden, "reqRepoWriter", "user should have a permission to write to a repo") 422 return 423 } 424 } 425 } 426 427 // reqRepoBranchWriter user should have a permission to write to a branch, or be a site admin 428 func reqRepoBranchWriter(ctx *context.APIContext) { 429 options, ok := web.GetForm(ctx).(api.FileOptionInterface) 430 if !ok || (!ctx.Repo.CanWriteToBranch(ctx, ctx.Doer, options.Branch()) && !ctx.IsUserSiteAdmin()) { 431 ctx.Error(http.StatusForbidden, "reqRepoBranchWriter", "user should have a permission to write to this branch") 432 return 433 } 434 } 435 436 // reqRepoReader user should have specific read permission or be a repo admin or a site admin 437 func reqRepoReader(unitType unit.Type) func(ctx *context.APIContext) { 438 return func(ctx *context.APIContext) { 439 if !ctx.Repo.CanRead(unitType) && !ctx.IsUserRepoAdmin() && !ctx.IsUserSiteAdmin() { 440 ctx.Error(http.StatusForbidden, "reqRepoReader", "user should have specific read permission or be a repo admin or a site admin") 441 return 442 } 443 } 444 } 445 446 // reqAnyRepoReader user should have any permission to read repository or permissions of site admin 447 func reqAnyRepoReader() func(ctx *context.APIContext) { 448 return func(ctx *context.APIContext) { 449 if !ctx.Repo.Permission.HasAnyUnitAccess() && !ctx.IsUserSiteAdmin() { 450 ctx.Error(http.StatusForbidden, "reqAnyRepoReader", "user should have any permission to read repository or permissions of site admin") 451 return 452 } 453 } 454 } 455 456 // reqOrgOwnership user should be an organization owner, or a site admin 457 func reqOrgOwnership() func(ctx *context.APIContext) { 458 return func(ctx *context.APIContext) { 459 if ctx.IsUserSiteAdmin() { 460 return 461 } 462 463 var orgID int64 464 if ctx.Org.Organization != nil { 465 orgID = ctx.Org.Organization.ID 466 } else if ctx.Org.Team != nil { 467 orgID = ctx.Org.Team.OrgID 468 } else { 469 ctx.Error(http.StatusInternalServerError, "", "reqOrgOwnership: unprepared context") 470 return 471 } 472 473 isOwner, err := organization.IsOrganizationOwner(ctx, orgID, ctx.Doer.ID) 474 if err != nil { 475 ctx.Error(http.StatusInternalServerError, "IsOrganizationOwner", err) 476 return 477 } else if !isOwner { 478 if ctx.Org.Organization != nil { 479 ctx.Error(http.StatusForbidden, "", "Must be an organization owner") 480 } else { 481 ctx.NotFound() 482 } 483 return 484 } 485 } 486 } 487 488 // reqTeamMembership user should be an team member, or a site admin 489 func reqTeamMembership() func(ctx *context.APIContext) { 490 return func(ctx *context.APIContext) { 491 if ctx.IsUserSiteAdmin() { 492 return 493 } 494 if ctx.Org.Team == nil { 495 ctx.Error(http.StatusInternalServerError, "", "reqTeamMembership: unprepared context") 496 return 497 } 498 499 orgID := ctx.Org.Team.OrgID 500 isOwner, err := organization.IsOrganizationOwner(ctx, orgID, ctx.Doer.ID) 501 if err != nil { 502 ctx.Error(http.StatusInternalServerError, "IsOrganizationOwner", err) 503 return 504 } else if isOwner { 505 return 506 } 507 508 if isTeamMember, err := organization.IsTeamMember(ctx, orgID, ctx.Org.Team.ID, ctx.Doer.ID); err != nil { 509 ctx.Error(http.StatusInternalServerError, "IsTeamMember", err) 510 return 511 } else if !isTeamMember { 512 isOrgMember, err := organization.IsOrganizationMember(ctx, orgID, ctx.Doer.ID) 513 if err != nil { 514 ctx.Error(http.StatusInternalServerError, "IsOrganizationMember", err) 515 } else if isOrgMember { 516 ctx.Error(http.StatusForbidden, "", "Must be a team member") 517 } else { 518 ctx.NotFound() 519 } 520 return 521 } 522 } 523 } 524 525 // reqOrgMembership user should be an organization member, or a site admin 526 func reqOrgMembership() func(ctx *context.APIContext) { 527 return func(ctx *context.APIContext) { 528 if ctx.IsUserSiteAdmin() { 529 return 530 } 531 532 var orgID int64 533 if ctx.Org.Organization != nil { 534 orgID = ctx.Org.Organization.ID 535 } else if ctx.Org.Team != nil { 536 orgID = ctx.Org.Team.OrgID 537 } else { 538 ctx.Error(http.StatusInternalServerError, "", "reqOrgMembership: unprepared context") 539 return 540 } 541 542 if isMember, err := organization.IsOrganizationMember(ctx, orgID, ctx.Doer.ID); err != nil { 543 ctx.Error(http.StatusInternalServerError, "IsOrganizationMember", err) 544 return 545 } else if !isMember { 546 if ctx.Org.Organization != nil { 547 ctx.Error(http.StatusForbidden, "", "Must be an organization member") 548 } else { 549 ctx.NotFound() 550 } 551 return 552 } 553 } 554 } 555 556 func reqGitHook() func(ctx *context.APIContext) { 557 return func(ctx *context.APIContext) { 558 if !ctx.Doer.CanEditGitHook() { 559 ctx.Error(http.StatusForbidden, "", "must be allowed to edit Git hooks") 560 return 561 } 562 } 563 } 564 565 // reqWebhooksEnabled requires webhooks to be enabled by admin. 566 func reqWebhooksEnabled() func(ctx *context.APIContext) { 567 return func(ctx *context.APIContext) { 568 if setting.DisableWebhooks { 569 ctx.Error(http.StatusForbidden, "", "webhooks disabled by administrator") 570 return 571 } 572 } 573 } 574 575 func orgAssignment(args ...bool) func(ctx *context.APIContext) { 576 var ( 577 assignOrg bool 578 assignTeam bool 579 ) 580 if len(args) > 0 { 581 assignOrg = args[0] 582 } 583 if len(args) > 1 { 584 assignTeam = args[1] 585 } 586 return func(ctx *context.APIContext) { 587 ctx.Org = new(context.APIOrganization) 588 589 var err error 590 if assignOrg { 591 ctx.Org.Organization, err = organization.GetOrgByName(ctx, ctx.Params(":org")) 592 if err != nil { 593 if organization.IsErrOrgNotExist(err) { 594 redirectUserID, err := user_model.LookupUserRedirect(ctx, ctx.Params(":org")) 595 if err == nil { 596 context.RedirectToUser(ctx.Base, ctx.Params(":org"), redirectUserID) 597 } else if user_model.IsErrUserRedirectNotExist(err) { 598 ctx.NotFound("GetOrgByName", err) 599 } else { 600 ctx.Error(http.StatusInternalServerError, "LookupUserRedirect", err) 601 } 602 } else { 603 ctx.Error(http.StatusInternalServerError, "GetOrgByName", err) 604 } 605 return 606 } 607 ctx.ContextUser = ctx.Org.Organization.AsUser() 608 } 609 610 if assignTeam { 611 ctx.Org.Team, err = organization.GetTeamByID(ctx, ctx.ParamsInt64(":teamid")) 612 if err != nil { 613 if organization.IsErrTeamNotExist(err) { 614 ctx.NotFound() 615 } else { 616 ctx.Error(http.StatusInternalServerError, "GetTeamById", err) 617 } 618 return 619 } 620 } 621 } 622 } 623 624 func mustEnableIssues(ctx *context.APIContext) { 625 if !ctx.Repo.CanRead(unit.TypeIssues) { 626 if log.IsTrace() { 627 if ctx.IsSigned { 628 log.Trace("Permission Denied: User %-v cannot read %-v in Repo %-v\n"+ 629 "User in Repo has Permissions: %-+v", 630 ctx.Doer, 631 unit.TypeIssues, 632 ctx.Repo.Repository, 633 ctx.Repo.Permission) 634 } else { 635 log.Trace("Permission Denied: Anonymous user cannot read %-v in Repo %-v\n"+ 636 "Anonymous user in Repo has Permissions: %-+v", 637 unit.TypeIssues, 638 ctx.Repo.Repository, 639 ctx.Repo.Permission) 640 } 641 } 642 ctx.NotFound() 643 return 644 } 645 } 646 647 func mustAllowPulls(ctx *context.APIContext) { 648 if !(ctx.Repo.Repository.CanEnablePulls() && ctx.Repo.CanRead(unit.TypePullRequests)) { 649 if ctx.Repo.Repository.CanEnablePulls() && log.IsTrace() { 650 if ctx.IsSigned { 651 log.Trace("Permission Denied: User %-v cannot read %-v in Repo %-v\n"+ 652 "User in Repo has Permissions: %-+v", 653 ctx.Doer, 654 unit.TypePullRequests, 655 ctx.Repo.Repository, 656 ctx.Repo.Permission) 657 } else { 658 log.Trace("Permission Denied: Anonymous user cannot read %-v in Repo %-v\n"+ 659 "Anonymous user in Repo has Permissions: %-+v", 660 unit.TypePullRequests, 661 ctx.Repo.Repository, 662 ctx.Repo.Permission) 663 } 664 } 665 ctx.NotFound() 666 return 667 } 668 } 669 670 func mustEnableIssuesOrPulls(ctx *context.APIContext) { 671 if !ctx.Repo.CanRead(unit.TypeIssues) && 672 !(ctx.Repo.Repository.CanEnablePulls() && ctx.Repo.CanRead(unit.TypePullRequests)) { 673 if ctx.Repo.Repository.CanEnablePulls() && log.IsTrace() { 674 if ctx.IsSigned { 675 log.Trace("Permission Denied: User %-v cannot read %-v and %-v in Repo %-v\n"+ 676 "User in Repo has Permissions: %-+v", 677 ctx.Doer, 678 unit.TypeIssues, 679 unit.TypePullRequests, 680 ctx.Repo.Repository, 681 ctx.Repo.Permission) 682 } else { 683 log.Trace("Permission Denied: Anonymous user cannot read %-v and %-v in Repo %-v\n"+ 684 "Anonymous user in Repo has Permissions: %-+v", 685 unit.TypeIssues, 686 unit.TypePullRequests, 687 ctx.Repo.Repository, 688 ctx.Repo.Permission) 689 } 690 } 691 ctx.NotFound() 692 return 693 } 694 } 695 696 func mustEnableWiki(ctx *context.APIContext) { 697 if !(ctx.Repo.CanRead(unit.TypeWiki)) { 698 ctx.NotFound() 699 return 700 } 701 } 702 703 func mustNotBeArchived(ctx *context.APIContext) { 704 if ctx.Repo.Repository.IsArchived { 705 ctx.Error(http.StatusLocked, "RepoArchived", fmt.Errorf("%s is archived", ctx.Repo.Repository.LogString())) 706 return 707 } 708 } 709 710 func mustEnableAttachments(ctx *context.APIContext) { 711 if !setting.Attachment.Enabled { 712 ctx.NotFound() 713 return 714 } 715 } 716 717 // bind binding an obj to a func(ctx *context.APIContext) 718 func bind[T any](_ T) any { 719 return func(ctx *context.APIContext) { 720 theObj := new(T) // create a new form obj for every request but not use obj directly 721 errs := binding.Bind(ctx.Req, theObj) 722 if len(errs) > 0 { 723 ctx.Error(http.StatusUnprocessableEntity, "validationError", fmt.Sprintf("%s: %s", errs[0].FieldNames, errs[0].Error())) 724 return 725 } 726 web.SetForm(ctx, theObj) 727 } 728 } 729 730 func buildAuthGroup() *auth.Group { 731 group := auth.NewGroup( 732 &auth.OAuth2{}, 733 &auth.HTTPSign{}, 734 &auth.Basic{}, // FIXME: this should be removed once we don't allow basic auth in API 735 ) 736 if setting.Service.EnableReverseProxyAuthAPI { 737 group.Add(&auth.ReverseProxy{}) 738 } 739 740 if setting.IsWindows && auth_model.IsSSPIEnabled(db.DefaultContext) { 741 group.Add(&auth.SSPI{}) // it MUST be the last, see the comment of SSPI 742 } 743 744 return group 745 } 746 747 func apiAuth(authMethod auth.Method) func(*context.APIContext) { 748 return func(ctx *context.APIContext) { 749 ar, err := common.AuthShared(ctx.Base, nil, authMethod) 750 if err != nil { 751 ctx.Error(http.StatusUnauthorized, "APIAuth", err) 752 return 753 } 754 ctx.Doer = ar.Doer 755 ctx.IsSigned = ar.Doer != nil 756 ctx.IsBasicAuth = ar.IsBasicAuth 757 } 758 } 759 760 // verifyAuthWithOptions checks authentication according to options 761 func verifyAuthWithOptions(options *common.VerifyOptions) func(ctx *context.APIContext) { 762 return func(ctx *context.APIContext) { 763 // Check prohibit login users. 764 if ctx.IsSigned { 765 if !ctx.Doer.IsActive && setting.Service.RegisterEmailConfirm { 766 ctx.Data["Title"] = ctx.Tr("auth.active_your_account") 767 ctx.JSON(http.StatusForbidden, map[string]string{ 768 "message": "This account is not activated.", 769 }) 770 return 771 } 772 if !ctx.Doer.IsActive || ctx.Doer.ProhibitLogin { 773 log.Info("Failed authentication attempt for %s from %s", ctx.Doer.Name, ctx.RemoteAddr()) 774 ctx.Data["Title"] = ctx.Tr("auth.prohibit_login") 775 ctx.JSON(http.StatusForbidden, map[string]string{ 776 "message": "This account is prohibited from signing in, please contact your site administrator.", 777 }) 778 return 779 } 780 781 if ctx.Doer.MustChangePassword { 782 ctx.JSON(http.StatusForbidden, map[string]string{ 783 "message": "You must change your password. Change it at: " + setting.AppURL + "/user/change_password", 784 }) 785 return 786 } 787 } 788 789 // Redirect to dashboard if user tries to visit any non-login page. 790 if options.SignOutRequired && ctx.IsSigned && ctx.Req.URL.RequestURI() != "/" { 791 ctx.Redirect(setting.AppSubURL + "/") 792 return 793 } 794 795 if options.SignInRequired { 796 if !ctx.IsSigned { 797 // Restrict API calls with error message. 798 ctx.JSON(http.StatusForbidden, map[string]string{ 799 "message": "Only signed in user is allowed to call APIs.", 800 }) 801 return 802 } else if !ctx.Doer.IsActive && setting.Service.RegisterEmailConfirm { 803 ctx.Data["Title"] = ctx.Tr("auth.active_your_account") 804 ctx.JSON(http.StatusForbidden, map[string]string{ 805 "message": "This account is not activated.", 806 }) 807 return 808 } 809 } 810 811 if options.AdminRequired { 812 if !ctx.Doer.IsAdmin { 813 ctx.JSON(http.StatusForbidden, map[string]string{ 814 "message": "You have no permission to request for this.", 815 }) 816 return 817 } 818 } 819 } 820 } 821 822 func individualPermsChecker(ctx *context.APIContext) { 823 // org permissions have been checked in context.OrgAssignment(), but individual permissions haven't been checked. 824 if ctx.ContextUser.IsIndividual() { 825 switch { 826 case ctx.ContextUser.Visibility == api.VisibleTypePrivate: 827 if ctx.Doer == nil || (ctx.ContextUser.ID != ctx.Doer.ID && !ctx.Doer.IsAdmin) { 828 ctx.NotFound("Visit Project", nil) 829 return 830 } 831 case ctx.ContextUser.Visibility == api.VisibleTypeLimited: 832 if ctx.Doer == nil { 833 ctx.NotFound("Visit Project", nil) 834 return 835 } 836 } 837 } 838 } 839 840 // check for and warn against deprecated authentication options 841 func checkDeprecatedAuthMethods(ctx *context.APIContext) { 842 if ctx.FormString("token") != "" || ctx.FormString("access_token") != "" { 843 ctx.Resp.Header().Set("X-Gitea-Warning", "token and access_token API authentication is deprecated and will be removed in gitea 1.23. Please use AuthorizationHeaderToken instead. Existing queries will continue to work but without authorization.") 844 } 845 } 846 847 // Routes registers all v1 APIs routes to web application. 848 func Routes() *web.Route { 849 m := web.NewRoute() 850 851 m.Use(securityHeaders()) 852 if setting.CORSConfig.Enabled { 853 m.Use(cors.Handler(cors.Options{ 854 AllowedOrigins: setting.CORSConfig.AllowDomain, 855 AllowedMethods: setting.CORSConfig.Methods, 856 AllowCredentials: setting.CORSConfig.AllowCredentials, 857 AllowedHeaders: append([]string{"Authorization", "X-Gitea-OTP"}, setting.CORSConfig.Headers...), 858 MaxAge: int(setting.CORSConfig.MaxAge.Seconds()), 859 })) 860 } 861 m.Use(context.APIContexter()) 862 863 m.Use(checkDeprecatedAuthMethods) 864 865 // Get user from session if logged in. 866 m.Use(apiAuth(buildAuthGroup())) 867 868 m.Use(verifyAuthWithOptions(&common.VerifyOptions{ 869 SignInRequired: setting.Service.RequireSignInView, 870 })) 871 872 addActionsRoutes := func( 873 m *web.Route, 874 reqChecker func(ctx *context.APIContext), 875 act actions.API, 876 ) { 877 m.Group("/actions", func() { 878 m.Group("/secrets", func() { 879 m.Get("", reqToken(), reqChecker, act.ListActionsSecrets) 880 m.Combo("/{secretname}"). 881 Put(reqToken(), reqChecker, bind(api.CreateOrUpdateSecretOption{}), act.CreateOrUpdateSecret). 882 Delete(reqToken(), reqChecker, act.DeleteSecret) 883 }) 884 885 m.Group("/variables", func() { 886 m.Get("", reqToken(), reqChecker, act.ListVariables) 887 m.Combo("/{variablename}"). 888 Get(reqToken(), reqChecker, act.GetVariable). 889 Delete(reqToken(), reqChecker, act.DeleteVariable). 890 Post(reqToken(), reqChecker, bind(api.CreateVariableOption{}), act.CreateVariable). 891 Put(reqToken(), reqChecker, bind(api.UpdateVariableOption{}), act.UpdateVariable) 892 }) 893 894 m.Group("/runners", func() { 895 m.Get("/registration-token", reqToken(), reqChecker, act.GetRegistrationToken) 896 }) 897 }) 898 } 899 900 m.Group("", func() { 901 // Miscellaneous (no scope required) 902 if setting.API.EnableSwagger { 903 m.Get("/swagger", func(ctx *context.APIContext) { 904 ctx.Redirect(setting.AppSubURL + "/api/swagger") 905 }) 906 } 907 908 if setting.Federation.Enabled { 909 m.Get("/nodeinfo", misc.NodeInfo) 910 m.Group("/activitypub", func() { 911 // deprecated, remove in 1.20, use /user-id/{user-id} instead 912 m.Group("/user/{username}", func() { 913 m.Get("", activitypub.Person) 914 m.Post("/inbox", activitypub.ReqHTTPSignature(), activitypub.PersonInbox) 915 }, context.UserAssignmentAPI(), checkTokenPublicOnly()) 916 m.Group("/user-id/{user-id}", func() { 917 m.Get("", activitypub.Person) 918 m.Post("/inbox", activitypub.ReqHTTPSignature(), activitypub.PersonInbox) 919 }, context.UserIDAssignmentAPI(), checkTokenPublicOnly()) 920 }, tokenRequiresScopes(auth_model.AccessTokenScopeCategoryActivityPub)) 921 } 922 923 // Misc (public accessible) 924 m.Group("", func() { 925 m.Get("/version", misc.Version) 926 m.Get("/signing-key.gpg", misc.SigningKey) 927 m.Post("/markup", reqToken(), bind(api.MarkupOption{}), misc.Markup) 928 m.Post("/markdown", reqToken(), bind(api.MarkdownOption{}), misc.Markdown) 929 m.Post("/markdown/raw", reqToken(), misc.MarkdownRaw) 930 m.Get("/gitignore/templates", misc.ListGitignoresTemplates) 931 m.Get("/gitignore/templates/{name}", misc.GetGitignoreTemplateInfo) 932 m.Get("/licenses", misc.ListLicenseTemplates) 933 m.Get("/licenses/{name}", misc.GetLicenseTemplateInfo) 934 m.Get("/label/templates", misc.ListLabelTemplates) 935 m.Get("/label/templates/{name}", misc.GetLabelTemplate) 936 937 m.Group("/settings", func() { 938 m.Get("/ui", settings.GetGeneralUISettings) 939 m.Get("/api", settings.GetGeneralAPISettings) 940 m.Get("/attachment", settings.GetGeneralAttachmentSettings) 941 m.Get("/repository", settings.GetGeneralRepoSettings) 942 }) 943 }) 944 945 // Notifications (requires 'notifications' scope) 946 m.Group("/notifications", func() { 947 m.Combo(""). 948 Get(reqToken(), notify.ListNotifications). 949 Put(reqToken(), notify.ReadNotifications) 950 m.Get("/new", reqToken(), notify.NewAvailable) 951 m.Combo("/threads/{id}"). 952 Get(reqToken(), notify.GetThread). 953 Patch(reqToken(), notify.ReadThread) 954 }, tokenRequiresScopes(auth_model.AccessTokenScopeCategoryNotification)) 955 956 // Users (requires user scope) 957 m.Group("/users", func() { 958 m.Get("/search", reqExploreSignIn(), user.Search) 959 960 m.Group("/{username}", func() { 961 m.Get("", reqExploreSignIn(), user.GetInfo) 962 963 if setting.Service.EnableUserHeatmap { 964 m.Get("/heatmap", user.GetUserHeatmapData) 965 } 966 967 m.Get("/repos", tokenRequiresScopes(auth_model.AccessTokenScopeCategoryRepository), reqExploreSignIn(), user.ListUserRepos) 968 m.Group("/tokens", func() { 969 m.Combo("").Get(user.ListAccessTokens). 970 Post(bind(api.CreateAccessTokenOption{}), reqToken(), user.CreateAccessToken) 971 m.Combo("/{id}").Delete(reqToken(), user.DeleteAccessToken) 972 }, reqSelfOrAdmin(), reqBasicOrRevProxyAuth()) 973 974 m.Get("/activities/feeds", user.ListUserActivityFeeds) 975 }, context.UserAssignmentAPI(), checkTokenPublicOnly(), individualPermsChecker) 976 }, tokenRequiresScopes(auth_model.AccessTokenScopeCategoryUser)) 977 978 // Users (requires user scope) 979 m.Group("/users", func() { 980 m.Group("/{username}", func() { 981 m.Get("/keys", user.ListPublicKeys) 982 m.Get("/gpg_keys", user.ListGPGKeys) 983 984 m.Get("/followers", user.ListFollowers) 985 m.Group("/following", func() { 986 m.Get("", user.ListFollowing) 987 m.Get("/{target}", user.CheckFollowing) 988 }) 989 990 m.Get("/starred", user.GetStarredRepos) 991 992 m.Get("/subscriptions", user.GetWatchedRepos) 993 }, context.UserAssignmentAPI(), checkTokenPublicOnly()) 994 }, tokenRequiresScopes(auth_model.AccessTokenScopeCategoryUser), reqToken()) 995 996 // Users (requires user scope) 997 m.Group("/user", func() { 998 m.Get("", user.GetAuthenticatedUser) 999 m.Group("/settings", func() { 1000 m.Get("", user.GetUserSettings) 1001 m.Patch("", bind(api.UserSettingsOptions{}), user.UpdateUserSettings) 1002 }, reqToken()) 1003 m.Combo("/emails"). 1004 Get(user.ListEmails). 1005 Post(bind(api.CreateEmailOption{}), user.AddEmail). 1006 Delete(bind(api.DeleteEmailOption{}), user.DeleteEmail) 1007 1008 // manage user-level actions features 1009 m.Group("/actions", func() { 1010 m.Group("/secrets", func() { 1011 m.Combo("/{secretname}"). 1012 Put(bind(api.CreateOrUpdateSecretOption{}), user.CreateOrUpdateSecret). 1013 Delete(user.DeleteSecret) 1014 }) 1015 1016 m.Group("/variables", func() { 1017 m.Get("", user.ListVariables) 1018 m.Combo("/{variablename}"). 1019 Get(user.GetVariable). 1020 Delete(user.DeleteVariable). 1021 Post(bind(api.CreateVariableOption{}), user.CreateVariable). 1022 Put(bind(api.UpdateVariableOption{}), user.UpdateVariable) 1023 }) 1024 1025 m.Group("/runners", func() { 1026 m.Get("/registration-token", reqToken(), user.GetRegistrationToken) 1027 }) 1028 }) 1029 1030 m.Get("/followers", user.ListMyFollowers) 1031 m.Group("/following", func() { 1032 m.Get("", user.ListMyFollowing) 1033 m.Group("/{username}", func() { 1034 m.Get("", user.CheckMyFollowing) 1035 m.Put("", user.Follow) 1036 m.Delete("", user.Unfollow) 1037 }, context.UserAssignmentAPI()) 1038 }) 1039 1040 // (admin:public_key scope) 1041 m.Group("/keys", func() { 1042 m.Combo("").Get(user.ListMyPublicKeys). 1043 Post(bind(api.CreateKeyOption{}), user.CreatePublicKey) 1044 m.Combo("/{id}").Get(user.GetPublicKey). 1045 Delete(user.DeletePublicKey) 1046 }) 1047 1048 // (admin:application scope) 1049 m.Group("/applications", func() { 1050 m.Combo("/oauth2"). 1051 Get(user.ListOauth2Applications). 1052 Post(bind(api.CreateOAuth2ApplicationOptions{}), user.CreateOauth2Application) 1053 m.Combo("/oauth2/{id}"). 1054 Delete(user.DeleteOauth2Application). 1055 Patch(bind(api.CreateOAuth2ApplicationOptions{}), user.UpdateOauth2Application). 1056 Get(user.GetOauth2Application) 1057 }) 1058 1059 // (admin:gpg_key scope) 1060 m.Group("/gpg_keys", func() { 1061 m.Combo("").Get(user.ListMyGPGKeys). 1062 Post(bind(api.CreateGPGKeyOption{}), user.CreateGPGKey) 1063 m.Combo("/{id}").Get(user.GetGPGKey). 1064 Delete(user.DeleteGPGKey) 1065 }) 1066 m.Get("/gpg_key_token", user.GetVerificationToken) 1067 m.Post("/gpg_key_verify", bind(api.VerifyGPGKeyOption{}), user.VerifyUserGPGKey) 1068 1069 // (repo scope) 1070 m.Combo("/repos", tokenRequiresScopes(auth_model.AccessTokenScopeCategoryRepository)).Get(user.ListMyRepos). 1071 Post(bind(api.CreateRepoOption{}), repo.Create) 1072 1073 // (repo scope) 1074 m.Group("/starred", func() { 1075 m.Get("", user.GetMyStarredRepos) 1076 m.Group("/{username}/{reponame}", func() { 1077 m.Get("", user.IsStarring) 1078 m.Put("", user.Star) 1079 m.Delete("", user.Unstar) 1080 }, repoAssignment(), checkTokenPublicOnly()) 1081 }, tokenRequiresScopes(auth_model.AccessTokenScopeCategoryRepository)) 1082 m.Get("/times", repo.ListMyTrackedTimes) 1083 m.Get("/stopwatches", repo.GetStopwatches) 1084 m.Get("/subscriptions", user.GetMyWatchedRepos) 1085 m.Get("/teams", org.ListUserTeams) 1086 m.Group("/hooks", func() { 1087 m.Combo("").Get(user.ListHooks). 1088 Post(bind(api.CreateHookOption{}), user.CreateHook) 1089 m.Combo("/{id}").Get(user.GetHook). 1090 Patch(bind(api.EditHookOption{}), user.EditHook). 1091 Delete(user.DeleteHook) 1092 }, reqWebhooksEnabled()) 1093 1094 m.Group("/avatar", func() { 1095 m.Post("", bind(api.UpdateUserAvatarOption{}), user.UpdateAvatar) 1096 m.Delete("", user.DeleteAvatar) 1097 }) 1098 1099 m.Group("/blocks", func() { 1100 m.Get("", user.ListBlocks) 1101 m.Group("/{username}", func() { 1102 m.Get("", user.CheckUserBlock) 1103 m.Put("", user.BlockUser) 1104 m.Delete("", user.UnblockUser) 1105 }, context.UserAssignmentAPI(), checkTokenPublicOnly()) 1106 }) 1107 }, tokenRequiresScopes(auth_model.AccessTokenScopeCategoryUser), reqToken()) 1108 1109 // Repositories (requires repo scope, org scope) 1110 m.Post("/org/{org}/repos", 1111 // FIXME: we need org in context 1112 tokenRequiresScopes(auth_model.AccessTokenScopeCategoryOrganization, auth_model.AccessTokenScopeCategoryRepository), 1113 reqToken(), 1114 bind(api.CreateRepoOption{}), 1115 repo.CreateOrgRepoDeprecated) 1116 1117 // requires repo scope 1118 // FIXME: Don't expose repository id outside of the system 1119 m.Combo("/repositories/{id}", reqToken(), tokenRequiresScopes(auth_model.AccessTokenScopeCategoryRepository)).Get(repo.GetByID) 1120 1121 // Repos (requires repo scope) 1122 m.Group("/repos", func() { 1123 m.Get("/search", repo.Search) 1124 1125 // (repo scope) 1126 m.Post("/migrate", reqToken(), bind(api.MigrateRepoOptions{}), repo.Migrate) 1127 1128 m.Group("/{username}/{reponame}", func() { 1129 m.Get("/compare/*", reqRepoReader(unit.TypeCode), repo.CompareDiff) 1130 1131 m.Combo("").Get(reqAnyRepoReader(), repo.Get). 1132 Delete(reqToken(), reqOwner(), repo.Delete). 1133 Patch(reqToken(), reqAdmin(), bind(api.EditRepoOption{}), repo.Edit) 1134 m.Post("/generate", reqToken(), reqRepoReader(unit.TypeCode), bind(api.GenerateRepoOption{}), repo.Generate) 1135 m.Group("/transfer", func() { 1136 m.Post("", reqOwner(), bind(api.TransferRepoOption{}), repo.Transfer) 1137 m.Post("/accept", repo.AcceptTransfer) 1138 m.Post("/reject", repo.RejectTransfer) 1139 }, reqToken()) 1140 addActionsRoutes( 1141 m, 1142 reqOwner(), 1143 repo.NewAction(), 1144 ) 1145 m.Group("/hooks/git", func() { 1146 m.Combo("").Get(repo.ListGitHooks) 1147 m.Group("/{id}", func() { 1148 m.Combo("").Get(repo.GetGitHook). 1149 Patch(bind(api.EditGitHookOption{}), repo.EditGitHook). 1150 Delete(repo.DeleteGitHook) 1151 }) 1152 }, reqToken(), reqAdmin(), reqGitHook(), context.ReferencesGitRepo(true)) 1153 m.Group("/hooks", func() { 1154 m.Combo("").Get(repo.ListHooks). 1155 Post(bind(api.CreateHookOption{}), repo.CreateHook) 1156 m.Group("/{id}", func() { 1157 m.Combo("").Get(repo.GetHook). 1158 Patch(bind(api.EditHookOption{}), repo.EditHook). 1159 Delete(repo.DeleteHook) 1160 m.Post("/tests", context.ReferencesGitRepo(), context.RepoRefForAPI, repo.TestHook) 1161 }) 1162 }, reqToken(), reqAdmin(), reqWebhooksEnabled()) 1163 m.Group("/collaborators", func() { 1164 m.Get("", reqAnyRepoReader(), repo.ListCollaborators) 1165 m.Group("/{collaborator}", func() { 1166 m.Combo("").Get(reqAnyRepoReader(), repo.IsCollaborator). 1167 Put(reqAdmin(), bind(api.AddCollaboratorOption{}), repo.AddCollaborator). 1168 Delete(reqAdmin(), repo.DeleteCollaborator) 1169 m.Get("/permission", repo.GetRepoPermissions) 1170 }) 1171 }, reqToken()) 1172 m.Get("/assignees", reqToken(), reqAnyRepoReader(), repo.GetAssignees) 1173 m.Get("/reviewers", reqToken(), reqAnyRepoReader(), repo.GetReviewers) 1174 m.Group("/teams", func() { 1175 m.Get("", reqAnyRepoReader(), repo.ListTeams) 1176 m.Combo("/{team}").Get(reqAnyRepoReader(), repo.IsTeam). 1177 Put(reqAdmin(), repo.AddTeam). 1178 Delete(reqAdmin(), repo.DeleteTeam) 1179 }, reqToken()) 1180 m.Get("/raw/*", context.ReferencesGitRepo(), context.RepoRefForAPI, reqRepoReader(unit.TypeCode), repo.GetRawFile) 1181 m.Get("/media/*", context.ReferencesGitRepo(), context.RepoRefForAPI, reqRepoReader(unit.TypeCode), repo.GetRawFileOrLFS) 1182 m.Get("/archive/*", reqRepoReader(unit.TypeCode), repo.GetArchive) 1183 m.Combo("/forks").Get(repo.ListForks). 1184 Post(reqToken(), reqRepoReader(unit.TypeCode), bind(api.CreateForkOption{}), repo.CreateFork) 1185 m.Group("/branches", func() { 1186 m.Get("", repo.ListBranches) 1187 m.Get("/*", repo.GetBranch) 1188 m.Delete("/*", reqToken(), reqRepoWriter(unit.TypeCode), mustNotBeArchived, repo.DeleteBranch) 1189 m.Post("", reqToken(), reqRepoWriter(unit.TypeCode), mustNotBeArchived, bind(api.CreateBranchRepoOption{}), repo.CreateBranch) 1190 }, context.ReferencesGitRepo(), reqRepoReader(unit.TypeCode)) 1191 m.Group("/branch_protections", func() { 1192 m.Get("", repo.ListBranchProtections) 1193 m.Post("", bind(api.CreateBranchProtectionOption{}), mustNotBeArchived, repo.CreateBranchProtection) 1194 m.Group("/{name}", func() { 1195 m.Get("", repo.GetBranchProtection) 1196 m.Patch("", bind(api.EditBranchProtectionOption{}), mustNotBeArchived, repo.EditBranchProtection) 1197 m.Delete("", repo.DeleteBranchProtection) 1198 }) 1199 }, reqToken(), reqAdmin()) 1200 m.Group("/tags", func() { 1201 m.Get("", repo.ListTags) 1202 m.Get("/*", repo.GetTag) 1203 m.Post("", reqToken(), reqRepoWriter(unit.TypeCode), mustNotBeArchived, bind(api.CreateTagOption{}), repo.CreateTag) 1204 m.Delete("/*", reqToken(), reqRepoWriter(unit.TypeCode), mustNotBeArchived, repo.DeleteTag) 1205 }, reqRepoReader(unit.TypeCode), context.ReferencesGitRepo(true)) 1206 m.Group("/keys", func() { 1207 m.Combo("").Get(repo.ListDeployKeys). 1208 Post(bind(api.CreateKeyOption{}), repo.CreateDeployKey) 1209 m.Combo("/{id}").Get(repo.GetDeployKey). 1210 Delete(repo.DeleteDeploykey) 1211 }, reqToken(), reqAdmin()) 1212 m.Group("/times", func() { 1213 m.Combo("").Get(repo.ListTrackedTimesByRepository) 1214 m.Combo("/{timetrackingusername}").Get(repo.ListTrackedTimesByUser) 1215 }, mustEnableIssues, reqToken()) 1216 m.Group("/wiki", func() { 1217 m.Combo("/page/{pageName}"). 1218 Get(repo.GetWikiPage). 1219 Patch(mustNotBeArchived, reqToken(), reqRepoWriter(unit.TypeWiki), bind(api.CreateWikiPageOptions{}), repo.EditWikiPage). 1220 Delete(mustNotBeArchived, reqToken(), reqRepoWriter(unit.TypeWiki), repo.DeleteWikiPage) 1221 m.Get("/revisions/{pageName}", repo.ListPageRevisions) 1222 m.Post("/new", reqToken(), mustNotBeArchived, reqRepoWriter(unit.TypeWiki), bind(api.CreateWikiPageOptions{}), repo.NewWikiPage) 1223 m.Get("/pages", repo.ListWikiPages) 1224 }, mustEnableWiki) 1225 m.Post("/markup", reqToken(), bind(api.MarkupOption{}), misc.Markup) 1226 m.Post("/markdown", reqToken(), bind(api.MarkdownOption{}), misc.Markdown) 1227 m.Post("/markdown/raw", reqToken(), misc.MarkdownRaw) 1228 m.Get("/stargazers", repo.ListStargazers) 1229 m.Get("/subscribers", repo.ListSubscribers) 1230 m.Group("/subscription", func() { 1231 m.Get("", user.IsWatching) 1232 m.Put("", user.Watch) 1233 m.Delete("", user.Unwatch) 1234 }, reqToken()) 1235 m.Group("/releases", func() { 1236 m.Combo("").Get(repo.ListReleases). 1237 Post(reqToken(), reqRepoWriter(unit.TypeReleases), context.ReferencesGitRepo(), bind(api.CreateReleaseOption{}), repo.CreateRelease) 1238 m.Combo("/latest").Get(repo.GetLatestRelease) 1239 m.Group("/{id}", func() { 1240 m.Combo("").Get(repo.GetRelease). 1241 Patch(reqToken(), reqRepoWriter(unit.TypeReleases), context.ReferencesGitRepo(), bind(api.EditReleaseOption{}), repo.EditRelease). 1242 Delete(reqToken(), reqRepoWriter(unit.TypeReleases), repo.DeleteRelease) 1243 m.Group("/assets", func() { 1244 m.Combo("").Get(repo.ListReleaseAttachments). 1245 Post(reqToken(), reqRepoWriter(unit.TypeReleases), repo.CreateReleaseAttachment) 1246 m.Combo("/{attachment_id}").Get(repo.GetReleaseAttachment). 1247 Patch(reqToken(), reqRepoWriter(unit.TypeReleases), bind(api.EditAttachmentOptions{}), repo.EditReleaseAttachment). 1248 Delete(reqToken(), reqRepoWriter(unit.TypeReleases), repo.DeleteReleaseAttachment) 1249 }) 1250 }) 1251 m.Group("/tags", func() { 1252 m.Combo("/{tag}"). 1253 Get(repo.GetReleaseByTag). 1254 Delete(reqToken(), reqRepoWriter(unit.TypeReleases), repo.DeleteReleaseByTag) 1255 }) 1256 }, reqRepoReader(unit.TypeReleases)) 1257 m.Post("/mirror-sync", reqToken(), reqRepoWriter(unit.TypeCode), mustNotBeArchived, repo.MirrorSync) 1258 m.Post("/push_mirrors-sync", reqAdmin(), reqToken(), mustNotBeArchived, repo.PushMirrorSync) 1259 m.Group("/push_mirrors", func() { 1260 m.Combo("").Get(repo.ListPushMirrors). 1261 Post(mustNotBeArchived, bind(api.CreatePushMirrorOption{}), repo.AddPushMirror) 1262 m.Combo("/{name}"). 1263 Delete(mustNotBeArchived, repo.DeletePushMirrorByRemoteName). 1264 Get(repo.GetPushMirrorByName) 1265 }, reqAdmin(), reqToken()) 1266 1267 m.Get("/editorconfig/{filename}", context.ReferencesGitRepo(), context.RepoRefForAPI, reqRepoReader(unit.TypeCode), repo.GetEditorconfig) 1268 m.Group("/pulls", func() { 1269 m.Combo("").Get(repo.ListPullRequests). 1270 Post(reqToken(), mustNotBeArchived, bind(api.CreatePullRequestOption{}), repo.CreatePullRequest) 1271 m.Get("/pinned", repo.ListPinnedPullRequests) 1272 m.Group("/{index}", func() { 1273 m.Combo("").Get(repo.GetPullRequest). 1274 Patch(reqToken(), bind(api.EditPullRequestOption{}), repo.EditPullRequest) 1275 m.Get(".{diffType:diff|patch}", repo.DownloadPullDiffOrPatch) 1276 m.Post("/update", reqToken(), repo.UpdatePullRequest) 1277 m.Get("/commits", repo.GetPullRequestCommits) 1278 m.Get("/files", repo.GetPullRequestFiles) 1279 m.Combo("/merge").Get(repo.IsPullRequestMerged). 1280 Post(reqToken(), mustNotBeArchived, bind(forms.MergePullRequestForm{}), repo.MergePullRequest). 1281 Delete(reqToken(), mustNotBeArchived, repo.CancelScheduledAutoMerge) 1282 m.Group("/reviews", func() { 1283 m.Combo(""). 1284 Get(repo.ListPullReviews). 1285 Post(reqToken(), bind(api.CreatePullReviewOptions{}), repo.CreatePullReview) 1286 m.Group("/{id}", func() { 1287 m.Combo(""). 1288 Get(repo.GetPullReview). 1289 Delete(reqToken(), repo.DeletePullReview). 1290 Post(reqToken(), bind(api.SubmitPullReviewOptions{}), repo.SubmitPullReview) 1291 m.Combo("/comments"). 1292 Get(repo.GetPullReviewComments) 1293 m.Post("/dismissals", reqToken(), bind(api.DismissPullReviewOptions{}), repo.DismissPullReview) 1294 m.Post("/undismissals", reqToken(), repo.UnDismissPullReview) 1295 }) 1296 }) 1297 m.Combo("/requested_reviewers", reqToken()). 1298 Delete(bind(api.PullReviewRequestOptions{}), repo.DeleteReviewRequests). 1299 Post(bind(api.PullReviewRequestOptions{}), repo.CreateReviewRequests) 1300 }) 1301 m.Get("/{base}/*", repo.GetPullRequestByBaseHead) 1302 }, mustAllowPulls, reqRepoReader(unit.TypeCode), context.ReferencesGitRepo()) 1303 m.Group("/statuses", func() { 1304 m.Combo("/{sha}").Get(repo.GetCommitStatuses). 1305 Post(reqToken(), reqRepoWriter(unit.TypeCode), bind(api.CreateStatusOption{}), repo.NewCommitStatus) 1306 }, reqRepoReader(unit.TypeCode)) 1307 m.Group("/commits", func() { 1308 m.Get("", context.ReferencesGitRepo(), repo.GetAllCommits) 1309 m.Group("/{ref}", func() { 1310 m.Get("/status", repo.GetCombinedCommitStatusByRef) 1311 m.Get("/statuses", repo.GetCommitStatusesByRef) 1312 }, context.ReferencesGitRepo()) 1313 m.Group("/{sha}", func() { 1314 m.Get("/pull", repo.GetCommitPullRequest) 1315 }, context.ReferencesGitRepo()) 1316 }, reqRepoReader(unit.TypeCode)) 1317 m.Group("/git", func() { 1318 m.Group("/commits", func() { 1319 m.Get("/{sha}", repo.GetSingleCommit) 1320 m.Get("/{sha}.{diffType:diff|patch}", repo.DownloadCommitDiffOrPatch) 1321 }) 1322 m.Get("/refs", repo.GetGitAllRefs) 1323 m.Get("/refs/*", repo.GetGitRefs) 1324 m.Get("/trees/{sha}", repo.GetTree) 1325 m.Get("/blobs/{sha}", repo.GetBlob) 1326 m.Get("/tags/{sha}", repo.GetAnnotatedTag) 1327 m.Get("/notes/{sha}", repo.GetNote) 1328 }, context.ReferencesGitRepo(true), reqRepoReader(unit.TypeCode)) 1329 m.Post("/diffpatch", reqRepoWriter(unit.TypeCode), reqToken(), bind(api.ApplyDiffPatchFileOptions{}), mustNotBeArchived, repo.ApplyDiffPatch) 1330 m.Group("/contents", func() { 1331 m.Get("", repo.GetContentsList) 1332 m.Post("", reqToken(), bind(api.ChangeFilesOptions{}), reqRepoBranchWriter, mustNotBeArchived, repo.ChangeFiles) 1333 m.Get("/*", repo.GetContents) 1334 m.Group("/*", func() { 1335 m.Post("", bind(api.CreateFileOptions{}), reqRepoBranchWriter, mustNotBeArchived, repo.CreateFile) 1336 m.Put("", bind(api.UpdateFileOptions{}), reqRepoBranchWriter, mustNotBeArchived, repo.UpdateFile) 1337 m.Delete("", bind(api.DeleteFileOptions{}), reqRepoBranchWriter, mustNotBeArchived, repo.DeleteFile) 1338 }, reqToken()) 1339 }, reqRepoReader(unit.TypeCode)) 1340 m.Get("/signing-key.gpg", misc.SigningKey) 1341 m.Group("/topics", func() { 1342 m.Combo("").Get(repo.ListTopics). 1343 Put(reqToken(), reqAdmin(), bind(api.RepoTopicOptions{}), repo.UpdateTopics) 1344 m.Group("/{topic}", func() { 1345 m.Combo("").Put(reqToken(), repo.AddTopic). 1346 Delete(reqToken(), repo.DeleteTopic) 1347 }, reqAdmin()) 1348 }, reqAnyRepoReader()) 1349 m.Get("/issue_templates", context.ReferencesGitRepo(), repo.GetIssueTemplates) 1350 m.Get("/issue_config", context.ReferencesGitRepo(), repo.GetIssueConfig) 1351 m.Get("/issue_config/validate", context.ReferencesGitRepo(), repo.ValidateIssueConfig) 1352 m.Get("/languages", reqRepoReader(unit.TypeCode), repo.GetLanguages) 1353 m.Get("/activities/feeds", repo.ListRepoActivityFeeds) 1354 m.Get("/new_pin_allowed", repo.AreNewIssuePinsAllowed) 1355 m.Group("/avatar", func() { 1356 m.Post("", bind(api.UpdateRepoAvatarOption{}), repo.UpdateAvatar) 1357 m.Delete("", repo.DeleteAvatar) 1358 }, reqAdmin(), reqToken()) 1359 }, repoAssignment(), checkTokenPublicOnly()) 1360 }, tokenRequiresScopes(auth_model.AccessTokenScopeCategoryRepository)) 1361 1362 // Notifications (requires notifications scope) 1363 m.Group("/repos", func() { 1364 m.Group("/{username}/{reponame}", func() { 1365 m.Combo("/notifications", reqToken()). 1366 Get(notify.ListRepoNotifications). 1367 Put(notify.ReadRepoNotifications) 1368 }, repoAssignment(), checkTokenPublicOnly()) 1369 }, tokenRequiresScopes(auth_model.AccessTokenScopeCategoryNotification)) 1370 1371 // Issue (requires issue scope) 1372 m.Group("/repos", func() { 1373 m.Get("/issues/search", repo.SearchIssues) 1374 1375 m.Group("/{username}/{reponame}", func() { 1376 m.Group("/issues", func() { 1377 m.Combo("").Get(repo.ListIssues). 1378 Post(reqToken(), mustNotBeArchived, bind(api.CreateIssueOption{}), reqRepoReader(unit.TypeIssues), repo.CreateIssue) 1379 m.Get("/pinned", reqRepoReader(unit.TypeIssues), repo.ListPinnedIssues) 1380 m.Group("/comments", func() { 1381 m.Get("", repo.ListRepoIssueComments) 1382 m.Group("/{id}", func() { 1383 m.Combo(""). 1384 Get(repo.GetIssueComment). 1385 Patch(mustNotBeArchived, reqToken(), bind(api.EditIssueCommentOption{}), repo.EditIssueComment). 1386 Delete(reqToken(), repo.DeleteIssueComment) 1387 m.Combo("/reactions"). 1388 Get(repo.GetIssueCommentReactions). 1389 Post(reqToken(), bind(api.EditReactionOption{}), repo.PostIssueCommentReaction). 1390 Delete(reqToken(), bind(api.EditReactionOption{}), repo.DeleteIssueCommentReaction) 1391 m.Group("/assets", func() { 1392 m.Combo(""). 1393 Get(repo.ListIssueCommentAttachments). 1394 Post(reqToken(), mustNotBeArchived, repo.CreateIssueCommentAttachment) 1395 m.Combo("/{attachment_id}"). 1396 Get(repo.GetIssueCommentAttachment). 1397 Patch(reqToken(), mustNotBeArchived, bind(api.EditAttachmentOptions{}), repo.EditIssueCommentAttachment). 1398 Delete(reqToken(), mustNotBeArchived, repo.DeleteIssueCommentAttachment) 1399 }, mustEnableAttachments) 1400 }) 1401 }) 1402 m.Group("/{index}", func() { 1403 m.Combo("").Get(repo.GetIssue). 1404 Patch(reqToken(), bind(api.EditIssueOption{}), repo.EditIssue). 1405 Delete(reqToken(), reqAdmin(), context.ReferencesGitRepo(), repo.DeleteIssue) 1406 m.Group("/comments", func() { 1407 m.Combo("").Get(repo.ListIssueComments). 1408 Post(reqToken(), mustNotBeArchived, bind(api.CreateIssueCommentOption{}), repo.CreateIssueComment) 1409 m.Combo("/{id}", reqToken()).Patch(bind(api.EditIssueCommentOption{}), repo.EditIssueCommentDeprecated). 1410 Delete(repo.DeleteIssueCommentDeprecated) 1411 }) 1412 m.Get("/timeline", repo.ListIssueCommentsAndTimeline) 1413 m.Group("/labels", func() { 1414 m.Combo("").Get(repo.ListIssueLabels). 1415 Post(reqToken(), bind(api.IssueLabelsOption{}), repo.AddIssueLabels). 1416 Put(reqToken(), bind(api.IssueLabelsOption{}), repo.ReplaceIssueLabels). 1417 Delete(reqToken(), repo.ClearIssueLabels) 1418 m.Delete("/{id}", reqToken(), repo.DeleteIssueLabel) 1419 }) 1420 m.Group("/times", func() { 1421 m.Combo(""). 1422 Get(repo.ListTrackedTimes). 1423 Post(bind(api.AddTimeOption{}), repo.AddTime). 1424 Delete(repo.ResetIssueTime) 1425 m.Delete("/{id}", repo.DeleteTime) 1426 }, reqToken()) 1427 m.Combo("/deadline").Post(reqToken(), bind(api.EditDeadlineOption{}), repo.UpdateIssueDeadline) 1428 m.Group("/stopwatch", func() { 1429 m.Post("/start", repo.StartIssueStopwatch) 1430 m.Post("/stop", repo.StopIssueStopwatch) 1431 m.Delete("/delete", repo.DeleteIssueStopwatch) 1432 }, reqToken()) 1433 m.Group("/subscriptions", func() { 1434 m.Get("", repo.GetIssueSubscribers) 1435 m.Get("/check", reqToken(), repo.CheckIssueSubscription) 1436 m.Put("/{user}", reqToken(), repo.AddIssueSubscription) 1437 m.Delete("/{user}", reqToken(), repo.DelIssueSubscription) 1438 }) 1439 m.Combo("/reactions"). 1440 Get(repo.GetIssueReactions). 1441 Post(reqToken(), bind(api.EditReactionOption{}), repo.PostIssueReaction). 1442 Delete(reqToken(), bind(api.EditReactionOption{}), repo.DeleteIssueReaction) 1443 m.Group("/assets", func() { 1444 m.Combo(""). 1445 Get(repo.ListIssueAttachments). 1446 Post(reqToken(), mustNotBeArchived, repo.CreateIssueAttachment) 1447 m.Combo("/{attachment_id}"). 1448 Get(repo.GetIssueAttachment). 1449 Patch(reqToken(), mustNotBeArchived, bind(api.EditAttachmentOptions{}), repo.EditIssueAttachment). 1450 Delete(reqToken(), mustNotBeArchived, repo.DeleteIssueAttachment) 1451 }, mustEnableAttachments) 1452 m.Combo("/dependencies"). 1453 Get(repo.GetIssueDependencies). 1454 Post(reqToken(), mustNotBeArchived, bind(api.IssueMeta{}), repo.CreateIssueDependency). 1455 Delete(reqToken(), mustNotBeArchived, bind(api.IssueMeta{}), repo.RemoveIssueDependency) 1456 m.Combo("/blocks"). 1457 Get(repo.GetIssueBlocks). 1458 Post(reqToken(), bind(api.IssueMeta{}), repo.CreateIssueBlocking). 1459 Delete(reqToken(), bind(api.IssueMeta{}), repo.RemoveIssueBlocking) 1460 m.Group("/pin", func() { 1461 m.Combo(""). 1462 Post(reqToken(), reqAdmin(), repo.PinIssue). 1463 Delete(reqToken(), reqAdmin(), repo.UnpinIssue) 1464 m.Patch("/{position}", reqToken(), reqAdmin(), repo.MoveIssuePin) 1465 }) 1466 }) 1467 }, mustEnableIssuesOrPulls) 1468 m.Group("/labels", func() { 1469 m.Combo("").Get(repo.ListLabels). 1470 Post(reqToken(), reqRepoWriter(unit.TypeIssues, unit.TypePullRequests), bind(api.CreateLabelOption{}), repo.CreateLabel) 1471 m.Combo("/{id}").Get(repo.GetLabel). 1472 Patch(reqToken(), reqRepoWriter(unit.TypeIssues, unit.TypePullRequests), bind(api.EditLabelOption{}), repo.EditLabel). 1473 Delete(reqToken(), reqRepoWriter(unit.TypeIssues, unit.TypePullRequests), repo.DeleteLabel) 1474 }) 1475 m.Group("/milestones", func() { 1476 m.Combo("").Get(repo.ListMilestones). 1477 Post(reqToken(), reqRepoWriter(unit.TypeIssues, unit.TypePullRequests), bind(api.CreateMilestoneOption{}), repo.CreateMilestone) 1478 m.Combo("/{id}").Get(repo.GetMilestone). 1479 Patch(reqToken(), reqRepoWriter(unit.TypeIssues, unit.TypePullRequests), bind(api.EditMilestoneOption{}), repo.EditMilestone). 1480 Delete(reqToken(), reqRepoWriter(unit.TypeIssues, unit.TypePullRequests), repo.DeleteMilestone) 1481 }) 1482 }, repoAssignment(), checkTokenPublicOnly()) 1483 }, tokenRequiresScopes(auth_model.AccessTokenScopeCategoryIssue)) 1484 1485 // NOTE: these are Gitea package management API - see packages.CommonRoutes and packages.DockerContainerRoutes for endpoints that implement package manager APIs 1486 m.Group("/packages/{username}", func() { 1487 m.Group("/{type}/{name}/{version}", func() { 1488 m.Get("", reqToken(), packages.GetPackage) 1489 m.Delete("", reqToken(), reqPackageAccess(perm.AccessModeWrite), packages.DeletePackage) 1490 m.Get("/files", reqToken(), packages.ListPackageFiles) 1491 }) 1492 m.Get("/", reqToken(), packages.ListPackages) 1493 }, tokenRequiresScopes(auth_model.AccessTokenScopeCategoryPackage), context.UserAssignmentAPI(), context.PackageAssignmentAPI(), reqPackageAccess(perm.AccessModeRead), checkTokenPublicOnly()) 1494 1495 // Organizations 1496 m.Get("/user/orgs", reqToken(), tokenRequiresScopes(auth_model.AccessTokenScopeCategoryUser, auth_model.AccessTokenScopeCategoryOrganization), org.ListMyOrgs) 1497 m.Group("/users/{username}/orgs", func() { 1498 m.Get("", reqToken(), org.ListUserOrgs) 1499 m.Get("/{org}/permissions", reqToken(), org.GetUserOrgsPermissions) 1500 }, tokenRequiresScopes(auth_model.AccessTokenScopeCategoryUser, auth_model.AccessTokenScopeCategoryOrganization), context.UserAssignmentAPI(), checkTokenPublicOnly()) 1501 m.Post("/orgs", tokenRequiresScopes(auth_model.AccessTokenScopeCategoryOrganization), reqToken(), bind(api.CreateOrgOption{}), org.Create) 1502 m.Get("/orgs", org.GetAll, tokenRequiresScopes(auth_model.AccessTokenScopeCategoryOrganization)) 1503 m.Group("/orgs/{org}", func() { 1504 m.Combo("").Get(org.Get). 1505 Patch(reqToken(), reqOrgOwnership(), bind(api.EditOrgOption{}), org.Edit). 1506 Delete(reqToken(), reqOrgOwnership(), org.Delete) 1507 m.Combo("/repos").Get(user.ListOrgRepos). 1508 Post(reqToken(), bind(api.CreateRepoOption{}), repo.CreateOrgRepo) 1509 m.Group("/members", func() { 1510 m.Get("", reqToken(), org.ListMembers) 1511 m.Combo("/{username}").Get(reqToken(), org.IsMember). 1512 Delete(reqToken(), reqOrgOwnership(), org.DeleteMember) 1513 }) 1514 addActionsRoutes( 1515 m, 1516 reqOrgOwnership(), 1517 org.NewAction(), 1518 ) 1519 m.Group("/public_members", func() { 1520 m.Get("", org.ListPublicMembers) 1521 m.Combo("/{username}").Get(org.IsPublicMember). 1522 Put(reqToken(), reqOrgMembership(), org.PublicizeMember). 1523 Delete(reqToken(), reqOrgMembership(), org.ConcealMember) 1524 }) 1525 m.Group("/teams", func() { 1526 m.Get("", org.ListTeams) 1527 m.Post("", reqOrgOwnership(), bind(api.CreateTeamOption{}), org.CreateTeam) 1528 m.Get("/search", org.SearchTeam) 1529 }, reqToken(), reqOrgMembership()) 1530 m.Group("/labels", func() { 1531 m.Get("", org.ListLabels) 1532 m.Post("", reqToken(), reqOrgOwnership(), bind(api.CreateLabelOption{}), org.CreateLabel) 1533 m.Combo("/{id}").Get(reqToken(), org.GetLabel). 1534 Patch(reqToken(), reqOrgOwnership(), bind(api.EditLabelOption{}), org.EditLabel). 1535 Delete(reqToken(), reqOrgOwnership(), org.DeleteLabel) 1536 }) 1537 m.Group("/hooks", func() { 1538 m.Combo("").Get(org.ListHooks). 1539 Post(bind(api.CreateHookOption{}), org.CreateHook) 1540 m.Combo("/{id}").Get(org.GetHook). 1541 Patch(bind(api.EditHookOption{}), org.EditHook). 1542 Delete(org.DeleteHook) 1543 }, reqToken(), reqOrgOwnership(), reqWebhooksEnabled()) 1544 m.Group("/avatar", func() { 1545 m.Post("", bind(api.UpdateUserAvatarOption{}), org.UpdateAvatar) 1546 m.Delete("", org.DeleteAvatar) 1547 }, reqToken(), reqOrgOwnership()) 1548 m.Get("/activities/feeds", org.ListOrgActivityFeeds) 1549 1550 m.Group("/blocks", func() { 1551 m.Get("", org.ListBlocks) 1552 m.Group("/{username}", func() { 1553 m.Get("", org.CheckUserBlock) 1554 m.Put("", org.BlockUser) 1555 m.Delete("", org.UnblockUser) 1556 }) 1557 }, reqToken(), reqOrgOwnership()) 1558 }, tokenRequiresScopes(auth_model.AccessTokenScopeCategoryOrganization), orgAssignment(true), checkTokenPublicOnly()) 1559 m.Group("/teams/{teamid}", func() { 1560 m.Combo("").Get(reqToken(), org.GetTeam). 1561 Patch(reqToken(), reqOrgOwnership(), bind(api.EditTeamOption{}), org.EditTeam). 1562 Delete(reqToken(), reqOrgOwnership(), org.DeleteTeam) 1563 m.Group("/members", func() { 1564 m.Get("", reqToken(), org.GetTeamMembers) 1565 m.Combo("/{username}"). 1566 Get(reqToken(), org.GetTeamMember). 1567 Put(reqToken(), reqOrgOwnership(), org.AddTeamMember). 1568 Delete(reqToken(), reqOrgOwnership(), org.RemoveTeamMember) 1569 }) 1570 m.Group("/repos", func() { 1571 m.Get("", reqToken(), org.GetTeamRepos) 1572 m.Combo("/{org}/{reponame}"). 1573 Put(reqToken(), org.AddTeamRepository). 1574 Delete(reqToken(), org.RemoveTeamRepository). 1575 Get(reqToken(), org.GetTeamRepo) 1576 }) 1577 m.Get("/activities/feeds", org.ListTeamActivityFeeds) 1578 }, tokenRequiresScopes(auth_model.AccessTokenScopeCategoryOrganization), orgAssignment(false, true), reqToken(), reqTeamMembership(), checkTokenPublicOnly()) 1579 1580 m.Group("/admin", func() { 1581 m.Group("/cron", func() { 1582 m.Get("", admin.ListCronTasks) 1583 m.Post("/{task}", admin.PostCronTask) 1584 }) 1585 m.Get("/orgs", admin.GetAllOrgs) 1586 m.Group("/users", func() { 1587 m.Get("", admin.SearchUsers) 1588 m.Post("", bind(api.CreateUserOption{}), admin.CreateUser) 1589 m.Group("/{username}", func() { 1590 m.Combo("").Patch(bind(api.EditUserOption{}), admin.EditUser). 1591 Delete(admin.DeleteUser) 1592 m.Group("/keys", func() { 1593 m.Post("", bind(api.CreateKeyOption{}), admin.CreatePublicKey) 1594 m.Delete("/{id}", admin.DeleteUserPublicKey) 1595 }) 1596 m.Get("/orgs", org.ListUserOrgs) 1597 m.Post("/orgs", bind(api.CreateOrgOption{}), admin.CreateOrg) 1598 m.Post("/repos", bind(api.CreateRepoOption{}), admin.CreateRepo) 1599 m.Post("/rename", bind(api.RenameUserOption{}), admin.RenameUser) 1600 m.Get("/badges", admin.ListUserBadges) 1601 m.Post("/badges", bind(api.UserBadgeOption{}), admin.AddUserBadges) 1602 m.Delete("/badges", bind(api.UserBadgeOption{}), admin.DeleteUserBadges) 1603 }, context.UserAssignmentAPI()) 1604 }) 1605 m.Group("/emails", func() { 1606 m.Get("", admin.GetAllEmails) 1607 m.Get("/search", admin.SearchEmail) 1608 }) 1609 m.Group("/unadopted", func() { 1610 m.Get("", admin.ListUnadoptedRepositories) 1611 m.Post("/{username}/{reponame}", admin.AdoptRepository) 1612 m.Delete("/{username}/{reponame}", admin.DeleteUnadoptedRepository) 1613 }) 1614 m.Group("/hooks", func() { 1615 m.Combo("").Get(admin.ListHooks). 1616 Post(bind(api.CreateHookOption{}), admin.CreateHook) 1617 m.Combo("/{id}").Get(admin.GetHook). 1618 Patch(bind(api.EditHookOption{}), admin.EditHook). 1619 Delete(admin.DeleteHook) 1620 }) 1621 m.Group("/runners", func() { 1622 m.Get("/registration-token", admin.GetRegistrationToken) 1623 }) 1624 }, tokenRequiresScopes(auth_model.AccessTokenScopeCategoryAdmin), reqToken(), reqSiteAdmin()) 1625 1626 m.Group("/topics", func() { 1627 m.Get("/search", repo.TopicSearch) 1628 }, tokenRequiresScopes(auth_model.AccessTokenScopeCategoryRepository)) 1629 }, sudo()) 1630 1631 return m 1632 } 1633 1634 func securityHeaders() func(http.Handler) http.Handler { 1635 return func(next http.Handler) http.Handler { 1636 return http.HandlerFunc(func(resp http.ResponseWriter, req *http.Request) { 1637 // CORB: https://www.chromium.org/Home/chromium-security/corb-for-developers 1638 // http://stackoverflow.com/a/3146618/244009 1639 resp.Header().Set("x-content-type-options", "nosniff") 1640 next.ServeHTTP(resp, req) 1641 }) 1642 } 1643 }