mirror of
				https://gitee.com/gitea/gitea
				synced 2025-11-04 16:40:24 +08:00 
			
		
		
		
	Add sudo functionality to the API (#4809)
This commit is contained in:
		@@ -73,3 +73,7 @@ using BasicAuth, as follows:
 | 
				
			|||||||
$ curl --request GET --url https://yourusername:yourpassword@gitea.your.host/api/v1/users/yourusername/tokens
 | 
					$ curl --request GET --url https://yourusername:yourpassword@gitea.your.host/api/v1/users/yourusername/tokens
 | 
				
			||||||
[{"name":"test","sha1":"..."},{"name":"dev","sha1":"..."}]
 | 
					[{"name":"test","sha1":"..."},{"name":"dev","sha1":"..."}]
 | 
				
			||||||
```
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## Sudo
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					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.
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -9,6 +9,8 @@ import (
 | 
				
			|||||||
	"net/http"
 | 
						"net/http"
 | 
				
			||||||
	"testing"
 | 
						"testing"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"github.com/stretchr/testify/assert"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"code.gitea.io/gitea/models"
 | 
						"code.gitea.io/gitea/models"
 | 
				
			||||||
	api "code.gitea.io/sdk/gitea"
 | 
						api "code.gitea.io/sdk/gitea"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
@@ -71,3 +73,30 @@ func TestAPIAdminDeleteUnauthorizedKey(t *testing.T) {
 | 
				
			|||||||
		adminUsername, newPublicKey.ID)
 | 
							adminUsername, newPublicKey.ID)
 | 
				
			||||||
	session.MakeRequest(t, req, http.StatusForbidden)
 | 
						session.MakeRequest(t, req, http.StatusForbidden)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func TestAPISudoUser(t *testing.T) {
 | 
				
			||||||
 | 
						prepareTestEnv(t)
 | 
				
			||||||
 | 
						adminUsername := "user1"
 | 
				
			||||||
 | 
						normalUsername := "user2"
 | 
				
			||||||
 | 
						session := loginUser(t, adminUsername)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						urlStr := fmt.Sprintf("/api/v1/user?sudo=%s", normalUsername)
 | 
				
			||||||
 | 
						req := NewRequest(t, "GET", urlStr)
 | 
				
			||||||
 | 
						resp := session.MakeRequest(t, req, http.StatusOK)
 | 
				
			||||||
 | 
						var user api.User
 | 
				
			||||||
 | 
						DecodeJSON(t, resp, &user)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						assert.Equal(t, normalUsername, user.UserName)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func TestAPISudoUserForbidden(t *testing.T) {
 | 
				
			||||||
 | 
						prepareTestEnv(t)
 | 
				
			||||||
 | 
						adminUsername := "user1"
 | 
				
			||||||
 | 
						normalUsername := "user2"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						session := loginUser(t, normalUsername)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						urlStr := fmt.Sprintf("/api/v1/user?sudo=%s", adminUsername)
 | 
				
			||||||
 | 
						req := NewRequest(t, "GET", urlStr)
 | 
				
			||||||
 | 
						session.MakeRequest(t, req, http.StatusForbidden)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -24,6 +24,8 @@
 | 
				
			|||||||
//     - Token :
 | 
					//     - Token :
 | 
				
			||||||
//     - AccessToken :
 | 
					//     - AccessToken :
 | 
				
			||||||
//     - AuthorizationHeaderToken :
 | 
					//     - AuthorizationHeaderToken :
 | 
				
			||||||
 | 
					//     - SudoParam :
 | 
				
			||||||
 | 
					//     - SudoHeader :
 | 
				
			||||||
//
 | 
					//
 | 
				
			||||||
//     SecurityDefinitions:
 | 
					//     SecurityDefinitions:
 | 
				
			||||||
//     BasicAuth:
 | 
					//     BasicAuth:
 | 
				
			||||||
@@ -40,6 +42,16 @@
 | 
				
			|||||||
//          type: apiKey
 | 
					//          type: apiKey
 | 
				
			||||||
//          name: Authorization
 | 
					//          name: Authorization
 | 
				
			||||||
//          in: header
 | 
					//          in: header
 | 
				
			||||||
 | 
					//     SudoParam:
 | 
				
			||||||
 | 
					//          type: apiKey
 | 
				
			||||||
 | 
					//          name: sudo
 | 
				
			||||||
 | 
					//          in: query
 | 
				
			||||||
 | 
					//          description: Sudo API request as the user provided as the key. Admin privileges are required.
 | 
				
			||||||
 | 
					//     SudoHeader:
 | 
				
			||||||
 | 
					//          type: apiKey
 | 
				
			||||||
 | 
					//          name: Sudo
 | 
				
			||||||
 | 
					//          in: header
 | 
				
			||||||
 | 
					//          description: Sudo API request as the user provided as the key. Admin privileges are required.
 | 
				
			||||||
//
 | 
					//
 | 
				
			||||||
// swagger:meta
 | 
					// swagger:meta
 | 
				
			||||||
package v1
 | 
					package v1
 | 
				
			||||||
@@ -50,6 +62,7 @@ import (
 | 
				
			|||||||
	"code.gitea.io/gitea/models"
 | 
						"code.gitea.io/gitea/models"
 | 
				
			||||||
	"code.gitea.io/gitea/modules/auth"
 | 
						"code.gitea.io/gitea/modules/auth"
 | 
				
			||||||
	"code.gitea.io/gitea/modules/context"
 | 
						"code.gitea.io/gitea/modules/context"
 | 
				
			||||||
 | 
						"code.gitea.io/gitea/modules/log"
 | 
				
			||||||
	"code.gitea.io/gitea/modules/setting"
 | 
						"code.gitea.io/gitea/modules/setting"
 | 
				
			||||||
	"code.gitea.io/gitea/routers/api/v1/admin"
 | 
						"code.gitea.io/gitea/routers/api/v1/admin"
 | 
				
			||||||
	"code.gitea.io/gitea/routers/api/v1/misc"
 | 
						"code.gitea.io/gitea/routers/api/v1/misc"
 | 
				
			||||||
@@ -64,6 +77,36 @@ import (
 | 
				
			|||||||
	"gopkg.in/macaron.v1"
 | 
						"gopkg.in/macaron.v1"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func sudo() macaron.Handler {
 | 
				
			||||||
 | 
						return func(ctx *context.APIContext) {
 | 
				
			||||||
 | 
							sudo := ctx.Query("sudo")
 | 
				
			||||||
 | 
							if len(sudo) <= 0 {
 | 
				
			||||||
 | 
								sudo = ctx.Req.Header.Get("Sudo")
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if len(sudo) > 0 {
 | 
				
			||||||
 | 
								if ctx.User.IsAdmin {
 | 
				
			||||||
 | 
									user, err := models.GetUserByName(sudo)
 | 
				
			||||||
 | 
									if err != nil {
 | 
				
			||||||
 | 
										if models.IsErrUserNotExist(err) {
 | 
				
			||||||
 | 
											ctx.Status(404)
 | 
				
			||||||
 | 
										} else {
 | 
				
			||||||
 | 
											ctx.Error(500, "GetUserByName", err)
 | 
				
			||||||
 | 
										}
 | 
				
			||||||
 | 
										return
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
									log.Trace("Sudo from (%s) to: %s", ctx.User.Name, user.Name)
 | 
				
			||||||
 | 
									ctx.User = user
 | 
				
			||||||
 | 
								} else {
 | 
				
			||||||
 | 
									ctx.JSON(403, map[string]string{
 | 
				
			||||||
 | 
										"message": "Only administrators allowed to sudo.",
 | 
				
			||||||
 | 
									})
 | 
				
			||||||
 | 
									return
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func repoAssignment() macaron.Handler {
 | 
					func repoAssignment() macaron.Handler {
 | 
				
			||||||
	return func(ctx *context.APIContext) {
 | 
						return func(ctx *context.APIContext) {
 | 
				
			||||||
		userName := ctx.Params(":username")
 | 
							userName := ctx.Params(":username")
 | 
				
			||||||
@@ -589,5 +632,5 @@ func RegisterRoutes(m *macaron.Macaron) {
 | 
				
			|||||||
		m.Group("/topics", func() {
 | 
							m.Group("/topics", func() {
 | 
				
			||||||
			m.Get("/search", repo.TopicSearch)
 | 
								m.Get("/search", repo.TopicSearch)
 | 
				
			||||||
		})
 | 
							})
 | 
				
			||||||
	}, context.APIContexter())
 | 
						}, context.APIContexter(), sudo())
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -8008,6 +8008,18 @@
 | 
				
			|||||||
    "BasicAuth": {
 | 
					    "BasicAuth": {
 | 
				
			||||||
      "type": "basic"
 | 
					      "type": "basic"
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
 | 
					    "SudoHeader": {
 | 
				
			||||||
 | 
					      "description": "Sudo API request as the user provided as the key. Admin privileges are required.",
 | 
				
			||||||
 | 
					      "type": "apiKey",
 | 
				
			||||||
 | 
					      "name": "Sudo",
 | 
				
			||||||
 | 
					      "in": "header"
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    "SudoParam": {
 | 
				
			||||||
 | 
					      "description": "Sudo API request as the user provided as the key. Admin privileges are required.",
 | 
				
			||||||
 | 
					      "type": "apiKey",
 | 
				
			||||||
 | 
					      "name": "sudo",
 | 
				
			||||||
 | 
					      "in": "query"
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
    "Token": {
 | 
					    "Token": {
 | 
				
			||||||
      "type": "apiKey",
 | 
					      "type": "apiKey",
 | 
				
			||||||
      "name": "token",
 | 
					      "name": "token",
 | 
				
			||||||
@@ -8026,6 +8038,12 @@
 | 
				
			|||||||
    },
 | 
					    },
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
      "AuthorizationHeaderToken": []
 | 
					      "AuthorizationHeaderToken": []
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      "SudoParam": []
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      "SudoHeader": []
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  ]
 | 
					  ]
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
		Reference in New Issue
	
	Block a user