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.

app.go 11KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406
  1. // Copyright 2014 The Gogs Authors. All rights reserved.
  2. // Copyright 2018 The Gitea Authors. All rights reserved.
  3. // SPDX-License-Identifier: MIT
  4. package user
  5. import (
  6. "errors"
  7. "fmt"
  8. "net/http"
  9. "strconv"
  10. "strings"
  11. auth_model "code.gitea.io/gitea/models/auth"
  12. "code.gitea.io/gitea/modules/context"
  13. api "code.gitea.io/gitea/modules/structs"
  14. "code.gitea.io/gitea/modules/web"
  15. "code.gitea.io/gitea/routers/api/v1/utils"
  16. "code.gitea.io/gitea/services/convert"
  17. )
  18. // ListAccessTokens list all the access tokens
  19. func ListAccessTokens(ctx *context.APIContext) {
  20. // swagger:operation GET /users/{username}/tokens user userGetTokens
  21. // ---
  22. // summary: List the authenticated user's access tokens
  23. // produces:
  24. // - application/json
  25. // parameters:
  26. // - name: username
  27. // in: path
  28. // description: username of user
  29. // type: string
  30. // required: true
  31. // - name: page
  32. // in: query
  33. // description: page number of results to return (1-based)
  34. // type: integer
  35. // - name: limit
  36. // in: query
  37. // description: page size of results
  38. // type: integer
  39. // responses:
  40. // "200":
  41. // "$ref": "#/responses/AccessTokenList"
  42. // "403":
  43. // "$ref": "#/responses/forbidden"
  44. opts := auth_model.ListAccessTokensOptions{UserID: ctx.ContextUser.ID, ListOptions: utils.GetListOptions(ctx)}
  45. count, err := auth_model.CountAccessTokens(ctx, opts)
  46. if err != nil {
  47. ctx.InternalServerError(err)
  48. return
  49. }
  50. tokens, err := auth_model.ListAccessTokens(ctx, opts)
  51. if err != nil {
  52. ctx.InternalServerError(err)
  53. return
  54. }
  55. apiTokens := make([]*api.AccessToken, len(tokens))
  56. for i := range tokens {
  57. apiTokens[i] = &api.AccessToken{
  58. ID: tokens[i].ID,
  59. Name: tokens[i].Name,
  60. TokenLastEight: tokens[i].TokenLastEight,
  61. Scopes: tokens[i].Scope.StringSlice(),
  62. }
  63. }
  64. ctx.SetTotalCountHeader(count)
  65. ctx.JSON(http.StatusOK, &apiTokens)
  66. }
  67. // CreateAccessToken create access tokens
  68. func CreateAccessToken(ctx *context.APIContext) {
  69. // swagger:operation POST /users/{username}/tokens user userCreateToken
  70. // ---
  71. // summary: Create an access token
  72. // consumes:
  73. // - application/json
  74. // produces:
  75. // - application/json
  76. // parameters:
  77. // - name: username
  78. // in: path
  79. // description: username of user
  80. // required: true
  81. // type: string
  82. // - name: body
  83. // in: body
  84. // schema:
  85. // "$ref": "#/definitions/CreateAccessTokenOption"
  86. // responses:
  87. // "201":
  88. // "$ref": "#/responses/AccessToken"
  89. // "400":
  90. // "$ref": "#/responses/error"
  91. // "403":
  92. // "$ref": "#/responses/forbidden"
  93. form := web.GetForm(ctx).(*api.CreateAccessTokenOption)
  94. t := &auth_model.AccessToken{
  95. UID: ctx.ContextUser.ID,
  96. Name: form.Name,
  97. }
  98. exist, err := auth_model.AccessTokenByNameExists(ctx, t)
  99. if err != nil {
  100. ctx.InternalServerError(err)
  101. return
  102. }
  103. if exist {
  104. ctx.Error(http.StatusBadRequest, "AccessTokenByNameExists", errors.New("access token name has been used already"))
  105. return
  106. }
  107. scope, err := auth_model.AccessTokenScope(strings.Join(form.Scopes, ",")).Normalize()
  108. if err != nil {
  109. ctx.Error(http.StatusBadRequest, "AccessTokenScope.Normalize", fmt.Errorf("invalid access token scope provided: %w", err))
  110. return
  111. }
  112. t.Scope = scope
  113. if err := auth_model.NewAccessToken(ctx, t); err != nil {
  114. ctx.Error(http.StatusInternalServerError, "NewAccessToken", err)
  115. return
  116. }
  117. ctx.JSON(http.StatusCreated, &api.AccessToken{
  118. Name: t.Name,
  119. Token: t.Token,
  120. ID: t.ID,
  121. TokenLastEight: t.TokenLastEight,
  122. })
  123. }
  124. // DeleteAccessToken delete access tokens
  125. func DeleteAccessToken(ctx *context.APIContext) {
  126. // swagger:operation DELETE /users/{username}/tokens/{token} user userDeleteAccessToken
  127. // ---
  128. // summary: delete an access token
  129. // produces:
  130. // - application/json
  131. // parameters:
  132. // - name: username
  133. // in: path
  134. // description: username of user
  135. // type: string
  136. // required: true
  137. // - name: token
  138. // in: path
  139. // description: token to be deleted, identified by ID and if not available by name
  140. // type: string
  141. // required: true
  142. // responses:
  143. // "204":
  144. // "$ref": "#/responses/empty"
  145. // "403":
  146. // "$ref": "#/responses/forbidden"
  147. // "404":
  148. // "$ref": "#/responses/notFound"
  149. // "422":
  150. // "$ref": "#/responses/error"
  151. token := ctx.Params(":id")
  152. tokenID, _ := strconv.ParseInt(token, 0, 64)
  153. if tokenID == 0 {
  154. tokens, err := auth_model.ListAccessTokens(ctx, auth_model.ListAccessTokensOptions{
  155. Name: token,
  156. UserID: ctx.ContextUser.ID,
  157. })
  158. if err != nil {
  159. ctx.Error(http.StatusInternalServerError, "ListAccessTokens", err)
  160. return
  161. }
  162. switch len(tokens) {
  163. case 0:
  164. ctx.NotFound()
  165. return
  166. case 1:
  167. tokenID = tokens[0].ID
  168. default:
  169. ctx.Error(http.StatusUnprocessableEntity, "DeleteAccessTokenByID", fmt.Errorf("multiple matches for token name '%s'", token))
  170. return
  171. }
  172. }
  173. if tokenID == 0 {
  174. ctx.Error(http.StatusInternalServerError, "Invalid TokenID", nil)
  175. return
  176. }
  177. if err := auth_model.DeleteAccessTokenByID(ctx, tokenID, ctx.ContextUser.ID); err != nil {
  178. if auth_model.IsErrAccessTokenNotExist(err) {
  179. ctx.NotFound()
  180. } else {
  181. ctx.Error(http.StatusInternalServerError, "DeleteAccessTokenByID", err)
  182. }
  183. return
  184. }
  185. ctx.Status(http.StatusNoContent)
  186. }
  187. // CreateOauth2Application is the handler to create a new OAuth2 Application for the authenticated user
  188. func CreateOauth2Application(ctx *context.APIContext) {
  189. // swagger:operation POST /user/applications/oauth2 user userCreateOAuth2Application
  190. // ---
  191. // summary: creates a new OAuth2 application
  192. // produces:
  193. // - application/json
  194. // parameters:
  195. // - name: body
  196. // in: body
  197. // required: true
  198. // schema:
  199. // "$ref": "#/definitions/CreateOAuth2ApplicationOptions"
  200. // responses:
  201. // "201":
  202. // "$ref": "#/responses/OAuth2Application"
  203. // "400":
  204. // "$ref": "#/responses/error"
  205. data := web.GetForm(ctx).(*api.CreateOAuth2ApplicationOptions)
  206. app, err := auth_model.CreateOAuth2Application(ctx, auth_model.CreateOAuth2ApplicationOptions{
  207. Name: data.Name,
  208. UserID: ctx.Doer.ID,
  209. RedirectURIs: data.RedirectURIs,
  210. ConfidentialClient: data.ConfidentialClient,
  211. })
  212. if err != nil {
  213. ctx.Error(http.StatusBadRequest, "", "error creating oauth2 application")
  214. return
  215. }
  216. secret, err := app.GenerateClientSecret()
  217. if err != nil {
  218. ctx.Error(http.StatusBadRequest, "", "error creating application secret")
  219. return
  220. }
  221. app.ClientSecret = secret
  222. ctx.JSON(http.StatusCreated, convert.ToOAuth2Application(app))
  223. }
  224. // ListOauth2Applications list all the Oauth2 application
  225. func ListOauth2Applications(ctx *context.APIContext) {
  226. // swagger:operation GET /user/applications/oauth2 user userGetOauth2Application
  227. // ---
  228. // summary: List the authenticated user's oauth2 applications
  229. // produces:
  230. // - application/json
  231. // parameters:
  232. // - name: page
  233. // in: query
  234. // description: page number of results to return (1-based)
  235. // type: integer
  236. // - name: limit
  237. // in: query
  238. // description: page size of results
  239. // type: integer
  240. // responses:
  241. // "200":
  242. // "$ref": "#/responses/OAuth2ApplicationList"
  243. apps, total, err := auth_model.ListOAuth2Applications(ctx.Doer.ID, utils.GetListOptions(ctx))
  244. if err != nil {
  245. ctx.Error(http.StatusInternalServerError, "ListOAuth2Applications", err)
  246. return
  247. }
  248. apiApps := make([]*api.OAuth2Application, len(apps))
  249. for i := range apps {
  250. apiApps[i] = convert.ToOAuth2Application(apps[i])
  251. apiApps[i].ClientSecret = "" // Hide secret on application list
  252. }
  253. ctx.SetTotalCountHeader(total)
  254. ctx.JSON(http.StatusOK, &apiApps)
  255. }
  256. // DeleteOauth2Application delete OAuth2 Application
  257. func DeleteOauth2Application(ctx *context.APIContext) {
  258. // swagger:operation DELETE /user/applications/oauth2/{id} user userDeleteOAuth2Application
  259. // ---
  260. // summary: delete an OAuth2 Application
  261. // produces:
  262. // - application/json
  263. // parameters:
  264. // - name: id
  265. // in: path
  266. // description: token to be deleted
  267. // type: integer
  268. // format: int64
  269. // required: true
  270. // responses:
  271. // "204":
  272. // "$ref": "#/responses/empty"
  273. // "404":
  274. // "$ref": "#/responses/notFound"
  275. appID := ctx.ParamsInt64(":id")
  276. if err := auth_model.DeleteOAuth2Application(appID, ctx.Doer.ID); err != nil {
  277. if auth_model.IsErrOAuthApplicationNotFound(err) {
  278. ctx.NotFound()
  279. } else {
  280. ctx.Error(http.StatusInternalServerError, "DeleteOauth2ApplicationByID", err)
  281. }
  282. return
  283. }
  284. ctx.Status(http.StatusNoContent)
  285. }
  286. // GetOauth2Application get OAuth2 Application
  287. func GetOauth2Application(ctx *context.APIContext) {
  288. // swagger:operation GET /user/applications/oauth2/{id} user userGetOAuth2Application
  289. // ---
  290. // summary: get an OAuth2 Application
  291. // produces:
  292. // - application/json
  293. // parameters:
  294. // - name: id
  295. // in: path
  296. // description: Application ID to be found
  297. // type: integer
  298. // format: int64
  299. // required: true
  300. // responses:
  301. // "200":
  302. // "$ref": "#/responses/OAuth2Application"
  303. // "404":
  304. // "$ref": "#/responses/notFound"
  305. appID := ctx.ParamsInt64(":id")
  306. app, err := auth_model.GetOAuth2ApplicationByID(ctx, appID)
  307. if err != nil {
  308. if auth_model.IsErrOauthClientIDInvalid(err) || auth_model.IsErrOAuthApplicationNotFound(err) {
  309. ctx.NotFound()
  310. } else {
  311. ctx.Error(http.StatusInternalServerError, "GetOauth2ApplicationByID", err)
  312. }
  313. return
  314. }
  315. if app.UID != ctx.Doer.ID {
  316. ctx.NotFound()
  317. return
  318. }
  319. app.ClientSecret = ""
  320. ctx.JSON(http.StatusOK, convert.ToOAuth2Application(app))
  321. }
  322. // UpdateOauth2Application update OAuth2 Application
  323. func UpdateOauth2Application(ctx *context.APIContext) {
  324. // swagger:operation PATCH /user/applications/oauth2/{id} user userUpdateOAuth2Application
  325. // ---
  326. // summary: update an OAuth2 Application, this includes regenerating the client secret
  327. // produces:
  328. // - application/json
  329. // parameters:
  330. // - name: id
  331. // in: path
  332. // description: application to be updated
  333. // type: integer
  334. // format: int64
  335. // required: true
  336. // - name: body
  337. // in: body
  338. // required: true
  339. // schema:
  340. // "$ref": "#/definitions/CreateOAuth2ApplicationOptions"
  341. // responses:
  342. // "200":
  343. // "$ref": "#/responses/OAuth2Application"
  344. // "404":
  345. // "$ref": "#/responses/notFound"
  346. appID := ctx.ParamsInt64(":id")
  347. data := web.GetForm(ctx).(*api.CreateOAuth2ApplicationOptions)
  348. app, err := auth_model.UpdateOAuth2Application(auth_model.UpdateOAuth2ApplicationOptions{
  349. Name: data.Name,
  350. UserID: ctx.Doer.ID,
  351. ID: appID,
  352. RedirectURIs: data.RedirectURIs,
  353. ConfidentialClient: data.ConfidentialClient,
  354. })
  355. if err != nil {
  356. if auth_model.IsErrOauthClientIDInvalid(err) || auth_model.IsErrOAuthApplicationNotFound(err) {
  357. ctx.NotFound()
  358. } else {
  359. ctx.Error(http.StatusInternalServerError, "UpdateOauth2ApplicationByID", err)
  360. }
  361. return
  362. }
  363. app.ClientSecret, err = app.GenerateClientSecret()
  364. if err != nil {
  365. ctx.Error(http.StatusBadRequest, "", "error updating application secret")
  366. return
  367. }
  368. ctx.JSON(http.StatusOK, convert.ToOAuth2Application(app))
  369. }