Skip to content

Commit d293a2b

Browse files
zeripathtechknowlogick
authored andcommitted
Add sudo functionality to the API (#4809)
1 parent e6a0381 commit d293a2b

File tree

4 files changed

+95
-1
lines changed

4 files changed

+95
-1
lines changed

docs/content/doc/advanced/api-usage.en-us.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,3 +73,7 @@ using BasicAuth, as follows:
7373
$ curl --request GET --url https://yourusername:[email protected]/api/v1/users/yourusername/tokens
7474
[{"name":"test","sha1":"..."},{"name":"dev","sha1":"..."}]
7575
```
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.

integrations/api_admin_test.go

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ import (
99
"net/http"
1010
"testing"
1111

12+
"github.com/stretchr/testify/assert"
13+
1214
"code.gitea.io/gitea/models"
1315
api "code.gitea.io/sdk/gitea"
1416
)
@@ -71,3 +73,30 @@ func TestAPIAdminDeleteUnauthorizedKey(t *testing.T) {
7173
adminUsername, newPublicKey.ID)
7274
session.MakeRequest(t, req, http.StatusForbidden)
7375
}
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+
}

routers/api/v1/api.go

Lines changed: 44 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@
2424
// - Token :
2525
// - AccessToken :
2626
// - AuthorizationHeaderToken :
27+
// - SudoParam :
28+
// - SudoHeader :
2729
//
2830
// SecurityDefinitions:
2931
// BasicAuth:
@@ -40,6 +42,16 @@
4042
// type: apiKey
4143
// name: Authorization
4244
// 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.
4355
//
4456
// swagger:meta
4557
package v1
@@ -50,6 +62,7 @@ import (
5062
"code.gitea.io/gitea/models"
5163
"code.gitea.io/gitea/modules/auth"
5264
"code.gitea.io/gitea/modules/context"
65+
"code.gitea.io/gitea/modules/log"
5366
"code.gitea.io/gitea/modules/setting"
5467
"code.gitea.io/gitea/routers/api/v1/admin"
5568
"code.gitea.io/gitea/routers/api/v1/misc"
@@ -64,6 +77,36 @@ import (
6477
"gopkg.in/macaron.v1"
6578
)
6679

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+
67110
func repoAssignment() macaron.Handler {
68111
return func(ctx *context.APIContext) {
69112
userName := ctx.Params(":username")
@@ -589,5 +632,5 @@ func RegisterRoutes(m *macaron.Macaron) {
589632
m.Group("/topics", func() {
590633
m.Get("/search", repo.TopicSearch)
591634
})
592-
}, context.APIContexter())
635+
}, context.APIContexter(), sudo())
593636
}

templates/swagger/v1_json.tmpl

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8008,6 +8008,18 @@
80088008
"BasicAuth": {
80098009
"type": "basic"
80108010
},
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+
},
80118023
"Token": {
80128024
"type": "apiKey",
80138025
"name": "token",
@@ -8026,6 +8038,12 @@
80268038
},
80278039
{
80288040
"AuthorizationHeaderToken": []
8041+
},
8042+
{
8043+
"SudoParam": []
8044+
},
8045+
{
8046+
"SudoHeader": []
80298047
}
80308048
]
80318049
}

0 commit comments

Comments
 (0)