Merge pull request #1852 from jmccann/ghe_org_secrets

WIP: Check remote for org secrets permissions
This commit is contained in:
Brad Rydzewski 2016-11-20 23:30:54 +01:00 committed by GitHub
commit af296dcd2d
14 changed files with 371 additions and 10 deletions

24
cache/helper.go vendored
View file

@ -8,7 +8,7 @@ import (
"golang.org/x/net/context"
)
// GetPerm returns the user permissions repositories from the cache
// GetPerms returns the user permissions repositories from the cache
// associated with the current repository.
func GetPerms(c context.Context, user *model.User, owner, name string) (*model.Perm, error) {
key := fmt.Sprintf("perms:%s:%s/%s",
@ -31,6 +31,28 @@ func GetPerms(c context.Context, user *model.User, owner, name string) (*model.P
return perm, nil
}
// GetTeamPerms returns the user permissions from the cache
// associated with the current organization.
func GetTeamPerms(c context.Context, user *model.User, org string) (*model.Perm, error) {
key := fmt.Sprintf("perms:%s:%s",
user.Login,
org,
)
// if we fetch from the cache we can return immediately
val, err := Get(c, key)
if err == nil {
return val.(*model.Perm), nil
}
// else we try to grab from the remote system and
// populate our cache.
perm, err := remote.TeamPerm(c, user, org)
if err != nil {
return nil, err
}
Set(c, key, perm)
return perm, nil
}
// GetRepos returns the list of user repositories from the cache
// associated with the current context.
func GetRepos(c context.Context, user *model.User) ([]*model.RepoLite, error) {

View file

@ -104,6 +104,11 @@ func (c *config) Teams(u *model.User) ([]*model.Team, error) {
return convertTeamList(resp.Values), nil
}
// TeamPerm is not supported by the Bitbucket driver.
func (c *config) TeamPerm(u *model.User, org string) (*model.Perm, error) {
return nil, nil
}
// Repo returns the named Bitbucket repository.
func (c *config) Repo(u *model.User, owner, name string) (*model.Repo, error) {
repo, err := c.newClient(u).FindRepo(owner, name)

View file

@ -115,6 +115,11 @@ func (*Config) Teams(u *model.User) ([]*model.Team, error) {
return teams, nil
}
// TeamPerm is not supported by the Stash driver.
func (*Config) TeamPerm(u *model.User, org string) (*model.Perm, error) {
return nil, nil
}
func (c *Config) Repo(u *model.User, owner, name string) (*model.Repo, error) {
repo, err := internal.NewClientWithToken(c.URL, c.Consumer, u.Token).FindRepo(owner, name)
if err != nil {

View file

@ -69,6 +69,11 @@ func (c *client) Teams(u *model.User) ([]*model.Team, error) {
return empty, nil
}
// TeamPerm is not supported by the Gerrit driver.
func (c *client) TeamPerm(u *model.User, org string) (*model.Perm, error) {
return nil, nil
}
// Repo is not supported by the Gerrit driver.
func (c *client) Repo(u *model.User, owner, name string) (*model.Repo, error) {
return nil, nil

View file

@ -94,6 +94,18 @@ func convertPerm(from *github.Repository) *model.Perm {
}
}
// convertTeamPerm is a helper function used to convert a GitHub organization
// permissions to the common Drone permissions structure.
func convertTeamPerm(from *github.Membership) *model.Perm {
admin := false
if *from.Role == "admin" {
admin = true
}
return &model.Perm{
Admin: admin,
}
}
// convertRepoList is a helper function used to convert a GitHub repository
// list to the common Drone repository structure.
func convertRepoList(from []github.Repository) []*model.RepoLite {

View file

@ -13,6 +13,8 @@ func Handler() http.Handler {
e := gin.New()
e.GET("/api/v3/repos/:owner/:name", getRepo)
e.GET("/api/v3/orgs/:org/memberships/:user", getMembership)
e.GET("/api/v3/user/memberships/orgs/:org", getMembership)
return e
}
@ -26,6 +28,17 @@ func getRepo(c *gin.Context) {
}
}
func getMembership(c *gin.Context) {
switch c.Param("org") {
case "org_not_found":
c.String(404, "")
case "github":
c.String(200, membershipIsMemberPayload)
default:
c.String(200, membershipIsOwnerPayload)
}
}
var repoPayload = `
{
"owner": {
@ -45,3 +58,85 @@ var repoPayload = `
}
}
`
var membershipIsOwnerPayload = `
{
"url": "https://api.github.com/orgs/octocat/memberships/octocat",
"state": "active",
"role": "admin",
"organization_url": "https://api.github.com/orgs/octocat",
"user": {
"login": "octocat",
"id": 5555555,
"avatar_url": "https://github.com/images/error/octocat_happy.gif",
"gravatar_id": "",
"url": "https://api.github.com/users/octocat",
"html_url": "https://github.com/octocat",
"followers_url": "https://api.github.com/users/octocat/followers",
"following_url": "https://api.github.com/users/octocat/following{/other_user}",
"gists_url": "https://api.github.com/users/octocat/gists{/gist_id}",
"starred_url": "https://api.github.com/users/octocat/starred{/owner}{/repo}",
"subscriptions_url": "https://api.github.com/users/octocat/subscriptions",
"organizations_url": "https://api.github.com/users/octocat/orgs",
"repos_url": "https://api.github.com/users/octocat/repos",
"events_url": "https://api.github.com/users/octocat/events{/privacy}",
"received_events_url": "https://api.github.com/users/octocat/received_events",
"type": "User",
"site_admin": false
},
"organization": {
"login": "octocat",
"id": 5555556,
"url": "https://api.github.com/orgs/octocat",
"repos_url": "https://api.github.com/orgs/octocat/repos",
"events_url": "https://api.github.com/orgs/octocat/events",
"hooks_url": "https://api.github.com/orgs/octocat/hooks",
"issues_url": "https://api.github.com/orgs/octocat/issues",
"members_url": "https://api.github.com/orgs/octocat/members{/member}",
"public_members_url": "https://api.github.com/orgs/octocat/public_members{/member}",
"avatar_url": "https://github.com/images/error/octocat_happy.gif",
"description": ""
}
}
`
var membershipIsMemberPayload = `
{
"url": "https://api.github.com/orgs/github/memberships/octocat",
"state": "active",
"role": "member",
"organization_url": "https://api.github.com/orgs/github",
"user": {
"login": "octocat",
"id": 5555555,
"avatar_url": "https://github.com/images/error/octocat_happy.gif",
"gravatar_id": "",
"url": "https://api.github.com/users/octocat",
"html_url": "https://github.com/octocat",
"followers_url": "https://api.github.com/users/octocat/followers",
"following_url": "https://api.github.com/users/octocat/following{/other_user}",
"gists_url": "https://api.github.com/users/octocat/gists{/gist_id}",
"starred_url": "https://api.github.com/users/octocat/starred{/owner}{/repo}",
"subscriptions_url": "https://api.github.com/users/octocat/subscriptions",
"organizations_url": "https://api.github.com/users/octocat/orgs",
"repos_url": "https://api.github.com/users/octocat/repos",
"events_url": "https://api.github.com/users/octocat/events{/privacy}",
"received_events_url": "https://api.github.com/users/octocat/received_events",
"type": "User",
"site_admin": false
},
"organization": {
"login": "octocat",
"id": 5555557,
"url": "https://api.github.com/orgs/github",
"repos_url": "https://api.github.com/orgs/github/repos",
"events_url": "https://api.github.com/orgs/github/events",
"hooks_url": "https://api.github.com/orgs/github/hooks",
"issues_url": "https://api.github.com/orgs/github/issues",
"members_url": "https://api.github.com/orgs/github/members{/member}",
"public_members_url": "https://api.github.com/orgs/github/public_members{/member}",
"avatar_url": "https://github.com/images/error/octocat_happy.gif",
"description": ""
}
}
`

View file

@ -158,6 +158,16 @@ func (c *client) Teams(u *model.User) ([]*model.Team, error) {
return teams, nil
}
// TeamPerm returns the user permissions for the named GitHub organization.
func (c *client) TeamPerm(u *model.User, org string) (*model.Perm, error) {
client := c.newClientToken(u.Token)
membership, _, err := client.Organizations.GetOrgMembership(u.Login, org)
if err != nil {
return nil, err
}
return convertTeamPerm(membership), nil
}
// Repo returns the named GitHub repository.
func (c *client) Repo(u *model.User, owner, name string) (*model.Repo, error) {
client := c.newClientToken(u.Token)

View file

@ -110,6 +110,23 @@ func Test_github(t *testing.T) {
})
})
g.Describe("Requesting organization permissions", func() {
g.It("Should return the permission details of an admin", func() {
perm, err := c.TeamPerm(fakeUser, "octocat")
g.Assert(err == nil).IsTrue()
g.Assert(perm.Admin).IsTrue()
})
g.It("Should return the permission details of a member", func() {
perm, err := c.TeamPerm(fakeUser, "github")
g.Assert(err == nil).IsTrue()
g.Assert(perm.Admin).IsFalse()
})
g.It("Should handle a not found error", func() {
_, err := c.TeamPerm(fakeUser, "org_not_found")
g.Assert(err != nil).IsTrue()
})
})
g.It("Should return a user repository list")
g.It("Should return a user team list")

View file

@ -194,6 +194,11 @@ func (g *Gitlab) Teams(u *model.User) ([]*model.Team, error) {
return teams, nil
}
// TeamPerm is not supported by the Gitlab driver.
func (g *Gitlab) TeamPerm(u *model.User, org string) (*model.Perm, error) {
return nil, nil
}
// Repo fetches the named repository from the remote system.
func (g *Gitlab) Repo(u *model.User, owner, name string) (*model.Repo, error) {
client := NewClient(g.URL, u.Token, g.SkipVerify)

View file

@ -126,6 +126,11 @@ func (c *client) Teams(u *model.User) ([]*model.Team, error) {
return teams, nil
}
// TeamPerm is not supported by the Gogs driver.
func (c *client) TeamPerm(u *model.User, org string) (*model.Perm, error) {
return nil, nil
}
// Repo returns the named Gogs repository.
func (c *client) Repo(u *model.User, owner, name string) (*model.Repo, error) {
client := c.newClientToken(u.Token)

View file

@ -267,3 +267,26 @@ func (_m *Remote) Teams(u *model.User) ([]*model.Team, error) {
return r0, r1
}
// TeamPerm provides a mock function with given fields: u, org
func (_m *Remote) TeamPerm(u *model.User, org string) (*model.Perm, error) {
ret := _m.Called(u, org)
var r0 *model.Perm
if rf, ok := ret.Get(0).(func(*model.User, string) *model.Perm); ok {
r0 = rf(u, org)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*model.Perm)
}
}
var r1 error
if rf, ok := ret.Get(1).(func(*model.User, string) error); ok {
r1 = rf(u, org)
} else {
r1 = ret.Error(1)
}
return r0, r1
}

View file

@ -22,6 +22,10 @@ type Remote interface {
// Teams fetches a list of team memberships from the remote system.
Teams(u *model.User) ([]*model.Team, error)
// TeamPerm fetches the named organization permissions from
// the remote system for the specified user.
TeamPerm(u *model.User, org string) (*model.Perm, error)
// Repo fetches the named repository from the remote system.
Repo(u *model.User, owner, repo string) (*model.Repo, error)
@ -80,6 +84,12 @@ func Teams(c context.Context, u *model.User) ([]*model.Team, error) {
return FromContext(c).Teams(u)
}
// TeamPerm fetches the named organization permissions from
// the remote system for the specified user.
func TeamPerm(c context.Context, u *model.User, org string) (*model.Perm, error) {
return FromContext(c).TeamPerm(u, org)
}
// Repo fetches the named repository from the remote system.
func Repo(c context.Context, u *model.User, owner, repo string) (*model.Repo, error) {
return FromContext(c).Repo(u, owner, repo)

View file

@ -1,21 +1,73 @@
package session
import (
"github.com/drone/drone/cache"
"github.com/drone/drone/model"
log "github.com/Sirupsen/logrus"
"github.com/gin-gonic/gin"
)
func TeamPerm(c *gin.Context) *model.Perm {
user := User(c)
team := c.Param("team")
perm := &model.Perm{}
switch {
// if the user is not authenticated
case user == nil:
perm.Admin = false
perm.Pull = false
perm.Push = false
// if the user is a DRONE_ADMIN
case user.Admin:
perm.Admin = true
perm.Pull = true
perm.Push = true
// otherwise if the user is authenticated we should
// check the remote system to get the users permissiosn.
default:
log.Debugf("Fetching team permission for %s %s",
user.Login, team)
var err error
perm, err = cache.GetTeamPerms(c, user, team)
if err != nil {
// debug
log.Errorf("Error fetching team permission for %s %s",
user.Login, team)
perm.Admin = false
perm.Pull = false
perm.Push = false
}
}
if user != nil {
log.Debugf("%s granted %+v team permission to %s",
user.Login, perm, team)
} else {
log.Debugf("Guest granted %+v to %s", perm, team)
perm.Admin = false
perm.Pull = false
perm.Push = false
}
return perm
}
func MustTeamAdmin() gin.HandlerFunc {
return func(c *gin.Context) {
user := User(c)
switch {
case user == nil:
c.String(401, "User not authorized")
c.Abort()
case user.Admin == false:
c.String(413, "User not authorized")
c.Abort()
default:
perm := TeamPerm(c)
if perm.Admin {
c.Next()
}
c.String(401, "User not authorized")
c.Abort()
}
}

View file

@ -0,0 +1,95 @@
package session
import (
"testing"
"github.com/drone/drone/cache"
"github.com/drone/drone/model"
"github.com/franela/goblin"
"github.com/gin-gonic/gin"
)
func TestTeamPerm(t *testing.T) {
g := goblin.Goblin(t)
g.Describe("TeamPerm", func() {
var c *gin.Context
g.BeforeEach(func() {
c = new(gin.Context)
cache.ToContext(c, cache.Default())
})
g.It("Should set admin to false (user not logged in)", func() {
p := TeamPerm(c)
g.Assert(p.Admin).IsFalse("admin should be false")
})
g.It("Should set admin to true (user is DRONE_ADMIN)", func() {
// Set DRONE_ADMIN user
c.Set("user", fakeUserAdmin)
p := TeamPerm(c)
g.Assert(p.Admin).IsTrue("admin should be false")
})
g.It("Should set admin to false (user logged in, not owner of org)", func() {
// Set fake org
params := gin.Params{
gin.Param{
Key: "team",
Value: "test_org",
},
}
c.Params = params
// Set cache to show user does not Owner/Admin
cache.Set(c, "perms:octocat:test_org", fakeTeamPerm)
// Set User
c.Set("user", fakeUser)
p := TeamPerm(c)
g.Assert(p.Admin).IsFalse("admin should be false")
})
g.It("Should set admin to true (user logged in, owner of org)", func() {
// Set fake org
params := gin.Params{
gin.Param{
Key: "team",
Value: "test_org",
},
}
c.Params = params
// Set cache to show user is Owner/Admin
cache.Set(c, "perms:octocat:test_org", fakeTeamPermAdmin)
// Set User
c.Set("user", fakeUser)
p := TeamPerm(c)
g.Assert(p.Admin).IsTrue("admin should be true")
})
})
}
var (
fakeUserAdmin = &model.User{
Login: "octocatAdmin",
Token: "cfcd2084",
Admin: true,
}
fakeUser = &model.User{
Login: "octocat",
Token: "cfcd2084",
Admin: false,
}
fakeTeamPermAdmin = &model.Perm{
Admin: true,
}
fakeTeamPerm = &model.Perm{
Admin: false,
}
)