You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

api.go 60KB

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