Skip to content

Commit

Permalink
ESSNTL-4817: require inventory read permission
Browse files Browse the repository at this point in the history
  • Loading branch information
psegedy committed Aug 14, 2023
1 parent 31c3da8 commit 1e44b11
Show file tree
Hide file tree
Showing 2 changed files with 73 additions and 33 deletions.
84 changes: 51 additions & 33 deletions manager/middlewares/rbac.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,12 @@ const KeyInventoryGroups = "inventoryGroups"
var allPerms = "patch:*:*"
var readPerms = map[string]bool{allPerms: true, "patch:*:read": true}
var writePerms = map[string]bool{allPerms: true, "patch:*:write": true}
var inventoryReadPerms = map[string]bool{
"inventory:*:*": true,
"inventory:*:read": true,
"inventory:hosts:*": true,
"inventory:hosts:read": true,
}

// handlerName to permissions mapping
var granularPerms = map[string]struct {
Expand All @@ -48,48 +54,60 @@ func makeClient(identity string) *api.Client {
DefaultHeaders: map[string]string{xRHIdentity: identity},
}
if rbacURL == "" {
rbacURL = utils.FailIfEmpty(utils.Cfg.RbacAddress, "RBAC_ADDRESS") + base.RBACApiPrefix + "/access/?application=patch,inventory"
rbacURL = utils.FailIfEmpty(utils.Cfg.RbacAddress, "RBAC_ADDRESS") + base.RBACApiPrefix +
"/access/?application=patch,inventory"
}
return &client
}

