61039345 |
// +build integration,!no-etcd
package integration
import ( |
7d4a61e2 |
"fmt" |
fdd9bb5b |
"reflect" |
61039345 |
"strings"
"testing" |
7d4a61e2 |
"time" |
61039345 |
|
83c702b4 |
kapierror "k8s.io/kubernetes/pkg/api/errors"
"k8s.io/kubernetes/pkg/fields"
"k8s.io/kubernetes/pkg/labels"
"k8s.io/kubernetes/pkg/util"
"k8s.io/kubernetes/pkg/util/wait" |
61039345 |
|
fdd9bb5b |
authorizationapi "github.com/openshift/origin/pkg/authorization/api" |
d3b5c424 |
"github.com/openshift/origin/pkg/client" |
01221dec |
policy "github.com/openshift/origin/pkg/cmd/admin/policy" |
d1bc1b89 |
"github.com/openshift/origin/pkg/cmd/server/bootstrappolicy" |
8c5b680f |
testutil "github.com/openshift/origin/test/util" |
61039345 |
)
|
969809c1 |
func TestAuthorizationRestrictedAccessForProjectAdmins(t *testing.T) { |
47caf770 |
_, clusterAdminKubeConfig, err := testutil.StartTestMaster() |
61039345 |
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
|
47caf770 |
clusterAdminClient, err := testutil.GetClusterAdminClient(clusterAdminKubeConfig) |
61039345 |
if err != nil { |
e6d83541 |
t.Fatalf("unexpected error: %v", err) |
61039345 |
}
|
47caf770 |
clusterAdminClientConfig, err := testutil.GetClusterAdminClientConfig(clusterAdminKubeConfig) |
61039345 |
if err != nil { |
e6d83541 |
t.Fatalf("unexpected error: %v", err) |
61039345 |
} |
47caf770 |
haroldClient, err := testutil.CreateNewProject(clusterAdminClient, *clusterAdminClientConfig, "hammer-project", "harold")
if err != nil { |
e6d83541 |
t.Fatalf("unexpected error: %v", err) |
47caf770 |
} |
e6d83541 |
|
47caf770 |
markClient, err := testutil.CreateNewProject(clusterAdminClient, *clusterAdminClientConfig, "mallet-project", "mark") |
61039345 |
if err != nil { |
e6d83541 |
t.Fatalf("unexpected error: %v", err) |
61039345 |
}
|
969809c1 |
_, err = haroldClient.DeploymentConfigs("hammer-project").List(labels.Everything(), fields.Everything()) |
61039345 |
if err != nil { |
e6d83541 |
t.Fatalf("unexpected error: %v", err) |
61039345 |
}
|
969809c1 |
_, err = markClient.DeploymentConfigs("hammer-project").List(labels.Everything(), fields.Everything()) |
c76aabad |
if (err == nil) || !kapierror.IsForbidden(err) { |
e6d83541 |
t.Fatalf("unexpected error: %v", err) |
61039345 |
}
// projects are a special case where a get of a project actually sets a namespace. Make sure that
// the namespace is properly special cased and set for authorization rules
_, err = haroldClient.Projects().Get("hammer-project")
if err != nil { |
e6d83541 |
t.Fatalf("unexpected error: %v", err) |
61039345 |
}
_, err = markClient.Projects().Get("hammer-project") |
c76aabad |
if (err == nil) || !kapierror.IsForbidden(err) { |
e6d83541 |
t.Fatalf("unexpected error: %v", err) |
61039345 |
}
// wait for the project authorization cache to catch the change. It is on a one second period |
f333605e |
waitForProject(t, haroldClient, "hammer-project", 1*time.Second, 10)
waitForProject(t, markClient, "mallet-project", 1*time.Second, 10) |
7d4a61e2 |
} |
61039345 |
|
7d4a61e2 |
// waitForProject will execute a client list of projects looking for the project with specified name
// if not found, it will retry up to numRetries at the specified delayInterval
func waitForProject(t *testing.T, client client.Interface, projectName string, delayInterval time.Duration, numRetries int) {
for i := 0; i <= numRetries; i++ {
projects, err := client.Projects().List(labels.Everything(), fields.Everything())
if err != nil {
t.Errorf("unexpected error: %v", err)
}
if (len(projects.Items) == 1) && (projects.Items[0].Name == projectName) {
fmt.Printf("Waited %v times with interval %v\n", i, delayInterval)
return
} else {
time.Sleep(delayInterval)
}
}
t.Errorf("expected project %v not found", projectName) |
61039345 |
} |
fdd9bb5b |
|
969809c1 |
func TestAuthorizationOnlyResolveRolesForBindingsThatMatter(t *testing.T) { |
47caf770 |
_, clusterAdminKubeConfig, err := testutil.StartTestMaster() |
1d15a871 |
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
|
47caf770 |
clusterAdminClient, err := testutil.GetClusterAdminClient(clusterAdminKubeConfig) |
1d15a871 |
if err != nil { |
e6d83541 |
t.Fatalf("unexpected error: %v", err) |
1d15a871 |
}
|
01221dec |
addValerie := &policy.RoleModificationOptions{ |
ba6a7bc8 |
RoleNamespace: "",
RoleName: bootstrappolicy.ViewRoleName,
RoleBindingAccessor: policy.NewClusterRoleBindingAccessor(clusterAdminClient),
Users: []string{"valerie"}, |
1d15a871 |
} |
01221dec |
if err := addValerie.AddRole(); err != nil { |
e6d83541 |
t.Fatalf("unexpected error: %v", err) |
1d15a871 |
}
|
ba6a7bc8 |
if err = clusterAdminClient.ClusterRoles().Delete(bootstrappolicy.ViewRoleName); err != nil { |
e6d83541 |
t.Fatalf("unexpected error: %v", err) |
1d15a871 |
}
|
01221dec |
addEdgar := &policy.RoleModificationOptions{ |
ba6a7bc8 |
RoleNamespace: "",
RoleName: bootstrappolicy.EditRoleName,
RoleBindingAccessor: policy.NewClusterRoleBindingAccessor(clusterAdminClient),
Users: []string{"edgar"}, |
1d15a871 |
} |
01221dec |
if err := addEdgar.AddRole(); err != nil { |
e6d83541 |
t.Fatalf("unexpected error: %v", err) |
1d15a871 |
}
// try to add Valerie to a non-existent role |
01221dec |
if err := addValerie.AddRole(); !kapierror.IsNotFound(err) { |
e6d83541 |
t.Fatalf("unexpected error: %v", err) |
1d15a871 |
}
}
|
fdd9bb5b |
// TODO this list should start collapsing as we continue to tighten access on generated system ids |
1ab326d9 |
var globalClusterAdminUsers = util.NewStringSet()
var globalClusterAdminGroups = util.NewStringSet("system:cluster-admins", "system:masters") |
fdd9bb5b |
|
d3b5c424 |
type resourceAccessReviewTest struct {
clientInterface client.ResourceAccessReviewInterface
review *authorizationapi.ResourceAccessReview
|
f35dc17e |
response authorizationapi.ResourceAccessReviewResponse
err string |
d3b5c424 |
}
func (test resourceAccessReviewTest) run(t *testing.T) {
actualResponse, err := test.clientInterface.Create(test.review)
if len(test.err) > 0 {
if err == nil {
t.Errorf("Expected error: %v", test.err)
} else if !strings.Contains(err.Error(), test.err) {
t.Errorf("expected %v, got %v", test.err, err)
}
} else {
if err != nil {
t.Errorf("unexpected error: %v", err)
}
}
|
05ead3f2 |
if actualResponse.Namespace != test.response.Namespace || !reflect.DeepEqual(actualResponse.Users.List(), test.response.Users.List()) || !reflect.DeepEqual(actualResponse.Groups.List(), test.response.Groups.List()) { |
f35dc17e |
t.Errorf("%#v: expected %v, got %v", test.review, test.response, actualResponse) |
d3b5c424 |
}
}
|
969809c1 |
func TestAuthorizationResourceAccessReview(t *testing.T) { |
47caf770 |
_, clusterAdminKubeConfig, err := testutil.StartTestMaster() |
fdd9bb5b |
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
|
47caf770 |
clusterAdminClient, err := testutil.GetClusterAdminClient(clusterAdminKubeConfig)
if err != nil { |
e6d83541 |
t.Fatalf("unexpected error: %v", err) |
47caf770 |
}
clusterAdminClientConfig, err := testutil.GetClusterAdminClientConfig(clusterAdminKubeConfig) |
fdd9bb5b |
if err != nil { |
e6d83541 |
t.Fatalf("unexpected error: %v", err) |
fdd9bb5b |
}
|
47caf770 |
haroldClient, err := testutil.CreateNewProject(clusterAdminClient, *clusterAdminClientConfig, "hammer-project", "harold") |
fdd9bb5b |
if err != nil { |
e6d83541 |
t.Fatalf("unexpected error: %v", err) |
fdd9bb5b |
} |
47caf770 |
|
e6d83541 |
markClient, err := testutil.CreateNewProject(clusterAdminClient, *clusterAdminClientConfig, "mallet-project", "mark") |
fdd9bb5b |
if err != nil { |
e6d83541 |
t.Fatalf("unexpected error: %v", err) |
fdd9bb5b |
}
|
01221dec |
addValerie := &policy.RoleModificationOptions{ |
ba6a7bc8 |
RoleNamespace: "",
RoleName: bootstrappolicy.ViewRoleName,
RoleBindingAccessor: policy.NewLocalRoleBindingAccessor("hammer-project", haroldClient),
Users: []string{"valerie"}, |
fdd9bb5b |
} |
01221dec |
if err := addValerie.AddRole(); err != nil { |
e6d83541 |
t.Fatalf("unexpected error: %v", err) |
fdd9bb5b |
}
|
01221dec |
addEdgar := &policy.RoleModificationOptions{ |
ba6a7bc8 |
RoleNamespace: "",
RoleName: bootstrappolicy.EditRoleName,
RoleBindingAccessor: policy.NewLocalRoleBindingAccessor("mallet-project", markClient),
Users: []string{"edgar"}, |
fdd9bb5b |
} |
01221dec |
if err := addEdgar.AddRole(); err != nil { |
e6d83541 |
t.Fatalf("unexpected error: %v", err) |
fdd9bb5b |
}
requestWhoCanViewDeployments := &authorizationapi.ResourceAccessReview{Verb: "get", Resource: "deployments"}
|
d3b5c424 |
{
test := resourceAccessReviewTest{
clientInterface: haroldClient.ResourceAccessReviews("hammer-project"),
review: requestWhoCanViewDeployments, |
f35dc17e |
response: authorizationapi.ResourceAccessReviewResponse{ |
f69463d3 |
Users: util.NewStringSet("harold", "valerie"), |
f35dc17e |
Groups: globalClusterAdminGroups,
Namespace: "hammer-project",
}, |
d3b5c424 |
} |
f35dc17e |
test.response.Users.Insert(globalClusterAdminUsers.List()...) |
4dd6cd97 |
test.response.Groups.Insert("system:cluster-readers") |
d3b5c424 |
test.run(t) |
fdd9bb5b |
} |
d3b5c424 |
{
test := resourceAccessReviewTest{
clientInterface: markClient.ResourceAccessReviews("mallet-project"),
review: requestWhoCanViewDeployments, |
f35dc17e |
response: authorizationapi.ResourceAccessReviewResponse{ |
f69463d3 |
Users: util.NewStringSet("mark", "edgar"), |
f35dc17e |
Groups: globalClusterAdminGroups,
Namespace: "mallet-project",
}, |
d3b5c424 |
} |
f35dc17e |
test.response.Users.Insert(globalClusterAdminUsers.List()...) |
4dd6cd97 |
test.response.Groups.Insert("system:cluster-readers") |
d3b5c424 |
test.run(t) |
fdd9bb5b |
} |
d3b5c424 |
// mark should not be able to make global access review requests
{
test := resourceAccessReviewTest{ |
384f11fa |
clientInterface: markClient.ClusterResourceAccessReviews(), |
d3b5c424 |
review: requestWhoCanViewDeployments, |
92763011 |
err: "cannot ", |
d3b5c424 |
}
test.run(t) |
fdd9bb5b |
}
|
d3b5c424 |
// a cluster-admin should be able to make global access review requests
{
test := resourceAccessReviewTest{ |
384f11fa |
clientInterface: clusterAdminClient.ClusterResourceAccessReviews(), |
d3b5c424 |
review: requestWhoCanViewDeployments, |
f35dc17e |
response: authorizationapi.ResourceAccessReviewResponse{
Users: globalClusterAdminUsers,
Groups: globalClusterAdminGroups,
}, |
d3b5c424 |
} |
4dd6cd97 |
test.response.Groups.Insert("system:cluster-readers") |
d3b5c424 |
test.run(t) |
fdd9bb5b |
} |
d3b5c424 |
} |
fdd9bb5b |
|
d3b5c424 |
type subjectAccessReviewTest struct { |
05ead3f2 |
description string |
d3b5c424 |
clientInterface client.SubjectAccessReviewInterface
review *authorizationapi.SubjectAccessReview
response authorizationapi.SubjectAccessReviewResponse
err string
}
func (test subjectAccessReviewTest) run(t *testing.T) { |
8c5b680f |
failMessage := ""
err := wait.Poll(testutil.PolicyCachePollInterval, testutil.PolicyCachePollTimeout, func() (bool, error) {
actualResponse, err := test.clientInterface.Create(test.review)
if len(test.err) > 0 {
if err == nil {
failMessage = fmt.Sprintf("%s: Expected error: %v", test.description, test.err)
return false, nil
} else if !strings.HasPrefix(err.Error(), test.err) {
failMessage = fmt.Sprintf("%s: expected\n\t%v\ngot\n\t%v", test.description, test.err, err)
return false, nil
}
} else {
if err != nil {
failMessage = fmt.Sprintf("%s: unexpected error: %v", test.description, err)
return false, nil
} |
d3b5c424 |
} |
8c5b680f |
if (actualResponse.Namespace != test.response.Namespace) ||
(actualResponse.Allowed != test.response.Allowed) ||
(!strings.HasPrefix(actualResponse.Reason, test.response.Reason)) {
failMessage = fmt.Sprintf("%s: from review\n\t%#v\nexpected\n\t%#v\ngot\n\t%#v", test.description, test.review, &test.response, actualResponse)
return false, nil |
d3b5c424 |
}
|
8c5b680f |
failMessage = ""
return true, nil
})
if err != nil {
t.Error(err)
}
if len(failMessage) != 0 {
t.Error(failMessage) |
fdd9bb5b |
} |
d3b5c424 |
} |
fdd9bb5b |
|
969809c1 |
func TestAuthorizationSubjectAccessReview(t *testing.T) { |
47caf770 |
_, clusterAdminKubeConfig, err := testutil.StartTestMaster() |
d3b5c424 |
if err != nil {
t.Fatalf("unexpected error: %v", err) |
fdd9bb5b |
}
|
47caf770 |
clusterAdminClient, err := testutil.GetClusterAdminClient(clusterAdminKubeConfig) |
fdd9bb5b |
if err != nil { |
e6d83541 |
t.Fatalf("unexpected error: %v", err) |
fdd9bb5b |
}
|
47caf770 |
clusterAdminClientConfig, err := testutil.GetClusterAdminClientConfig(clusterAdminKubeConfig) |
d3b5c424 |
if err != nil { |
e6d83541 |
t.Fatalf("unexpected error: %v", err) |
d3b5c424 |
} |
47caf770 |
haroldClient, err := testutil.CreateNewProject(clusterAdminClient, *clusterAdminClientConfig, "hammer-project", "harold")
if err != nil { |
e6d83541 |
t.Fatalf("unexpected error: %v", err) |
47caf770 |
}
|
e6d83541 |
markClient, err := testutil.CreateNewProject(clusterAdminClient, *clusterAdminClientConfig, "mallet-project", "mark") |
fdd9bb5b |
if err != nil { |
e6d83541 |
t.Fatalf("unexpected error: %v", err) |
fdd9bb5b |
} |
d3b5c424 |
|
33608784 |
dannyClient, _, _, err := testutil.GetClientForUser(*clusterAdminClientConfig, "danny") |
05ead3f2 |
if err != nil {
t.Fatalf("error requesting token: %v", err)
}
addDanny := &policy.RoleModificationOptions{
RoleNamespace: "",
RoleName: bootstrappolicy.ViewRoleName,
RoleBindingAccessor: policy.NewLocalRoleBindingAccessor("default", clusterAdminClient),
Users: []string{"danny"},
}
if err := addDanny.AddRole(); err != nil {
t.Errorf("unexpected error: %v", err)
}
askCanDannyGetProject := &authorizationapi.SubjectAccessReview{User: "danny", Verb: "get", Resource: "projects"}
subjectAccessReviewTest{
description: "cluster admin told danny can get project default",
clientInterface: clusterAdminClient.SubjectAccessReviews("default"),
review: askCanDannyGetProject,
response: authorizationapi.SubjectAccessReviewResponse{
Allowed: true,
Reason: "allowed by rule in default",
Namespace: "default",
},
}.run(t)
subjectAccessReviewTest{
description: "cluster admin told danny cannot get projects cluster-wide",
clientInterface: clusterAdminClient.ClusterSubjectAccessReviews(),
review: askCanDannyGetProject,
response: authorizationapi.SubjectAccessReviewResponse{
Allowed: false,
Reason: `User "danny" cannot get projects at the cluster scope`,
Namespace: "",
},
}.run(t)
subjectAccessReviewTest{
description: "as danny, can I make cluster subject access reviews",
clientInterface: dannyClient.ClusterSubjectAccessReviews(),
review: askCanDannyGetProject,
err: `User "danny" cannot create subjectaccessreviews at the cluster scope`,
}.run(t)
|
01221dec |
addValerie := &policy.RoleModificationOptions{ |
ba6a7bc8 |
RoleNamespace: "",
RoleName: bootstrappolicy.ViewRoleName,
RoleBindingAccessor: policy.NewLocalRoleBindingAccessor("hammer-project", haroldClient),
Users: []string{"valerie"}, |
d3b5c424 |
} |
01221dec |
if err := addValerie.AddRole(); err != nil { |
d3b5c424 |
t.Errorf("unexpected error: %v", err)
}
|
01221dec |
addEdgar := &policy.RoleModificationOptions{ |
ba6a7bc8 |
RoleNamespace: "",
RoleName: bootstrappolicy.EditRoleName,
RoleBindingAccessor: policy.NewLocalRoleBindingAccessor("mallet-project", markClient),
Users: []string{"edgar"}, |
fdd9bb5b |
} |
01221dec |
if err := addEdgar.AddRole(); err != nil { |
e6d83541 |
t.Fatalf("unexpected error: %v", err) |
fdd9bb5b |
} |
d3b5c424 |
|
f69463d3 |
askCanValerieGetProject := &authorizationapi.SubjectAccessReview{User: "valerie", Verb: "get", Resource: "projects"} |
d3b5c424 |
subjectAccessReviewTest{ |
05ead3f2 |
description: "harold told valerie can get project hammer-project", |
d3b5c424 |
clientInterface: haroldClient.SubjectAccessReviews("hammer-project"),
review: askCanValerieGetProject,
response: authorizationapi.SubjectAccessReviewResponse{
Allowed: true,
Reason: "allowed by rule in hammer-project",
Namespace: "hammer-project",
},
}.run(t)
subjectAccessReviewTest{ |
05ead3f2 |
description: "mark told valerie cannot get project mallet-project", |
d3b5c424 |
clientInterface: markClient.SubjectAccessReviews("mallet-project"),
review: askCanValerieGetProject,
response: authorizationapi.SubjectAccessReviewResponse{
Allowed: false, |
05ead3f2 |
Reason: `User "valerie" cannot get projects in project "mallet-project"`, |
d3b5c424 |
Namespace: "mallet-project",
},
}.run(t)
|
f69463d3 |
askCanEdgarDeletePods := &authorizationapi.SubjectAccessReview{User: "edgar", Verb: "delete", Resource: "pods"} |
d3b5c424 |
subjectAccessReviewTest{ |
05ead3f2 |
description: "mark told edgar can delete pods in mallet-project", |
d3b5c424 |
clientInterface: markClient.SubjectAccessReviews("mallet-project"),
review: askCanEdgarDeletePods,
response: authorizationapi.SubjectAccessReviewResponse{
Allowed: true,
Reason: "allowed by rule in mallet-project",
Namespace: "mallet-project",
},
}.run(t)
subjectAccessReviewTest{ |
05ead3f2 |
description: "harold denied ability to run subject access review in project mallet-project", |
d3b5c424 |
clientInterface: haroldClient.SubjectAccessReviews("mallet-project"),
review: askCanEdgarDeletePods, |
05ead3f2 |
err: `User "harold" cannot create subjectaccessreviews in project "mallet-project"`, |
d3b5c424 |
}.run(t)
|
f69463d3 |
askCanHaroldUpdateProject := &authorizationapi.SubjectAccessReview{User: "harold", Verb: "update", Resource: "projects"} |
d3b5c424 |
subjectAccessReviewTest{ |
05ead3f2 |
description: "harold told harold can update project hammer-project", |
d3b5c424 |
clientInterface: haroldClient.SubjectAccessReviews("hammer-project"),
review: askCanHaroldUpdateProject,
response: authorizationapi.SubjectAccessReviewResponse{
Allowed: true,
Reason: "allowed by rule in hammer-project",
Namespace: "hammer-project",
},
}.run(t)
|
f35dc17e |
askCanClusterAdminsCreateProject := &authorizationapi.SubjectAccessReview{Groups: util.NewStringSet("system:cluster-admins"), Verb: "create", Resource: "projects"} |
d3b5c424 |
subjectAccessReviewTest{ |
05ead3f2 |
description: "cluster admin told cluster admins can create projects", |
384f11fa |
clientInterface: clusterAdminClient.ClusterSubjectAccessReviews(), |
d3b5c424 |
review: askCanClusterAdminsCreateProject,
response: authorizationapi.SubjectAccessReviewResponse{
Allowed: true, |
c3fc484a |
Reason: "allowed by cluster rule", |
d3b5c424 |
Namespace: "",
},
}.run(t)
subjectAccessReviewTest{ |
05ead3f2 |
description: "harold denied ability to run cluster subject access review", |
384f11fa |
clientInterface: haroldClient.ClusterSubjectAccessReviews(), |
d3b5c424 |
review: askCanClusterAdminsCreateProject, |
05ead3f2 |
err: `User "harold" cannot create subjectaccessreviews at the cluster scope`, |
d3b5c424 |
}.run(t) |
5b57b99f |
|
05ead3f2 |
askCanICreatePods := &authorizationapi.SubjectAccessReview{Verb: "create", Resource: "pods"} |
5b57b99f |
subjectAccessReviewTest{ |
05ead3f2 |
description: "harold told he can create pods in project hammer-project", |
5b57b99f |
clientInterface: haroldClient.SubjectAccessReviews("hammer-project"),
review: askCanICreatePods,
response: authorizationapi.SubjectAccessReviewResponse{
Allowed: true,
Reason: "allowed by rule in hammer-project",
Namespace: "hammer-project",
},
}.run(t)
askCanICreatePolicyBindings := &authorizationapi.SubjectAccessReview{Verb: "create", Resource: "policybindings"}
subjectAccessReviewTest{ |
05ead3f2 |
description: "harold told he can create policybindings in project hammer-project", |
5b57b99f |
clientInterface: haroldClient.SubjectAccessReviews("hammer-project"),
review: askCanICreatePolicyBindings,
response: authorizationapi.SubjectAccessReviewResponse{
Allowed: false, |
05ead3f2 |
Reason: `User "harold" cannot create policybindings in project "hammer-project"`, |
5b57b99f |
Namespace: "hammer-project",
},
}.run(t)
|
fdd9bb5b |
} |