From 79d74d208fe5cf9a5cdcb701cc48f115569a96d0 Mon Sep 17 00:00:00 2001
From: Bo-Yi Wu <appleboy.tw@gmail.com>
Date: Tue, 15 Aug 2023 20:32:43 +0800
Subject: [PATCH] Add API route to list org secrets (#26485)

- Add a new function `CountOrgSecrets` in the file
`models/secret/secret.go`
- Add a new file `modules/structs/secret.go`
- Add a new function `ListActionsSecrets` in the file
`routers/api/v1/api.go`
- Add a new file `routers/api/v1/org/action.go`
- Add a new function `listActionsSecrets` in the file
`routers/api/v1/org/action.go`

go-sdk: https://gitea.com/gitea/go-sdk/pulls/629

---------

Signed-off-by: Bo-Yi Wu <appleboy.tw@gmail.com>
Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
Co-authored-by: techknowlogick <matti@mdranta.net>
Co-authored-by: Giteabot <teabot@gitea.io>
---
 models/secret/secret.go          |  5 +++
 modules/structs/secret.go        | 15 +++++++
 routers/api/v1/api.go            |  3 ++
 routers/api/v1/org/action.go     | 72 ++++++++++++++++++++++++++++++++
 routers/api/v1/swagger/action.go | 13 ++++++
 templates/swagger/v1_json.tmpl   | 64 ++++++++++++++++++++++++++++
 6 files changed, 172 insertions(+)
 create mode 100644 modules/structs/secret.go
 create mode 100644 routers/api/v1/org/action.go
 create mode 100644 routers/api/v1/swagger/action.go

diff --git a/models/secret/secret.go b/models/secret/secret.go
index 5a17cc37a5..c9c95e82d3 100644
--- a/models/secret/secret.go
+++ b/models/secret/secret.go
@@ -88,3 +88,8 @@ func FindSecrets(ctx context.Context, opts FindSecretsOptions) ([]*Secret, error
 		Where(opts.toConds()).
 		Find(&secrets)
 }
+
+// CountSecrets counts the secrets
+func CountSecrets(ctx context.Context, opts *FindSecretsOptions) (int64, error) {
+	return db.GetEngine(ctx).Where(opts.toConds()).Count(new(Secret))
+}
diff --git a/modules/structs/secret.go b/modules/structs/secret.go
new file mode 100644
index 0000000000..6a41db4cd2
--- /dev/null
+++ b/modules/structs/secret.go
@@ -0,0 +1,15 @@
+// Copyright 2023 The Gitea Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+package structs
+
+import "time"
+
+// User represents a secret
+// swagger:model
+type Secret struct {
+	// the secret's name
+	Name string `json:"name"`
+	// swagger:strfmt date-time
+	Created time.Time `json:"created_at"`
+}
diff --git a/routers/api/v1/api.go b/routers/api/v1/api.go
index 397eb10558..ccae83a940 100644
--- a/routers/api/v1/api.go
+++ b/routers/api/v1/api.go
@@ -1298,6 +1298,9 @@ func Routes() *web.Route {
 				m.Combo("/{username}").Get(reqToken(), org.IsMember).
 					Delete(reqToken(), reqOrgOwnership(), org.DeleteMember)
 			})
