diff --git a/pkg/authorization/authorizer/scope/converter.go b/pkg/authorization/authorizer/scope/converter.go index 07ad842d59d0..4b7326c25992 100644 --- a/pkg/authorization/authorizer/scope/converter.go +++ b/pkg/authorization/authorizer/scope/converter.go @@ -121,9 +121,12 @@ const ( UserInfo = UserIndicator + "info" UserAccessCheck = UserIndicator + "check-access" - // UserListProject gives explicit permission to see the projects a user can see. This is often used to prime secondary ACL systems + // UserListScopedProjects gives explicit permission to see the projects that this token can see. + UserListScopedProjects = UserIndicator + "list-scoped-projects" + + // UserListAllProjects gives explicit permission to see the projects a user can see. This is often used to prime secondary ACL systems // unrelated to openshift and to display projects for selection in a secondary UI. - UserListProject = UserIndicator + "list-projects" + UserListAllProjects = UserIndicator + "list-projects" // UserFull includes all permissions of the user UserFull = UserIndicator + "full" @@ -138,7 +141,7 @@ func (userEvaluator) Handles(scope string) bool { func (userEvaluator) Validate(scope string) error { switch scope { - case UserFull, UserInfo, UserAccessCheck, UserListProject: + case UserFull, UserInfo, UserAccessCheck, UserListScopedProjects, UserListAllProjects: return nil } @@ -151,7 +154,9 @@ func (userEvaluator) Describe(scope string) (string, string, error) { return "Read-only access to your user information (including username, identities, and group membership)", "", nil case UserAccessCheck: return `Read-only access to view your privileges (for example, "can I create builds?")`, "", nil - case UserListProject: + case UserListScopedProjects: + return `Read-only access to list your projects viewable with this token and view their metadata (display name, description, etc.)`, "", nil + case UserListAllProjects: return `Read-only access to list your projects and view their metadata (display name, description, etc.)`, "", nil case UserFull: return `Full read/write access with all of your permissions`, `Includes any access you have to escalating resources like secrets`, nil @@ -171,10 +176,15 @@ func (userEvaluator) ResolveRules(scope, namespace string, clusterPolicyGetter c {Verbs: sets.NewString("create"), APIGroups: []string{authorizationapi.GroupName}, Resources: sets.NewString("subjectaccessreviews", "localsubjectaccessreviews"), AttributeRestrictions: &authorizationapi.IsPersonalSubjectAccessReview{}}, authorizationapi.NewRule("create").Groups(authorizationapi.GroupName).Resources("selfsubjectrulesreviews").RuleOrDie(), }, nil - case UserListProject: + case UserListScopedProjects: return []authorizationapi.PolicyRule{ {Verbs: sets.NewString("list", "watch"), APIGroups: []string{projectapi.GroupName}, Resources: sets.NewString("projects")}, }, nil + case UserListAllProjects: + return []authorizationapi.PolicyRule{ + {Verbs: sets.NewString("list", "watch"), APIGroups: []string{projectapi.GroupName}, Resources: sets.NewString("projects")}, + {Verbs: sets.NewString("get"), APIGroups: []string{kapi.GroupName}, Resources: sets.NewString("namespaces")}, + }, nil case UserFull: return []authorizationapi.PolicyRule{ {Verbs: sets.NewString("*"), APIGroups: []string{"*"}, Resources: sets.NewString("*")}, @@ -187,7 +197,7 @@ func (userEvaluator) ResolveRules(scope, namespace string, clusterPolicyGetter c func (userEvaluator) ResolveGettableNamespaces(scope string, clusterPolicyGetter client.ClusterPolicyLister) ([]string, error) { switch scope { - case UserFull: + case UserFull, UserListAllProjects: return []string{"*"}, nil default: return []string{}, nil diff --git a/pkg/authorization/authorizer/scope/converter_test.go b/pkg/authorization/authorizer/scope/converter_test.go index 734272fa27d5..5c153d464309 100644 --- a/pkg/authorization/authorizer/scope/converter_test.go +++ b/pkg/authorization/authorizer/scope/converter_test.go @@ -53,8 +53,8 @@ func TestUserEvaluator(t *testing.T) { numRules: 4, }, { - name: "list-projects", - scopes: []string{UserListProject}, + name: "list--scoped-projects", + scopes: []string{UserListScopedProjects}, numRules: 2, }, } diff --git a/pkg/serviceaccounts/oauthclient/oauthclientregistry.go b/pkg/serviceaccounts/oauthclient/oauthclientregistry.go index 4ad1a7811bce..6dde582756e4 100644 --- a/pkg/serviceaccounts/oauthclient/oauthclientregistry.go +++ b/pkg/serviceaccounts/oauthclient/oauthclientregistry.go @@ -83,7 +83,12 @@ func (a *saOAuthClientAdapter) GetClient(ctx kapi.Context, name string) (*oautha func getScopeRestrictionsFor(namespace, name string) []oauthapi.ScopeRestriction { return []oauthapi.ScopeRestriction{ - {ExactValues: []string{scopeauthorizer.UserInfo, scopeauthorizer.UserAccessCheck}}, + {ExactValues: []string{ + scopeauthorizer.UserInfo, + scopeauthorizer.UserAccessCheck, + scopeauthorizer.UserListScopedProjects, + scopeauthorizer.UserListAllProjects, + }}, {ClusterRole: &oauthapi.ClusterRoleScopeRestriction{RoleNames: []string{"*"}, Namespaces: []string{namespace}, AllowEscalation: true}}, } } diff --git a/test/cmd/authentication.sh b/test/cmd/authentication.sh index c26a311867f2..925208473635 100755 --- a/test/cmd/authentication.sh +++ b/test/cmd/authentication.sh @@ -32,13 +32,16 @@ whoamitoken="$(oc process -f "${OS_ROOT}/test/testdata/authentication/scoped-tok os::cmd::expect_success_and_text "oc get user/~ --token='${whoamitoken}'" "${username}" os::cmd::expect_failure_and_text "oc get pods --token='${whoamitoken}' -n '${project}'" "prevent this action; User \"scoped-user\" cannot list pods in project \"${project}\"" -listprojecttoken="$(oc process -f "${OS_ROOT}/test/testdata/authentication/scoped-token-template.yaml" TOKEN_PREFIX=listproject SCOPE=user:list-projects USER_NAME="${username}" USER_UID="${useruid}" | oc create -f - -o name | awk -F/ '{print $2}')" +listprojecttoken="$(oc process -f "${OS_ROOT}/test/testdata/authentication/scoped-token-template.yaml" TOKEN_PREFIX=listproject SCOPE=user:list-scoped-projects USER_NAME="${username}" USER_UID="${useruid}" | oc create -f - -o name | awk -F/ '{print $2}')" # this token doesn't have rights to see any projects even though it can hit the list endpoint, so an empty list is correct # we'll add another scope that allows listing all known projects even if this token has no other powers in them. os::cmd::expect_success_and_not_text "oc get projects --token='${listprojecttoken}'" "${project}" os::cmd::expect_failure_and_text "oc get user/~ --token='${listprojecttoken}'" 'prevent this action; User "scoped-user" cannot get users at the cluster scope' os::cmd::expect_failure_and_text "oc get pods --token='${listprojecttoken}' -n '${project}'" "prevent this action; User \"scoped-user\" cannot list pods in project \"${project}\"" +listprojecttoken="$(oc process -f "${OS_ROOT}/test/testdata/authentication/scoped-token-template.yaml" TOKEN_PREFIX=listallprojects SCOPE=user:list-projects USER_NAME="${username}" USER_UID="${useruid}" | oc create -f - -o name | awk -F/ '{print $2}')" +os::cmd::expect_success_and_text "oc get projects --token='${listprojecttoken}'" "${project}" + adminnonescalatingpowerstoken="$(oc process -f "${OS_ROOT}/test/testdata/authentication/scoped-token-template.yaml" TOKEN_PREFIX=admin SCOPE=role:admin:* USER_NAME="${username}" USER_UID="${useruid}" | oc create -f - -o name | awk -F/ '{print $2}')" os::cmd::expect_failure_and_text "oc get user/~ --token='${adminnonescalatingpowerstoken}'" 'prevent this action; User "scoped-user" cannot get users at the cluster scope' os::cmd::expect_failure_and_text "oc get secrets --token='${adminnonescalatingpowerstoken}' -n '${project}'" "prevent this action; User \"scoped-user\" cannot list secrets in project \"${project}\"" diff --git a/test/integration/project_test.go b/test/integration/project_test.go index 8670b14ded0e..3535344f57f7 100644 --- a/test/integration/project_test.go +++ b/test/integration/project_test.go @@ -321,7 +321,7 @@ func TestScopedProjectAccess(t *testing.T) { } oneTwoBobClient, _, _, err := testutil.GetScopedClientForUser(clusterAdminClient, *clusterAdminClientConfig, "bob", []string{ - scope.UserListProject, + scope.UserListScopedProjects, scope.ClusterRoleIndicator + "view:one", scope.ClusterRoleIndicator + "view:two", }) @@ -330,13 +330,13 @@ func TestScopedProjectAccess(t *testing.T) { } twoThreeBobClient, _, _, err := testutil.GetScopedClientForUser(clusterAdminClient, *clusterAdminClientConfig, "bob", []string{ - scope.UserListProject, + scope.UserListScopedProjects, scope.ClusterRoleIndicator + "view:two", scope.ClusterRoleIndicator + "view:three", }) allBobClient, _, _, err := testutil.GetScopedClientForUser(clusterAdminClient, *clusterAdminClientConfig, "bob", []string{ - scope.UserListProject, + scope.UserListScopedProjects, scope.ClusterRoleIndicator + "view:*", }) if err != nil {