Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add API for get user org permissions #17232

Merged
merged 12 commits into from
Oct 12, 2021
149 changes: 149 additions & 0 deletions integrations/api_user_org_perm_test.go
Original file line number Diff line number Diff line change
@@ -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)
}
13 changes: 13 additions & 0 deletions models/org.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
9 changes: 9 additions & 0 deletions modules/structs/org.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
5 changes: 4 additions & 1 deletion routers/api/v1/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -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() {
Expand Down
74 changes: 74 additions & 0 deletions routers/api/v1/org/org.go
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,80 @@ 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":
6543 marked this conversation as resolved.
Show resolved Hide resolved
// "$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
}

op.IsOwner = false
op.IsAdmin = false
op.CanWrite = false
6543 marked this conversation as resolved.
Show resolved Hide resolved
6543 marked this conversation as resolved.
Show resolved Hide resolved
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
Expand Down
7 changes: 7 additions & 0 deletions routers/api/v1/swagger/org.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"`
}
72 changes: 72 additions & 0 deletions templates/swagger/v1_json.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -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": [
Expand Down Expand Up @@ -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",
Expand Down Expand Up @@ -17742,6 +17808,12 @@
}
}
},
"OrganizationPermissions": {
"description": "OrganizationPermissions",
"schema": {
"$ref": "#/definitions/OrganizationPermissions"
}
},
"PublicKey": {
"description": "PublicKey",
"schema": {
Expand Down