func checkPermissions(access *rbac.AccessPagination, handlerName, method string) bool {
granted := false
grantedPatch := false
grantedInventory := false
for _, a := range access.Data {
if granted {
return true
}
if p, has := granularPerms[handlerName]; has {
// API handler requires granular permissions
if a.Permission == p.Permission {
// the required permission is present, e.g. patch:template:write
return true
}
if p.Read && !p.Write && readPerms[a.Permission] {
// required permission is read permission
// check whether we have either patch:*:read or patch:*:*
return true
}
if p.Write && !p.Read && writePerms[a.Permission] {
// required permission is write permission
// check whether we have either patch:*:write or patch:*:*
return true
switch {
case !grantedPatch:
if p, has := granularPerms[handlerName]; has {
// API handler requires granular permissions
if a.Permission == p.Permission {
// the required permission is present, e.g. patch:template:write
grantedPatch = true
continue
}
if p.Read && !p.Write && readPerms[a.Permission] {
// required permission is read permission
// check whether we have either patch:*:read or patch:*:*
grantedPatch = true
continue
}
if p.Write && !p.Read && writePerms[a.Permission] {
// required permission is write permission
// check whether we have either patch:*:write or patch:*:*
grantedPatch = true
continue
}
// we need both read and write permissions - patch:*:*
grantedPatch = (a.Permission == allPerms)
} else {
// not granular
// require read permissions for GET and POST
// require write permissions for PUT and DELETE
switch method {
case "GET", "POST":
grantedPatch = readPerms[a.Permission]
case "PUT", "DELETE":
grantedPatch = writePerms[a.Permission]
}
}
// we need both read and write permissions - patch:*:*
granted = (a.Permission == allPerms)
} else {
// not granular
// require read permissions for GET and POST
// require write permissions for PUT and DELETE
switch method {
case "GET", "POST":
granted = readPerms[a.Permission]
case "PUT", "DELETE":
granted = writePerms[a.Permission]
case !grantedInventory:
if inventoryReadPerms[a.Permission] {
grantedInventory = true
continue
}
case grantedPatch && grantedInventory:
return true
}
}
return granted
return grantedPatch && grantedInventory
}

func isAccessGranted(c *gin.Context) bool {
Expand Down Expand Up @@ -128,7 +146,7 @@ func findInventoryGroups(access *rbac.AccessPagination) map[string]string {
}
groups := []string{}
for _, a := range access.Data {
if a.Permission != "inventory:hosts:read" {
if !inventoryReadPerms[a.Permission] {
continue
}

Expand Down
22 changes: 22 additions & 0 deletions manager/middlewares/rbac_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,20 +46,23 @@ func TestPermissionsSingleWrite(t *testing.T) {
access := rbac.AccessPagination{
Data: []rbac.Access{
{Permission: "patch:*:*"},
{Permission: "inventory:*:*"},
},
}
assert.True(t, checkPermissions(&access, handler, "PUT"))

access = rbac.AccessPagination{
Data: []rbac.Access{
{Permission: "patch:*:write"},
{Permission: "inventory:*:*"},
},
}
assert.True(t, checkPermissions(&access, handler, "PUT"))

access = rbac.AccessPagination{
Data: []rbac.Access{
{Permission: "patch:template:write"},
{Permission: "inventory:*:*"},
},
}
assert.True(t, checkPermissions(&access, handler, "PUT"))
Expand All @@ -74,13 +77,15 @@ func TestPermissionsSingleWrite(t *testing.T) {
access = rbac.AccessPagination{
Data: []rbac.Access{
{Permission: "patch:asdf:read"},
{Permission: "inventory:*:*"},
},
}
assert.False(t, checkPermissions(&access, handler, "PUT"))

access = rbac.AccessPagination{
Data: []rbac.Access{
{Permission: "patch:*:read"},
{Permission: "inventory:*:*"},
},
}
assert.False(t, checkPermissions(&access, handler, "PUT"))
Expand All @@ -97,46 +102,53 @@ func TestPermissionsSingleRead(t *testing.T) {
access := rbac.AccessPagination{
Data: []rbac.Access{
{Permission: "patch:*:*"},
{Permission: "inventory:*:*"},
},
}
assert.True(t, checkPermissions(&access, handler, "GET"))

access = rbac.AccessPagination{
Data: []rbac.Access{
{Permission: "patch:*:read"},
{Permission: "inventory:*:*"},
},
}
assert.True(t, checkPermissions(&access, handler, "GET"))

access = rbac.AccessPagination{
Data: []rbac.Access{
{Permission: "patch:single:read"},
{Permission: "inventory:*:*"},
},
}
assert.True(t, checkPermissions(&access, handler, "GET"))

access = rbac.AccessPagination{
Data: []rbac.Access{
{Permission: "patch:asdf:read"},
{Permission: "inventory:*:*"},
},
}
assert.False(t, checkPermissions(&access, handler, "GET"))

access = rbac.AccessPagination{
Data: []rbac.Access{
{Permission: "patch:asdf:write"},
{Permission: "inventory:*:*"},
},
}
assert.False(t, checkPermissions(&access, handler, "GET"))

access = rbac.AccessPagination{
Data: []rbac.Access{
{Permission: "patch:*:write"},
{Permission: "inventory:*:*"},
},
}
assert.False(t, checkPermissions(&access, handler, "GET"))
}

// nolint:funlen
func TestPermissionsSingleReadWrite(t *testing.T) {
// handler needs `patch:single:read`
handler := "SingleReadWrite"
Expand All @@ -148,48 +160,55 @@ func TestPermissionsSingleReadWrite(t *testing.T) {
access := rbac.AccessPagination{
Data: []rbac.Access{
{Permission: "patch:*:*"},
{Permission: "inventory:*:*"},
},
}
assert.True(t, checkPermissions(&access, handler, "PUT"))

access = rbac.AccessPagination{
Data: []rbac.Access{
{Permission: "patch:single:*"},
{Permission: "inventory:*:*"},
},
}
assert.True(t, checkPermissions(&access, handler, "PUT"))

access = rbac.AccessPagination{
Data: []rbac.Access{
{Permission: "patch:*:read"},
{Permission: "inventory:*:*"},
},
}
assert.False(t, checkPermissions(&access, handler, "PUT"))

access = rbac.AccessPagination{
Data: []rbac.Access{
{Permission: "patch:single:read"},
{Permission: "inventory:*:*"},
},
}
assert.False(t, checkPermissions(&access, handler, "PUT"))

access = rbac.AccessPagination{
Data: []rbac.Access{
{Permission: "patch:asdf:read"},
{Permission: "inventory:*:*"},
},
}
assert.False(t, checkPermissions(&access, handler, "PUT"))

access = rbac.AccessPagination{
Data: []rbac.Access{
{Permission: "patch:asdf:write"},
{Permission: "inventory:*:*"},
},
}
assert.False(t, checkPermissions(&access, handler, "PUT"))

access = rbac.AccessPagination{
Data: []rbac.Access{
{Permission: "patch:*:write"},
{Permission: "inventory:*:*"},
},
}
assert.False(t, checkPermissions(&access, handler, "PUT"))
Expand All @@ -206,20 +225,23 @@ func TestPermissionsRead(t *testing.T) {
access := rbac.AccessPagination{
Data: []rbac.Access{
{Permission: "patch:*:*"},
{Permission: "inventory:*:*"},
},
}
assert.True(t, checkPermissions(&access, handler, "GET"))

access = rbac.AccessPagination{
Data: []rbac.Access{
{Permission: "patch:*:read"},
{Permission: "inventory:*:*"},
},
}
assert.True(t, checkPermissions(&access, handler, "GET"))

access = rbac.AccessPagination{
Data: []rbac.Access{
{Permission: "patch:*:write"},
{Permission: "inventory:*:*"},
},
}
assert.False(t, checkPermissions(&access, handler, "GET"))
Expand Down

0 comments on commit 1e44b11

Please sign in to comment.