| ... | ... |
@@ -39,8 +39,9 @@ type Interface interface {
|
| 39 | 39 |
RoleBindingsNamespacer |
| 40 | 40 |
PolicyBindingsNamespacer |
| 41 | 41 |
ResourceAccessReviewsNamespacer |
| 42 |
- RootResourceAccessReviews |
|
| 42 |
+ ClusterResourceAccessReviews |
|
| 43 | 43 |
SubjectAccessReviewsNamespacer |
| 44 |
+ ClusterSubjectAccessReviews |
|
| 44 | 45 |
TemplatesNamespacer |
| 45 | 46 |
TemplateConfigsNamespacer |
| 46 | 47 |
OAuthAccessTokensInterface |
| ... | ... |
@@ -176,9 +177,9 @@ func (c *Client) ResourceAccessReviews(namespace string) ResourceAccessReviewInt |
| 176 | 176 |
return newResourceAccessReviews(c, namespace) |
| 177 | 177 |
} |
| 178 | 178 |
|
| 179 |
-// RootResourceAccessReviews provides a REST client for RootResourceAccessReviews |
|
| 180 |
-func (c *Client) RootResourceAccessReviews() ResourceAccessReviewInterface {
|
|
| 181 |
- return newRootResourceAccessReviews(c) |
|
| 179 |
+// ClusterResourceAccessReviews provides a REST client for ClusterResourceAccessReviews |
|
| 180 |
+func (c *Client) ClusterResourceAccessReviews() ResourceAccessReviewInterface {
|
|
| 181 |
+ return newClusterResourceAccessReviews(c) |
|
| 182 | 182 |
} |
| 183 | 183 |
|
| 184 | 184 |
// SubjectAccessReviews provides a REST client for SubjectAccessReviews |
| ... | ... |
@@ -186,9 +187,9 @@ func (c *Client) SubjectAccessReviews(namespace string) SubjectAccessReviewInter |
| 186 | 186 |
return newSubjectAccessReviews(c, namespace) |
| 187 | 187 |
} |
| 188 | 188 |
|
| 189 |
-// RootSubjectAccessReviews provides a REST client for RootSubjectAccessReviews |
|
| 190 |
-func (c *Client) RootSubjectAccessReviews() SubjectAccessReviewInterface {
|
|
| 191 |
- return newRootSubjectAccessReviews(c) |
|
| 189 |
+// ClusterSubjectAccessReviews provides a REST client for SubjectAccessReviews |
|
| 190 |
+func (c *Client) ClusterSubjectAccessReviews() SubjectAccessReviewInterface {
|
|
| 191 |
+ return newClusterSubjectAccessReviews(c) |
|
| 192 | 192 |
} |
| 193 | 193 |
|
| 194 | 194 |
// OAuthAccessTokens provides a REST client for OAuthAccessTokens |
| ... | ... |
@@ -132,8 +132,8 @@ func (c *Fake) ResourceAccessReviews(namespace string) ResourceAccessReviewInter |
| 132 | 132 |
return &FakeResourceAccessReviews{Fake: c}
|
| 133 | 133 |
} |
| 134 | 134 |
|
| 135 |
-func (c *Fake) RootResourceAccessReviews() ResourceAccessReviewInterface {
|
|
| 136 |
- return &FakeRootResourceAccessReviews{Fake: c}
|
|
| 135 |
+func (c *Fake) ClusterResourceAccessReviews() ResourceAccessReviewInterface {
|
|
| 136 |
+ return &FakeClusterResourceAccessReviews{Fake: c}
|
|
| 137 | 137 |
} |
| 138 | 138 |
|
| 139 | 139 |
func (c *Fake) SubjectAccessReviews(namespace string) SubjectAccessReviewInterface {
|
| ... | ... |
@@ -142,4 +142,8 @@ func (c *Fake) SubjectAccessReviews(namespace string) SubjectAccessReviewInterfa |
| 142 | 142 |
|
| 143 | 143 |
func (c *Fake) OAuthAccessTokens() OAuthAccessTokenInterface {
|
| 144 | 144 |
return &FakeOAuthAccessTokens{Fake: c}
|
| 145 |
+ |
|
| 146 |
+} |
|
| 147 |
+func (c *Fake) ClusterSubjectAccessReviews() SubjectAccessReviewInterface {
|
|
| 148 |
+ return &FakeClusterSubjectAccessReviews{Fake: c}
|
|
| 145 | 149 |
} |
| ... | ... |
@@ -1,6 +1,10 @@ |
| 1 | 1 |
package client |
| 2 | 2 |
|
| 3 | 3 |
import ( |
| 4 |
+ kapi "github.com/GoogleCloudPlatform/kubernetes/pkg/api" |
|
| 5 |
+ "github.com/GoogleCloudPlatform/kubernetes/pkg/fields" |
|
| 6 |
+ "github.com/GoogleCloudPlatform/kubernetes/pkg/labels" |
|
| 7 |
+ |
|
| 4 | 8 |
projectapi "github.com/openshift/origin/pkg/project/api" |
| 5 | 9 |
) |
| 6 | 10 |
|
| ... | ... |
@@ -12,3 +16,8 @@ func (c *FakeProjectRequests) Create(project *projectapi.ProjectRequest) (*proje |
| 12 | 12 |
obj, err := c.Fake.Invokes(FakeAction{Action: "create-newProject", Value: project}, &projectapi.ProjectRequest{})
|
| 13 | 13 |
return obj.(*projectapi.Project), err |
| 14 | 14 |
} |
| 15 |
+ |
|
| 16 |
+func (c *FakeProjectRequests) List(label labels.Selector, field fields.Selector) (*kapi.Status, error) {
|
|
| 17 |
+ obj, err := c.Fake.Invokes(FakeAction{Action: "list-newProject"}, &kapi.Status{})
|
|
| 18 |
+ return obj.(*kapi.Status), err |
|
| 19 |
+} |
| ... | ... |
@@ -13,11 +13,11 @@ func (c *FakeResourceAccessReviews) Create(resourceAccessReview *authorizationap |
| 13 | 13 |
return obj.(*authorizationapi.ResourceAccessReviewResponse), err |
| 14 | 14 |
} |
| 15 | 15 |
|
| 16 |
-type FakeRootResourceAccessReviews struct {
|
|
| 16 |
+type FakeClusterResourceAccessReviews struct {
|
|
| 17 | 17 |
Fake *Fake |
| 18 | 18 |
} |
| 19 | 19 |
|
| 20 |
-func (c *FakeRootResourceAccessReviews) Create(resourceAccessReview *authorizationapi.ResourceAccessReview) (*authorizationapi.ResourceAccessReviewResponse, error) {
|
|
| 21 |
- obj, err := c.Fake.Invokes(FakeAction{Action: "create-root-resourceAccessReview", Value: resourceAccessReview}, &authorizationapi.ResourceAccessReviewResponse{})
|
|
| 20 |
+func (c *FakeClusterResourceAccessReviews) Create(resourceAccessReview *authorizationapi.ResourceAccessReview) (*authorizationapi.ResourceAccessReviewResponse, error) {
|
|
| 21 |
+ obj, err := c.Fake.Invokes(FakeAction{Action: "create-cluster-resourceAccessReview", Value: resourceAccessReview}, &authorizationapi.ResourceAccessReviewResponse{})
|
|
| 22 | 22 |
return obj.(*authorizationapi.ResourceAccessReviewResponse), err |
| 23 | 23 |
} |
| ... | ... |
@@ -12,3 +12,12 @@ func (c *FakeSubjectAccessReviews) Create(subjectAccessReview *authorizationapi. |
| 12 | 12 |
obj, err := c.Fake.Invokes(FakeAction{Action: "create-subjectAccessReview", Value: subjectAccessReview}, &authorizationapi.SubjectAccessReviewResponse{})
|
| 13 | 13 |
return obj.(*authorizationapi.SubjectAccessReviewResponse), err |
| 14 | 14 |
} |
| 15 |
+ |
|
| 16 |
+type FakeClusterSubjectAccessReviews struct {
|
|
| 17 |
+ Fake *Fake |
|
| 18 |
+} |
|
| 19 |
+ |
|
| 20 |
+func (c *FakeClusterSubjectAccessReviews) Create(resourceAccessReview *authorizationapi.SubjectAccessReview) (*authorizationapi.SubjectAccessReviewResponse, error) {
|
|
| 21 |
+ obj, err := c.Fake.Invokes(FakeAction{Action: "create-cluster-subjectAccessReview", Value: resourceAccessReview}, &authorizationapi.SubjectAccessReviewResponse{})
|
|
| 22 |
+ return obj.(*authorizationapi.SubjectAccessReviewResponse), err |
|
| 23 |
+} |
| ... | ... |
@@ -1,6 +1,10 @@ |
| 1 | 1 |
package client |
| 2 | 2 |
|
| 3 | 3 |
import ( |
| 4 |
+ kapi "github.com/GoogleCloudPlatform/kubernetes/pkg/api" |
|
| 5 |
+ "github.com/GoogleCloudPlatform/kubernetes/pkg/fields" |
|
| 6 |
+ "github.com/GoogleCloudPlatform/kubernetes/pkg/labels" |
|
| 7 |
+ |
|
| 4 | 8 |
projectapi "github.com/openshift/origin/pkg/project/api" |
| 5 | 9 |
_ "github.com/openshift/origin/pkg/user/api/v1beta1" |
| 6 | 10 |
) |
| ... | ... |
@@ -13,22 +17,30 @@ type ProjectRequestsInterface interface {
|
| 13 | 13 |
// UserInterface exposes methods on user resources. |
| 14 | 14 |
type ProjectRequestInterface interface {
|
| 15 | 15 |
Create(p *projectapi.ProjectRequest) (*projectapi.Project, error) |
| 16 |
+ List(label labels.Selector, field fields.Selector) (*kapi.Status, error) |
|
| 16 | 17 |
} |
| 17 | 18 |
|
| 18 |
-type newProjectRequestsStruct struct {
|
|
| 19 |
+type projectRequests struct {
|
|
| 19 | 20 |
r *Client |
| 20 | 21 |
} |
| 21 | 22 |
|
| 22 | 23 |
// newUsers returns a users |
| 23 |
-func newProjectRequests(c *Client) *newProjectRequestsStruct {
|
|
| 24 |
- return &newProjectRequestsStruct{
|
|
| 24 |
+func newProjectRequests(c *Client) *projectRequests {
|
|
| 25 |
+ return &projectRequests{
|
|
| 25 | 26 |
r: c, |
| 26 | 27 |
} |
| 27 | 28 |
} |
| 28 | 29 |
|
| 29 | 30 |
// Create creates a new ProjectRequest |
| 30 |
-func (c *newProjectRequestsStruct) Create(p *projectapi.ProjectRequest) (result *projectapi.Project, err error) {
|
|
| 31 |
+func (c *projectRequests) Create(p *projectapi.ProjectRequest) (result *projectapi.Project, err error) {
|
|
| 31 | 32 |
result = &projectapi.Project{}
|
| 32 | 33 |
err = c.r.Post().Resource("projectrequests").Body(p).Do().Into(result)
|
| 33 | 34 |
return |
| 34 | 35 |
} |
| 36 |
+ |
|
| 37 |
+// List returns a status object indicating that a user can call the Create or an error indicating why not |
|
| 38 |
+func (c *projectRequests) List(label labels.Selector, field fields.Selector) (result *kapi.Status, err error) {
|
|
| 39 |
+ result = &kapi.Status{}
|
|
| 40 |
+ err = c.r.Get().Resource("projectrequests").LabelsSelectorParam(label).FieldsSelectorParam(field).Do().Into(result)
|
|
| 41 |
+ return result, err |
|
| 42 |
+} |
| ... | ... |
@@ -9,9 +9,9 @@ type ResourceAccessReviewsNamespacer interface {
|
| 9 | 9 |
ResourceAccessReviews(namespace string) ResourceAccessReviewInterface |
| 10 | 10 |
} |
| 11 | 11 |
|
| 12 |
-// RootResourceAccessReviews has methods to work with ResourceAccessReview resources in the root scope |
|
| 13 |
-type RootResourceAccessReviews interface {
|
|
| 14 |
- RootResourceAccessReviews() ResourceAccessReviewInterface |
|
| 12 |
+// ClusterResourceAccessReviews has methods to work with ResourceAccessReview resources in the cluster scope |
|
| 13 |
+type ClusterResourceAccessReviews interface {
|
|
| 14 |
+ ClusterResourceAccessReviews() ResourceAccessReviewInterface |
|
| 15 | 15 |
} |
| 16 | 16 |
|
| 17 | 17 |
// ResourceAccessReviewInterface exposes methods on ResourceAccessReview resources. |
| ... | ... |
@@ -40,20 +40,20 @@ func (c *resourceAccessReviews) Create(policy *authorizationapi.ResourceAccessRe |
| 40 | 40 |
return |
| 41 | 41 |
} |
| 42 | 42 |
|
| 43 |
-// rootResourceAccessReviews implements RootResourceAccessReviews interface |
|
| 44 |
-type rootResourceAccessReviews struct {
|
|
| 43 |
+// clusterResourceAccessReviews implements ClusterResourceAccessReviews interface |
|
| 44 |
+type clusterResourceAccessReviews struct {
|
|
| 45 | 45 |
r *Client |
| 46 | 46 |
} |
| 47 | 47 |
|
| 48 |
-// newRootResourceAccessReviews returns a rootResourceAccessReviews |
|
| 49 |
-func newRootResourceAccessReviews(c *Client) *rootResourceAccessReviews {
|
|
| 50 |
- return &rootResourceAccessReviews{
|
|
| 48 |
+// newClusterResourceAccessReviews returns a clusterResourceAccessReviews |
|
| 49 |
+func newClusterResourceAccessReviews(c *Client) *clusterResourceAccessReviews {
|
|
| 50 |
+ return &clusterResourceAccessReviews{
|
|
| 51 | 51 |
r: c, |
| 52 | 52 |
} |
| 53 | 53 |
} |
| 54 | 54 |
|
| 55 | 55 |
// Create creates new policy. Returns the server's representation of the policy and error if one occurs. |
| 56 |
-func (c *rootResourceAccessReviews) Create(policy *authorizationapi.ResourceAccessReview) (result *authorizationapi.ResourceAccessReviewResponse, err error) {
|
|
| 56 |
+func (c *clusterResourceAccessReviews) Create(policy *authorizationapi.ResourceAccessReview) (result *authorizationapi.ResourceAccessReviewResponse, err error) {
|
|
| 57 | 57 |
result = &authorizationapi.ResourceAccessReviewResponse{}
|
| 58 | 58 |
err = c.r.Post().Resource("resourceAccessReviews").Body(policy).Do().Into(result)
|
| 59 | 59 |
return |
| ... | ... |
@@ -9,6 +9,11 @@ type SubjectAccessReviewsNamespacer interface {
|
| 9 | 9 |
SubjectAccessReviews(namespace string) SubjectAccessReviewInterface |
| 10 | 10 |
} |
| 11 | 11 |
|
| 12 |
+// ClusterSubjectAccessReviews has methods to work with SubjectAccessReview resources in the cluster scope |
|
| 13 |
+type ClusterSubjectAccessReviews interface {
|
|
| 14 |
+ ClusterSubjectAccessReviews() SubjectAccessReviewInterface |
|
| 15 |
+} |
|
| 16 |
+ |
|
| 12 | 17 |
// SubjectAccessReviewInterface exposes methods on SubjectAccessReview resources. |
| 13 | 18 |
type SubjectAccessReviewInterface interface {
|
| 14 | 19 |
Create(policy *authorizationapi.SubjectAccessReview) (*authorizationapi.SubjectAccessReviewResponse, error) |
| ... | ... |
@@ -35,20 +40,20 @@ func (c *subjectAccessReviews) Create(policy *authorizationapi.SubjectAccessRevi |
| 35 | 35 |
return |
| 36 | 36 |
} |
| 37 | 37 |
|
| 38 |
-// rootSubjectAccessReviews implements RootSubjectAccessReviews interface |
|
| 39 |
-type rootSubjectAccessReviews struct {
|
|
| 38 |
+// clusterSubjectAccessReviews implements ClusterSubjectAccessReviews interface |
|
| 39 |
+type clusterSubjectAccessReviews struct {
|
|
| 40 | 40 |
r *Client |
| 41 | 41 |
} |
| 42 | 42 |
|
| 43 |
-// newRootSubjectAccessReviews returns a rootSubjectAccessReviews |
|
| 44 |
-func newRootSubjectAccessReviews(c *Client) *rootSubjectAccessReviews {
|
|
| 45 |
- return &rootSubjectAccessReviews{
|
|
| 43 |
+// newClusterSubjectAccessReviews returns a clusterSubjectAccessReviews |
|
| 44 |
+func newClusterSubjectAccessReviews(c *Client) *clusterSubjectAccessReviews {
|
|
| 45 |
+ return &clusterSubjectAccessReviews{
|
|
| 46 | 46 |
r: c, |
| 47 | 47 |
} |
| 48 | 48 |
} |
| 49 | 49 |
|
| 50 | 50 |
// Create creates new policy. Returns the server's representation of the policy and error if one occurs. |
| 51 |
-func (c *rootSubjectAccessReviews) Create(policy *authorizationapi.SubjectAccessReview) (result *authorizationapi.SubjectAccessReviewResponse, err error) {
|
|
| 51 |
+func (c *clusterSubjectAccessReviews) Create(policy *authorizationapi.SubjectAccessReview) (result *authorizationapi.SubjectAccessReviewResponse, err error) {
|
|
| 52 | 52 |
result = &authorizationapi.SubjectAccessReviewResponse{}
|
| 53 | 53 |
err = c.r.Post().Resource("subjectAccessReviews").Body(policy).Do().Into(result)
|
| 54 | 54 |
return |
| ... | ... |
@@ -84,6 +84,14 @@ type MasterConfig struct {
|
| 84 | 84 |
|
| 85 | 85 |
// PolicyConfig holds information about where to locate critical pieces of bootstrapping policy |
| 86 | 86 |
PolicyConfig PolicyConfig |
| 87 |
+ |
|
| 88 |
+ // ProjectRequestConfig holds information about how to handle new project requests |
|
| 89 |
+ ProjectRequestConfig ProjectRequestConfig |
|
| 90 |
+} |
|
| 91 |
+ |
|
| 92 |
+type ProjectRequestConfig struct {
|
|
| 93 |
+ // ProjectRequestMessage is the string presented to a user if they are unable to request a project via the projectrequest api endpoint |
|
| 94 |
+ ProjectRequestMessage string |
|
| 87 | 95 |
} |
| 88 | 96 |
|
| 89 | 97 |
type PolicyConfig struct {
|
| ... | ... |
@@ -80,6 +80,14 @@ type MasterConfig struct {
|
| 80 | 80 |
ImageConfig ImageConfig `json:"imageConfig"` |
| 81 | 81 |
|
| 82 | 82 |
PolicyConfig PolicyConfig `json:"policyConfig"` |
| 83 |
+ |
|
| 84 |
+ // ProjectRequestConfig holds information about how to handle new project requests |
|
| 85 |
+ ProjectRequestConfig ProjectRequestConfig `json:"projectRequestConfig"` |
|
| 86 |
+} |
|
| 87 |
+ |
|
| 88 |
+type ProjectRequestConfig struct {
|
|
| 89 |
+ // ProjectRequestMessage is the string presented to a user if they are unable to request a project via the projectrequest api endpoint |
|
| 90 |
+ ProjectRequestMessage string `json:"projectRequestMessage"` |
|
| 83 | 91 |
} |
| 84 | 92 |
|
| 85 | 93 |
type PolicyConfig struct {
|
| ... | ... |
@@ -81,6 +81,8 @@ func ValidateMasterConfig(config *api.MasterConfig) fielderrors.ValidationErrorL |
| 81 | 81 |
|
| 82 | 82 |
allErrs = append(allErrs, ValidateServingInfo(config.ServingInfo).Prefix("servingInfo")...)
|
| 83 | 83 |
|
| 84 |
+ allErrs = append(allErrs, ValidateProjectRequestConfig(config.ProjectRequestConfig).Prefix("projectRequestConfig")...)
|
|
| 85 |
+ |
|
| 84 | 86 |
return allErrs |
| 85 | 87 |
} |
| 86 | 88 |
|
| ... | ... |
@@ -185,3 +187,10 @@ func ValidatePolicyConfig(config api.PolicyConfig) fielderrors.ValidationErrorLi |
| 185 | 185 |
|
| 186 | 186 |
return allErrs |
| 187 | 187 |
} |
| 188 |
+ |
|
| 189 |
+// ValidateProjectRequestConfig is stub for now. no validation is required. |
|
| 190 |
+func ValidateProjectRequestConfig(config api.ProjectRequestConfig) fielderrors.ValidationErrorList {
|
|
| 191 |
+ allErrs := fielderrors.ValidationErrorList{}
|
|
| 192 |
+ |
|
| 193 |
+ return allErrs |
|
| 194 |
+} |
| ... | ... |
@@ -102,7 +102,7 @@ func GetBootstrapMasterRoles(masterNamespace string) []authorizationapi.Role {
|
| 102 | 102 |
}, |
| 103 | 103 |
Rules: []authorizationapi.PolicyRule{
|
| 104 | 104 |
{Verbs: util.NewStringSet("get"), Resources: util.NewStringSet("users"), ResourceNames: util.NewStringSet("~")},
|
| 105 |
- {Verbs: util.NewStringSet("get"), Resources: util.NewStringSet("projectrequests")},
|
|
| 105 |
+ {Verbs: util.NewStringSet("list"), Resources: util.NewStringSet("projectrequests")},
|
|
| 106 | 106 |
{Verbs: util.NewStringSet("list"), Resources: util.NewStringSet("projects")},
|
| 107 | 107 |
{Verbs: util.NewStringSet("create"), Resources: util.NewStringSet("subjectaccessreviews"), AttributeRestrictions: runtime.EmbeddedObject{&authorizationapi.IsPersonalSubjectAccessReview{}}},
|
| 108 | 108 |
}, |
| ... | ... |
@@ -242,7 +242,7 @@ func (c *MasterConfig) InstallProtectedAPI(container *restful.Container) []strin |
| 242 | 242 |
"routes": routeregistry.NewREST(routeEtcd, routeAllocator), |
| 243 | 243 |
|
| 244 | 244 |
"projects": projectStorage, |
| 245 |
- "projectRequests": projectrequeststorage.NewREST(c.Options.PolicyConfig.MasterAuthorizationNamespace, roleBindingStorage, *projectStorage), |
|
| 245 |
+ "projectRequests": projectrequeststorage.NewREST(c.Options.ProjectRequestConfig.ProjectRequestMessage, c.Options.PolicyConfig.MasterAuthorizationNamespace, roleBindingStorage, *projectStorage, c.PolicyClient()), |
|
| 246 | 246 |
|
| 247 | 247 |
"users": userStorage, |
| 248 | 248 |
"identities": identityStorage, |
| ... | ... |
@@ -1,13 +1,19 @@ |
| 1 | 1 |
package delegated |
| 2 | 2 |
|
| 3 | 3 |
import ( |
| 4 |
+ "errors" |
|
| 5 |
+ |
|
| 4 | 6 |
kapi "github.com/GoogleCloudPlatform/kubernetes/pkg/api" |
| 7 |
+ kapierror "github.com/GoogleCloudPlatform/kubernetes/pkg/api/errors" |
|
| 5 | 8 |
"github.com/GoogleCloudPlatform/kubernetes/pkg/api/rest" |
| 9 |
+ "github.com/GoogleCloudPlatform/kubernetes/pkg/fields" |
|
| 10 |
+ "github.com/GoogleCloudPlatform/kubernetes/pkg/labels" |
|
| 6 | 11 |
"github.com/GoogleCloudPlatform/kubernetes/pkg/runtime" |
| 7 | 12 |
"github.com/GoogleCloudPlatform/kubernetes/pkg/util" |
| 8 | 13 |
|
| 9 | 14 |
authorizationapi "github.com/openshift/origin/pkg/authorization/api" |
| 10 | 15 |
"github.com/openshift/origin/pkg/authorization/registry/rolebinding" |
| 16 |
+ "github.com/openshift/origin/pkg/client" |
|
| 11 | 17 |
|
| 12 | 18 |
projectapi "github.com/openshift/origin/pkg/project/api" |
| 13 | 19 |
projectstorage "github.com/openshift/origin/pkg/project/registry/project/proxy" |
| ... | ... |
@@ -15,17 +21,21 @@ import ( |
| 15 | 15 |
) |
| 16 | 16 |
|
| 17 | 17 |
type REST struct {
|
| 18 |
+ message string |
|
| 18 | 19 |
masterNamespace string |
| 19 | 20 |
roleBindingStorage rolebinding.Storage |
| 20 | 21 |
|
| 21 |
- projectStorage projectstorage.REST |
|
| 22 |
+ projectStorage projectstorage.REST |
|
| 23 |
+ openshiftClient *client.Client |
|
| 22 | 24 |
} |
| 23 | 25 |
|
| 24 |
-func NewREST(masterNamespace string, roleBindingStorage rolebinding.Storage, projectStorage projectstorage.REST) *REST {
|
|
| 26 |
+func NewREST(message, masterNamespace string, roleBindingStorage rolebinding.Storage, projectStorage projectstorage.REST, openshiftClient *client.Client) *REST {
|
|
| 25 | 27 |
return &REST{
|
| 28 |
+ message: message, |
|
| 26 | 29 |
masterNamespace: masterNamespace, |
| 27 | 30 |
roleBindingStorage: roleBindingStorage, |
| 28 | 31 |
projectStorage: projectStorage, |
| 32 |
+ openshiftClient: openshiftClient, |
|
| 29 | 33 |
} |
| 30 | 34 |
} |
| 31 | 35 |
|
| ... | ... |
@@ -33,6 +43,10 @@ func (r *REST) New() runtime.Object {
|
| 33 | 33 |
return &projectapi.ProjectRequest{}
|
| 34 | 34 |
} |
| 35 | 35 |
|
| 36 |
+func (r *REST) NewList() runtime.Object {
|
|
| 37 |
+ return &kapi.Status{}
|
|
| 38 |
+} |
|
| 39 |
+ |
|
| 36 | 40 |
func (r *REST) Create(ctx kapi.Context, obj runtime.Object) (runtime.Object, error) {
|
| 37 | 41 |
if err := rest.BeforeCreate(projectrequestregistry.Strategy, ctx, obj); err != nil {
|
| 38 | 42 |
return nil, err |
| ... | ... |
@@ -65,3 +79,32 @@ func (r *REST) Create(ctx kapi.Context, obj runtime.Object) (runtime.Object, err |
| 65 | 65 |
|
| 66 | 66 |
return realizedProject, nil |
| 67 | 67 |
} |
| 68 |
+ |
|
| 69 |
+func (r *REST) List(ctx kapi.Context, label labels.Selector, field fields.Selector) (runtime.Object, error) {
|
|
| 70 |
+ userInfo, exists := kapi.UserFrom(ctx) |
|
| 71 |
+ if !exists {
|
|
| 72 |
+ return nil, errors.New("a user must be provided")
|
|
| 73 |
+ } |
|
| 74 |
+ |
|
| 75 |
+ // the caller might not have permission to run a subject access review (he has it by default, but it could have been removed). |
|
| 76 |
+ // So we'll escalate for the subject access review to determine rights |
|
| 77 |
+ accessReview := &authorizationapi.SubjectAccessReview{
|
|
| 78 |
+ Verb: "create", |
|
| 79 |
+ Resource: "projectrequests", |
|
| 80 |
+ User: userInfo.GetName(), |
|
| 81 |
+ Groups: util.NewStringSet(userInfo.GetGroups()...), |
|
| 82 |
+ } |
|
| 83 |
+ accessReviewResponse, err := r.openshiftClient.ClusterSubjectAccessReviews().Create(accessReview) |
|
| 84 |
+ if err != nil {
|
|
| 85 |
+ return nil, err |
|
| 86 |
+ } |
|
| 87 |
+ if accessReviewResponse.Allowed {
|
|
| 88 |
+ return &kapi.Status{Status: kapi.StatusSuccess}, nil
|
|
| 89 |
+ } |
|
| 90 |
+ |
|
| 91 |
+ forbiddenError, _ := kapierror.NewForbidden("ProjectRequest", "", errors.New("You may not request a new project via this API.")).(*kapierror.StatusError)
|
|
| 92 |
+ if len(r.message) > 0 {
|
|
| 93 |
+ forbiddenError.ErrStatus.Message = r.message |
|
| 94 |
+ } |
|
| 95 |
+ return nil, forbiddenError |
|
| 96 |
+} |
| ... | ... |
@@ -246,7 +246,7 @@ func TestResourceAccessReview(t *testing.T) {
|
| 246 | 246 |
// mark should not be able to make global access review requests |
| 247 | 247 |
{
|
| 248 | 248 |
test := resourceAccessReviewTest{
|
| 249 |
- clientInterface: markClient.RootResourceAccessReviews(), |
|
| 249 |
+ clientInterface: markClient.ClusterResourceAccessReviews(), |
|
| 250 | 250 |
review: requestWhoCanViewDeployments, |
| 251 | 251 |
err: "forbidden", |
| 252 | 252 |
} |
| ... | ... |
@@ -256,7 +256,7 @@ func TestResourceAccessReview(t *testing.T) {
|
| 256 | 256 |
// a cluster-admin should be able to make global access review requests |
| 257 | 257 |
{
|
| 258 | 258 |
test := resourceAccessReviewTest{
|
| 259 |
- clientInterface: clusterAdminClient.RootResourceAccessReviews(), |
|
| 259 |
+ clientInterface: clusterAdminClient.ClusterResourceAccessReviews(), |
|
| 260 | 260 |
review: requestWhoCanViewDeployments, |
| 261 | 261 |
response: authorizationapi.ResourceAccessReviewResponse{
|
| 262 | 262 |
Users: globalClusterAdminUsers, |
| ... | ... |
@@ -391,7 +391,7 @@ func TestSubjectAccessReview(t *testing.T) {
|
| 391 | 391 |
|
| 392 | 392 |
askCanClusterAdminsCreateProject := &authorizationapi.SubjectAccessReview{Groups: util.NewStringSet("system:cluster-admins"), Verb: "create", Resource: "projects"}
|
| 393 | 393 |
subjectAccessReviewTest{
|
| 394 |
- clientInterface: clusterAdminClient.RootSubjectAccessReviews(), |
|
| 394 |
+ clientInterface: clusterAdminClient.ClusterSubjectAccessReviews(), |
|
| 395 | 395 |
review: askCanClusterAdminsCreateProject, |
| 396 | 396 |
response: authorizationapi.SubjectAccessReviewResponse{
|
| 397 | 397 |
Allowed: true, |
| ... | ... |
@@ -400,7 +400,7 @@ func TestSubjectAccessReview(t *testing.T) {
|
| 400 | 400 |
}, |
| 401 | 401 |
}.run(t) |
| 402 | 402 |
subjectAccessReviewTest{
|
| 403 |
- clientInterface: haroldClient.RootSubjectAccessReviews(), |
|
| 403 |
+ clientInterface: haroldClient.ClusterSubjectAccessReviews(), |
|
| 404 | 404 |
review: askCanClusterAdminsCreateProject, |
| 405 | 405 |
err: "forbidden", |
| 406 | 406 |
}.run(t) |
| ... | ... |
@@ -7,9 +7,14 @@ import ( |
| 7 | 7 |
"testing" |
| 8 | 8 |
"time" |
| 9 | 9 |
|
| 10 |
- "github.com/openshift/origin/pkg/client" |
|
| 10 |
+ kapi "github.com/GoogleCloudPlatform/kubernetes/pkg/api" |
|
| 11 |
+ "github.com/GoogleCloudPlatform/kubernetes/pkg/fields" |
|
| 12 |
+ "github.com/GoogleCloudPlatform/kubernetes/pkg/labels" |
|
| 11 | 13 |
|
| 14 |
+ authorizationapi "github.com/openshift/origin/pkg/authorization/api" |
|
| 15 |
+ "github.com/openshift/origin/pkg/client" |
|
| 12 | 16 |
osc "github.com/openshift/origin/pkg/cmd/cli/cmd" |
| 17 |
+ "github.com/openshift/origin/pkg/cmd/server/bootstrappolicy" |
|
| 13 | 18 |
"github.com/openshift/origin/pkg/cmd/util/tokencmd" |
| 14 | 19 |
testutil "github.com/openshift/origin/test/util" |
| 15 | 20 |
) |
| ... | ... |
@@ -45,6 +50,15 @@ func TestUnprivilegedNewProject(t *testing.T) {
|
| 45 | 45 |
t.Fatalf("unexpected error: %v", err)
|
| 46 | 46 |
} |
| 47 | 47 |
|
| 48 |
+ // confirm that we have access to request the project |
|
| 49 |
+ allowed, err := valerieOpenshiftClient.ProjectRequests().List(labels.Everything(), fields.Everything()) |
|
| 50 |
+ if err != nil {
|
|
| 51 |
+ t.Errorf("unexpected error: %v", err)
|
|
| 52 |
+ } |
|
| 53 |
+ if allowed.Status != kapi.StatusSuccess {
|
|
| 54 |
+ t.Errorf("expected %v, got %v", kapi.StatusSuccess, allowed.Status)
|
|
| 55 |
+ } |
|
| 56 |
+ |
|
| 48 | 57 |
requestProject := osc.NewProjectOptions{
|
| 49 | 58 |
ProjectName: "new-project", |
| 50 | 59 |
DisplayName: "display name here", |
| ... | ... |
@@ -60,3 +74,57 @@ func TestUnprivilegedNewProject(t *testing.T) {
|
| 60 | 60 |
|
| 61 | 61 |
waitForProject(t, valerieOpenshiftClient, "new-project", 5*time.Second, 10) |
| 62 | 62 |
} |
| 63 |
+ |
|
| 64 |
+func TestDeniedUnprivilegedNewProject(t *testing.T) {
|
|
| 65 |
+ _, clusterAdminKubeConfig, err := testutil.StartTestMaster() |
|
| 66 |
+ if err != nil {
|
|
| 67 |
+ t.Fatalf("unexpected error: %v", err)
|
|
| 68 |
+ } |
|
| 69 |
+ |
|
| 70 |
+ clusterAdminClient, err := testutil.GetClusterAdminClient(clusterAdminKubeConfig) |
|
| 71 |
+ if err != nil {
|
|
| 72 |
+ t.Errorf("unexpected error: %v", err)
|
|
| 73 |
+ } |
|
| 74 |
+ role, err := clusterAdminClient.Roles("master").Get(bootstrappolicy.SelfProvisionerRoleName)
|
|
| 75 |
+ if err != nil {
|
|
| 76 |
+ t.Errorf("unexpected error: %v", err)
|
|
| 77 |
+ } |
|
| 78 |
+ role.Rules = []authorizationapi.PolicyRule{}
|
|
| 79 |
+ clusterAdminClient.Roles("master").Update(role)
|
|
| 80 |
+ |
|
| 81 |
+ clusterAdminClientConfig, err := testutil.GetClusterAdminClientConfig(clusterAdminKubeConfig) |
|
| 82 |
+ if err != nil {
|
|
| 83 |
+ t.Errorf("unexpected error: %v", err)
|
|
| 84 |
+ } |
|
| 85 |
+ |
|
| 86 |
+ valerieClientConfig := *clusterAdminClientConfig |
|
| 87 |
+ valerieClientConfig.Username = "" |
|
| 88 |
+ valerieClientConfig.Password = "" |
|
| 89 |
+ valerieClientConfig.BearerToken = "" |
|
| 90 |
+ valerieClientConfig.CertFile = "" |
|
| 91 |
+ valerieClientConfig.KeyFile = "" |
|
| 92 |
+ valerieClientConfig.CertData = nil |
|
| 93 |
+ valerieClientConfig.KeyData = nil |
|
| 94 |
+ |
|
| 95 |
+ accessToken, err := tokencmd.RequestToken(&valerieClientConfig, nil, "valerie", "security!") |
|
| 96 |
+ if err != nil {
|
|
| 97 |
+ t.Fatalf("unexpected error: %v", err)
|
|
| 98 |
+ } |
|
| 99 |
+ |
|
| 100 |
+ valerieClientConfig.BearerToken = accessToken |
|
| 101 |
+ valerieOpenshiftClient, err := client.New(&valerieClientConfig) |
|
| 102 |
+ if err != nil {
|
|
| 103 |
+ t.Fatalf("unexpected error: %v", err)
|
|
| 104 |
+ } |
|
| 105 |
+ |
|
| 106 |
+ // confirm that we have access to request the project |
|
| 107 |
+ allowed, err := valerieOpenshiftClient.ProjectRequests().List(labels.Everything(), fields.Everything()) |
|
| 108 |
+ if err == nil {
|
|
| 109 |
+ t.Errorf("expected error: %v", err)
|
|
| 110 |
+ } |
|
| 111 |
+ expectedError := `ProjectRequest "" is forbidden: You may not request a new project via this API.` |
|
| 112 |
+ if err.Error() != expectedError {
|
|
| 113 |
+ t.Errorf("expected %v, got %v", expectedError, allowed.Status)
|
|
| 114 |
+ } |
|
| 115 |
+ |
|
| 116 |
+} |