Skip to content

Commit

Permalink
Allow Restricted Admin to create GR and GRB with FleetWorkspacePermis…
Browse files Browse the repository at this point in the history
…sions
  • Loading branch information
raulcabello committed Jun 19, 2024
1 parent 4e8a12d commit acc851a
Show file tree
Hide file tree
Showing 5 changed files with 315 additions and 11 deletions.
22 changes: 22 additions & 0 deletions pkg/auth/globalrole.go
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,18 @@ func (g *GlobalRoleResolver) ClusterRulesFromRole(gr *v3.GlobalRole) ([]rbacv1.P
}

func (g *GlobalRoleResolver) FleetWorkspacePermissionsResourceRulesFromRole(gr *v3.GlobalRole) []rbacv1.PolicyRule {
for _, name := range adminRoles {
if gr.Name == name {
return []rbacv1.PolicyRule{
{
Verbs: []string{"*"},
APIGroups: []string{"fleet.cattle.io"},
Resources: []string{"clusterregistrationtokens", "gitreporestrictions", "clusterregistrations", "clusters", "gitrepos", "bundles", "clustergroups"},
},
}
}
}

if gr == nil || gr.InheritedFleetWorkspacePermissions == nil {
return nil
}
Expand All @@ -82,6 +94,16 @@ func (g *GlobalRoleResolver) FleetWorkspacePermissionsResourceRulesFromRole(gr *
// use it to evaluate InheritedFleetWorkspacePermissions.WorkspaceVerbs. However, it shouldn't be used in a more generic evaluation
// of permissions on the workspace object.
func (g *GlobalRoleResolver) FleetWorkspacePermissionsWorkspaceVerbsFromRole(gr *v3.GlobalRole) []rbacv1.PolicyRule {
for _, name := range adminRoles {
if gr.Name == name {
return []rbacv1.PolicyRule{{
Verbs: []string{"*"},
APIGroups: []string{"management.cattle.io"},
Resources: []string{"fleetworkspaces"},
}}
}
}

if gr == nil || gr.InheritedFleetWorkspacePermissions == nil {
return nil
}
Expand Down
52 changes: 49 additions & 3 deletions pkg/resources/management.cattle.io/v3/globalrole/setup_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,9 @@ import (
)

const (
adminUser = "admin-userid"
testUser = "test-user"
adminUser = "admin-userid"
testUser = "test-user"
restrictedAdminUser = "restricted-admin-userid"
)

var (
Expand All @@ -42,7 +43,7 @@ var (
},
},
}
clusterRoles = []*v1.ClusterRole{adminCR, readPodsCR, baseCR}
clusterRoles = []*v1.ClusterRole{adminCR, readPodsCR, baseCR, restrictedAdminCR}

clusterRoleBindings = []*v1.ClusterRoleBinding{
{
Expand All @@ -51,6 +52,12 @@ var (
},
RoleRef: v1.RoleRef{APIGroup: v1.GroupName, Kind: "ClusterRole", Name: adminCR.Name},
},
{
Subjects: []v1.Subject{
{Kind: v1.UserKind, Name: restrictedAdminUser},
},
RoleRef: v1.RoleRef{APIGroup: v1.GroupName, Kind: "ClusterRole", Name: restrictedAdminCR.Name},
},
{
Subjects: []v1.Subject{
{Kind: v1.UserKind, Name: testUser},
Expand Down Expand Up @@ -87,6 +94,24 @@ var (
},
},
}
clusterOwnerRT = v3.RoleTemplate{
ObjectMeta: metav1.ObjectMeta{
Name: "cluster-owner",
},
Rules: []v1.PolicyRule{
{
APIGroups: []string{
"*",
},
Resources: []string{
"*",
},
Verbs: []string{
"*",
},
},
},
}
baseGR = v3.GlobalRole{
ObjectMeta: metav1.ObjectMeta{
Name: "base-gr",
Expand All @@ -106,13 +131,25 @@ var (
WorkspaceVerbs: []string{"GET"},
},
}
restrictedAdminGR = v3.GlobalRole{
ObjectMeta: metav1.ObjectMeta{
Name: "restricted-admin",
},
}
baseGRB = v3.GlobalRoleBinding{
ObjectMeta: metav1.ObjectMeta{
Name: "base-grb",
},
GlobalRoleName: baseGR.Name,
UserName: testUser,
}
restrictedAdminGRB = v3.GlobalRoleBinding{
ObjectMeta: metav1.ObjectMeta{
Name: "restricted-admin-grb",
},
GlobalRoleName: restrictedAdminCR.Name,
UserName: restrictedAdminUser,
}

ruleReadPods = v1.PolicyRule{
Verbs: []string{"GET", "WATCH"},
Expand Down Expand Up @@ -140,6 +177,12 @@ var (
},
Rules: []v1.PolicyRule{ruleAdmin},
}
restrictedAdminCR = &v1.ClusterRole{
ObjectMeta: metav1.ObjectMeta{
Name: "restricted-admin",
},
Rules: []v1.PolicyRule{},
}
readPodsCR = &v1.ClusterRole{
ObjectMeta: metav1.ObjectMeta{Name: "read-pods"},
Rules: []v1.PolicyRule{ruleReadPods},
Expand Down Expand Up @@ -272,10 +315,13 @@ func newDefaultState(t *testing.T) testState {
grbCacheMock := fake.NewMockNonNamespacedCacheInterface[*v3.GlobalRoleBinding](ctrl)
grbs := []*v3.GlobalRoleBinding{&baseGRB}
grbCacheMock.EXPECT().GetByIndex(gomock.Any(), resolvers.GetUserKey(testUser, "")).Return(grbs, nil).AnyTimes()
grbCacheMock.EXPECT().GetByIndex(gomock.Any(), resolvers.GetUserKey(restrictedAdminUser, "")).Return([]*v3.GlobalRoleBinding{&restrictedAdminGRB}, nil).AnyTimes()
grbCacheMock.EXPECT().GetByIndex(gomock.Any(), resolvers.GetUserKey(adminUser, "")).Return(grbs, nil).AnyTimes()
grbCacheMock.EXPECT().AddIndexer(gomock.Any(), gomock.Any()).AnyTimes()
grCacheMock.EXPECT().Get(baseGR.Name).Return(&baseGR, nil).AnyTimes()
rtCacheMock.EXPECT().Get(clusterOwnerRT.Name).Return(&clusterOwnerRT, nil).AnyTimes()
rtCacheMock.EXPECT().Get(baseRT.Name).Return(&baseRT, nil).AnyTimes()
rtCacheMock.EXPECT().Get(clusterOwnerRT.Name).Return(&clusterOwnerRT, nil).AnyTimes()
k8Fake := &k8testing.Fake{}
fakeSAR := &k8fake.FakeSubjectAccessReviews{Fake: &k8fake.FakeAuthorizationV1{Fake: k8Fake}}

Expand Down
181 changes: 181 additions & 0 deletions pkg/resources/management.cattle.io/v3/globalrole/validator_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -870,6 +870,187 @@ func TestAdmit(t *testing.T) {
},
allowed: true,
},
{
name: "restricted admin can create GR with InheritedFleetWorkspacePermissions and fleet rules",
args: args{
username: restrictedAdminUser,
newGR: func() *v3.GlobalRole {
baseGR := newDefaultGR()
baseGR.InheritedFleetWorkspacePermissions = &v3.FleetWorkspacePermission{
ResourceRules: []v1.PolicyRule{
{
Verbs: []string{"get", "list", "create", "delete"},
APIGroups: []string{"fleet.cattle.io"},
Resources: []string{"bundles", "gitrepos"},
},
},
WorkspaceVerbs: []string{
"get",
"create",
},
}
return baseGR
},
stateSetup: func(state testState) {
state.grCacheMock.EXPECT().Get(restrictedAdminGR.Name).Return(&restrictedAdminGR, nil).AnyTimes()
setSarResponse(false, nil, testUser, newDefaultGR().Name, state.sarMock)
},
},

allowed: true,
},
{
name: "restricted admin can create GR with InheritedFleetWorkspacePermissions and fleet rules and *",
args: args{
username: restrictedAdminUser,
newGR: func() *v3.GlobalRole {
baseGR := newDefaultGR()
baseGR.InheritedFleetWorkspacePermissions = &v3.FleetWorkspacePermission{
ResourceRules: []v1.PolicyRule{
{
Verbs: []string{"*"},
APIGroups: []string{"fleet.cattle.io"},
Resources: []string{"bundles", "gitrepos"},
},
},
WorkspaceVerbs: []string{
"*",
},
}
return baseGR
},
stateSetup: func(state testState) {
state.grCacheMock.EXPECT().Get(restrictedAdminGR.Name).Return(&restrictedAdminGR, nil).AnyTimes()
setSarResponse(false, nil, testUser, newDefaultGR().Name, state.sarMock)
},
},

allowed: true,
},
{
name: "restricted admin can't create GR with InheritedFleetWorkspacePermissions and pod rules",
args: args{
username: restrictedAdminUser,
newGR: func() *v3.GlobalRole {
baseGR := newDefaultGR()
baseGR.InheritedFleetWorkspacePermissions = &v3.FleetWorkspacePermission{
ResourceRules: []v1.PolicyRule{
{
Verbs: []string{"get", ""},
APIGroups: []string{""},
Resources: []string{"pods"},
},
},
WorkspaceVerbs: []string{
"get",
"create",
},
}
return baseGR
},
stateSetup: func(state testState) {
state.grCacheMock.EXPECT().Get(restrictedAdminGR.Name).Return(&restrictedAdminGR, nil).AnyTimes()
setSarResponse(false, nil, testUser, newDefaultGR().Name, state.sarMock)
},
},

allowed: false,
},
{
name: "restricted admin can update GR with InheritedFleetWorkspacePermissions and fleet rules",
args: args{
username: restrictedAdminUser,
oldGR: func() *v3.GlobalRole {
return newDefaultGR()
},
newGR: func() *v3.GlobalRole {
baseGR := newDefaultGR()
baseGR.InheritedFleetWorkspacePermissions = &v3.FleetWorkspacePermission{
ResourceRules: []v1.PolicyRule{
{
Verbs: []string{"get", "list", "create", "delete"},
APIGroups: []string{"fleet.cattle.io"},
Resources: []string{"bundles", "gitrepos"},
},
},
WorkspaceVerbs: []string{
"get",
"create",
},
}
return baseGR
},
stateSetup: func(state testState) {
state.grCacheMock.EXPECT().Get(restrictedAdminGR.Name).Return(&restrictedAdminGR, nil).AnyTimes()
setSarResponse(false, nil, testUser, newDefaultGR().Name, state.sarMock)
},
},

allowed: true,
},
{
name: "restricted admin can update GR with InheritedFleetWorkspacePermissions and fleet rules and *",
args: args{
username: restrictedAdminUser,
oldGR: func() *v3.GlobalRole {
return newDefaultGR()
},
newGR: func() *v3.GlobalRole {
baseGR := newDefaultGR()
baseGR.InheritedFleetWorkspacePermissions = &v3.FleetWorkspacePermission{
ResourceRules: []v1.PolicyRule{
{
Verbs: []string{"*"},
APIGroups: []string{"fleet.cattle.io"},
Resources: []string{"bundles", "gitrepos"},
},
},
WorkspaceVerbs: []string{
"*",
},
}
return baseGR
},
stateSetup: func(state testState) {
state.grCacheMock.EXPECT().Get(restrictedAdminGR.Name).Return(&restrictedAdminGR, nil).AnyTimes()
setSarResponse(false, nil, testUser, newDefaultGR().Name, state.sarMock)
},
},

allowed: true,
},
{
name: "restricted admin can't update GR with InheritedFleetWorkspacePermissions and pod rules",
args: args{
username: restrictedAdminUser,
oldGR: func() *v3.GlobalRole {
return newDefaultGR()
},
newGR: func() *v3.GlobalRole {
baseGR := newDefaultGR()
baseGR.InheritedFleetWorkspacePermissions = &v3.FleetWorkspacePermission{
ResourceRules: []v1.PolicyRule{
{
Verbs: []string{"get", ""},
APIGroups: []string{""},
Resources: []string{"pods"},
},
},
WorkspaceVerbs: []string{
"get",
"create",
},
}
return baseGR
},
stateSetup: func(state testState) {
state.grCacheMock.EXPECT().Get(restrictedAdminGR.Name).Return(&restrictedAdminGR, nil).AnyTimes()
setSarResponse(false, nil, testUser, newDefaultGR().Name, state.sarMock)
},
},

allowed: false,
},
}

for _, test := range tests {
Expand Down
Loading

0 comments on commit acc851a

Please sign in to comment.