* Add endpoint * Add swagger response + generate swagger * Stop execution if user / org is not found * Add tests Co-authored-by: 6543 <6543@obermui.de>tags/v1.16.0-rc1
@@ -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) | |||
} |
@@ -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) |
@@ -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 |
@@ -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() { |
@@ -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 |
@@ -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"` | |||
} |
@@ -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": { |