+			m.Group("/actions/secrets", func() {
+				m.Get("", reqToken(), reqOrgOwnership(), org.ListActionsSecrets)
+			})
 			m.Group("/public_members", func() {
 				m.Get("", org.ListPublicMembers)
 				m.Combo("/{username}").Get(org.IsPublicMember).
diff --git a/routers/api/v1/org/action.go b/routers/api/v1/org/action.go
new file mode 100644
index 0000000000..9deda2209c
--- /dev/null
+++ b/routers/api/v1/org/action.go
@@ -0,0 +1,72 @@
+// Copyright 2023 The Gitea Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+package org
+
+import (
+	"net/http"
+
+	"code.gitea.io/gitea/models/secret"
+	"code.gitea.io/gitea/modules/context"
+	api "code.gitea.io/gitea/modules/structs"
+	"code.gitea.io/gitea/routers/api/v1/utils"
+)
+
+// ListActionsSecrets list an organization's actions secrets
+func ListActionsSecrets(ctx *context.APIContext) {
+	// swagger:operation GET /orgs/{org}/actions/secrets organization orgListActionsSecrets
+	// ---
+	// summary: List an organization's actions secrets
+	// produces:
+	// - application/json
+	// parameters:
+	// - name: org
+	//   in: path
+	//   description: name of the organization
+	//   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/SecretList"
+
+	listActionsSecrets(ctx)
+}
+
+// listActionsSecrets list an organization's actions secrets
+func listActionsSecrets(ctx *context.APIContext) {
+	opts := &secret.FindSecretsOptions{
+		OwnerID:     ctx.Org.Organization.ID,
+		ListOptions: utils.GetListOptions(ctx),
+	}
+
+	count, err := secret.CountSecrets(ctx, opts)
+	if err != nil {
+		ctx.InternalServerError(err)
+		return
+	}
+
+	secrets, err := secret.FindSecrets(ctx, *opts)
+	if err != nil {
+		ctx.InternalServerError(err)
+		return
+	}
+
+	apiSecrets := make([]*api.Secret, len(secrets))
+	for k, v := range secrets {
+		apiSecrets[k] = &api.Secret{
+			Name:    v.Name,
+			Created: v.CreatedUnix.AsTime(),
+		}
+	}
+
+	ctx.SetTotalCountHeader(count)
+	ctx.JSON(http.StatusOK, apiSecrets)
+}
diff --git a/routers/api/v1/swagger/action.go b/routers/api/v1/swagger/action.go
new file mode 100644
index 0000000000..493b167f72
--- /dev/null
+++ b/routers/api/v1/swagger/action.go
@@ -0,0 +1,13 @@
+// Copyright 2023 The Gitea Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+package swagger
+
+import api "code.gitea.io/gitea/modules/structs"
+
+// SecretList
+// swagger:response SecretList
+type swaggerResponseSecretList struct {
+	// in:body
+	Body []api.Secret `json:"body"`
+}
diff --git a/templates/swagger/v1_json.tmpl b/templates/swagger/v1_json.tmpl
index a5bea8a4cb..da43c530af 100644
--- a/templates/swagger/v1_json.tmpl
+++ b/templates/swagger/v1_json.tmpl
@@ -1550,6 +1550,44 @@
         }
       }
     },
+    "/orgs/{org}/actions/secrets": {
+      "get": {
+        "produces": [
+          "application/json"
+        ],
+        "tags": [
+          "organization"
+        ],
+        "summary": "List an organization's actions secrets",
+        "operationId": "orgListActionsSecrets",
+        "parameters": [
+          {
+            "type": "string",
+            "description": "name of the organization",
+            "name": "org",
+            "in": "path",
+            "required": true
+          },
+          {
+            "type": "integer",
+            "description": "page number of results to return (1-based)",
+            "name": "page",
+            "in": "query"
+          },
+          {
+            "type": "integer",
+            "description": "page size of results",
+            "name": "limit",
+            "in": "query"
+          }
+        ],
+        "responses": {
+          "200": {
+            "$ref": "#/responses/SecretList"
+          }
+        }
+      }
+    },
     "/orgs/{org}/activities/feeds": {
       "get": {
         "produces": [
@@ -21291,6 +21329,23 @@
       },
       "x-go-package": "code.gitea.io/gitea/modules/structs"
     },
+    "Secret": {
+      "description": "User represents a secret",
+      "type": "object",
+      "properties": {
+        "created_at": {
+          "type": "string",
+          "format": "date-time",
+          "x-go-name": "Created"
+        },
+        "name": {
+          "description": "the secret's name",
+          "type": "string",
+          "x-go-name": "Name"
+        }
+      },
+      "x-go-package": "code.gitea.io/gitea/modules/structs"
+    },
     "ServerVersion": {
       "description": "ServerVersion wraps the version of the server",
       "type": "object",
@@ -22862,6 +22917,15 @@
         "$ref": "#/definitions/SearchResults"
       }
     },
+    "SecretList": {
+      "description": "SecretList",
+      "schema": {
+        "type": "array",
+        "items": {
+          "$ref": "#/definitions/Secret"
+        }
+      }
+    },
     "ServerVersion": {
       "description": "ServerVersion",
       "schema": {