2014-11-18 11:07:16 -05:00
// Copyright 2014 The Gogs Authors. All rights reserved.
2018-07-06 21:54:30 -04:00
// Copyright 2018 The Gitea Authors. All rights reserved.
2022-11-27 13:20:29 -05:00
// SPDX-License-Identifier: MIT
2014-11-18 11:07:16 -05:00
2015-12-04 17:16:42 -05:00
package user
2014-11-18 11:07:16 -05:00
import (
2020-04-13 21:02:48 +02:00
"errors"
2020-08-28 10:09:33 +02:00
"fmt"
2019-12-20 18:07:12 +01:00
"net/http"
2020-08-28 10:09:33 +02:00
"strconv"
2023-02-20 21:28:44 +00:00
"strings"
2019-12-20 18:07:12 +01:00
2022-08-25 10:31:57 +08:00
auth_model "code.gitea.io/gitea/models/auth"
2016-11-10 17:24:48 +01:00
"code.gitea.io/gitea/modules/context"
2019-08-23 09:40:30 -07:00
api "code.gitea.io/gitea/modules/structs"
2021-01-26 23:36:53 +08:00
"code.gitea.io/gitea/modules/web"
2020-01-24 19:00:29 +00:00
"code.gitea.io/gitea/routers/api/v1/utils"
2022-12-29 03:57:15 +01:00
"code.gitea.io/gitea/services/convert"
2014-11-18 11:07:16 -05:00
)
2016-11-24 15:04:31 +08:00
// ListAccessTokens list all the access tokens
2016-03-13 18:49:16 -04:00
func ListAccessTokens ( ctx * context . APIContext ) {
2017-11-12 23:02:25 -08:00
// swagger:operation GET /users/{username}/tokens user userGetTokens
// ---
// summary: List the authenticated user's access tokens
// produces:
// - application/json
2018-06-12 16:59:22 +02:00
// parameters:
// - name: username
// in: path
// description: username of user
// type: string
// required: true
2020-01-24 19:00:29 +00:00
// - name: page
// in: query
// description: page number of results to return (1-based)
// type: integer
// - name: limit
// in: query
2020-06-09 06:57:38 +02:00
// description: page size of results
2020-01-24 19:00:29 +00:00
// type: integer
2017-11-12 23:02:25 -08:00
// responses:
// "200":
// "$ref": "#/responses/AccessTokenList"
2019-12-20 18:07:12 +01:00
2022-08-25 10:31:57 +08:00
opts := auth_model . ListAccessTokensOptions { UserID : ctx . Doer . ID , ListOptions : utils . GetListOptions ( ctx ) }
2021-08-12 14:43:08 +02:00
2022-08-25 10:31:57 +08:00
count , err := auth_model . CountAccessTokens ( opts )
2021-08-12 14:43:08 +02:00
if err != nil {
ctx . InternalServerError ( err )
return
}
2022-08-25 10:31:57 +08:00
tokens , err := auth_model . ListAccessTokens ( opts )
2014-11-18 11:07:16 -05:00
if err != nil {
2021-08-12 14:43:08 +02:00
ctx . InternalServerError ( err )
2014-11-18 11:07:16 -05:00
return
}
apiTokens := make ( [ ] * api . AccessToken , len ( tokens ) )
for i := range tokens {
2017-02-26 00:25:35 -05:00
apiTokens [ i ] = & api . AccessToken {
2019-05-04 11:45:34 -04:00
ID : tokens [ i ] . ID ,
Name : tokens [ i ] . Name ,
TokenLastEight : tokens [ i ] . TokenLastEight ,
2023-02-20 21:28:44 +00:00
Scopes : tokens [ i ] . Scope . StringSlice ( ) ,
2017-02-26 00:25:35 -05:00
}
2014-11-18 11:07:16 -05:00
}
2021-08-12 14:43:08 +02:00
ctx . SetTotalCountHeader ( count )
2019-12-20 18:07:12 +01:00
ctx . JSON ( http . StatusOK , & apiTokens )
2014-11-18 11:07:16 -05:00
}
2016-11-24 15:04:31 +08:00
// CreateAccessToken create access tokens
2021-01-26 23:36:53 +08:00
func CreateAccessToken ( ctx * context . APIContext ) {
2017-11-12 23:02:25 -08:00
// swagger:operation POST /users/{username}/tokens user userCreateToken
// ---
// summary: Create an access token
// consumes:
// - application/json
// produces:
// - application/json
2018-06-12 16:59:22 +02:00
// parameters:
// - name: username
// in: path
// description: username of user
// required: true
2023-02-20 21:28:44 +00:00
// type: string
// - name: body
2018-10-21 04:40:42 +01:00
// in: body
// schema:
2021-08-01 21:44:15 +01:00
// "$ref": "#/definitions/CreateAccessTokenOption"
2017-11-12 23:02:25 -08:00
// responses:
2020-12-24 18:14:01 +00:00
// "201":
2017-11-12 23:02:25 -08:00
// "$ref": "#/responses/AccessToken"
2021-04-11 16:53:23 +08:00
// "400":
// "$ref": "#/responses/error"
2019-12-20 18:07:12 +01:00
2021-01-26 23:36:53 +08:00
form := web . GetForm ( ctx ) . ( * api . CreateAccessTokenOption )
2022-08-25 10:31:57 +08:00
t := & auth_model . AccessToken {
2022-03-22 08:03:22 +01:00
UID : ctx . Doer . ID ,
2014-11-18 11:07:16 -05:00
Name : form . Name ,
}
2020-04-13 21:02:48 +02:00
2022-08-25 10:31:57 +08:00
exist , err := auth_model . AccessTokenByNameExists ( t )
2020-04-13 21:02:48 +02:00
if err != nil {
ctx . InternalServerError ( err )
return
}
if exist {
ctx . Error ( http . StatusBadRequest , "AccessTokenByNameExists" , errors . New ( "access token name has been used already" ) )
return
}
2023-02-20 21:28:44 +00:00
scope , err := auth_model . AccessTokenScope ( strings . Join ( form . Scopes , "," ) ) . Normalize ( )
if err != nil {
ctx . Error ( http . StatusBadRequest , "AccessTokenScope.Normalize" , fmt . Errorf ( "invalid access token scope provided: %w" , err ) )
return
}
t . Scope = scope
2022-08-25 10:31:57 +08:00
if err := auth_model . NewAccessToken ( t ) ; err != nil {
2019-12-20 18:07:12 +01:00
ctx . Error ( http . StatusInternalServerError , "NewAccessToken" , err )
2014-11-18 11:07:16 -05:00
return
}
2019-12-20 18:07:12 +01:00
ctx . JSON ( http . StatusCreated , & api . AccessToken {
2019-05-12 13:29:07 -04:00
Name : t . Name ,
Token : t . Token ,
ID : t . ID ,
TokenLastEight : t . TokenLastEight ,
2017-02-26 00:25:35 -05:00
} )
2014-11-18 11:07:16 -05:00
}
2018-07-06 21:54:30 -04:00
// DeleteAccessToken delete access tokens
func DeleteAccessToken ( ctx * context . APIContext ) {
// swagger:operation DELETE /users/{username}/tokens/{token} user userDeleteAccessToken
// ---
// summary: delete an access token
// produces:
// - application/json
// parameters:
// - name: username
// in: path
// description: username of user
// type: string
// required: true
// - name: token
// in: path
2020-08-28 10:09:33 +02:00
// description: token to be deleted, identified by ID and if not available by name
// type: string
2018-07-06 21:54:30 -04:00
// required: true
// responses:
// "204":
// "$ref": "#/responses/empty"
2021-04-11 16:53:23 +08:00
// "404":
// "$ref": "#/responses/notFound"
2020-08-28 10:09:33 +02:00
// "422":
// "$ref": "#/responses/error"
token := ctx . Params ( ":id" )
tokenID , _ := strconv . ParseInt ( token , 0 , 64 )
if tokenID == 0 {
2022-08-25 10:31:57 +08:00
tokens , err := auth_model . ListAccessTokens ( auth_model . ListAccessTokensOptions {
2020-08-28 10:09:33 +02:00
Name : token ,
2022-03-22 08:03:22 +01:00
UserID : ctx . Doer . ID ,
2020-08-28 10:09:33 +02:00
} )
if err != nil {
ctx . Error ( http . StatusInternalServerError , "ListAccessTokens" , err )
return
}
switch len ( tokens ) {
case 0 :
ctx . NotFound ( )
return
case 1 :
tokenID = tokens [ 0 ] . ID
default :
2021-07-08 07:38:13 -04:00
ctx . Error ( http . StatusUnprocessableEntity , "DeleteAccessTokenByID" , fmt . Errorf ( "multiple matches for token name '%s'" , token ) )
2020-08-28 10:09:33 +02:00
return
}
}
if tokenID == 0 {
ctx . Error ( http . StatusInternalServerError , "Invalid TokenID" , nil )
return
}
2019-12-20 18:07:12 +01:00
2022-08-25 10:31:57 +08:00
if err := auth_model . DeleteAccessTokenByID ( tokenID , ctx . Doer . ID ) ; err != nil {
if auth_model . IsErrAccessTokenNotExist ( err ) {
2019-03-18 21:29:43 -05:00
ctx . NotFound ( )
2018-07-06 21:54:30 -04:00
} else {
2019-12-20 18:07:12 +01:00
ctx . Error ( http . StatusInternalServerError , "DeleteAccessTokenByID" , err )
2018-07-06 21:54:30 -04:00
}
return
}
2019-12-20 18:07:12 +01:00
ctx . Status ( http . StatusNoContent )
2018-07-06 21:54:30 -04:00
}
2020-02-29 07:19:32 +01:00
// CreateOauth2Application is the handler to create a new OAuth2 Application for the authenticated user
2021-01-26 23:36:53 +08:00
func CreateOauth2Application ( ctx * context . APIContext ) {
2020-02-29 07:19:32 +01:00
// swagger:operation POST /user/applications/oauth2 user userCreateOAuth2Application
// ---
// summary: creates a new OAuth2 application
// produces:
// - application/json
// parameters:
// - name: body
// in: body
// required: true
// schema:
// "$ref": "#/definitions/CreateOAuth2ApplicationOptions"
// responses:
// "201":
// "$ref": "#/responses/OAuth2Application"
2021-04-11 16:53:23 +08:00
// "400":
// "$ref": "#/responses/error"
2021-01-26 23:36:53 +08:00
data := web . GetForm ( ctx ) . ( * api . CreateOAuth2ApplicationOptions )
2022-08-25 10:31:57 +08:00
app , err := auth_model . CreateOAuth2Application ( ctx , auth_model . CreateOAuth2ApplicationOptions {
2022-10-24 09:59:24 +02:00
Name : data . Name ,
UserID : ctx . Doer . ID ,
RedirectURIs : data . RedirectURIs ,
ConfidentialClient : data . ConfidentialClient ,
2020-02-29 07:19:32 +01:00
} )
if err != nil {
ctx . Error ( http . StatusBadRequest , "" , "error creating oauth2 application" )
return
}
secret , err := app . GenerateClientSecret ( )
if err != nil {
ctx . Error ( http . StatusBadRequest , "" , "error creating application secret" )
return
}
app . ClientSecret = secret
ctx . JSON ( http . StatusCreated , convert . ToOAuth2Application ( app ) )
}
// ListOauth2Applications list all the Oauth2 application
func ListOauth2Applications ( ctx * context . APIContext ) {
// swagger:operation GET /user/applications/oauth2 user userGetOauth2Application
// ---
// summary: List the authenticated user's oauth2 applications
// produces:
// - application/json
// parameters:
// - name: page
// in: query
// description: page number of results to return (1-based)
// type: integer
// - name: limit
// in: query
2020-06-09 06:57:38 +02:00
// description: page size of results
2020-02-29 07:19:32 +01:00
// type: integer
// responses:
// "200":
// "$ref": "#/responses/OAuth2ApplicationList"
2022-08-25 10:31:57 +08:00
apps , total , err := auth_model . ListOAuth2Applications ( ctx . Doer . ID , utils . GetListOptions ( ctx ) )
2020-02-29 07:19:32 +01:00
if err != nil {
ctx . Error ( http . StatusInternalServerError , "ListOAuth2Applications" , err )
return
}
apiApps := make ( [ ] * api . OAuth2Application , len ( apps ) )
for i := range apps {
apiApps [ i ] = convert . ToOAuth2Application ( apps [ i ] )
apiApps [ i ] . ClientSecret = "" // Hide secret on application list
}
2021-08-12 14:43:08 +02:00
ctx . SetTotalCountHeader ( total )
2020-02-29 07:19:32 +01:00
ctx . JSON ( http . StatusOK , & apiApps )
}
// DeleteOauth2Application delete OAuth2 Application
func DeleteOauth2Application ( ctx * context . APIContext ) {
// swagger:operation DELETE /user/applications/oauth2/{id} user userDeleteOAuth2Application
// ---
// summary: delete an OAuth2 Application
// produces:
// - application/json
// parameters:
// - name: id
// in: path
// description: token to be deleted
// type: integer
// format: int64
// required: true
// responses:
// "204":
// "$ref": "#/responses/empty"
2021-04-11 16:53:23 +08:00
// "404":
// "$ref": "#/responses/notFound"
2020-02-29 07:19:32 +01:00
appID := ctx . ParamsInt64 ( ":id" )
2022-08-25 10:31:57 +08:00
if err := auth_model . DeleteOAuth2Application ( appID , ctx . Doer . ID ) ; err != nil {
if auth_model . IsErrOAuthApplicationNotFound ( err ) {
2021-04-11 04:49:10 +08:00
ctx . NotFound ( )
} else {
ctx . Error ( http . StatusInternalServerError , "DeleteOauth2ApplicationByID" , err )
}
2020-02-29 07:19:32 +01:00
return
}
ctx . Status ( http . StatusNoContent )
}
2020-04-09 20:37:31 -04:00
// GetOauth2Application get OAuth2 Application
func GetOauth2Application ( ctx * context . APIContext ) {
// swagger:operation GET /user/applications/oauth2/{id} user userGetOAuth2Application
// ---
// summary: get an OAuth2 Application
// produces:
// - application/json
// parameters:
// - name: id
// in: path
// description: Application ID to be found
// type: integer
// format: int64
// required: true
// responses:
// "200":
// "$ref": "#/responses/OAuth2Application"
2021-04-11 16:53:23 +08:00
// "404":
// "$ref": "#/responses/notFound"
2020-04-09 20:37:31 -04:00
appID := ctx . ParamsInt64 ( ":id" )
2022-08-25 10:31:57 +08:00
app , err := auth_model . GetOAuth2ApplicationByID ( ctx , appID )
2020-04-09 20:37:31 -04:00
if err != nil {
2022-08-25 10:31:57 +08:00
if auth_model . IsErrOauthClientIDInvalid ( err ) || auth_model . IsErrOAuthApplicationNotFound ( err ) {
2020-04-09 20:37:31 -04:00
ctx . NotFound ( )
} else {
ctx . Error ( http . StatusInternalServerError , "GetOauth2ApplicationByID" , err )
}
return
}
app . ClientSecret = ""
ctx . JSON ( http . StatusOK , convert . ToOAuth2Application ( app ) )
}
// UpdateOauth2Application update OAuth2 Application
2021-01-26 23:36:53 +08:00
func UpdateOauth2Application ( ctx * context . APIContext ) {
2020-04-09 20:37:31 -04:00
// swagger:operation PATCH /user/applications/oauth2/{id} user userUpdateOAuth2Application
// ---
// summary: update an OAuth2 Application, this includes regenerating the client secret
// produces:
// - application/json
// parameters:
// - name: id
// in: path
// description: application to be updated
// type: integer
// format: int64
// required: true
// - name: body
// in: body
// required: true
// schema:
// "$ref": "#/definitions/CreateOAuth2ApplicationOptions"
// responses:
// "200":
// "$ref": "#/responses/OAuth2Application"
2021-04-11 16:53:23 +08:00
// "404":
// "$ref": "#/responses/notFound"
2020-04-09 20:37:31 -04:00
appID := ctx . ParamsInt64 ( ":id" )
2021-01-26 23:36:53 +08:00
data := web . GetForm ( ctx ) . ( * api . CreateOAuth2ApplicationOptions )
2022-08-25 10:31:57 +08:00
app , err := auth_model . UpdateOAuth2Application ( auth_model . UpdateOAuth2ApplicationOptions {
2022-10-24 09:59:24 +02:00
Name : data . Name ,
UserID : ctx . Doer . ID ,
ID : appID ,
RedirectURIs : data . RedirectURIs ,
ConfidentialClient : data . ConfidentialClient ,
2020-04-09 20:37:31 -04:00
} )
if err != nil {
2022-08-25 10:31:57 +08:00
if auth_model . IsErrOauthClientIDInvalid ( err ) || auth_model . IsErrOAuthApplicationNotFound ( err ) {
2020-04-09 20:37:31 -04:00
ctx . NotFound ( )
} else {
ctx . Error ( http . StatusInternalServerError , "UpdateOauth2ApplicationByID" , err )
}
return
}
2020-04-30 19:50:47 +02:00
app . ClientSecret , err = app . GenerateClientSecret ( )
2020-04-09 20:37:31 -04:00
if err != nil {
ctx . Error ( http . StatusBadRequest , "" , "error updating application secret" )
return
}
ctx . JSON ( http . StatusOK , convert . ToOAuth2Application ( app ) )
}