THIS IS A TEST INSTANCE ONLY! REPOSITORIES CAN BE DELETED AT ANY TIME!

Browse Source

Add sudo functionality to the API (#4809)

pull/1441/head
zeripath 7 months ago
parent
commit
d293a2b9d6

+ 4
- 0
docs/content/doc/advanced/api-usage.en-us.md View File

@@ -73,3 +73,7 @@ using BasicAuth, as follows:
73 73
 $ curl --request GET --url https://yourusername:yourpassword@gitea.your.host/api/v1/users/yourusername/tokens
74 74
 [{"name":"test","sha1":"..."},{"name":"dev","sha1":"..."}]
75 75
 ```
76
+
77
+## Sudo
78
+
79
+The API allows admin users to sudo API requests as another user. Simply add either a `sudo=` parameter or `Sudo:` request header with the username of the user to sudo.

+ 29
- 0
integrations/api_admin_test.go View File

@@ -9,6 +9,8 @@ import (
9 9
 	"net/http"
10 10
 	"testing"
11 11
 
12
+	"github.com/stretchr/testify/assert"
13
+
12 14
 	"code.gitea.io/gitea/models"
13 15
 	api "code.gitea.io/sdk/gitea"
14 16
 )
@@ -71,3 +73,30 @@ func TestAPIAdminDeleteUnauthorizedKey(t *testing.T) {
71 73
 		adminUsername, newPublicKey.ID)
72 74
 	session.MakeRequest(t, req, http.StatusForbidden)
73 75
 }
76
+
77
+func TestAPISudoUser(t *testing.T) {
78
+	prepareTestEnv(t)
79
+	adminUsername := "user1"
80
+	normalUsername := "user2"
81
+	session := loginUser(t, adminUsername)
82
+
83
+	urlStr := fmt.Sprintf("/api/v1/user?sudo=%s", normalUsername)
84
+	req := NewRequest(t, "GET", urlStr)
85
+	resp := session.MakeRequest(t, req, http.StatusOK)
86
+	var user api.User
87
+	DecodeJSON(t, resp, &user)
88
+
89
+	assert.Equal(t, normalUsername, user.UserName)
90
+}
91
+
92
+func TestAPISudoUserForbidden(t *testing.T) {
93
+	prepareTestEnv(t)
94
+	adminUsername := "user1"
95
+	normalUsername := "user2"
96
+
97
+	session := loginUser(t, normalUsername)
98
+
99
+	urlStr := fmt.Sprintf("/api/v1/user?sudo=%s", adminUsername)
100
+	req := NewRequest(t, "GET", urlStr)
101
+	session.MakeRequest(t, req, http.StatusForbidden)
102
+}

+ 44
- 1
routers/api/v1/api.go View File

@@ -24,6 +24,8 @@
24 24
 //     - Token :
25 25
 //     - AccessToken :
26 26
 //     - AuthorizationHeaderToken :
27
+//     - SudoParam :
28
+//     - SudoHeader :
27 29
 //
28 30
 //     SecurityDefinitions:
29 31
 //     BasicAuth:
@@ -40,6 +42,16 @@
40 42
 //          type: apiKey
41 43
 //          name: Authorization
42 44
 //          in: header
45
+//     SudoParam:
46
+//          type: apiKey
47
+//          name: sudo
48
+//          in: query
49
+//          description: Sudo API request as the user provided as the key. Admin privileges are required.
50
+//     SudoHeader:
51
+//          type: apiKey
52
+//          name: Sudo
53
+//          in: header
54
+//          description: Sudo API request as the user provided as the key. Admin privileges are required.
43 55
 //
44 56
 // swagger:meta
45 57
 package v1
@@ -50,6 +62,7 @@ import (
50 62
 	"code.gitea.io/gitea/models"
51 63
 	"code.gitea.io/gitea/modules/auth"
52 64
 	"code.gitea.io/gitea/modules/context"
65
+	"code.gitea.io/gitea/modules/log"
53 66
 	"code.gitea.io/gitea/modules/setting"
54 67
 	"code.gitea.io/gitea/routers/api/v1/admin"
55 68
 	"code.gitea.io/gitea/routers/api/v1/misc"
@@ -64,6 +77,36 @@ import (
64 77
 	"gopkg.in/macaron.v1"
65 78
 )
66 79
 
80
+func sudo() macaron.Handler {
81
+	return func(ctx *context.APIContext) {
82
+		sudo := ctx.Query("sudo")
83
+		if len(sudo) <= 0 {
84
+			sudo = ctx.Req.Header.Get("Sudo")
85
+		}
86
+
87
+		if len(sudo) > 0 {
88
+			if ctx.User.IsAdmin {
89
+				user, err := models.GetUserByName(sudo)
90
+				if err != nil {
91
+					if models.IsErrUserNotExist(err) {
92
+						ctx.Status(404)
93
+					} else {
94
+						ctx.Error(500, "GetUserByName", err)
95
+					}
96
+					return
97
+				}
98
+				log.Trace("Sudo from (%s) to: %s", ctx.User.Name, user.Name)
99
+				ctx.User = user
100
+			} else {
101
+				ctx.JSON(403, map[string]string{
102
+					"message": "Only administrators allowed to sudo.",
103
+				})
104
+				return
105
+			}
106
+		}
107
+	}
108
+}
109
+
67 110
 func repoAssignment() macaron.Handler {
68 111
 	return func(ctx *context.APIContext) {
69 112
 		userName := ctx.Params(":username")
@@ -589,5 +632,5 @@ func RegisterRoutes(m *macaron.Macaron) {
589 632
 		m.Group("/topics", func() {
590 633
 			m.Get("/search", repo.TopicSearch)
591 634
 		})
592
-	}, context.APIContexter())
635
+	}, context.APIContexter(), sudo())
593 636
 }

+ 18
- 0
templates/swagger/v1_json.tmpl View File

@@ -8008,6 +8008,18 @@
8008 8008
     "BasicAuth": {
8009 8009
       "type": "basic"
8010 8010
     },
8011
+    "SudoHeader": {
8012
+      "description": "Sudo API request as the user provided as the key. Admin privileges are required.",
8013
+      "type": "apiKey",
8014
+      "name": "Sudo",
8015
+      "in": "header"
8016
+    },
8017
+    "SudoParam": {
8018
+      "description": "Sudo API request as the user provided as the key. Admin privileges are required.",
8019
+      "type": "apiKey",
8020
+      "name": "sudo",
8021
+      "in": "query"
8022
+    },
8011 8023
     "Token": {
8012 8024
       "type": "apiKey",
8013 8025
       "name": "token",
@@ -8026,6 +8038,12 @@
8026 8038
     },
8027 8039
     {
8028 8040
       "AuthorizationHeaderToken": []
8041
+    },
8042
+    {
8043
+      "SudoParam": []
8044
+    },
8045
+    {
8046
+      "SudoHeader": []
8029 8047
     }
8030 8048
   ]
8031 8049
 }

Loading…
Cancel
Save