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