Browse code

Allow anonymous users to check their access

Jordan Liggitt authored on 2016/07/16 05:06:51
Showing 5 changed files
... ...
@@ -51,15 +51,16 @@ const (
51 51
 
52 52
 // Roles
53 53
 const (
54
-	ClusterAdminRoleName    = "cluster-admin"
55
-	SudoerRoleName          = "sudoer"
56
-	ClusterReaderRoleName   = "cluster-reader"
57
-	AdminRoleName           = "admin"
58
-	EditRoleName            = "edit"
59
-	ViewRoleName            = "view"
60
-	SelfProvisionerRoleName = "self-provisioner"
61
-	BasicUserRoleName       = "basic-user"
62
-	StatusCheckerRoleName   = "cluster-status"
54
+	ClusterAdminRoleName       = "cluster-admin"
55
+	SudoerRoleName             = "sudoer"
56
+	ClusterReaderRoleName      = "cluster-reader"
57
+	AdminRoleName              = "admin"
58
+	EditRoleName               = "edit"
59
+	ViewRoleName               = "view"
60
+	SelfProvisionerRoleName    = "self-provisioner"
61
+	BasicUserRoleName          = "basic-user"
62
+	StatusCheckerRoleName      = "cluster-status"
63
+	SelfAccessReviewerRoleName = "self-access-reviewer"
63 64
 
64 65
 	RegistryAdminRoleName  = "registry-admin"
65 66
 	RegistryViewerRoleName = "registry-viewer"
... ...
@@ -97,29 +98,30 @@ const (
97 97
 
98 98
 // RoleBindings
99 99
 const (
100
-	SelfProvisionerRoleBindingName   = SelfProvisionerRoleName + "s"
101
-	DeployerRoleBindingName          = DeployerRoleName + "s"
102
-	ClusterAdminRoleBindingName      = ClusterAdminRoleName + "s"
103
-	ClusterReaderRoleBindingName     = ClusterReaderRoleName + "s"
104
-	BasicUserRoleBindingName         = BasicUserRoleName + "s"
105
-	OAuthTokenDeleterRoleBindingName = OAuthTokenDeleterRoleName + "s"
106
-	StatusCheckerRoleBindingName     = StatusCheckerRoleName + "-binding"
107
-	ImagePullerRoleBindingName       = ImagePullerRoleName + "s"
108
-	ImageBuilderRoleBindingName      = ImageBuilderRoleName + "s"
109
-	RouterRoleBindingName            = RouterRoleName + "s"
110
-	RegistryRoleBindingName          = RegistryRoleName + "s"
111
-	MasterRoleBindingName            = MasterRoleName + "s"
112
-	NodeRoleBindingName              = NodeRoleName + "s"
113
-	NodeProxierRoleBindingName       = NodeProxierRoleName + "s"
114
-	NodeAdminRoleBindingName         = NodeAdminRoleName + "s"
115
-	NodeReaderRoleBindingName        = NodeReaderRoleName + "s"
116
-	SDNReaderRoleBindingName         = SDNReaderRoleName + "s"
117
-	SDNManagerRoleBindingName        = SDNManagerRoleName + "s"
118
-	WebHooksRoleBindingName          = WebHooksRoleName + "s"
119
-	DiscoveryRoleBindingName         = DiscoveryRoleName + "-binding"
120
-	RegistryAdminRoleBindingName     = RegistryAdminRoleName + "s"
121
-	RegistryViewerRoleBindingName    = RegistryViewerRoleName + "s"
122
-	RegistryEditorRoleBindingName    = RegistryEditorRoleName + "s"
100
+	SelfAccessReviewerRoleBindingName = SelfAccessReviewerRoleName + "s"
101
+	SelfProvisionerRoleBindingName    = SelfProvisionerRoleName + "s"
102
+	DeployerRoleBindingName           = DeployerRoleName + "s"
103
+	ClusterAdminRoleBindingName       = ClusterAdminRoleName + "s"
104
+	ClusterReaderRoleBindingName      = ClusterReaderRoleName + "s"
105
+	BasicUserRoleBindingName          = BasicUserRoleName + "s"
106
+	OAuthTokenDeleterRoleBindingName  = OAuthTokenDeleterRoleName + "s"
107
+	StatusCheckerRoleBindingName      = StatusCheckerRoleName + "-binding"
108
+	ImagePullerRoleBindingName        = ImagePullerRoleName + "s"
109
+	ImageBuilderRoleBindingName       = ImageBuilderRoleName + "s"
110
+	RouterRoleBindingName             = RouterRoleName + "s"
111
+	RegistryRoleBindingName           = RegistryRoleName + "s"
112
+	MasterRoleBindingName             = MasterRoleName + "s"
113
+	NodeRoleBindingName               = NodeRoleName + "s"
114
+	NodeProxierRoleBindingName        = NodeProxierRoleName + "s"
115
+	NodeAdminRoleBindingName          = NodeAdminRoleName + "s"
116
+	NodeReaderRoleBindingName         = NodeReaderRoleName + "s"
117
+	SDNReaderRoleBindingName          = SDNReaderRoleName + "s"
118
+	SDNManagerRoleBindingName         = SDNManagerRoleName + "s"
119
+	WebHooksRoleBindingName           = WebHooksRoleName + "s"
120
+	DiscoveryRoleBindingName          = DiscoveryRoleName + "-binding"
121
+	RegistryAdminRoleBindingName      = RegistryAdminRoleName + "s"
122
+	RegistryViewerRoleBindingName     = RegistryViewerRoleName + "s"
123
+	RegistryEditorRoleBindingName     = RegistryEditorRoleName + "s"
123 124
 
124 125
 	BuildStrategyDockerRoleBindingName          = BuildStrategyDockerRoleName + "-binding"
125 126
 	BuildStrategyCustomRoleBindingName          = BuildStrategyCustomRoleName + "-binding"
... ...
@@ -361,6 +361,15 @@ func GetBootstrapClusterRoles() []authorizationapi.ClusterRole {
361 361
 		},
362 362
 		{
363 363
 			ObjectMeta: kapi.ObjectMeta{
364
+				Name: SelfAccessReviewerRoleName,
365
+			},
366
+			Rules: []authorizationapi.PolicyRule{
367
+				authorizationapi.NewRule("create").Groups(authzGroup).Resources("selfsubjectrulesreviews").RuleOrDie(),
368
+				{Verbs: sets.NewString("create"), APIGroups: []string{authzGroup}, Resources: sets.NewString("subjectaccessreviews", "localsubjectaccessreviews"), AttributeRestrictions: &authorizationapi.IsPersonalSubjectAccessReview{}},
369
+			},
370
+		},
371
+		{
372
+			ObjectMeta: kapi.ObjectMeta{
364 373
 				Name: SelfProvisionerRoleName,
365 374
 			},
366 375
 			Rules: []authorizationapi.PolicyRule{
... ...
@@ -774,6 +783,18 @@ func GetBootstrapClusterRoleBindings() []authorizationapi.ClusterRoleBinding {
774 774
 		},
775 775
 		{
776 776
 			ObjectMeta: kapi.ObjectMeta{
777
+				Name: SelfAccessReviewerRoleBindingName,
778
+			},
779
+			RoleRef: kapi.ObjectReference{
780
+				Name: SelfAccessReviewerRoleName,
781
+			},
782
+			Subjects: []kapi.ObjectReference{
783
+				{Kind: authorizationapi.SystemGroupKind, Name: AuthenticatedGroup},
784
+				{Kind: authorizationapi.SystemGroupKind, Name: UnauthenticatedGroup},
785
+			},
786
+		},
787
+		{
788
+			ObjectMeta: kapi.ObjectMeta{
777 789
 				Name: SelfProvisionerRoleBindingName,
778 790
 			},
779 791
 			RoleRef: kapi.ObjectReference{
... ...
@@ -21,6 +21,7 @@ import (
21 21
 	"github.com/openshift/origin/pkg/client"
22 22
 	policy "github.com/openshift/origin/pkg/cmd/admin/policy"
23 23
 	"github.com/openshift/origin/pkg/cmd/server/bootstrappolicy"
24
+	"github.com/openshift/origin/pkg/cmd/util/clientcmd"
24 25
 	deployapi "github.com/openshift/origin/pkg/deploy/api"
25 26
 	imageapi "github.com/openshift/origin/pkg/image/api"
26 27
 	oauthapi "github.com/openshift/origin/pkg/oauth/api"
... ...
@@ -732,6 +733,22 @@ func TestAuthorizationSubjectAccessReview(t *testing.T) {
732 732
 		t.Fatalf("error requesting token: %v", err)
733 733
 	}
734 734
 
735
+	anonymousConfig := clientcmd.AnonymousClientConfig(clusterAdminClientConfig)
736
+	anonymousClient, err := client.New(&anonymousConfig)
737
+	if err != nil {
738
+		t.Fatalf("error getting anonymous client: %v", err)
739
+	}
740
+
741
+	addAnonymous := &policy.RoleModificationOptions{
742
+		RoleNamespace:       "",
743
+		RoleName:            bootstrappolicy.EditRoleName,
744
+		RoleBindingAccessor: policy.NewLocalRoleBindingAccessor("hammer-project", clusterAdminClient),
745
+		Users:               []string{"system:anonymous"},
746
+	}
747
+	if err := addAnonymous.AddRole(); err != nil {
748
+		t.Errorf("unexpected error: %v", err)
749
+	}
750
+
735 751
 	addDanny := &policy.RoleModificationOptions{
736 752
 		RoleNamespace:       "",
737 753
 		RoleName:            bootstrappolicy.ViewRoleName,
... ...
@@ -774,6 +791,12 @@ func TestAuthorizationSubjectAccessReview(t *testing.T) {
774 774
 		clusterReview:    askCanDannyGetProject,
775 775
 		err:              `User "danny" cannot create subjectaccessreviews at the cluster scope`,
776 776
 	}.run(t)
777
+	subjectAccessReviewTest{
778
+		description:      "as anonymous, can I make cluster subject access reviews",
779
+		clusterInterface: anonymousClient.SubjectAccessReviews(),
780
+		clusterReview:    askCanDannyGetProject,
781
+		err:              `User "system:anonymous" cannot create subjectaccessreviews at the cluster scope`,
782
+	}.run(t)
777 783
 
778 784
 	addValerie := &policy.RoleModificationOptions{
779 785
 		RoleNamespace:       "",
... ...
@@ -834,12 +857,32 @@ func TestAuthorizationSubjectAccessReview(t *testing.T) {
834 834
 			Namespace: "mallet-project",
835 835
 		},
836 836
 	}.run(t)
837
+	// ensure unprivileged users cannot check other users' access
837 838
 	subjectAccessReviewTest{
838 839
 		description:    "harold denied ability to run subject access review in project mallet-project",
839 840
 		localInterface: haroldClient.LocalSubjectAccessReviews("mallet-project"),
840 841
 		localReview:    askCanEdgarDeletePods,
841 842
 		err:            `User "harold" cannot create localsubjectaccessreviews in project "mallet-project"`,
842 843
 	}.run(t)
844
+	subjectAccessReviewTest{
845
+		description:    "system:anonymous denied ability to run subject access review in project mallet-project",
846
+		localInterface: anonymousClient.LocalSubjectAccessReviews("mallet-project"),
847
+		localReview:    askCanEdgarDeletePods,
848
+		err:            `User "system:anonymous" cannot create localsubjectaccessreviews in project "mallet-project"`,
849
+	}.run(t)
850
+	// ensure message does not leak whether the namespace exists or not
851
+	subjectAccessReviewTest{
852
+		description:    "harold denied ability to run subject access review in project nonexistent-project",
853
+		localInterface: haroldClient.LocalSubjectAccessReviews("nonexistent-project"),
854
+		localReview:    askCanEdgarDeletePods,
855
+		err:            `User "harold" cannot create localsubjectaccessreviews in project "nonexistent-project"`,
856
+	}.run(t)
857
+	subjectAccessReviewTest{
858
+		description:    "system:anonymous denied ability to run subject access review in project nonexistent-project",
859
+		localInterface: anonymousClient.LocalSubjectAccessReviews("nonexistent-project"),
860
+		localReview:    askCanEdgarDeletePods,
861
+		err:            `User "system:anonymous" cannot create localsubjectaccessreviews in project "nonexistent-project"`,
862
+	}.run(t)
843 863
 
844 864
 	askCanHaroldUpdateProject := &authorizationapi.LocalSubjectAccessReview{
845 865
 		User:   "harold",
... ...
@@ -890,6 +933,61 @@ func TestAuthorizationSubjectAccessReview(t *testing.T) {
890 890
 			Namespace: "hammer-project",
891 891
 		},
892 892
 	}.run(t)
893
+	subjectAccessReviewTest{
894
+		description:    "system:anonymous told he can create pods in project hammer-project",
895
+		localInterface: anonymousClient.LocalSubjectAccessReviews("hammer-project"),
896
+		localReview:    askCanICreatePods,
897
+		response: authorizationapi.SubjectAccessReviewResponse{
898
+			Allowed:   true,
899
+			Reason:    "allowed by rule in hammer-project",
900
+			Namespace: "hammer-project",
901
+		},
902
+	}.run(t)
903
+
904
+	// test checking self permissions when denied
905
+	subjectAccessReviewTest{
906
+		description:    "harold told he cannot create pods in project mallet-project",
907
+		localInterface: haroldClient.LocalSubjectAccessReviews("mallet-project"),
908
+		localReview:    askCanICreatePods,
909
+		response: authorizationapi.SubjectAccessReviewResponse{
910
+			Allowed:   false,
911
+			Reason:    `User "harold" cannot create pods in project "mallet-project"`,
912
+			Namespace: "mallet-project",
913
+		},
914
+	}.run(t)
915
+	subjectAccessReviewTest{
916
+		description:    "system:anonymous told he cannot create pods in project mallet-project",
917
+		localInterface: anonymousClient.LocalSubjectAccessReviews("mallet-project"),
918
+		localReview:    askCanICreatePods,
919
+		response: authorizationapi.SubjectAccessReviewResponse{
920
+			Allowed:   false,
921
+			Reason:    `User "system:anonymous" cannot create pods in project "mallet-project"`,
922
+			Namespace: "mallet-project",
923
+		},
924
+	}.run(t)
925
+
926
+	// test checking self-permissions doesn't leak whether namespace exists or not
927
+	subjectAccessReviewTest{
928
+		description:    "harold told he cannot create pods in project nonexistent-project",
929
+		localInterface: haroldClient.LocalSubjectAccessReviews("nonexistent-project"),
930
+		localReview:    askCanICreatePods,
931
+		response: authorizationapi.SubjectAccessReviewResponse{
932
+			Allowed:   false,
933
+			Reason:    `User "harold" cannot create pods in project "nonexistent-project"`,
934
+			Namespace: "nonexistent-project",
935
+		},
936
+	}.run(t)
937
+	subjectAccessReviewTest{
938
+		description:    "system:anonymous told he cannot create pods in project nonexistent-project",
939
+		localInterface: anonymousClient.LocalSubjectAccessReviews("nonexistent-project"),
940
+		localReview:    askCanICreatePods,
941
+		response: authorizationapi.SubjectAccessReviewResponse{
942
+			Allowed:   false,
943
+			Reason:    `User "system:anonymous" cannot create pods in project "nonexistent-project"`,
944
+			Namespace: "nonexistent-project",
945
+		},
946
+	}.run(t)
947
+
893 948
 	askCanICreatePolicyBindings := &authorizationapi.LocalSubjectAccessReview{
894 949
 		Action: authorizationapi.AuthorizationAttributes{Verb: "create", Resource: "policybindings"},
895 950
 	}
... ...
@@ -73,6 +73,22 @@ items:
73 73
   userNames: null
74 74
 - apiVersion: v1
75 75
   groupNames:
76
+  - system:authenticated
77
+  - system:unauthenticated
78
+  kind: ClusterRoleBinding
79
+  metadata:
80
+    creationTimestamp: null
81
+    name: self-access-reviewers
82
+  roleRef:
83
+    name: self-access-reviewer
84
+  subjects:
85
+  - kind: SystemGroup
86
+    name: system:authenticated
87
+  - kind: SystemGroup
88
+    name: system:unauthenticated
89
+  userNames: null
90
+- apiVersion: v1
91
+  groupNames:
76 92
   - system:authenticated:oauth
77 93
   kind: ClusterRoleBinding
78 94
   metadata:
... ...
@@ -1301,6 +1301,29 @@ items:
1301 1301
   kind: ClusterRole
1302 1302
   metadata:
1303 1303
     creationTimestamp: null
1304
+    name: self-access-reviewer
1305
+  rules:
1306
+  - apiGroups:
1307
+    - ""
1308
+    attributeRestrictions: null
1309
+    resources:
1310
+    - selfsubjectrulesreviews
1311
+    verbs:
1312
+    - create
1313
+  - apiGroups:
1314
+    - ""
1315
+    attributeRestrictions:
1316
+      apiVersion: v1
1317
+      kind: IsPersonalSubjectAccessReview
1318
+    resources:
1319
+    - localsubjectaccessreviews
1320
+    - subjectaccessreviews
1321
+    verbs:
1322
+    - create
1323
+- apiVersion: v1
1324
+  kind: ClusterRole
1325
+  metadata:
1326
+    creationTimestamp: null
1304 1327
     name: self-provisioner
1305 1328
   rules:
1306 1329
   - apiGroups: