aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRomain <romdum@users.noreply.github.com>2021-10-12 12:47:19 +0200
committerGitHub <noreply@github.com>2021-10-12 12:47:19 +0200
commitd0a681fbc3fb626adcddbbb13f8c96c0bbd72c02 (patch)
treeed807f45d54993e20f63af81d9d964ddc2f258fe
parent7b8723158e2a50834617f47b07c29f5436fede6d (diff)
downloadgitea-d0a681fbc3fb626adcddbbb13f8c96c0bbd72c02.tar.gz
gitea-d0a681fbc3fb626adcddbbb13f8c96c0bbd72c02.zip
[API] Add endpount to get user org permissions (#17232)
* Add endpoint * Add swagger response + generate swagger * Stop execution if user / org is not found * Add tests Co-authored-by: 6543 <6543@obermui.de>
-rw-r--r--integrations/api_user_org_perm_test.go149
-rw-r--r--models/org.go13
-rw-r--r--modules/structs/org.go9
-rw-r--r--routers/api/v1/api.go5
-rw-r--r--routers/api/v1/org/org.go71
-rw-r--r--routers/api/v1/swagger/org.go7
-rw-r--r--templates/swagger/v1_json.tmpl72
7 files changed, 325 insertions, 1 deletions
diff --git a/integrations/api_user_org_perm_test.go b/integrations/api_user_org_perm_test.go
new file mode 100644
index 0000000000..abba24701e
--- /dev/null
+++ b/integrations/api_user_org_perm_test.go
@@ -0,0 +1,149 @@
+// Copyright 2021 The Gitea Authors. All rights reserved.
+// Use of this source code is governed by a MIT-style
+// license that can be found in the LICENSE file.
+
+package integrations
+
+import (
+ "fmt"
+ "net/http"
+ "testing"
+
+ api "code.gitea.io/gitea/modules/structs"
+ "github.com/stretchr/testify/assert"
+)
+
+type apiUserOrgPermTestCase struct {
+ LoginUser string
+ User string
+ Organization string
+ ExpectedOrganizationPermissions api.OrganizationPermissions
+}
+
+func TestTokenNeeded(t *testing.T) {
+ defer prepareTestEnv(t)()
+
+ session := emptyTestSession(t)
+ req := NewRequest(t, "GET", "/api/v1/users/user1/orgs/user6/permissions")
+ session.MakeRequest(t, req, http.StatusUnauthorized)
+}
+
+func sampleTest(t *testing.T, auoptc apiUserOrgPermTestCase) {
+ defer prepareTestEnv(t)()
+
+ session := loginUser(t, auoptc.LoginUser)
+ token := getTokenForLoggedInUser(t, session)
+
+ req := NewRequest(t, "GET", fmt.Sprintf("/api/v1/users/%s/orgs/%s/permissions?token=%s", auoptc.User, auoptc.Organization, token))
+ resp := session.MakeRequest(t, req, http.StatusOK)
+
+ var apiOP api.OrganizationPermissions
+ DecodeJSON(t, resp, &apiOP)
+ assert.Equal(t, auoptc.ExpectedOrganizationPermissions.IsOwner, apiOP.IsOwner)
+ assert.Equal(t, auoptc.ExpectedOrganizationPermissions.IsAdmin, apiOP.IsAdmin)
+ assert.Equal(t, auoptc.ExpectedOrganizationPermissions.CanWrite, apiOP.CanWrite)
+ assert.Equal(t, auoptc.ExpectedOrganizationPermissions.CanRead, apiOP.CanRead)
+ assert.Equal(t, auoptc.ExpectedOrganizationPermissions.CanCreateRepository, apiOP.CanCreateRepository)
+}
+
+func TestWithOwnerUser(t *testing.T) {
+ sampleTest(t, apiUserOrgPermTestCase{
+ LoginUser: "user2",
+ User: "user2",
+ Organization: "user3",
+ ExpectedOrganizationPermissions: api.OrganizationPermissions{
+ IsOwner: true,
+ IsAdmin: true,
+ CanWrite: true,
+ CanRead: true,
+ CanCreateRepository: true,
+ },
+ })
+}
+
+func TestCanWriteUser(t *testing.T) {
+ sampleTest(t, apiUserOrgPermTestCase{
+ LoginUser: "user4",
+ User: "user4",
+ Organization: "user3",
+ ExpectedOrganizationPermissions: api.OrganizationPermissions{
+ IsOwner: false,
+ IsAdmin: false,
+ CanWrite: true,
+ CanRead: true,
+ CanCreateRepository: false,
+ },
+ })
+}
+
+func TestAdminUser(t *testing.T) {
+ sampleTest(t, apiUserOrgPermTestCase{
+ LoginUser: "user1",
+ User: "user28",
+ Organization: "user3",
+ ExpectedOrganizationPermissions: api.OrganizationPermissions{
+ IsOwner: false,
+ IsAdmin: true,
+ CanWrite: true,
+ CanRead: true,
+ CanCreateRepository: true,
+ },
+ })
+}
+
+func TestAdminCanNotCreateRepo(t *testing.T) {
+ sampleTest(t, apiUserOrgPermTestCase{
+ LoginUser: "user1",
+ User: "user28",
+ Organization: "user6",
+ ExpectedOrganizationPermissions: api.OrganizationPermissions{
+ IsOwner: false,
+ IsAdmin: true,
+ CanWrite: true,
+ CanRead: true,
+ CanCreateRepository: false,
+ },
+ })
+}
+
+func TestCanReadUser(t *testing.T) {
+ sampleTest(t, apiUserOrgPermTestCase{
+ LoginUser: "user1",
+ User: "user24",
+ Organization: "org25",
+ ExpectedOrganizationPermissions: api.OrganizationPermissions{
+ IsOwner: false,
+ IsAdmin: false,
+ CanWrite: false,
+ CanRead: true,
+ CanCreateRepository: false,
+ },
+ })
+}
+
+func TestUnknowUser(t *testing.T) {
+ defer prepareTestEnv(t)()
+
+ session := loginUser(t, "user1")
+ token := getTokenForLoggedInUser(t, session)
+
+ req := NewRequest(t, "GET", fmt.Sprintf("/api/v1/users/unknow/orgs/org25/permissions?token=%s", token))
+ resp := session.MakeRequest(t, req, http.StatusNotFound)
+
+ var apiError api.APIError
+ DecodeJSON(t, resp, &apiError)
+ assert.Equal(t, "GetUserByName", apiError.Message)
+}
+
+func TestUnknowOrganization(t *testing.T) {
+ defer prepareTestEnv(t)()
+
+ session := loginUser(t, "user1")
+ token := getTokenForLoggedInUser(t, session)
+
+ req := NewRequest(t, "GET", fmt.Sprintf("/api/v1/users/user1/orgs/unknow/permissions?token=%s", token))
+ resp := session.MakeRequest(t, req, http.StatusNotFound)
+ var apiError api.APIError
+ DecodeJSON(t, resp, &apiError)
+ assert.Equal(t, "GetUserByName", apiError.Message)
+}
diff --git a/models/org.go b/models/org.go
index eadd1e157c..8cba485a89 100644
--- a/models/org.go
+++ b/models/org.go
@@ -392,6 +392,19 @@ func CanCreateOrgRepo(orgID, uid int64) (bool, error) {
Exist(new(Team))
}
+// GetOrgUserMaxAuthorizeLevel returns highest authorize level of user in an organization
+func (org *User) GetOrgUserMaxAuthorizeLevel(uid int64) (AccessMode, error) {
+ var authorize AccessMode
+ _, err := db.GetEngine(db.DefaultContext).
+ Select("max(team.authorize)").
+ Table("team").
+ Join("INNER", "team_user", "team_user.team_id = team.id").
+ Where("team_user.uid = ?", uid).
+ And("team_user.org_id = ?", org.ID).
+ Get(&authorize)
+ return authorize, err
+}
+
// GetUsersWhoCanCreateOrgRepo returns users which are able to create repo in organization
func GetUsersWhoCanCreateOrgRepo(orgID int64) ([]*User, error) {
return getUsersWhoCanCreateOrgRepo(db.GetEngine(db.DefaultContext), orgID)
diff --git a/modules/structs/org.go b/modules/structs/org.go
index 38c6c6d6d8..4ae0ca8b6f 100644
--- a/modules/structs/org.go
+++ b/modules/structs/org.go
@@ -17,6 +17,15 @@ type Organization struct {
RepoAdminChangeTeamAccess bool `json:"repo_admin_change_team_access"`
}
+// OrganizationPermissions list differents users permissions on an organization
+type OrganizationPermissions struct {
+ IsOwner bool `json:"is_owner"`
+ IsAdmin bool `json:"is_admin"`
+ CanWrite bool `json:"can_write"`
+ CanRead bool `json:"can_read"`
+ CanCreateRepository bool `json:"can_create_repository"`
+}
+
// CreateOrgOption options for creating an organization
type CreateOrgOption struct {
// required: true
diff --git a/routers/api/v1/api.go b/routers/api/v1/api.go
index 0a967e3c5a..d11bbf3c06 100644
--- a/routers/api/v1/api.go
+++ b/routers/api/v1/api.go
@@ -973,7 +973,10 @@ func Routes(sessioner func(http.Handler) http.Handler) *web.Route {
// Organizations
m.Get("/user/orgs", reqToken(), org.ListMyOrgs)
- m.Get("/users/{username}/orgs", org.ListUserOrgs)
+ m.Group("/users/{username}/orgs", func() {
+ m.Get("", org.ListUserOrgs)
+ m.Get("/{org}/permissions", reqToken(), org.GetUserOrgsPermissions)
+ })
m.Post("/orgs", reqToken(), bind(api.CreateOrgOption{}), org.Create)
m.Get("/orgs", org.GetAll)
m.Group("/orgs/{org}", func() {
diff --git a/routers/api/v1/org/org.go b/routers/api/v1/org/org.go
index cf4c328ebb..d3aa92f46d 100644
--- a/routers/api/v1/org/org.go
+++ b/routers/api/v1/org/org.go
@@ -97,6 +97,77 @@ func ListUserOrgs(ctx *context.APIContext) {
listUserOrgs(ctx, u)
}
+// GetUserOrgsPermissions get user permissions in organization
+func GetUserOrgsPermissions(ctx *context.APIContext) {
+ // swagger:operation GET /users/{username}/orgs/{org}/permissions organization orgGetUserPermissions
+ // ---
+ // summary: Get user permissions in organization
+ // produces:
+ // - application/json
+ // parameters:
+ // - name: username
+ // in: path
+ // description: username of user
+ // type: string
+ // required: true
+ // - name: org
+ // in: path
+ // description: name of the organization
+ // type: string
+ // required: true
+ // responses:
+ // "200":
+ // "$ref": "#/responses/OrganizationPermissions"
+ // "403":
+ // "$ref": "#/responses/forbidden"
+ // "404":
+ // "$ref": "#/responses/notFound"
+
+ var u *models.User
+ if u = user.GetUserByParams(ctx); u == nil {
+ return
+ }
+
+ var o *models.User
+ if o = user.GetUserByParamsName(ctx, ":org"); o == nil {
+ return
+ }
+
+ op := api.OrganizationPermissions{}
+
+ if !models.HasOrgOrUserVisible(o, u) {
+ ctx.NotFound("HasOrgOrUserVisible", nil)
+ return
+ }
+
+ authorizeLevel, err := o.GetOrgUserMaxAuthorizeLevel(u.ID)
+ if err != nil {
+ ctx.Error(http.StatusInternalServerError, "GetOrgUserAuthorizeLevel", err)
+ return
+ }
+
+ if authorizeLevel > models.AccessModeNone {
+ op.CanRead = true
+ }
+ if authorizeLevel > models.AccessModeRead {
+ op.CanWrite = true
+ }
+ if authorizeLevel > models.AccessModeWrite {
+ op.IsAdmin = true
+ }
+ if authorizeLevel > models.AccessModeAdmin {
+ op.IsOwner = true
+ }
+
+ op.CanCreateRepository, err = o.CanCreateOrgRepo(u.ID)
+ if err != nil {
+ ctx.Error(http.StatusInternalServerError, "CanCreateOrgRepo", err)
+ return
+ }
+
+ ctx.JSON(http.StatusOK, op)
+}
+
// GetAll return list of all public organizations
func GetAll(ctx *context.APIContext) {
// swagger:operation Get /orgs organization orgGetAll
diff --git a/routers/api/v1/swagger/org.go b/routers/api/v1/swagger/org.go
index c962e7b188..d98e821ba7 100644
--- a/routers/api/v1/swagger/org.go
+++ b/routers/api/v1/swagger/org.go
@@ -35,3 +35,10 @@ type swaggerResponseTeamList struct {
// in:body
Body []api.Team `json:"body"`
}
+
+// OrganizationPermissions
+// swagger:response OrganizationPermissions
+type swaggerResponseOrganizationPermissions struct {
+ // in:body
+ Body api.OrganizationPermissions `json:"body"`
+}
diff --git a/templates/swagger/v1_json.tmpl b/templates/swagger/v1_json.tmpl
index c6fa664af6..afb93c50fe 100644
--- a/templates/swagger/v1_json.tmpl
+++ b/templates/swagger/v1_json.tmpl
@@ -11856,6 +11856,45 @@
}
}
},
+ "/users/{username}/orgs/{org}/permissions": {
+ "get": {
+ "produces": [
+ "application/json"
+ ],
+ "tags": [
+ "organization"
+ ],
+ "summary": "Get user permissions in organization",
+ "operationId": "orgGetUserPermissions",
+ "parameters": [
+ {
+ "type": "string",
+ "description": "username of user",
+ "name": "username",
+ "in": "path",
+ "required": true
+ },
+ {
+ "type": "string",
+ "description": "name of the organization",
+ "name": "org",
+ "in": "path",
+ "required": true
+ }
+ ],
+ "responses": {
+ "200": {
+ "$ref": "#/responses/OrganizationPermissions"
+ },
+ "403": {
+ "$ref": "#/responses/forbidden"
+ },
+ "404": {
+ "$ref": "#/responses/notFound"
+ }
+ }
+ }
+ },
"/users/{username}/repos": {
"get": {
"produces": [
@@ -15877,6 +15916,33 @@
},
"x-go-package": "code.gitea.io/gitea/modules/structs"
},
+ "OrganizationPermissions": {
+ "description": "OrganizationPermissions list differents users permissions on an organization",
+ "type": "object",
+ "properties": {
+ "can_create_repository": {
+ "type": "boolean",
+ "x-go-name": "CanCreateRepository"
+ },
+ "can_read": {
+ "type": "boolean",
+ "x-go-name": "CanRead"
+ },
+ "can_write": {
+ "type": "boolean",
+ "x-go-name": "CanWrite"
+ },
+ "is_admin": {
+ "type": "boolean",
+ "x-go-name": "IsAdmin"
+ },
+ "is_owner": {
+ "type": "boolean",
+ "x-go-name": "IsOwner"
+ }
+ },
+ "x-go-package": "code.gitea.io/gitea/modules/structs"
+ },
"PRBranchInfo": {
"description": "PRBranchInfo information about a branch",
"type": "object",
@@ -17742,6 +17808,12 @@
}
}
},
+ "OrganizationPermissions": {
+ "description": "OrganizationPermissions",
+ "schema": {
+ "$ref": "#/definitions/OrganizationPermissions"
+ }
+ },
"PublicKey": {
"description": "PublicKey",
"schema": {