summaryrefslogtreecommitdiffstats
path: root/routers/api/v1/user/app.go
diff options
context:
space:
mode:
authorDaniel Baumann <daniel@debian.org>2024-10-18 20:33:49 +0200
committerDaniel Baumann <daniel@debian.org>2024-12-12 23:57:56 +0100
commite68b9d00a6e05b3a941f63ffb696f91e554ac5ec (patch)
tree97775d6c13b0f416af55314eb6a89ef792474615 /routers/api/v1/user/app.go
parentInitial commit. (diff)
downloadforgejo-e68b9d00a6e05b3a941f63ffb696f91e554ac5ec.tar.xz
forgejo-e68b9d00a6e05b3a941f63ffb696f91e554ac5ec.zip
Adding upstream version 9.0.3.
Signed-off-by: Daniel Baumann <daniel@debian.org>
Diffstat (limited to 'routers/api/v1/user/app.go')
-rw-r--r--routers/api/v1/user/app.go434
1 files changed, 434 insertions, 0 deletions
diff --git a/routers/api/v1/user/app.go b/routers/api/v1/user/app.go
new file mode 100644
index 0000000..c4fb2ea
--- /dev/null
+++ b/routers/api/v1/user/app.go
@@ -0,0 +1,434 @@
+// Copyright 2014 The Gogs Authors. All rights reserved.
+// Copyright 2018 The Gitea Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+package user
+
+import (
+ "errors"
+ "fmt"
+ "net/http"
+ "strconv"
+ "strings"
+
+ auth_model "code.gitea.io/gitea/models/auth"
+ "code.gitea.io/gitea/models/db"
+ api "code.gitea.io/gitea/modules/structs"
+ "code.gitea.io/gitea/modules/web"
+ "code.gitea.io/gitea/routers/api/v1/utils"
+ "code.gitea.io/gitea/services/context"
+ "code.gitea.io/gitea/services/convert"
+)
+
+// ListAccessTokens list all the access tokens
+func ListAccessTokens(ctx *context.APIContext) {
+ // swagger:operation GET /users/{username}/tokens user userGetTokens
+ // ---
+ // summary: List the authenticated user's access tokens
+ // produces:
+ // - application/json
+ // parameters:
+ // - name: username
+ // in: path
+ // description: username of user
+ // type: string
+ // required: true
+ // - name: page
+ // in: query
+ // description: page number of results to return (1-based)
+ // type: integer
+ // - name: limit
+ // in: query
+ // description: page size of results
+ // type: integer
+ // responses:
+ // "200":
+ // "$ref": "#/responses/AccessTokenList"
+ // "403":
+ // "$ref": "#/responses/forbidden"
+ // "404":
+ // "$ref": "#/responses/notFound"
+
+ opts := auth_model.ListAccessTokensOptions{UserID: ctx.ContextUser.ID, ListOptions: utils.GetListOptions(ctx)}
+
+ tokens, count, err := db.FindAndCount[auth_model.AccessToken](ctx, opts)
+ if err != nil {
+ ctx.InternalServerError(err)
+ return
+ }
+
+ apiTokens := make([]*api.AccessToken, len(tokens))
+ for i := range tokens {
+ apiTokens[i] = &api.AccessToken{
+ ID: tokens[i].ID,
+ Name: tokens[i].Name,
+ TokenLastEight: tokens[i].TokenLastEight,
+ Scopes: tokens[i].Scope.StringSlice(),
+ }
+ }
+
+ ctx.SetTotalCountHeader(count)
+ ctx.JSON(http.StatusOK, &apiTokens)
+}
+
+// CreateAccessToken create access tokens
+func CreateAccessToken(ctx *context.APIContext) {
+ // swagger:operation POST /users/{username}/tokens user userCreateToken
+ // ---
+ // summary: Create an access token
+ // consumes:
+ // - application/json
+ // produces:
+ // - application/json
+ // parameters:
+ // - name: username
+ // in: path
+ // description: username of user
+ // required: true
+ // type: string
+ // - name: body
+ // in: body
+ // schema:
+ // "$ref": "#/definitions/CreateAccessTokenOption"
+ // responses:
+ // "201":
+ // "$ref": "#/responses/AccessToken"
+ // "400":
+ // "$ref": "#/responses/error"
+ // "403":
+ // "$ref": "#/responses/forbidden"
+ // "404":
+ // "$ref": "#/responses/notFound"
+
+ form := web.GetForm(ctx).(*api.CreateAccessTokenOption)
+
+ t := &auth_model.AccessToken{
+ UID: ctx.ContextUser.ID,
+ Name: form.Name,
+ }
+
+ exist, err := auth_model.AccessTokenByNameExists(ctx, t)
+ if err != nil {
+ ctx.InternalServerError(err)
+ return
+ }
+ if exist {
+ ctx.Error(http.StatusBadRequest, "AccessTokenByNameExists", errors.New("access token name has been used already"))
+ return
+ }
+
+ 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
+ }
+ if scope == "" {
+ ctx.Error(http.StatusBadRequest, "AccessTokenScope", "access token must have a scope")
+ return
+ }
+ t.Scope = scope
+
+ if err := auth_model.NewAccessToken(ctx, t); err != nil {
+ ctx.Error(http.StatusInternalServerError, "NewAccessToken", err)
+ return
+ }
+ ctx.JSON(http.StatusCreated, &api.AccessToken{
+ Name: t.Name,
+ Token: t.Token,
+ ID: t.ID,
+ TokenLastEight: t.TokenLastEight,
+ Scopes: t.Scope.StringSlice(),
+ })
+}
+
+// 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
+ // description: token to be deleted, identified by ID and if not available by name
+ // type: string
+ // required: true
+ // responses:
+ // "204":
+ // "$ref": "#/responses/empty"
+ // "403":
+ // "$ref": "#/responses/forbidden"
+ // "404":
+ // "$ref": "#/responses/notFound"
+ // "422":
+ // "$ref": "#/responses/error"
+
+ token := ctx.Params(":id")
+ tokenID, _ := strconv.ParseInt(token, 0, 64)
+
+ if tokenID == 0 {
+ tokens, err := db.Find[auth_model.AccessToken](ctx, auth_model.ListAccessTokensOptions{
+ Name: token,
+ UserID: ctx.ContextUser.ID,
+ })
+ 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:
+ ctx.Error(http.StatusUnprocessableEntity, "DeleteAccessTokenByID", fmt.Errorf("multiple matches for token name '%s'", token))
+ return
+ }
+ }
+ if tokenID == 0 {
+ ctx.Error(http.StatusInternalServerError, "Invalid TokenID", nil)
+ return
+ }
+
+ if err := auth_model.DeleteAccessTokenByID(ctx, tokenID, ctx.ContextUser.ID); err != nil {
+ if auth_model.IsErrAccessTokenNotExist(err) {
+ ctx.NotFound()
+ } else {
+ ctx.Error(http.StatusInternalServerError, "DeleteAccessTokenByID", err)
+ }
+ return
+ }
+
+ ctx.Status(http.StatusNoContent)
+}
+
+// CreateOauth2Application is the handler to create a new OAuth2 Application for the authenticated user
+func CreateOauth2Application(ctx *context.APIContext) {
+ // 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"
+ // "400":
+ // "$ref": "#/responses/error"
+ // "401":
+ // "$ref": "#/responses/unauthorized"
+ // "403":
+ // "$ref": "#/responses/forbidden"
+
+ data := web.GetForm(ctx).(*api.CreateOAuth2ApplicationOptions)
+
+ app, err := auth_model.CreateOAuth2Application(ctx, auth_model.CreateOAuth2ApplicationOptions{
+ Name: data.Name,
+ UserID: ctx.Doer.ID,
+ RedirectURIs: data.RedirectURIs,
+ ConfidentialClient: data.ConfidentialClient,
+ })
+ if err != nil {
+ ctx.Error(http.StatusBadRequest, "", "error creating oauth2 application")
+ return
+ }
+ secret, err := app.GenerateClientSecret(ctx)
+ 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 userGetOAuth2Applications
+ // ---
+ // 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
+ // description: page size of results
+ // type: integer
+ // responses:
+ // "200":
+ // "$ref": "#/responses/OAuth2ApplicationList"
+ // "401":
+ // "$ref": "#/responses/unauthorized"
+ // "403":
+ // "$ref": "#/responses/forbidden"
+
+ apps, total, err := db.FindAndCount[auth_model.OAuth2Application](ctx, auth_model.FindOAuth2ApplicationsOptions{
+ ListOptions: utils.GetListOptions(ctx),
+ OwnerID: ctx.Doer.ID,
+ })
+ 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
+ }
+
+ ctx.SetTotalCountHeader(total)
+ 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"
+ // "401":
+ // "$ref": "#/responses/unauthorized"
+ // "403":
+ // "$ref": "#/responses/forbidden"
+ // "404":
+ // "$ref": "#/responses/notFound"
+ appID := ctx.ParamsInt64(":id")
+ if err := auth_model.DeleteOAuth2Application(ctx, appID, ctx.Doer.ID); err != nil {
+ if auth_model.IsErrOAuthApplicationNotFound(err) {
+ ctx.NotFound()
+ } else {
+ ctx.Error(http.StatusInternalServerError, "DeleteOauth2ApplicationByID", err)
+ }
+ return
+ }
+
+ ctx.Status(http.StatusNoContent)
+}
+
+// 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"
+ // "401":
+ // "$ref": "#/responses/unauthorized"
+ // "403":
+ // "$ref": "#/responses/forbidden"
+ // "404":
+ // "$ref": "#/responses/notFound"
+ appID := ctx.ParamsInt64(":id")
+ app, err := auth_model.GetOAuth2ApplicationByID(ctx, appID)
+ if err != nil {
+ if auth_model.IsErrOauthClientIDInvalid(err) || auth_model.IsErrOAuthApplicationNotFound(err) {
+ ctx.NotFound()
+ } else {
+ ctx.Error(http.StatusInternalServerError, "GetOauth2ApplicationByID", err)
+ }
+ return
+ }
+ if app.UID != ctx.Doer.ID {
+ ctx.NotFound()
+ return
+ }
+
+ app.ClientSecret = ""
+
+ ctx.JSON(http.StatusOK, convert.ToOAuth2Application(app))
+}
+
+// UpdateOauth2Application update OAuth2 Application
+func UpdateOauth2Application(ctx *context.APIContext) {
+ // 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"
+ // "401":
+ // "$ref": "#/responses/unauthorized"
+ // "403":
+ // "$ref": "#/responses/forbidden"
+ // "404":
+ // "$ref": "#/responses/notFound"
+ appID := ctx.ParamsInt64(":id")
+
+ data := web.GetForm(ctx).(*api.CreateOAuth2ApplicationOptions)
+
+ app, err := auth_model.UpdateOAuth2Application(ctx, auth_model.UpdateOAuth2ApplicationOptions{
+ Name: data.Name,
+ UserID: ctx.Doer.ID,
+ ID: appID,
+ RedirectURIs: data.RedirectURIs,
+ ConfidentialClient: data.ConfidentialClient,
+ })
+ if err != nil {
+ if auth_model.IsErrOauthClientIDInvalid(err) || auth_model.IsErrOAuthApplicationNotFound(err) {
+ ctx.NotFound()
+ } else {
+ ctx.Error(http.StatusInternalServerError, "UpdateOauth2ApplicationByID", err)
+ }
+ return
+ }
+ app.ClientSecret, err = app.GenerateClientSecret(ctx)
+ if err != nil {
+ ctx.Error(http.StatusBadRequest, "", "error updating application secret")
+ return
+ }
+
+ ctx.JSON(http.StatusOK, convert.ToOAuth2Application(app))
+}