| ... | ... |
@@ -18,4 +18,9 @@ when that change will happen. |
| 18 | 18 |
|
| 19 | 19 |
1. The `pauseControllers` field in `master-config.yaml` is deprecated as of Origin 1.0.4 and will |
| 20 | 20 |
no longer be supported in Origin 1.1. After that, a warning will be printed on startup if it |
| 21 |
- is set to true. |
|
| 22 | 21 |
\ No newline at end of file |
| 22 |
+ is set to true. |
|
| 23 |
+ |
|
| 24 |
+1. The `/ns/namespace-name/subjectaccessreview` endpoint is deprecated, use `/subjectaccessreview` |
|
| 25 |
+(with the `namespace` field set) or `/ns/namespace-name/localsubjectaccessreview`. In |
|
| 26 |
+Origin 1.y / OSE 3.y, support for `/ns/namespace-name/subjectaccessreview` wil be removed. |
|
| 27 |
+At that time, the openshift docker registry image must be upgraded in order to continue functioning. |
| 1 | 2 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1 @@ |
| 0 |
+Local Subject Access Reviews are objects that allow you to determine whether a given user or group can perform a particular action in a given namespace. Leaving `user` and `groups` empty allows you determine whether the identity making the request can perform the action. |
|
| 0 | 1 |
\ No newline at end of file |
| ... | ... |
@@ -6837,6 +6837,194 @@ |
| 6837 | 6837 |
] |
| 6838 | 6838 |
}, |
| 6839 | 6839 |
{
|
| 6840 |
+ "path": "/oapi/v1/namespaces/{namespace}/localresourceaccessreviews",
|
|
| 6841 |
+ "description": "OpenShift REST API, version v1", |
|
| 6842 |
+ "operations": [ |
|
| 6843 |
+ {
|
|
| 6844 |
+ "type": "v1.LocalResourceAccessReview", |
|
| 6845 |
+ "method": "POST", |
|
| 6846 |
+ "summary": "create a LocalResourceAccessReview", |
|
| 6847 |
+ "nickname": "createNamespacedLocalResourceAccessReview", |
|
| 6848 |
+ "parameters": [ |
|
| 6849 |
+ {
|
|
| 6850 |
+ "type": "string", |
|
| 6851 |
+ "paramType": "query", |
|
| 6852 |
+ "name": "pretty", |
|
| 6853 |
+ "description": "If 'true', then the output is pretty printed.", |
|
| 6854 |
+ "required": false, |
|
| 6855 |
+ "allowMultiple": false |
|
| 6856 |
+ }, |
|
| 6857 |
+ {
|
|
| 6858 |
+ "type": "v1.LocalResourceAccessReview", |
|
| 6859 |
+ "paramType": "body", |
|
| 6860 |
+ "name": "body", |
|
| 6861 |
+ "description": "", |
|
| 6862 |
+ "required": true, |
|
| 6863 |
+ "allowMultiple": false |
|
| 6864 |
+ }, |
|
| 6865 |
+ {
|
|
| 6866 |
+ "type": "string", |
|
| 6867 |
+ "paramType": "path", |
|
| 6868 |
+ "name": "namespace", |
|
| 6869 |
+ "description": "object name and auth scope, such as for teams and projects", |
|
| 6870 |
+ "required": true, |
|
| 6871 |
+ "allowMultiple": false |
|
| 6872 |
+ } |
|
| 6873 |
+ ], |
|
| 6874 |
+ "responseMessages": [ |
|
| 6875 |
+ {
|
|
| 6876 |
+ "code": 200, |
|
| 6877 |
+ "message": "OK", |
|
| 6878 |
+ "responseModel": "v1.LocalResourceAccessReview" |
|
| 6879 |
+ } |
|
| 6880 |
+ ], |
|
| 6881 |
+ "produces": [ |
|
| 6882 |
+ "application/json" |
|
| 6883 |
+ ], |
|
| 6884 |
+ "consumes": [ |
|
| 6885 |
+ "*/*" |
|
| 6886 |
+ ] |
|
| 6887 |
+ } |
|
| 6888 |
+ ] |
|
| 6889 |
+ }, |
|
| 6890 |
+ {
|
|
| 6891 |
+ "path": "/oapi/v1/localresourceaccessreviews", |
|
| 6892 |
+ "description": "OpenShift REST API, version v1", |
|
| 6893 |
+ "operations": [ |
|
| 6894 |
+ {
|
|
| 6895 |
+ "type": "v1.LocalResourceAccessReview", |
|
| 6896 |
+ "method": "POST", |
|
| 6897 |
+ "summary": "create a LocalResourceAccessReview", |
|
| 6898 |
+ "nickname": "createLocalResourceAccessReview", |
|
| 6899 |
+ "parameters": [ |
|
| 6900 |
+ {
|
|
| 6901 |
+ "type": "string", |
|
| 6902 |
+ "paramType": "query", |
|
| 6903 |
+ "name": "pretty", |
|
| 6904 |
+ "description": "If 'true', then the output is pretty printed.", |
|
| 6905 |
+ "required": false, |
|
| 6906 |
+ "allowMultiple": false |
|
| 6907 |
+ }, |
|
| 6908 |
+ {
|
|
| 6909 |
+ "type": "v1.LocalResourceAccessReview", |
|
| 6910 |
+ "paramType": "body", |
|
| 6911 |
+ "name": "body", |
|
| 6912 |
+ "description": "", |
|
| 6913 |
+ "required": true, |
|
| 6914 |
+ "allowMultiple": false |
|
| 6915 |
+ } |
|
| 6916 |
+ ], |
|
| 6917 |
+ "responseMessages": [ |
|
| 6918 |
+ {
|
|
| 6919 |
+ "code": 200, |
|
| 6920 |
+ "message": "OK", |
|
| 6921 |
+ "responseModel": "v1.LocalResourceAccessReview" |
|
| 6922 |
+ } |
|
| 6923 |
+ ], |
|
| 6924 |
+ "produces": [ |
|
| 6925 |
+ "application/json" |
|
| 6926 |
+ ], |
|
| 6927 |
+ "consumes": [ |
|
| 6928 |
+ "*/*" |
|
| 6929 |
+ ] |
|
| 6930 |
+ } |
|
| 6931 |
+ ] |
|
| 6932 |
+ }, |
|
| 6933 |
+ {
|
|
| 6934 |
+ "path": "/oapi/v1/namespaces/{namespace}/localsubjectaccessreviews",
|
|
| 6935 |
+ "description": "OpenShift REST API, version v1", |
|
| 6936 |
+ "operations": [ |
|
| 6937 |
+ {
|
|
| 6938 |
+ "type": "v1.LocalSubjectAccessReview", |
|
| 6939 |
+ "method": "POST", |
|
| 6940 |
+ "summary": "create a LocalSubjectAccessReview", |
|
| 6941 |
+ "nickname": "createNamespacedLocalSubjectAccessReview", |
|
| 6942 |
+ "parameters": [ |
|
| 6943 |
+ {
|
|
| 6944 |
+ "type": "string", |
|
| 6945 |
+ "paramType": "query", |
|
| 6946 |
+ "name": "pretty", |
|
| 6947 |
+ "description": "If 'true', then the output is pretty printed.", |
|
| 6948 |
+ "required": false, |
|
| 6949 |
+ "allowMultiple": false |
|
| 6950 |
+ }, |
|
| 6951 |
+ {
|
|
| 6952 |
+ "type": "v1.LocalSubjectAccessReview", |
|
| 6953 |
+ "paramType": "body", |
|
| 6954 |
+ "name": "body", |
|
| 6955 |
+ "description": "", |
|
| 6956 |
+ "required": true, |
|
| 6957 |
+ "allowMultiple": false |
|
| 6958 |
+ }, |
|
| 6959 |
+ {
|
|
| 6960 |
+ "type": "string", |
|
| 6961 |
+ "paramType": "path", |
|
| 6962 |
+ "name": "namespace", |
|
| 6963 |
+ "description": "object name and auth scope, such as for teams and projects", |
|
| 6964 |
+ "required": true, |
|
| 6965 |
+ "allowMultiple": false |
|
| 6966 |
+ } |
|
| 6967 |
+ ], |
|
| 6968 |
+ "responseMessages": [ |
|
| 6969 |
+ {
|
|
| 6970 |
+ "code": 200, |
|
| 6971 |
+ "message": "OK", |
|
| 6972 |
+ "responseModel": "v1.LocalSubjectAccessReview" |
|
| 6973 |
+ } |
|
| 6974 |
+ ], |
|
| 6975 |
+ "produces": [ |
|
| 6976 |
+ "application/json" |
|
| 6977 |
+ ], |
|
| 6978 |
+ "consumes": [ |
|
| 6979 |
+ "*/*" |
|
| 6980 |
+ ] |
|
| 6981 |
+ } |
|
| 6982 |
+ ] |
|
| 6983 |
+ }, |
|
| 6984 |
+ {
|
|
| 6985 |
+ "path": "/oapi/v1/localsubjectaccessreviews", |
|
| 6986 |
+ "description": "OpenShift REST API, version v1", |
|
| 6987 |
+ "operations": [ |
|
| 6988 |
+ {
|
|
| 6989 |
+ "type": "v1.LocalSubjectAccessReview", |
|
| 6990 |
+ "method": "POST", |
|
| 6991 |
+ "summary": "create a LocalSubjectAccessReview", |
|
| 6992 |
+ "nickname": "createLocalSubjectAccessReview", |
|
| 6993 |
+ "parameters": [ |
|
| 6994 |
+ {
|
|
| 6995 |
+ "type": "string", |
|
| 6996 |
+ "paramType": "query", |
|
| 6997 |
+ "name": "pretty", |
|
| 6998 |
+ "description": "If 'true', then the output is pretty printed.", |
|
| 6999 |
+ "required": false, |
|
| 7000 |
+ "allowMultiple": false |
|
| 7001 |
+ }, |
|
| 7002 |
+ {
|
|
| 7003 |
+ "type": "v1.LocalSubjectAccessReview", |
|
| 7004 |
+ "paramType": "body", |
|
| 7005 |
+ "name": "body", |
|
| 7006 |
+ "description": "", |
|
| 7007 |
+ "required": true, |
|
| 7008 |
+ "allowMultiple": false |
|
| 7009 |
+ } |
|
| 7010 |
+ ], |
|
| 7011 |
+ "responseMessages": [ |
|
| 7012 |
+ {
|
|
| 7013 |
+ "code": 200, |
|
| 7014 |
+ "message": "OK", |
|
| 7015 |
+ "responseModel": "v1.LocalSubjectAccessReview" |
|
| 7016 |
+ } |
|
| 7017 |
+ ], |
|
| 7018 |
+ "produces": [ |
|
| 7019 |
+ "application/json" |
|
| 7020 |
+ ], |
|
| 7021 |
+ "consumes": [ |
|
| 7022 |
+ "*/*" |
|
| 7023 |
+ ] |
|
| 7024 |
+ } |
|
| 7025 |
+ ] |
|
| 7026 |
+ }, |
|
| 7027 |
+ {
|
|
| 6840 | 7028 |
"path": "/oapi/v1/netnamespaces", |
| 6841 | 7029 |
"description": "OpenShift REST API, version v1", |
| 6842 | 7030 |
"operations": [ |
| ... | ... |
@@ -16111,6 +16299,97 @@ |
| 16111 | 16111 |
} |
| 16112 | 16112 |
} |
| 16113 | 16113 |
}, |
| 16114 |
+ "v1.LocalResourceAccessReview": {
|
|
| 16115 |
+ "id": "v1.LocalResourceAccessReview", |
|
| 16116 |
+ "required": [ |
|
| 16117 |
+ "namespace", |
|
| 16118 |
+ "verb", |
|
| 16119 |
+ "resource", |
|
| 16120 |
+ "resourceName" |
|
| 16121 |
+ ], |
|
| 16122 |
+ "properties": {
|
|
| 16123 |
+ "kind": {
|
|
| 16124 |
+ "type": "string", |
|
| 16125 |
+ "description": "kind of object, in CamelCase; cannot be updated; see http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#types-kinds" |
|
| 16126 |
+ }, |
|
| 16127 |
+ "apiVersion": {
|
|
| 16128 |
+ "type": "string", |
|
| 16129 |
+ "description": "version of the schema the object should have; see http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#resources" |
|
| 16130 |
+ }, |
|
| 16131 |
+ "namespace": {
|
|
| 16132 |
+ "type": "string", |
|
| 16133 |
+ "description": "namespace of the action being requested" |
|
| 16134 |
+ }, |
|
| 16135 |
+ "verb": {
|
|
| 16136 |
+ "type": "string", |
|
| 16137 |
+ "description": "one of get, list, watch, create, update, delete" |
|
| 16138 |
+ }, |
|
| 16139 |
+ "resource": {
|
|
| 16140 |
+ "type": "string", |
|
| 16141 |
+ "description": "one of the existing resource types" |
|
| 16142 |
+ }, |
|
| 16143 |
+ "resourceName": {
|
|
| 16144 |
+ "type": "string", |
|
| 16145 |
+ "description": "name of the resource being requested for a get or delete" |
|
| 16146 |
+ }, |
|
| 16147 |
+ "content": {
|
|
| 16148 |
+ "type": "string", |
|
| 16149 |
+ "description": "actual content of the request for create and update" |
|
| 16150 |
+ } |
|
| 16151 |
+ } |
|
| 16152 |
+ }, |
|
| 16153 |
+ "v1.LocalSubjectAccessReview": {
|
|
| 16154 |
+ "id": "v1.LocalSubjectAccessReview", |
|
| 16155 |
+ "required": [ |
|
| 16156 |
+ "namespace", |
|
| 16157 |
+ "verb", |
|
| 16158 |
+ "resource", |
|
| 16159 |
+ "resourceName", |
|
| 16160 |
+ "user", |
|
| 16161 |
+ "groups" |
|
| 16162 |
+ ], |
|
| 16163 |
+ "properties": {
|
|
| 16164 |
+ "kind": {
|
|
| 16165 |
+ "type": "string", |
|
| 16166 |
+ "description": "kind of object, in CamelCase; cannot be updated; see http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#types-kinds" |
|
| 16167 |
+ }, |
|
| 16168 |
+ "apiVersion": {
|
|
| 16169 |
+ "type": "string", |
|
| 16170 |
+ "description": "version of the schema the object should have; see http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#resources" |
|
| 16171 |
+ }, |
|
| 16172 |
+ "namespace": {
|
|
| 16173 |
+ "type": "string", |
|
| 16174 |
+ "description": "namespace of the action being requested" |
|
| 16175 |
+ }, |
|
| 16176 |
+ "verb": {
|
|
| 16177 |
+ "type": "string", |
|
| 16178 |
+ "description": "one of get, list, watch, create, update, delete" |
|
| 16179 |
+ }, |
|
| 16180 |
+ "resource": {
|
|
| 16181 |
+ "type": "string", |
|
| 16182 |
+ "description": "one of the existing resource types" |
|
| 16183 |
+ }, |
|
| 16184 |
+ "resourceName": {
|
|
| 16185 |
+ "type": "string", |
|
| 16186 |
+ "description": "name of the resource being requested for a get or delete" |
|
| 16187 |
+ }, |
|
| 16188 |
+ "content": {
|
|
| 16189 |
+ "type": "string", |
|
| 16190 |
+ "description": "actual content of the request for create and update" |
|
| 16191 |
+ }, |
|
| 16192 |
+ "user": {
|
|
| 16193 |
+ "type": "string", |
|
| 16194 |
+ "description": "optional, if both user and groups are empty, the current authenticated user is used" |
|
| 16195 |
+ }, |
|
| 16196 |
+ "groups": {
|
|
| 16197 |
+ "type": "array", |
|
| 16198 |
+ "items": {
|
|
| 16199 |
+ "type": "string" |
|
| 16200 |
+ }, |
|
| 16201 |
+ "description": "optional, list of groups to which the user belongs" |
|
| 16202 |
+ } |
|
| 16203 |
+ } |
|
| 16204 |
+ }, |
|
| 16114 | 16205 |
"v1.NetNamespaceList": {
|
| 16115 | 16206 |
"id": "v1.NetNamespaceList", |
| 16116 | 16207 |
"required": [ |
| ... | ... |
@@ -16835,8 +17114,10 @@ |
| 16835 | 16835 |
"v1.ResourceAccessReview": {
|
| 16836 | 16836 |
"id": "v1.ResourceAccessReview", |
| 16837 | 16837 |
"required": [ |
| 16838 |
+ "namespace", |
|
| 16838 | 16839 |
"verb", |
| 16839 |
- "resource" |
|
| 16840 |
+ "resource", |
|
| 16841 |
+ "resourceName" |
|
| 16840 | 16842 |
], |
| 16841 | 16843 |
"properties": {
|
| 16842 | 16844 |
"kind": {
|
| ... | ... |
@@ -16847,6 +17128,10 @@ |
| 16847 | 16847 |
"type": "string", |
| 16848 | 16848 |
"description": "version of the schema the object should have; see http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#resources" |
| 16849 | 16849 |
}, |
| 16850 |
+ "namespace": {
|
|
| 16851 |
+ "type": "string", |
|
| 16852 |
+ "description": "namespace of the action being requested" |
|
| 16853 |
+ }, |
|
| 16850 | 16854 |
"verb": {
|
| 16851 | 16855 |
"type": "string", |
| 16852 | 16856 |
"description": "one of get, list, watch, create, update, delete" |
| ... | ... |
@@ -16855,13 +17140,13 @@ |
| 16855 | 16855 |
"type": "string", |
| 16856 | 16856 |
"description": "one of the existing resource types" |
| 16857 | 16857 |
}, |
| 16858 |
- "content": {
|
|
| 16858 |
+ "resourceName": {
|
|
| 16859 | 16859 |
"type": "string", |
| 16860 |
- "description": "actual content of the request for a create or update" |
|
| 16860 |
+ "description": "name of the resource being requested for a get or delete" |
|
| 16861 | 16861 |
}, |
| 16862 |
- "resourceName": {
|
|
| 16862 |
+ "content": {
|
|
| 16863 | 16863 |
"type": "string", |
| 16864 |
- "description": "name of the resource being requested for a get or delete operation" |
|
| 16864 |
+ "description": "actual content of the request for create and update" |
|
| 16865 | 16865 |
} |
| 16866 | 16866 |
} |
| 16867 | 16867 |
}, |
| ... | ... |
@@ -17002,11 +17287,12 @@ |
| 17002 | 17002 |
"v1.SubjectAccessReview": {
|
| 17003 | 17003 |
"id": "v1.SubjectAccessReview", |
| 17004 | 17004 |
"required": [ |
| 17005 |
+ "namespace", |
|
| 17005 | 17006 |
"verb", |
| 17006 | 17007 |
"resource", |
| 17008 |
+ "resourceName", |
|
| 17007 | 17009 |
"user", |
| 17008 |
- "groups", |
|
| 17009 |
- "resourceName" |
|
| 17010 |
+ "groups" |
|
| 17010 | 17011 |
], |
| 17011 | 17012 |
"properties": {
|
| 17012 | 17013 |
"kind": {
|
| ... | ... |
@@ -17017,6 +17303,10 @@ |
| 17017 | 17017 |
"type": "string", |
| 17018 | 17018 |
"description": "version of the schema the object should have; see http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#resources" |
| 17019 | 17019 |
}, |
| 17020 |
+ "namespace": {
|
|
| 17021 |
+ "type": "string", |
|
| 17022 |
+ "description": "namespace of the action being requested" |
|
| 17023 |
+ }, |
|
| 17020 | 17024 |
"verb": {
|
| 17021 | 17025 |
"type": "string", |
| 17022 | 17026 |
"description": "one of get, list, watch, create, update, delete" |
| ... | ... |
@@ -17025,6 +17315,14 @@ |
| 17025 | 17025 |
"type": "string", |
| 17026 | 17026 |
"description": "one of the existing resource types" |
| 17027 | 17027 |
}, |
| 17028 |
+ "resourceName": {
|
|
| 17029 |
+ "type": "string", |
|
| 17030 |
+ "description": "name of the resource being requested for a get or delete" |
|
| 17031 |
+ }, |
|
| 17032 |
+ "content": {
|
|
| 17033 |
+ "type": "string", |
|
| 17034 |
+ "description": "actual content of the request for create and update" |
|
| 17035 |
+ }, |
|
| 17028 | 17036 |
"user": {
|
| 17029 | 17037 |
"type": "string", |
| 17030 | 17038 |
"description": "optional, if both user and groups are empty, the current authenticated user is used" |
| ... | ... |
@@ -17035,14 +17333,6 @@ |
| 17035 | 17035 |
"type": "string" |
| 17036 | 17036 |
}, |
| 17037 | 17037 |
"description": "optional, list of groups to which the user belongs" |
| 17038 |
- }, |
|
| 17039 |
- "content": {
|
|
| 17040 |
- "type": "string", |
|
| 17041 |
- "description": "actual content of the request for create and update" |
|
| 17042 |
- }, |
|
| 17043 |
- "resourceName": {
|
|
| 17044 |
- "type": "string", |
|
| 17045 |
- "description": "name of the resource being requested for a get or delete" |
|
| 17046 | 17038 |
} |
| 17047 | 17039 |
} |
| 17048 | 17040 |
}, |
| ... | ... |
@@ -18,6 +18,19 @@ import ( |
| 18 | 18 |
util "k8s.io/kubernetes/pkg/util" |
| 19 | 19 |
) |
| 20 | 20 |
|
| 21 |
+func deepCopy_api_AuthorizationAttributes(in api.AuthorizationAttributes, out *api.AuthorizationAttributes, c *conversion.Cloner) error {
|
|
| 22 |
+ out.Namespace = in.Namespace |
|
| 23 |
+ out.Verb = in.Verb |
|
| 24 |
+ out.Resource = in.Resource |
|
| 25 |
+ out.ResourceName = in.ResourceName |
|
| 26 |
+ if newVal, err := c.DeepCopy(in.Content); err != nil {
|
|
| 27 |
+ return err |
|
| 28 |
+ } else {
|
|
| 29 |
+ out.Content = newVal.(runtime.EmbeddedObject) |
|
| 30 |
+ } |
|
| 31 |
+ return nil |
|
| 32 |
+} |
|
| 33 |
+ |
|
| 21 | 34 |
func deepCopy_api_ClusterPolicy(in api.ClusterPolicy, out *api.ClusterPolicy, c *conversion.Cloner) error {
|
| 22 | 35 |
if newVal, err := c.DeepCopy(in.TypeMeta); err != nil {
|
| 23 | 36 |
return err |
| ... | ... |
@@ -257,6 +270,43 @@ func deepCopy_api_IsPersonalSubjectAccessReview(in api.IsPersonalSubjectAccessRe |
| 257 | 257 |
return nil |
| 258 | 258 |
} |
| 259 | 259 |
|
| 260 |
+func deepCopy_api_LocalResourceAccessReview(in api.LocalResourceAccessReview, out *api.LocalResourceAccessReview, c *conversion.Cloner) error {
|
|
| 261 |
+ if newVal, err := c.DeepCopy(in.TypeMeta); err != nil {
|
|
| 262 |
+ return err |
|
| 263 |
+ } else {
|
|
| 264 |
+ out.TypeMeta = newVal.(pkgapi.TypeMeta) |
|
| 265 |
+ } |
|
| 266 |
+ if err := deepCopy_api_AuthorizationAttributes(in.Action, &out.Action, c); err != nil {
|
|
| 267 |
+ return err |
|
| 268 |
+ } |
|
| 269 |
+ return nil |
|
| 270 |
+} |
|
| 271 |
+ |
|
| 272 |
+func deepCopy_api_LocalSubjectAccessReview(in api.LocalSubjectAccessReview, out *api.LocalSubjectAccessReview, c *conversion.Cloner) error {
|
|
| 273 |
+ if newVal, err := c.DeepCopy(in.TypeMeta); err != nil {
|
|
| 274 |
+ return err |
|
| 275 |
+ } else {
|
|
| 276 |
+ out.TypeMeta = newVal.(pkgapi.TypeMeta) |
|
| 277 |
+ } |
|
| 278 |
+ if err := deepCopy_api_AuthorizationAttributes(in.Action, &out.Action, c); err != nil {
|
|
| 279 |
+ return err |
|
| 280 |
+ } |
|
| 281 |
+ out.User = in.User |
|
| 282 |
+ if in.Groups != nil {
|
|
| 283 |
+ out.Groups = make(util.StringSet) |
|
| 284 |
+ for key, val := range in.Groups {
|
|
| 285 |
+ if newVal, err := c.DeepCopy(val); err != nil {
|
|
| 286 |
+ return err |
|
| 287 |
+ } else {
|
|
| 288 |
+ out.Groups[key] = newVal.(util.Empty) |
|
| 289 |
+ } |
|
| 290 |
+ } |
|
| 291 |
+ } else {
|
|
| 292 |
+ out.Groups = nil |
|
| 293 |
+ } |
|
| 294 |
+ return nil |
|
| 295 |
+} |
|
| 296 |
+ |
|
| 260 | 297 |
func deepCopy_api_Policy(in api.Policy, out *api.Policy, c *conversion.Cloner) error {
|
| 261 | 298 |
if newVal, err := c.DeepCopy(in.TypeMeta); err != nil {
|
| 262 | 299 |
return err |
| ... | ... |
@@ -435,14 +485,9 @@ func deepCopy_api_ResourceAccessReview(in api.ResourceAccessReview, out *api.Res |
| 435 | 435 |
} else {
|
| 436 | 436 |
out.TypeMeta = newVal.(pkgapi.TypeMeta) |
| 437 | 437 |
} |
| 438 |
- out.Verb = in.Verb |
|
| 439 |
- out.Resource = in.Resource |
|
| 440 |
- if newVal, err := c.DeepCopy(in.Content); err != nil {
|
|
| 438 |
+ if err := deepCopy_api_AuthorizationAttributes(in.Action, &out.Action, c); err != nil {
|
|
| 441 | 439 |
return err |
| 442 |
- } else {
|
|
| 443 |
- out.Content = newVal.(runtime.EmbeddedObject) |
|
| 444 | 440 |
} |
| 445 |
- out.ResourceName = in.ResourceName |
|
| 446 | 441 |
return nil |
| 447 | 442 |
} |
| 448 | 443 |
|
| ... | ... |
@@ -601,8 +646,9 @@ func deepCopy_api_SubjectAccessReview(in api.SubjectAccessReview, out *api.Subje |
| 601 | 601 |
} else {
|
| 602 | 602 |
out.TypeMeta = newVal.(pkgapi.TypeMeta) |
| 603 | 603 |
} |
| 604 |
- out.Verb = in.Verb |
|
| 605 |
- out.Resource = in.Resource |
|
| 604 |
+ if err := deepCopy_api_AuthorizationAttributes(in.Action, &out.Action, c); err != nil {
|
|
| 605 |
+ return err |
|
| 606 |
+ } |
|
| 606 | 607 |
out.User = in.User |
| 607 | 608 |
if in.Groups != nil {
|
| 608 | 609 |
out.Groups = make(util.StringSet) |
| ... | ... |
@@ -616,12 +662,6 @@ func deepCopy_api_SubjectAccessReview(in api.SubjectAccessReview, out *api.Subje |
| 616 | 616 |
} else {
|
| 617 | 617 |
out.Groups = nil |
| 618 | 618 |
} |
| 619 |
- if newVal, err := c.DeepCopy(in.Content); err != nil {
|
|
| 620 |
- return err |
|
| 621 |
- } else {
|
|
| 622 |
- out.Content = newVal.(runtime.EmbeddedObject) |
|
| 623 |
- } |
|
| 624 |
- out.ResourceName = in.ResourceName |
|
| 625 | 619 |
return nil |
| 626 | 620 |
} |
| 627 | 621 |
|
| ... | ... |
@@ -2549,6 +2589,7 @@ func deepCopy_api_UserList(in userapi.UserList, out *userapi.UserList, c *conver |
| 2549 | 2549 |
|
| 2550 | 2550 |
func init() {
|
| 2551 | 2551 |
err := pkgapi.Scheme.AddGeneratedDeepCopyFuncs( |
| 2552 |
+ deepCopy_api_AuthorizationAttributes, |
|
| 2552 | 2553 |
deepCopy_api_ClusterPolicy, |
| 2553 | 2554 |
deepCopy_api_ClusterPolicyBinding, |
| 2554 | 2555 |
deepCopy_api_ClusterPolicyBindingList, |
| ... | ... |
@@ -2558,6 +2599,8 @@ func init() {
|
| 2558 | 2558 |
deepCopy_api_ClusterRoleBindingList, |
| 2559 | 2559 |
deepCopy_api_ClusterRoleList, |
| 2560 | 2560 |
deepCopy_api_IsPersonalSubjectAccessReview, |
| 2561 |
+ deepCopy_api_LocalResourceAccessReview, |
|
| 2562 |
+ deepCopy_api_LocalSubjectAccessReview, |
|
| 2561 | 2563 |
deepCopy_api_Policy, |
| 2562 | 2564 |
deepCopy_api_PolicyBinding, |
| 2563 | 2565 |
deepCopy_api_PolicyBindingList, |
| ... | ... |
@@ -200,22 +200,6 @@ func convert_api_PolicyList_To_v1_PolicyList(in *api.PolicyList, out *v1.PolicyL |
| 200 | 200 |
return nil |
| 201 | 201 |
} |
| 202 | 202 |
|
| 203 |
-func convert_api_ResourceAccessReview_To_v1_ResourceAccessReview(in *api.ResourceAccessReview, out *v1.ResourceAccessReview, s conversion.Scope) error {
|
|
| 204 |
- if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found {
|
|
| 205 |
- defaulting.(func(*api.ResourceAccessReview))(in) |
|
| 206 |
- } |
|
| 207 |
- if err := convert_api_TypeMeta_To_v1_TypeMeta(&in.TypeMeta, &out.TypeMeta, s); err != nil {
|
|
| 208 |
- return err |
|
| 209 |
- } |
|
| 210 |
- out.Verb = in.Verb |
|
| 211 |
- out.Resource = in.Resource |
|
| 212 |
- if err := s.Convert(&in.Content, &out.Content, 0); err != nil {
|
|
| 213 |
- return err |
|
| 214 |
- } |
|
| 215 |
- out.ResourceName = in.ResourceName |
|
| 216 |
- return nil |
|
| 217 |
-} |
|
| 218 |
- |
|
| 219 | 203 |
func convert_api_Role_To_v1_Role(in *api.Role, out *v1.Role, s conversion.Scope) error {
|
| 220 | 204 |
if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found {
|
| 221 | 205 |
defaulting.(func(*api.Role))(in) |
| ... | ... |
@@ -469,22 +453,6 @@ func convert_v1_PolicyList_To_api_PolicyList(in *v1.PolicyList, out *api.PolicyL |
| 469 | 469 |
return nil |
| 470 | 470 |
} |
| 471 | 471 |
|
| 472 |
-func convert_v1_ResourceAccessReview_To_api_ResourceAccessReview(in *v1.ResourceAccessReview, out *api.ResourceAccessReview, s conversion.Scope) error {
|
|
| 473 |
- if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found {
|
|
| 474 |
- defaulting.(func(*v1.ResourceAccessReview))(in) |
|
| 475 |
- } |
|
| 476 |
- if err := convert_v1_TypeMeta_To_api_TypeMeta(&in.TypeMeta, &out.TypeMeta, s); err != nil {
|
|
| 477 |
- return err |
|
| 478 |
- } |
|
| 479 |
- out.Verb = in.Verb |
|
| 480 |
- out.Resource = in.Resource |
|
| 481 |
- if err := s.Convert(&in.Content, &out.Content, 0); err != nil {
|
|
| 482 |
- return err |
|
| 483 |
- } |
|
| 484 |
- out.ResourceName = in.ResourceName |
|
| 485 |
- return nil |
|
| 486 |
-} |
|
| 487 |
- |
|
| 488 | 472 |
func convert_v1_Role_To_api_Role(in *v1.Role, out *api.Role, s conversion.Scope) error {
|
| 489 | 473 |
if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found {
|
| 490 | 474 |
defaulting.(func(*v1.Role))(in) |
| ... | ... |
@@ -3196,7 +3164,6 @@ func init() {
|
| 3196 | 3196 |
convert_api_ProjectSpec_To_v1_ProjectSpec, |
| 3197 | 3197 |
convert_api_ProjectStatus_To_v1_ProjectStatus, |
| 3198 | 3198 |
convert_api_Project_To_v1_Project, |
| 3199 |
- convert_api_ResourceAccessReview_To_v1_ResourceAccessReview, |
|
| 3200 | 3199 |
convert_api_ResourceRequirements_To_v1_ResourceRequirements, |
| 3201 | 3200 |
convert_api_RoleBindingList_To_v1_RoleBindingList, |
| 3202 | 3201 |
convert_api_RoleList_To_v1_RoleList, |
| ... | ... |
@@ -3274,7 +3241,6 @@ func init() {
|
| 3274 | 3274 |
convert_v1_ProjectSpec_To_api_ProjectSpec, |
| 3275 | 3275 |
convert_v1_ProjectStatus_To_api_ProjectStatus, |
| 3276 | 3276 |
convert_v1_Project_To_api_Project, |
| 3277 |
- convert_v1_ResourceAccessReview_To_api_ResourceAccessReview, |
|
| 3278 | 3277 |
convert_v1_ResourceRequirements_To_api_ResourceRequirements, |
| 3279 | 3278 |
convert_v1_RoleBindingList_To_api_RoleBindingList, |
| 3280 | 3279 |
convert_v1_RoleList_To_api_RoleList, |
| ... | ... |
@@ -19,6 +19,19 @@ import ( |
| 19 | 19 |
util "k8s.io/kubernetes/pkg/util" |
| 20 | 20 |
) |
| 21 | 21 |
|
| 22 |
+func deepCopy_v1_AuthorizationAttributes(in v1.AuthorizationAttributes, out *v1.AuthorizationAttributes, c *conversion.Cloner) error {
|
|
| 23 |
+ out.Namespace = in.Namespace |
|
| 24 |
+ out.Verb = in.Verb |
|
| 25 |
+ out.Resource = in.Resource |
|
| 26 |
+ out.ResourceName = in.ResourceName |
|
| 27 |
+ if newVal, err := c.DeepCopy(in.Content); err != nil {
|
|
| 28 |
+ return err |
|
| 29 |
+ } else {
|
|
| 30 |
+ out.Content = newVal.(runtime.RawExtension) |
|
| 31 |
+ } |
|
| 32 |
+ return nil |
|
| 33 |
+} |
|
| 34 |
+ |
|
| 22 | 35 |
func deepCopy_v1_ClusterPolicy(in v1.ClusterPolicy, out *v1.ClusterPolicy, c *conversion.Cloner) error {
|
| 23 | 36 |
if newVal, err := c.DeepCopy(in.TypeMeta); err != nil {
|
| 24 | 37 |
return err |
| ... | ... |
@@ -246,6 +259,39 @@ func deepCopy_v1_IsPersonalSubjectAccessReview(in v1.IsPersonalSubjectAccessRevi |
| 246 | 246 |
return nil |
| 247 | 247 |
} |
| 248 | 248 |
|
| 249 |
+func deepCopy_v1_LocalResourceAccessReview(in v1.LocalResourceAccessReview, out *v1.LocalResourceAccessReview, c *conversion.Cloner) error {
|
|
| 250 |
+ if newVal, err := c.DeepCopy(in.TypeMeta); err != nil {
|
|
| 251 |
+ return err |
|
| 252 |
+ } else {
|
|
| 253 |
+ out.TypeMeta = newVal.(pkgapiv1.TypeMeta) |
|
| 254 |
+ } |
|
| 255 |
+ if err := deepCopy_v1_AuthorizationAttributes(in.AuthorizationAttributes, &out.AuthorizationAttributes, c); err != nil {
|
|
| 256 |
+ return err |
|
| 257 |
+ } |
|
| 258 |
+ return nil |
|
| 259 |
+} |
|
| 260 |
+ |
|
| 261 |
+func deepCopy_v1_LocalSubjectAccessReview(in v1.LocalSubjectAccessReview, out *v1.LocalSubjectAccessReview, c *conversion.Cloner) error {
|
|
| 262 |
+ if newVal, err := c.DeepCopy(in.TypeMeta); err != nil {
|
|
| 263 |
+ return err |
|
| 264 |
+ } else {
|
|
| 265 |
+ out.TypeMeta = newVal.(pkgapiv1.TypeMeta) |
|
| 266 |
+ } |
|
| 267 |
+ if err := deepCopy_v1_AuthorizationAttributes(in.AuthorizationAttributes, &out.AuthorizationAttributes, c); err != nil {
|
|
| 268 |
+ return err |
|
| 269 |
+ } |
|
| 270 |
+ out.User = in.User |
|
| 271 |
+ if in.GroupsSlice != nil {
|
|
| 272 |
+ out.GroupsSlice = make([]string, len(in.GroupsSlice)) |
|
| 273 |
+ for i := range in.GroupsSlice {
|
|
| 274 |
+ out.GroupsSlice[i] = in.GroupsSlice[i] |
|
| 275 |
+ } |
|
| 276 |
+ } else {
|
|
| 277 |
+ out.GroupsSlice = nil |
|
| 278 |
+ } |
|
| 279 |
+ return nil |
|
| 280 |
+} |
|
| 281 |
+ |
|
| 249 | 282 |
func deepCopy_v1_NamedClusterRole(in v1.NamedClusterRole, out *v1.NamedClusterRole, c *conversion.Cloner) error {
|
| 250 | 283 |
out.Name = in.Name |
| 251 | 284 |
if err := deepCopy_v1_ClusterRole(in.Role, &out.Role, c); err != nil {
|
| ... | ... |
@@ -436,14 +482,9 @@ func deepCopy_v1_ResourceAccessReview(in v1.ResourceAccessReview, out *v1.Resour |
| 436 | 436 |
} else {
|
| 437 | 437 |
out.TypeMeta = newVal.(pkgapiv1.TypeMeta) |
| 438 | 438 |
} |
| 439 |
- out.Verb = in.Verb |
|
| 440 |
- out.Resource = in.Resource |
|
| 441 |
- if newVal, err := c.DeepCopy(in.Content); err != nil {
|
|
| 439 |
+ if err := deepCopy_v1_AuthorizationAttributes(in.AuthorizationAttributes, &out.AuthorizationAttributes, c); err != nil {
|
|
| 442 | 440 |
return err |
| 443 |
- } else {
|
|
| 444 |
- out.Content = newVal.(runtime.RawExtension) |
|
| 445 | 441 |
} |
| 446 |
- out.ResourceName = in.ResourceName |
|
| 447 | 442 |
return nil |
| 448 | 443 |
} |
| 449 | 444 |
|
| ... | ... |
@@ -586,8 +627,9 @@ func deepCopy_v1_SubjectAccessReview(in v1.SubjectAccessReview, out *v1.SubjectA |
| 586 | 586 |
} else {
|
| 587 | 587 |
out.TypeMeta = newVal.(pkgapiv1.TypeMeta) |
| 588 | 588 |
} |
| 589 |
- out.Verb = in.Verb |
|
| 590 |
- out.Resource = in.Resource |
|
| 589 |
+ if err := deepCopy_v1_AuthorizationAttributes(in.AuthorizationAttributes, &out.AuthorizationAttributes, c); err != nil {
|
|
| 590 |
+ return err |
|
| 591 |
+ } |
|
| 591 | 592 |
out.User = in.User |
| 592 | 593 |
if in.GroupsSlice != nil {
|
| 593 | 594 |
out.GroupsSlice = make([]string, len(in.GroupsSlice)) |
| ... | ... |
@@ -597,12 +639,6 @@ func deepCopy_v1_SubjectAccessReview(in v1.SubjectAccessReview, out *v1.SubjectA |
| 597 | 597 |
} else {
|
| 598 | 598 |
out.GroupsSlice = nil |
| 599 | 599 |
} |
| 600 |
- if newVal, err := c.DeepCopy(in.Content); err != nil {
|
|
| 601 |
- return err |
|
| 602 |
- } else {
|
|
| 603 |
- out.Content = newVal.(runtime.RawExtension) |
|
| 604 |
- } |
|
| 605 |
- out.ResourceName = in.ResourceName |
|
| 606 | 600 |
return nil |
| 607 | 601 |
} |
| 608 | 602 |
|
| ... | ... |
@@ -2426,6 +2462,7 @@ func deepCopy_v1_UserList(in userapiv1.UserList, out *userapiv1.UserList, c *con |
| 2426 | 2426 |
|
| 2427 | 2427 |
func init() {
|
| 2428 | 2428 |
err := api.Scheme.AddGeneratedDeepCopyFuncs( |
| 2429 |
+ deepCopy_v1_AuthorizationAttributes, |
|
| 2429 | 2430 |
deepCopy_v1_ClusterPolicy, |
| 2430 | 2431 |
deepCopy_v1_ClusterPolicyBinding, |
| 2431 | 2432 |
deepCopy_v1_ClusterPolicyBindingList, |
| ... | ... |
@@ -2435,6 +2472,8 @@ func init() {
|
| 2435 | 2435 |
deepCopy_v1_ClusterRoleBindingList, |
| 2436 | 2436 |
deepCopy_v1_ClusterRoleList, |
| 2437 | 2437 |
deepCopy_v1_IsPersonalSubjectAccessReview, |
| 2438 |
+ deepCopy_v1_LocalResourceAccessReview, |
|
| 2439 |
+ deepCopy_v1_LocalSubjectAccessReview, |
|
| 2438 | 2440 |
deepCopy_v1_NamedClusterRole, |
| 2439 | 2441 |
deepCopy_v1_NamedClusterRoleBinding, |
| 2440 | 2442 |
deepCopy_v1_NamedRole, |
| ... | ... |
@@ -219,22 +219,6 @@ func convert_api_PolicyList_To_v1beta3_PolicyList(in *api.PolicyList, out *v1bet |
| 219 | 219 |
return nil |
| 220 | 220 |
} |
| 221 | 221 |
|
| 222 |
-func convert_api_ResourceAccessReview_To_v1beta3_ResourceAccessReview(in *api.ResourceAccessReview, out *v1beta3.ResourceAccessReview, s conversion.Scope) error {
|
|
| 223 |
- if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found {
|
|
| 224 |
- defaulting.(func(*api.ResourceAccessReview))(in) |
|
| 225 |
- } |
|
| 226 |
- if err := convert_api_TypeMeta_To_v1beta3_TypeMeta(&in.TypeMeta, &out.TypeMeta, s); err != nil {
|
|
| 227 |
- return err |
|
| 228 |
- } |
|
| 229 |
- out.Verb = in.Verb |
|
| 230 |
- out.Resource = in.Resource |
|
| 231 |
- if err := s.Convert(&in.Content, &out.Content, 0); err != nil {
|
|
| 232 |
- return err |
|
| 233 |
- } |
|
| 234 |
- out.ResourceName = in.ResourceName |
|
| 235 |
- return nil |
|
| 236 |
-} |
|
| 237 |
- |
|
| 238 | 222 |
func convert_api_Role_To_v1beta3_Role(in *api.Role, out *v1beta3.Role, s conversion.Scope) error {
|
| 239 | 223 |
if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found {
|
| 240 | 224 |
defaulting.(func(*api.Role))(in) |
| ... | ... |
@@ -507,22 +491,6 @@ func convert_v1beta3_PolicyList_To_api_PolicyList(in *v1beta3.PolicyList, out *a |
| 507 | 507 |
return nil |
| 508 | 508 |
} |
| 509 | 509 |
|
| 510 |
-func convert_v1beta3_ResourceAccessReview_To_api_ResourceAccessReview(in *v1beta3.ResourceAccessReview, out *api.ResourceAccessReview, s conversion.Scope) error {
|
|
| 511 |
- if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found {
|
|
| 512 |
- defaulting.(func(*v1beta3.ResourceAccessReview))(in) |
|
| 513 |
- } |
|
| 514 |
- if err := convert_v1beta3_TypeMeta_To_api_TypeMeta(&in.TypeMeta, &out.TypeMeta, s); err != nil {
|
|
| 515 |
- return err |
|
| 516 |
- } |
|
| 517 |
- out.Verb = in.Verb |
|
| 518 |
- out.Resource = in.Resource |
|
| 519 |
- if err := s.Convert(&in.Content, &out.Content, 0); err != nil {
|
|
| 520 |
- return err |
|
| 521 |
- } |
|
| 522 |
- out.ResourceName = in.ResourceName |
|
| 523 |
- return nil |
|
| 524 |
-} |
|
| 525 |
- |
|
| 526 | 510 |
func convert_v1beta3_Role_To_api_Role(in *v1beta3.Role, out *api.Role, s conversion.Scope) error {
|
| 527 | 511 |
if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found {
|
| 528 | 512 |
defaulting.(func(*v1beta3.Role))(in) |
| ... | ... |
@@ -3130,7 +3098,6 @@ func init() {
|
| 3130 | 3130 |
convert_api_ProjectSpec_To_v1beta3_ProjectSpec, |
| 3131 | 3131 |
convert_api_ProjectStatus_To_v1beta3_ProjectStatus, |
| 3132 | 3132 |
convert_api_Project_To_v1beta3_Project, |
| 3133 |
- convert_api_ResourceAccessReview_To_v1beta3_ResourceAccessReview, |
|
| 3134 | 3133 |
convert_api_ResourceRequirements_To_v1beta3_ResourceRequirements, |
| 3135 | 3134 |
convert_api_RoleBindingList_To_v1beta3_RoleBindingList, |
| 3136 | 3135 |
convert_api_RoleList_To_v1beta3_RoleList, |
| ... | ... |
@@ -3206,7 +3173,6 @@ func init() {
|
| 3206 | 3206 |
convert_v1beta3_ProjectSpec_To_api_ProjectSpec, |
| 3207 | 3207 |
convert_v1beta3_ProjectStatus_To_api_ProjectStatus, |
| 3208 | 3208 |
convert_v1beta3_Project_To_api_Project, |
| 3209 |
- convert_v1beta3_ResourceAccessReview_To_api_ResourceAccessReview, |
|
| 3210 | 3209 |
convert_v1beta3_ResourceRequirements_To_api_ResourceRequirements, |
| 3211 | 3210 |
convert_v1beta3_RoleBindingList_To_api_RoleBindingList, |
| 3212 | 3211 |
convert_v1beta3_RoleList_To_api_RoleList, |
| ... | ... |
@@ -19,6 +19,19 @@ import ( |
| 19 | 19 |
util "k8s.io/kubernetes/pkg/util" |
| 20 | 20 |
) |
| 21 | 21 |
|
| 22 |
+func deepCopy_v1beta3_AuthorizationAttributes(in v1beta3.AuthorizationAttributes, out *v1beta3.AuthorizationAttributes, c *conversion.Cloner) error {
|
|
| 23 |
+ out.Namespace = in.Namespace |
|
| 24 |
+ out.Verb = in.Verb |
|
| 25 |
+ out.Resource = in.Resource |
|
| 26 |
+ out.ResourceName = in.ResourceName |
|
| 27 |
+ if newVal, err := c.DeepCopy(in.Content); err != nil {
|
|
| 28 |
+ return err |
|
| 29 |
+ } else {
|
|
| 30 |
+ out.Content = newVal.(runtime.RawExtension) |
|
| 31 |
+ } |
|
| 32 |
+ return nil |
|
| 33 |
+} |
|
| 34 |
+ |
|
| 22 | 35 |
func deepCopy_v1beta3_ClusterPolicy(in v1beta3.ClusterPolicy, out *v1beta3.ClusterPolicy, c *conversion.Cloner) error {
|
| 23 | 36 |
if newVal, err := c.DeepCopy(in.TypeMeta); err != nil {
|
| 24 | 37 |
return err |
| ... | ... |
@@ -246,6 +259,39 @@ func deepCopy_v1beta3_IsPersonalSubjectAccessReview(in v1beta3.IsPersonalSubject |
| 246 | 246 |
return nil |
| 247 | 247 |
} |
| 248 | 248 |
|
| 249 |
+func deepCopy_v1beta3_LocalResourceAccessReview(in v1beta3.LocalResourceAccessReview, out *v1beta3.LocalResourceAccessReview, c *conversion.Cloner) error {
|
|
| 250 |
+ if newVal, err := c.DeepCopy(in.TypeMeta); err != nil {
|
|
| 251 |
+ return err |
|
| 252 |
+ } else {
|
|
| 253 |
+ out.TypeMeta = newVal.(pkgapiv1beta3.TypeMeta) |
|
| 254 |
+ } |
|
| 255 |
+ if err := deepCopy_v1beta3_AuthorizationAttributes(in.AuthorizationAttributes, &out.AuthorizationAttributes, c); err != nil {
|
|
| 256 |
+ return err |
|
| 257 |
+ } |
|
| 258 |
+ return nil |
|
| 259 |
+} |
|
| 260 |
+ |
|
| 261 |
+func deepCopy_v1beta3_LocalSubjectAccessReview(in v1beta3.LocalSubjectAccessReview, out *v1beta3.LocalSubjectAccessReview, c *conversion.Cloner) error {
|
|
| 262 |
+ if newVal, err := c.DeepCopy(in.TypeMeta); err != nil {
|
|
| 263 |
+ return err |
|
| 264 |
+ } else {
|
|
| 265 |
+ out.TypeMeta = newVal.(pkgapiv1beta3.TypeMeta) |
|
| 266 |
+ } |
|
| 267 |
+ if err := deepCopy_v1beta3_AuthorizationAttributes(in.AuthorizationAttributes, &out.AuthorizationAttributes, c); err != nil {
|
|
| 268 |
+ return err |
|
| 269 |
+ } |
|
| 270 |
+ out.User = in.User |
|
| 271 |
+ if in.GroupsSlice != nil {
|
|
| 272 |
+ out.GroupsSlice = make([]string, len(in.GroupsSlice)) |
|
| 273 |
+ for i := range in.GroupsSlice {
|
|
| 274 |
+ out.GroupsSlice[i] = in.GroupsSlice[i] |
|
| 275 |
+ } |
|
| 276 |
+ } else {
|
|
| 277 |
+ out.GroupsSlice = nil |
|
| 278 |
+ } |
|
| 279 |
+ return nil |
|
| 280 |
+} |
|
| 281 |
+ |
|
| 249 | 282 |
func deepCopy_v1beta3_NamedClusterRole(in v1beta3.NamedClusterRole, out *v1beta3.NamedClusterRole, c *conversion.Cloner) error {
|
| 250 | 283 |
out.Name = in.Name |
| 251 | 284 |
if err := deepCopy_v1beta3_ClusterRole(in.Role, &out.Role, c); err != nil {
|
| ... | ... |
@@ -444,14 +490,9 @@ func deepCopy_v1beta3_ResourceAccessReview(in v1beta3.ResourceAccessReview, out |
| 444 | 444 |
} else {
|
| 445 | 445 |
out.TypeMeta = newVal.(pkgapiv1beta3.TypeMeta) |
| 446 | 446 |
} |
| 447 |
- out.Verb = in.Verb |
|
| 448 |
- out.Resource = in.Resource |
|
| 449 |
- if newVal, err := c.DeepCopy(in.Content); err != nil {
|
|
| 447 |
+ if err := deepCopy_v1beta3_AuthorizationAttributes(in.AuthorizationAttributes, &out.AuthorizationAttributes, c); err != nil {
|
|
| 450 | 448 |
return err |
| 451 |
- } else {
|
|
| 452 |
- out.Content = newVal.(runtime.RawExtension) |
|
| 453 | 449 |
} |
| 454 |
- out.ResourceName = in.ResourceName |
|
| 455 | 450 |
return nil |
| 456 | 451 |
} |
| 457 | 452 |
|
| ... | ... |
@@ -594,8 +635,9 @@ func deepCopy_v1beta3_SubjectAccessReview(in v1beta3.SubjectAccessReview, out *v |
| 594 | 594 |
} else {
|
| 595 | 595 |
out.TypeMeta = newVal.(pkgapiv1beta3.TypeMeta) |
| 596 | 596 |
} |
| 597 |
- out.Verb = in.Verb |
|
| 598 |
- out.Resource = in.Resource |
|
| 597 |
+ if err := deepCopy_v1beta3_AuthorizationAttributes(in.AuthorizationAttributes, &out.AuthorizationAttributes, c); err != nil {
|
|
| 598 |
+ return err |
|
| 599 |
+ } |
|
| 599 | 600 |
out.User = in.User |
| 600 | 601 |
if in.GroupsSlice != nil {
|
| 601 | 602 |
out.GroupsSlice = make([]string, len(in.GroupsSlice)) |
| ... | ... |
@@ -605,12 +647,6 @@ func deepCopy_v1beta3_SubjectAccessReview(in v1beta3.SubjectAccessReview, out *v |
| 605 | 605 |
} else {
|
| 606 | 606 |
out.GroupsSlice = nil |
| 607 | 607 |
} |
| 608 |
- if newVal, err := c.DeepCopy(in.Content); err != nil {
|
|
| 609 |
- return err |
|
| 610 |
- } else {
|
|
| 611 |
- out.Content = newVal.(runtime.RawExtension) |
|
| 612 |
- } |
|
| 613 |
- out.ResourceName = in.ResourceName |
|
| 614 | 608 |
return nil |
| 615 | 609 |
} |
| 616 | 610 |
|
| ... | ... |
@@ -2416,6 +2452,7 @@ func deepCopy_v1beta3_UserList(in userapiv1beta3.UserList, out *userapiv1beta3.U |
| 2416 | 2416 |
|
| 2417 | 2417 |
func init() {
|
| 2418 | 2418 |
err := api.Scheme.AddGeneratedDeepCopyFuncs( |
| 2419 |
+ deepCopy_v1beta3_AuthorizationAttributes, |
|
| 2419 | 2420 |
deepCopy_v1beta3_ClusterPolicy, |
| 2420 | 2421 |
deepCopy_v1beta3_ClusterPolicyBinding, |
| 2421 | 2422 |
deepCopy_v1beta3_ClusterPolicyBindingList, |
| ... | ... |
@@ -2425,6 +2462,8 @@ func init() {
|
| 2425 | 2425 |
deepCopy_v1beta3_ClusterRoleBindingList, |
| 2426 | 2426 |
deepCopy_v1beta3_ClusterRoleList, |
| 2427 | 2427 |
deepCopy_v1beta3_IsPersonalSubjectAccessReview, |
| 2428 |
+ deepCopy_v1beta3_LocalResourceAccessReview, |
|
| 2429 |
+ deepCopy_v1beta3_LocalSubjectAccessReview, |
|
| 2428 | 2430 |
deepCopy_v1beta3_NamedClusterRole, |
| 2429 | 2431 |
deepCopy_v1beta3_NamedClusterRoleBinding, |
| 2430 | 2432 |
deepCopy_v1beta3_NamedRole, |
| ... | ... |
@@ -27,6 +27,8 @@ import ( |
| 27 | 27 |
func init() {
|
| 28 | 28 |
Validator.Register(&authorizationapi.SubjectAccessReview{}, authorizationvalidation.ValidateSubjectAccessReview, nil)
|
| 29 | 29 |
Validator.Register(&authorizationapi.ResourceAccessReview{}, authorizationvalidation.ValidateResourceAccessReview, nil)
|
| 30 |
+ Validator.Register(&authorizationapi.LocalSubjectAccessReview{}, authorizationvalidation.ValidateLocalSubjectAccessReview, nil)
|
|
| 31 |
+ Validator.Register(&authorizationapi.LocalResourceAccessReview{}, authorizationvalidation.ValidateLocalResourceAccessReview, nil)
|
|
| 30 | 32 |
|
| 31 | 33 |
Validator.Register(&authorizationapi.Policy{}, authorizationvalidation.ValidateLocalPolicy, authorizationvalidation.ValidateLocalPolicyUpdate)
|
| 32 | 34 |
Validator.Register(&authorizationapi.PolicyBinding{}, authorizationvalidation.ValidateLocalPolicyBinding, authorizationvalidation.ValidateLocalPolicyBindingUpdate)
|
| ... | ... |
@@ -10,15 +10,17 @@ func init() {
|
| 10 | 10 |
&RoleBinding{},
|
| 11 | 11 |
&Policy{},
|
| 12 | 12 |
&PolicyBinding{},
|
| 13 |
- &ResourceAccessReview{},
|
|
| 14 |
- &SubjectAccessReview{},
|
|
| 15 |
- &ResourceAccessReviewResponse{},
|
|
| 16 |
- &SubjectAccessReviewResponse{},
|
|
| 17 | 13 |
&PolicyList{},
|
| 18 | 14 |
&PolicyBindingList{},
|
| 19 | 15 |
&RoleBindingList{},
|
| 20 | 16 |
&RoleList{},
|
| 21 | 17 |
|
| 18 |
+ &ResourceAccessReview{},
|
|
| 19 |
+ &SubjectAccessReview{},
|
|
| 20 |
+ &LocalResourceAccessReview{},
|
|
| 21 |
+ &LocalSubjectAccessReview{},
|
|
| 22 |
+ &ResourceAccessReviewResponse{},
|
|
| 23 |
+ &SubjectAccessReviewResponse{},
|
|
| 22 | 24 |
&IsPersonalSubjectAccessReview{},
|
| 23 | 25 |
|
| 24 | 26 |
&ClusterRole{},
|
| ... | ... |
@@ -41,17 +43,19 @@ func (*ClusterPolicyBindingList) IsAnAPIObject() {}
|
| 41 | 41 |
func (*ClusterRoleBindingList) IsAnAPIObject() {}
|
| 42 | 42 |
func (*ClusterRoleList) IsAnAPIObject() {}
|
| 43 | 43 |
|
| 44 |
-func (*Role) IsAnAPIObject() {}
|
|
| 45 |
-func (*Policy) IsAnAPIObject() {}
|
|
| 46 |
-func (*PolicyBinding) IsAnAPIObject() {}
|
|
| 47 |
-func (*RoleBinding) IsAnAPIObject() {}
|
|
| 48 |
-func (*ResourceAccessReview) IsAnAPIObject() {}
|
|
| 49 |
-func (*SubjectAccessReview) IsAnAPIObject() {}
|
|
| 50 |
-func (*ResourceAccessReviewResponse) IsAnAPIObject() {}
|
|
| 51 |
-func (*SubjectAccessReviewResponse) IsAnAPIObject() {}
|
|
| 52 |
-func (*PolicyList) IsAnAPIObject() {}
|
|
| 53 |
-func (*PolicyBindingList) IsAnAPIObject() {}
|
|
| 54 |
-func (*RoleBindingList) IsAnAPIObject() {}
|
|
| 55 |
-func (*RoleList) IsAnAPIObject() {}
|
|
| 44 |
+func (*Role) IsAnAPIObject() {}
|
|
| 45 |
+func (*Policy) IsAnAPIObject() {}
|
|
| 46 |
+func (*PolicyBinding) IsAnAPIObject() {}
|
|
| 47 |
+func (*RoleBinding) IsAnAPIObject() {}
|
|
| 48 |
+func (*PolicyList) IsAnAPIObject() {}
|
|
| 49 |
+func (*PolicyBindingList) IsAnAPIObject() {}
|
|
| 50 |
+func (*RoleBindingList) IsAnAPIObject() {}
|
|
| 51 |
+func (*RoleList) IsAnAPIObject() {}
|
|
| 56 | 52 |
|
| 53 |
+func (*ResourceAccessReview) IsAnAPIObject() {}
|
|
| 54 |
+func (*SubjectAccessReview) IsAnAPIObject() {}
|
|
| 55 |
+func (*LocalResourceAccessReview) IsAnAPIObject() {}
|
|
| 56 |
+func (*LocalSubjectAccessReview) IsAnAPIObject() {}
|
|
| 57 |
+func (*ResourceAccessReviewResponse) IsAnAPIObject() {}
|
|
| 58 |
+func (*SubjectAccessReviewResponse) IsAnAPIObject() {}
|
|
| 57 | 59 |
func (*IsPersonalSubjectAccessReview) IsAnAPIObject() {}
|
| ... | ... |
@@ -64,15 +64,18 @@ const ( |
| 64 | 64 |
|
| 65 | 65 |
var ( |
| 66 | 66 |
GroupsToResources = map[string][]string{
|
| 67 |
- BuildGroupName: {"builds", "buildconfigs", "buildlogs", "buildconfigs/instantiate", "builds/log", "builds/clone", "buildconfigs/webhooks"},
|
|
| 68 |
- ImageGroupName: {"imagestreams", "imagestreammappings", "imagestreamtags", "imagestreamimages"},
|
|
| 69 |
- DeploymentGroupName: {"deployments", "deploymentconfigs", "generatedeploymentconfigs", "deploymentconfigrollbacks"},
|
|
| 70 |
- SDNGroupName: {"clusternetworks", "hostsubnets", "netnamespaces"},
|
|
| 71 |
- TemplateGroupName: {"templates", "templateconfigs", "processedtemplates"},
|
|
| 72 |
- UserGroupName: {"identities", "users", "useridentitymappings", "groups"},
|
|
| 73 |
- OAuthGroupName: {"oauthauthorizetokens", "oauthaccesstokens", "oauthclients", "oauthclientauthorizations"},
|
|
| 74 |
- PolicyOwnerGroupName: {"policies", "policybindings"},
|
|
| 75 |
- PermissionGrantingGroupName: {"roles", "rolebindings", "resourceaccessreviews", "subjectaccessreviews"},
|
|
| 67 |
+ BuildGroupName: {"builds", "buildconfigs", "buildlogs", "buildconfigs/instantiate", "builds/log", "builds/clone", "buildconfigs/webhooks"},
|
|
| 68 |
+ ImageGroupName: {"imagestreams", "imagestreammappings", "imagestreamtags", "imagestreamimages"},
|
|
| 69 |
+ DeploymentGroupName: {"deployments", "deploymentconfigs", "generatedeploymentconfigs", "deploymentconfigrollbacks"},
|
|
| 70 |
+ SDNGroupName: {"clusternetworks", "hostsubnets", "netnamespaces"},
|
|
| 71 |
+ TemplateGroupName: {"templates", "templateconfigs", "processedtemplates"},
|
|
| 72 |
+ UserGroupName: {"identities", "users", "useridentitymappings", "groups"},
|
|
| 73 |
+ OAuthGroupName: {"oauthauthorizetokens", "oauthaccesstokens", "oauthclients", "oauthclientauthorizations"},
|
|
| 74 |
+ PolicyOwnerGroupName: {"policies", "policybindings"},
|
|
| 75 |
+ |
|
| 76 |
+ // RAR and SAR are in this list to support backwards compatibility with clients that expect access to those resource in a namespace scope and a cluster scope. |
|
| 77 |
+ // TODO remove once we have eliminated the namespace scoped resource. |
|
| 78 |
+ PermissionGrantingGroupName: {"roles", "rolebindings", "resourceaccessreviews" /* cluster scoped*/, "subjectaccessreviews" /* cluster scoped*/, "localresourceaccessreviews", "localsubjectaccessreviews"},
|
|
| 76 | 79 |
OpenshiftExposedGroupName: {BuildGroupName, ImageGroupName, DeploymentGroupName, TemplateGroupName, "routes"},
|
| 77 | 80 |
OpenshiftAllGroupName: {OpenshiftExposedGroupName, UserGroupName, OAuthGroupName, PolicyOwnerGroupName, SDNGroupName, PermissionGrantingGroupName, OpenshiftStatusGroupName, "projects",
|
| 78 | 81 |
"clusterroles", "clusterrolebindings", "clusterpolicies", "clusterpolicybindings", "images" /* cluster scoped*/, "projectrequests"}, |
| ... | ... |
@@ -195,14 +198,8 @@ type ResourceAccessReviewResponse struct {
|
| 195 | 195 |
type ResourceAccessReview struct {
|
| 196 | 196 |
kapi.TypeMeta |
| 197 | 197 |
|
| 198 |
- // Verb is one of: get, list, watch, create, update, delete |
|
| 199 |
- Verb string |
|
| 200 |
- // Resource is one of the existing resource types |
|
| 201 |
- Resource string |
|
| 202 |
- // Content is the actual content of the request for create and update |
|
| 203 |
- Content kruntime.EmbeddedObject |
|
| 204 |
- // ResourceName is the name of the resource being requested for a "get" or deleted for a "delete" |
|
| 205 |
- ResourceName string |
|
| 198 |
+ // Action describes the action being tested |
|
| 199 |
+ Action AuthorizationAttributes |
|
| 206 | 200 |
} |
| 207 | 201 |
|
| 208 | 202 |
// SubjectAccessReviewResponse describes whether or not a user or group can perform an action |
| ... | ... |
@@ -221,18 +218,45 @@ type SubjectAccessReviewResponse struct {
|
| 221 | 221 |
type SubjectAccessReview struct {
|
| 222 | 222 |
kapi.TypeMeta |
| 223 | 223 |
|
| 224 |
- // Verb is one of: get, list, watch, create, update, delete |
|
| 225 |
- Verb string |
|
| 226 |
- // Resource is one of the existing resource types |
|
| 227 |
- Resource string |
|
| 224 |
+ // Action describes the action being tested |
|
| 225 |
+ Action AuthorizationAttributes |
|
| 228 | 226 |
// User is optional. If both User and Groups are empty, the current authenticated user is used. |
| 229 | 227 |
User string |
| 230 | 228 |
// Groups is optional. Groups is the list of groups to which the User belongs. |
| 231 | 229 |
Groups util.StringSet |
| 232 |
- // Content is the actual content of the request for create and update |
|
| 233 |
- Content kruntime.EmbeddedObject |
|
| 230 |
+} |
|
| 231 |
+ |
|
| 232 |
+// LocalResourceAccessReview is a means to request a list of which users and groups are authorized to perform the action specified by spec in a particular namespace |
|
| 233 |
+type LocalResourceAccessReview struct {
|
|
| 234 |
+ kapi.TypeMeta |
|
| 235 |
+ |
|
| 236 |
+ // Action describes the action being tested |
|
| 237 |
+ Action AuthorizationAttributes |
|
| 238 |
+} |
|
| 239 |
+ |
|
| 240 |
+// LocalSubjectAccessReview is an object for requesting information about whether a user or group can perform an action in a particular namespace |
|
| 241 |
+type LocalSubjectAccessReview struct {
|
|
| 242 |
+ kapi.TypeMeta |
|
| 243 |
+ |
|
| 244 |
+ // Action describes the action being tested. The Namespace element is FORCED to the current namespace. |
|
| 245 |
+ Action AuthorizationAttributes |
|
| 246 |
+ // User is optional. If both User and Groups are empty, the current authenticated user is used. |
|
| 247 |
+ User string |
|
| 248 |
+ // Groups is optional. Groups is the list of groups to which the User belongs. |
|
| 249 |
+ Groups util.StringSet |
|
| 250 |
+} |
|
| 251 |
+ |
|
| 252 |
+type AuthorizationAttributes struct {
|
|
| 253 |
+ // Namespace is the namespace of the action being requested. Currently, there is no distinction between no namespace and all namespaces |
|
| 254 |
+ Namespace string |
|
| 255 |
+ // Verb is one of: get, list, watch, create, update, delete |
|
| 256 |
+ Verb string |
|
| 257 |
+ // Resource is one of the existing resource types |
|
| 258 |
+ Resource string |
|
| 234 | 259 |
// ResourceName is the name of the resource being requested for a "get" or deleted for a "delete" |
| 235 | 260 |
ResourceName string |
| 261 |
+ // Content is the actual content of the request for create and update |
|
| 262 |
+ Content kruntime.EmbeddedObject |
|
| 236 | 263 |
} |
| 237 | 264 |
|
| 238 | 265 |
// PolicyList is a collection of Policies |
| ... | ... |
@@ -10,10 +10,57 @@ import ( |
| 10 | 10 |
newer "github.com/openshift/origin/pkg/authorization/api" |
| 11 | 11 |
) |
| 12 | 12 |
|
| 13 |
+func convert_v1_ResourceAccessReview_To_api_ResourceAccessReview(in *ResourceAccessReview, out *newer.ResourceAccessReview, s conversion.Scope) error {
|
|
| 14 |
+ if err := s.DefaultConvert(in, out, conversion.IgnoreMissingFields); err != nil {
|
|
| 15 |
+ return err |
|
| 16 |
+ } |
|
| 17 |
+ if err := s.DefaultConvert(&in.AuthorizationAttributes, &out.Action, conversion.IgnoreMissingFields); err != nil {
|
|
| 18 |
+ return err |
|
| 19 |
+ } |
|
| 20 |
+ |
|
| 21 |
+ return nil |
|
| 22 |
+} |
|
| 23 |
+ |
|
| 24 |
+func convert_api_ResourceAccessReview_To_v1_ResourceAccessReview(in *newer.ResourceAccessReview, out *ResourceAccessReview, s conversion.Scope) error {
|
|
| 25 |
+ if err := s.DefaultConvert(in, out, conversion.IgnoreMissingFields); err != nil {
|
|
| 26 |
+ return err |
|
| 27 |
+ } |
|
| 28 |
+ if err := s.DefaultConvert(&in.Action, &out.AuthorizationAttributes, conversion.IgnoreMissingFields); err != nil {
|
|
| 29 |
+ return err |
|
| 30 |
+ } |
|
| 31 |
+ |
|
| 32 |
+ return nil |
|
| 33 |
+} |
|
| 34 |
+ |
|
| 35 |
+func convert_v1_LocalResourceAccessReview_To_api_LocalResourceAccessReview(in *LocalResourceAccessReview, out *newer.LocalResourceAccessReview, s conversion.Scope) error {
|
|
| 36 |
+ if err := s.DefaultConvert(in, out, conversion.IgnoreMissingFields); err != nil {
|
|
| 37 |
+ return err |
|
| 38 |
+ } |
|
| 39 |
+ if err := s.DefaultConvert(&in.AuthorizationAttributes, &out.Action, conversion.IgnoreMissingFields); err != nil {
|
|
| 40 |
+ return err |
|
| 41 |
+ } |
|
| 42 |
+ |
|
| 43 |
+ return nil |
|
| 44 |
+} |
|
| 45 |
+ |
|
| 46 |
+func convert_api_LocalResourceAccessReview_To_v1_LocalResourceAccessReview(in *newer.LocalResourceAccessReview, out *LocalResourceAccessReview, s conversion.Scope) error {
|
|
| 47 |
+ if err := s.DefaultConvert(in, out, conversion.IgnoreMissingFields); err != nil {
|
|
| 48 |
+ return err |
|
| 49 |
+ } |
|
| 50 |
+ if err := s.DefaultConvert(&in.Action, &out.AuthorizationAttributes, conversion.IgnoreMissingFields); err != nil {
|
|
| 51 |
+ return err |
|
| 52 |
+ } |
|
| 53 |
+ |
|
| 54 |
+ return nil |
|
| 55 |
+} |
|
| 56 |
+ |
|
| 13 | 57 |
func convert_v1_SubjectAccessReview_To_api_SubjectAccessReview(in *SubjectAccessReview, out *newer.SubjectAccessReview, s conversion.Scope) error {
|
| 14 | 58 |
if err := s.DefaultConvert(in, out, conversion.IgnoreMissingFields); err != nil {
|
| 15 | 59 |
return err |
| 16 | 60 |
} |
| 61 |
+ if err := s.DefaultConvert(&in.AuthorizationAttributes, &out.Action, conversion.IgnoreMissingFields); err != nil {
|
|
| 62 |
+ return err |
|
| 63 |
+ } |
|
| 17 | 64 |
|
| 18 | 65 |
out.Groups = util.NewStringSet(in.GroupsSlice...) |
| 19 | 66 |
|
| ... | ... |
@@ -24,6 +71,35 @@ func convert_api_SubjectAccessReview_To_v1_SubjectAccessReview(in *newer.Subject |
| 24 | 24 |
if err := s.DefaultConvert(in, out, conversion.IgnoreMissingFields); err != nil {
|
| 25 | 25 |
return err |
| 26 | 26 |
} |
| 27 |
+ if err := s.DefaultConvert(&in.Action, &out.AuthorizationAttributes, conversion.IgnoreMissingFields); err != nil {
|
|
| 28 |
+ return err |
|
| 29 |
+ } |
|
| 30 |
+ |
|
| 31 |
+ out.GroupsSlice = in.Groups.List() |
|
| 32 |
+ |
|
| 33 |
+ return nil |
|
| 34 |
+} |
|
| 35 |
+ |
|
| 36 |
+func convert_v1_LocalSubjectAccessReview_To_api_LocalSubjectAccessReview(in *LocalSubjectAccessReview, out *newer.LocalSubjectAccessReview, s conversion.Scope) error {
|
|
| 37 |
+ if err := s.DefaultConvert(in, out, conversion.IgnoreMissingFields); err != nil {
|
|
| 38 |
+ return err |
|
| 39 |
+ } |
|
| 40 |
+ if err := s.DefaultConvert(&in.AuthorizationAttributes, &out.Action, conversion.IgnoreMissingFields); err != nil {
|
|
| 41 |
+ return err |
|
| 42 |
+ } |
|
| 43 |
+ |
|
| 44 |
+ out.Groups = util.NewStringSet(in.GroupsSlice...) |
|
| 45 |
+ |
|
| 46 |
+ return nil |
|
| 47 |
+} |
|
| 48 |
+ |
|
| 49 |
+func convert_api_LocalSubjectAccessReview_To_v1_LocalSubjectAccessReview(in *newer.LocalSubjectAccessReview, out *LocalSubjectAccessReview, s conversion.Scope) error {
|
|
| 50 |
+ if err := s.DefaultConvert(in, out, conversion.IgnoreMissingFields); err != nil {
|
|
| 51 |
+ return err |
|
| 52 |
+ } |
|
| 53 |
+ if err := s.DefaultConvert(&in.Action, &out.AuthorizationAttributes, conversion.IgnoreMissingFields); err != nil {
|
|
| 54 |
+ return err |
|
| 55 |
+ } |
|
| 27 | 56 |
|
| 28 | 57 |
out.GroupsSlice = in.Groups.List() |
| 29 | 58 |
|
| ... | ... |
@@ -312,6 +388,12 @@ func init() {
|
| 312 | 312 |
|
| 313 | 313 |
convert_v1_SubjectAccessReview_To_api_SubjectAccessReview, |
| 314 | 314 |
convert_api_SubjectAccessReview_To_v1_SubjectAccessReview, |
| 315 |
+ convert_v1_LocalSubjectAccessReview_To_api_LocalSubjectAccessReview, |
|
| 316 |
+ convert_api_LocalSubjectAccessReview_To_v1_LocalSubjectAccessReview, |
|
| 317 |
+ convert_v1_ResourceAccessReview_To_api_ResourceAccessReview, |
|
| 318 |
+ convert_api_ResourceAccessReview_To_v1_ResourceAccessReview, |
|
| 319 |
+ convert_v1_LocalResourceAccessReview_To_api_LocalResourceAccessReview, |
|
| 320 |
+ convert_api_LocalResourceAccessReview_To_v1_LocalResourceAccessReview, |
|
| 315 | 321 |
convert_v1_ResourceAccessReviewResponse_To_api_ResourceAccessReviewResponse, |
| 316 | 322 |
convert_api_ResourceAccessReviewResponse_To_v1_ResourceAccessReviewResponse, |
| 317 | 323 |
convert_v1_PolicyRule_To_api_PolicyRule, |
| ... | ... |
@@ -10,15 +10,17 @@ func init() {
|
| 10 | 10 |
&RoleBinding{},
|
| 11 | 11 |
&Policy{},
|
| 12 | 12 |
&PolicyBinding{},
|
| 13 |
- &ResourceAccessReview{},
|
|
| 14 |
- &SubjectAccessReview{},
|
|
| 15 |
- &ResourceAccessReviewResponse{},
|
|
| 16 |
- &SubjectAccessReviewResponse{},
|
|
| 17 | 13 |
&PolicyList{},
|
| 18 | 14 |
&PolicyBindingList{},
|
| 19 | 15 |
&RoleBindingList{},
|
| 20 | 16 |
&RoleList{},
|
| 21 | 17 |
|
| 18 |
+ &ResourceAccessReview{},
|
|
| 19 |
+ &SubjectAccessReview{},
|
|
| 20 |
+ &LocalResourceAccessReview{},
|
|
| 21 |
+ &LocalSubjectAccessReview{},
|
|
| 22 |
+ &ResourceAccessReviewResponse{},
|
|
| 23 |
+ &SubjectAccessReviewResponse{},
|
|
| 22 | 24 |
&IsPersonalSubjectAccessReview{},
|
| 23 | 25 |
|
| 24 | 26 |
&ClusterRole{},
|
| ... | ... |
@@ -41,17 +43,19 @@ func (*ClusterPolicyBindingList) IsAnAPIObject() {}
|
| 41 | 41 |
func (*ClusterRoleBindingList) IsAnAPIObject() {}
|
| 42 | 42 |
func (*ClusterRoleList) IsAnAPIObject() {}
|
| 43 | 43 |
|
| 44 |
-func (*Role) IsAnAPIObject() {}
|
|
| 45 |
-func (*Policy) IsAnAPIObject() {}
|
|
| 46 |
-func (*PolicyBinding) IsAnAPIObject() {}
|
|
| 47 |
-func (*RoleBinding) IsAnAPIObject() {}
|
|
| 48 |
-func (*ResourceAccessReview) IsAnAPIObject() {}
|
|
| 49 |
-func (*SubjectAccessReview) IsAnAPIObject() {}
|
|
| 50 |
-func (*ResourceAccessReviewResponse) IsAnAPIObject() {}
|
|
| 51 |
-func (*SubjectAccessReviewResponse) IsAnAPIObject() {}
|
|
| 52 |
-func (*PolicyList) IsAnAPIObject() {}
|
|
| 53 |
-func (*PolicyBindingList) IsAnAPIObject() {}
|
|
| 54 |
-func (*RoleBindingList) IsAnAPIObject() {}
|
|
| 55 |
-func (*RoleList) IsAnAPIObject() {}
|
|
| 44 |
+func (*Role) IsAnAPIObject() {}
|
|
| 45 |
+func (*Policy) IsAnAPIObject() {}
|
|
| 46 |
+func (*PolicyBinding) IsAnAPIObject() {}
|
|
| 47 |
+func (*RoleBinding) IsAnAPIObject() {}
|
|
| 48 |
+func (*PolicyList) IsAnAPIObject() {}
|
|
| 49 |
+func (*PolicyBindingList) IsAnAPIObject() {}
|
|
| 50 |
+func (*RoleBindingList) IsAnAPIObject() {}
|
|
| 51 |
+func (*RoleList) IsAnAPIObject() {}
|
|
| 56 | 52 |
|
| 53 |
+func (*ResourceAccessReview) IsAnAPIObject() {}
|
|
| 54 |
+func (*SubjectAccessReview) IsAnAPIObject() {}
|
|
| 55 |
+func (*LocalResourceAccessReview) IsAnAPIObject() {}
|
|
| 56 |
+func (*LocalSubjectAccessReview) IsAnAPIObject() {}
|
|
| 57 |
+func (*ResourceAccessReviewResponse) IsAnAPIObject() {}
|
|
| 58 |
+func (*SubjectAccessReviewResponse) IsAnAPIObject() {}
|
|
| 57 | 59 |
func (*IsPersonalSubjectAccessReview) IsAnAPIObject() {}
|
| ... | ... |
@@ -90,6 +90,16 @@ type PolicyBinding struct {
|
| 90 | 90 |
RoleBindings []NamedRoleBinding `json:"roleBindings" description:"all roleBindings held by this policyBinding"` |
| 91 | 91 |
} |
| 92 | 92 |
|
| 93 |
+type NamedRole struct {
|
|
| 94 |
+ Name string `json:"name" description:"name of the role"` |
|
| 95 |
+ Role Role `json:"role" description:"the role"` |
|
| 96 |
+} |
|
| 97 |
+ |
|
| 98 |
+type NamedRoleBinding struct {
|
|
| 99 |
+ Name string `json:"name" description:"name of the roleBinding"` |
|
| 100 |
+ RoleBinding RoleBinding `json:"roleBinding" description:"the roleBinding"` |
|
| 101 |
+} |
|
| 102 |
+ |
|
| 93 | 103 |
// ResourceAccessReviewResponse describes who can perform the action |
| 94 | 104 |
type ResourceAccessReviewResponse struct {
|
| 95 | 105 |
kapi.TypeMeta `json:",inline"` |
| ... | ... |
@@ -107,24 +117,8 @@ type ResourceAccessReviewResponse struct {
|
| 107 | 107 |
type ResourceAccessReview struct {
|
| 108 | 108 |
kapi.TypeMeta `json:",inline"` |
| 109 | 109 |
|
| 110 |
- // Verb is one of: get, list, watch, create, update, delete |
|
| 111 |
- Verb string `json:"verb" description:"one of get, list, watch, create, update, delete"` |
|
| 112 |
- // Resource is one of the existing resource types |
|
| 113 |
- Resource string `json:"resource" description:"one of the existing resource types"` |
|
| 114 |
- // Content is the actual content of the request for create and update |
|
| 115 |
- Content kruntime.RawExtension `json:"content,omitempty" description:"actual content of the request for a create or update"` |
|
| 116 |
- // ResourceName is the name of the resource being requested for a "get" or deleted for a "delete" |
|
| 117 |
- ResourceName string `json:"resourceName,omitempty" description:"name of the resource being requested for a get or delete operation"` |
|
| 118 |
-} |
|
| 119 |
- |
|
| 120 |
-type NamedRole struct {
|
|
| 121 |
- Name string `json:"name" description:"name of the role"` |
|
| 122 |
- Role Role `json:"role" description:"the role"` |
|
| 123 |
-} |
|
| 124 |
- |
|
| 125 |
-type NamedRoleBinding struct {
|
|
| 126 |
- Name string `json:"name" description:"name of the roleBinding"` |
|
| 127 |
- RoleBinding RoleBinding `json:"roleBinding" description:"the roleBinding"` |
|
| 110 |
+ // AuthorizationAttributes describes the action being tested. |
|
| 111 |
+ AuthorizationAttributes `json:",inline" description:"the action being tested"` |
|
| 128 | 112 |
} |
| 129 | 113 |
|
| 130 | 114 |
// SubjectAccessReviewResponse describes whether or not a user or group can perform an action |
| ... | ... |
@@ -143,18 +137,45 @@ type SubjectAccessReviewResponse struct {
|
| 143 | 143 |
type SubjectAccessReview struct {
|
| 144 | 144 |
kapi.TypeMeta `json:",inline"` |
| 145 | 145 |
|
| 146 |
- // Verb is one of: get, list, watch, create, update, delete |
|
| 147 |
- Verb string `json:"verb" description:"one of get, list, watch, create, update, delete"` |
|
| 148 |
- // Resource is one of the existing resource types |
|
| 149 |
- Resource string `json:"resource" description:"one of the existing resource types"` |
|
| 146 |
+ // AuthorizationAttributes describes the action being tested. |
|
| 147 |
+ AuthorizationAttributes `json:",inline" description:"the action being tested"` |
|
| 150 | 148 |
// User is optional. If both User and Groups are empty, the current authenticated user is used. |
| 151 | 149 |
User string `json:"user" description:"optional, if both user and groups are empty, the current authenticated user is used"` |
| 152 | 150 |
// GroupsSlice is optional. Groups is the list of groups to which the User belongs. |
| 153 | 151 |
GroupsSlice []string `json:"groups" description:"optional, list of groups to which the user belongs"` |
| 154 |
- // Content is the actual content of the request for create and update |
|
| 155 |
- Content kruntime.RawExtension `json:"content,omitempty" description:"actual content of the request for create and update"` |
|
| 152 |
+} |
|
| 153 |
+ |
|
| 154 |
+// LocalResourceAccessReview is a means to request a list of which users and groups are authorized to perform the action specified by spec in a particular namespace |
|
| 155 |
+type LocalResourceAccessReview struct {
|
|
| 156 |
+ kapi.TypeMeta `json:",inline"` |
|
| 157 |
+ |
|
| 158 |
+ // AuthorizationAttributes describes the action being tested. The Namespace element is FORCED to the current namespace. |
|
| 159 |
+ AuthorizationAttributes `json:",inline" description:"the action being tested"` |
|
| 160 |
+} |
|
| 161 |
+ |
|
| 162 |
+// LocalSubjectAccessReview is an object for requesting information about whether a user or group can perform an action in a particular namespace |
|
| 163 |
+type LocalSubjectAccessReview struct {
|
|
| 164 |
+ kapi.TypeMeta |
|
| 165 |
+ |
|
| 166 |
+ // AuthorizationAttributes describes the action being tested. The Namespace element is FORCED to the current namespace. |
|
| 167 |
+ AuthorizationAttributes `json:",inline" description:"the action being tested"` |
|
| 168 |
+ // User is optional. If both User and Groups are empty, the current authenticated user is used. |
|
| 169 |
+ User string `json:"user" description:"optional, if both user and groups are empty, the current authenticated user is used"` |
|
| 170 |
+ // Groups is optional. Groups is the list of groups to which the User belongs. |
|
| 171 |
+ GroupsSlice []string `json:"groups" description:"optional, list of groups to which the user belongs"` |
|
| 172 |
+} |
|
| 173 |
+ |
|
| 174 |
+type AuthorizationAttributes struct {
|
|
| 175 |
+ // Namespace is the namespace of the action being requested. Currently, there is no distinction between no namespace and all namespaces |
|
| 176 |
+ Namespace string `json:"namespace" description:"namespace of the action being requested"` |
|
| 177 |
+ // Verb is one of: get, list, watch, create, update, delete |
|
| 178 |
+ Verb string `json:"verb" description:"one of get, list, watch, create, update, delete"` |
|
| 179 |
+ // Resource is one of the existing resource types |
|
| 180 |
+ Resource string `json:"resource" description:"one of the existing resource types"` |
|
| 156 | 181 |
// ResourceName is the name of the resource being requested for a "get" or deleted for a "delete" |
| 157 | 182 |
ResourceName string `json:"resourceName" description:"name of the resource being requested for a get or delete"` |
| 183 |
+ // Content is the actual content of the request for create and update |
|
| 184 |
+ Content kruntime.RawExtension `json:"content,omitempty" description:"actual content of the request for create and update"` |
|
| 158 | 185 |
} |
| 159 | 186 |
|
| 160 | 187 |
// PolicyList is a collection of Policies |
| ... | ... |
@@ -10,10 +10,57 @@ import ( |
| 10 | 10 |
newer "github.com/openshift/origin/pkg/authorization/api" |
| 11 | 11 |
) |
| 12 | 12 |
|
| 13 |
+func convert_v1beta3_ResourceAccessReview_To_api_ResourceAccessReview(in *ResourceAccessReview, out *newer.ResourceAccessReview, s conversion.Scope) error {
|
|
| 14 |
+ if err := s.DefaultConvert(in, out, conversion.IgnoreMissingFields); err != nil {
|
|
| 15 |
+ return err |
|
| 16 |
+ } |
|
| 17 |
+ if err := s.DefaultConvert(&in.AuthorizationAttributes, &out.Action, conversion.IgnoreMissingFields); err != nil {
|
|
| 18 |
+ return err |
|
| 19 |
+ } |
|
| 20 |
+ |
|
| 21 |
+ return nil |
|
| 22 |
+} |
|
| 23 |
+ |
|
| 24 |
+func convert_api_ResourceAccessReview_To_v1beta3_ResourceAccessReview(in *newer.ResourceAccessReview, out *ResourceAccessReview, s conversion.Scope) error {
|
|
| 25 |
+ if err := s.DefaultConvert(in, out, conversion.IgnoreMissingFields); err != nil {
|
|
| 26 |
+ return err |
|
| 27 |
+ } |
|
| 28 |
+ if err := s.DefaultConvert(&in.Action, &out.AuthorizationAttributes, conversion.IgnoreMissingFields); err != nil {
|
|
| 29 |
+ return err |
|
| 30 |
+ } |
|
| 31 |
+ |
|
| 32 |
+ return nil |
|
| 33 |
+} |
|
| 34 |
+ |
|
| 35 |
+func convert_v1beta3_LocalResourceAccessReview_To_api_LocalResourceAccessReview(in *LocalResourceAccessReview, out *newer.LocalResourceAccessReview, s conversion.Scope) error {
|
|
| 36 |
+ if err := s.DefaultConvert(in, out, conversion.IgnoreMissingFields); err != nil {
|
|
| 37 |
+ return err |
|
| 38 |
+ } |
|
| 39 |
+ if err := s.DefaultConvert(&in.AuthorizationAttributes, &out.Action, conversion.IgnoreMissingFields); err != nil {
|
|
| 40 |
+ return err |
|
| 41 |
+ } |
|
| 42 |
+ |
|
| 43 |
+ return nil |
|
| 44 |
+} |
|
| 45 |
+ |
|
| 46 |
+func convert_api_LocalResourceAccessReview_To_v1beta3_LocalResourceAccessReview(in *newer.LocalResourceAccessReview, out *LocalResourceAccessReview, s conversion.Scope) error {
|
|
| 47 |
+ if err := s.DefaultConvert(in, out, conversion.IgnoreMissingFields); err != nil {
|
|
| 48 |
+ return err |
|
| 49 |
+ } |
|
| 50 |
+ if err := s.DefaultConvert(&in.Action, &out.AuthorizationAttributes, conversion.IgnoreMissingFields); err != nil {
|
|
| 51 |
+ return err |
|
| 52 |
+ } |
|
| 53 |
+ |
|
| 54 |
+ return nil |
|
| 55 |
+} |
|
| 56 |
+ |
|
| 13 | 57 |
func convert_v1beta3_SubjectAccessReview_To_api_SubjectAccessReview(in *SubjectAccessReview, out *newer.SubjectAccessReview, s conversion.Scope) error {
|
| 14 | 58 |
if err := s.DefaultConvert(in, out, conversion.IgnoreMissingFields); err != nil {
|
| 15 | 59 |
return err |
| 16 | 60 |
} |
| 61 |
+ if err := s.DefaultConvert(&in.AuthorizationAttributes, &out.Action, conversion.IgnoreMissingFields); err != nil {
|
|
| 62 |
+ return err |
|
| 63 |
+ } |
|
| 17 | 64 |
|
| 18 | 65 |
out.Groups = util.NewStringSet(in.GroupsSlice...) |
| 19 | 66 |
|
| ... | ... |
@@ -24,6 +71,35 @@ func convert_api_SubjectAccessReview_To_v1beta3_SubjectAccessReview(in *newer.Su |
| 24 | 24 |
if err := s.DefaultConvert(in, out, conversion.IgnoreMissingFields); err != nil {
|
| 25 | 25 |
return err |
| 26 | 26 |
} |
| 27 |
+ if err := s.DefaultConvert(&in.Action, &out.AuthorizationAttributes, conversion.IgnoreMissingFields); err != nil {
|
|
| 28 |
+ return err |
|
| 29 |
+ } |
|
| 30 |
+ |
|
| 31 |
+ out.GroupsSlice = in.Groups.List() |
|
| 32 |
+ |
|
| 33 |
+ return nil |
|
| 34 |
+} |
|
| 35 |
+ |
|
| 36 |
+func convert_v1beta3_LocalSubjectAccessReview_To_api_LocalSubjectAccessReview(in *LocalSubjectAccessReview, out *newer.LocalSubjectAccessReview, s conversion.Scope) error {
|
|
| 37 |
+ if err := s.DefaultConvert(in, out, conversion.IgnoreMissingFields); err != nil {
|
|
| 38 |
+ return err |
|
| 39 |
+ } |
|
| 40 |
+ if err := s.DefaultConvert(&in.AuthorizationAttributes, &out.Action, conversion.IgnoreMissingFields); err != nil {
|
|
| 41 |
+ return err |
|
| 42 |
+ } |
|
| 43 |
+ |
|
| 44 |
+ out.Groups = util.NewStringSet(in.GroupsSlice...) |
|
| 45 |
+ |
|
| 46 |
+ return nil |
|
| 47 |
+} |
|
| 48 |
+ |
|
| 49 |
+func convert_api_LocalSubjectAccessReview_To_v1beta3_LocalSubjectAccessReview(in *newer.LocalSubjectAccessReview, out *LocalSubjectAccessReview, s conversion.Scope) error {
|
|
| 50 |
+ if err := s.DefaultConvert(in, out, conversion.IgnoreMissingFields); err != nil {
|
|
| 51 |
+ return err |
|
| 52 |
+ } |
|
| 53 |
+ if err := s.DefaultConvert(&in.Action, &out.AuthorizationAttributes, conversion.IgnoreMissingFields); err != nil {
|
|
| 54 |
+ return err |
|
| 55 |
+ } |
|
| 27 | 56 |
|
| 28 | 57 |
out.GroupsSlice = in.Groups.List() |
| 29 | 58 |
|
| ... | ... |
@@ -186,6 +262,12 @@ func init() {
|
| 186 | 186 |
err := api.Scheme.AddConversionFuncs( |
| 187 | 187 |
convert_v1beta3_SubjectAccessReview_To_api_SubjectAccessReview, |
| 188 | 188 |
convert_api_SubjectAccessReview_To_v1beta3_SubjectAccessReview, |
| 189 |
+ convert_v1beta3_LocalSubjectAccessReview_To_api_LocalSubjectAccessReview, |
|
| 190 |
+ convert_api_LocalSubjectAccessReview_To_v1beta3_LocalSubjectAccessReview, |
|
| 191 |
+ convert_v1beta3_ResourceAccessReview_To_api_ResourceAccessReview, |
|
| 192 |
+ convert_api_ResourceAccessReview_To_v1beta3_ResourceAccessReview, |
|
| 193 |
+ convert_v1beta3_LocalResourceAccessReview_To_api_LocalResourceAccessReview, |
|
| 194 |
+ convert_api_LocalResourceAccessReview_To_v1beta3_LocalResourceAccessReview, |
|
| 189 | 195 |
convert_v1beta3_ResourceAccessReviewResponse_To_api_ResourceAccessReviewResponse, |
| 190 | 196 |
convert_api_ResourceAccessReviewResponse_To_v1beta3_ResourceAccessReviewResponse, |
| 191 | 197 |
convert_v1beta3_PolicyRule_To_api_PolicyRule, |
| ... | ... |
@@ -10,15 +10,17 @@ func init() {
|
| 10 | 10 |
&RoleBinding{},
|
| 11 | 11 |
&Policy{},
|
| 12 | 12 |
&PolicyBinding{},
|
| 13 |
- &ResourceAccessReview{},
|
|
| 14 |
- &SubjectAccessReview{},
|
|
| 15 |
- &ResourceAccessReviewResponse{},
|
|
| 16 |
- &SubjectAccessReviewResponse{},
|
|
| 17 | 13 |
&PolicyList{},
|
| 18 | 14 |
&PolicyBindingList{},
|
| 19 | 15 |
&RoleBindingList{},
|
| 20 | 16 |
&RoleList{},
|
| 21 | 17 |
|
| 18 |
+ &ResourceAccessReview{},
|
|
| 19 |
+ &SubjectAccessReview{},
|
|
| 20 |
+ &LocalResourceAccessReview{},
|
|
| 21 |
+ &LocalSubjectAccessReview{},
|
|
| 22 |
+ &ResourceAccessReviewResponse{},
|
|
| 23 |
+ &SubjectAccessReviewResponse{},
|
|
| 22 | 24 |
&IsPersonalSubjectAccessReview{},
|
| 23 | 25 |
|
| 24 | 26 |
&ClusterRole{},
|
| ... | ... |
@@ -41,17 +43,19 @@ func (*ClusterPolicyBindingList) IsAnAPIObject() {}
|
| 41 | 41 |
func (*ClusterRoleBindingList) IsAnAPIObject() {}
|
| 42 | 42 |
func (*ClusterRoleList) IsAnAPIObject() {}
|
| 43 | 43 |
|
| 44 |
-func (*Role) IsAnAPIObject() {}
|
|
| 45 |
-func (*Policy) IsAnAPIObject() {}
|
|
| 46 |
-func (*PolicyBinding) IsAnAPIObject() {}
|
|
| 47 |
-func (*RoleBinding) IsAnAPIObject() {}
|
|
| 48 |
-func (*ResourceAccessReview) IsAnAPIObject() {}
|
|
| 49 |
-func (*SubjectAccessReview) IsAnAPIObject() {}
|
|
| 50 |
-func (*ResourceAccessReviewResponse) IsAnAPIObject() {}
|
|
| 51 |
-func (*SubjectAccessReviewResponse) IsAnAPIObject() {}
|
|
| 52 |
-func (*PolicyList) IsAnAPIObject() {}
|
|
| 53 |
-func (*PolicyBindingList) IsAnAPIObject() {}
|
|
| 54 |
-func (*RoleBindingList) IsAnAPIObject() {}
|
|
| 55 |
-func (*RoleList) IsAnAPIObject() {}
|
|
| 44 |
+func (*Role) IsAnAPIObject() {}
|
|
| 45 |
+func (*Policy) IsAnAPIObject() {}
|
|
| 46 |
+func (*PolicyBinding) IsAnAPIObject() {}
|
|
| 47 |
+func (*RoleBinding) IsAnAPIObject() {}
|
|
| 48 |
+func (*PolicyList) IsAnAPIObject() {}
|
|
| 49 |
+func (*PolicyBindingList) IsAnAPIObject() {}
|
|
| 50 |
+func (*RoleBindingList) IsAnAPIObject() {}
|
|
| 51 |
+func (*RoleList) IsAnAPIObject() {}
|
|
| 56 | 52 |
|
| 53 |
+func (*ResourceAccessReview) IsAnAPIObject() {}
|
|
| 54 |
+func (*SubjectAccessReview) IsAnAPIObject() {}
|
|
| 55 |
+func (*LocalResourceAccessReview) IsAnAPIObject() {}
|
|
| 56 |
+func (*LocalSubjectAccessReview) IsAnAPIObject() {}
|
|
| 57 |
+func (*ResourceAccessReviewResponse) IsAnAPIObject() {}
|
|
| 58 |
+func (*SubjectAccessReviewResponse) IsAnAPIObject() {}
|
|
| 57 | 59 |
func (*IsPersonalSubjectAccessReview) IsAnAPIObject() {}
|
| ... | ... |
@@ -93,6 +93,16 @@ type PolicyBinding struct {
|
| 93 | 93 |
RoleBindings []NamedRoleBinding `json:"roleBindings"` |
| 94 | 94 |
} |
| 95 | 95 |
|
| 96 |
+type NamedRole struct {
|
|
| 97 |
+ Name string `json:"name"` |
|
| 98 |
+ Role Role `json:"role"` |
|
| 99 |
+} |
|
| 100 |
+ |
|
| 101 |
+type NamedRoleBinding struct {
|
|
| 102 |
+ Name string `json:"name"` |
|
| 103 |
+ RoleBinding RoleBinding `json:"roleBinding"` |
|
| 104 |
+} |
|
| 105 |
+ |
|
| 96 | 106 |
// ResourceAccessReviewResponse describes who can perform the action |
| 97 | 107 |
type ResourceAccessReviewResponse struct {
|
| 98 | 108 |
kapi.TypeMeta `json:",inline"` |
| ... | ... |
@@ -110,24 +120,8 @@ type ResourceAccessReviewResponse struct {
|
| 110 | 110 |
type ResourceAccessReview struct {
|
| 111 | 111 |
kapi.TypeMeta `json:",inline"` |
| 112 | 112 |
|
| 113 |
- // Verb is one of: get, list, watch, create, update, delete |
|
| 114 |
- Verb string `json:"verb"` |
|
| 115 |
- // Resource is one of the existing resource types |
|
| 116 |
- Resource string `json:"resource"` |
|
| 117 |
- // Content is the actual content of the request for create and update |
|
| 118 |
- Content kruntime.RawExtension `json:"content,omitempty"` |
|
| 119 |
- // ResourceName is the name of the resource being requested for a "get" or deleted for a "delete" |
|
| 120 |
- ResourceName string `json:"resourceName,omitempty"` |
|
| 121 |
-} |
|
| 122 |
- |
|
| 123 |
-type NamedRole struct {
|
|
| 124 |
- Name string `json:"name"` |
|
| 125 |
- Role Role `json:"role"` |
|
| 126 |
-} |
|
| 127 |
- |
|
| 128 |
-type NamedRoleBinding struct {
|
|
| 129 |
- Name string `json:"name"` |
|
| 130 |
- RoleBinding RoleBinding `json:"roleBinding"` |
|
| 113 |
+ // AuthorizationAttributes describes the action being tested |
|
| 114 |
+ AuthorizationAttributes `json:",inline"` |
|
| 131 | 115 |
} |
| 132 | 116 |
|
| 133 | 117 |
// SubjectAccessReviewResponse describes whether or not a user or group can perform an action |
| ... | ... |
@@ -146,18 +140,45 @@ type SubjectAccessReviewResponse struct {
|
| 146 | 146 |
type SubjectAccessReview struct {
|
| 147 | 147 |
kapi.TypeMeta `json:",inline"` |
| 148 | 148 |
|
| 149 |
- // Verb is one of: get, list, watch, create, update, delete |
|
| 150 |
- Verb string `json:"verb"` |
|
| 151 |
- // Resource is one of the existing resource types |
|
| 152 |
- Resource string `json:"resource"` |
|
| 149 |
+ // AuthorizationAttributes describes the action being tested |
|
| 150 |
+ AuthorizationAttributes `json:",inline"` |
|
| 153 | 151 |
// User is optional. If both User and Groups are empty, the current authenticated user is used. |
| 154 | 152 |
User string `json:"user"` |
| 155 | 153 |
// Groups is optional. Groups is the list of groups to which the User belongs. |
| 156 | 154 |
GroupsSlice []string `json:"groups"` |
| 157 |
- // Content is the actual content of the request for create and update |
|
| 158 |
- Content kruntime.RawExtension `json:"content,omitempty"` |
|
| 155 |
+} |
|
| 156 |
+ |
|
| 157 |
+// LocalResourceAccessReview is a means to request a list of which users and groups are authorized to perform the action specified by spec in a particular namespace |
|
| 158 |
+type LocalResourceAccessReview struct {
|
|
| 159 |
+ kapi.TypeMeta `json:",inline"` |
|
| 160 |
+ |
|
| 161 |
+ // AuthorizationAttributes describes the action being tested. The Namespace element is FORCED to the current namespace. |
|
| 162 |
+ AuthorizationAttributes `json:",inline"` |
|
| 163 |
+} |
|
| 164 |
+ |
|
| 165 |
+// LocalSubjectAccessReview is an object for requesting information about whether a user or group can perform an action in a particular namespace |
|
| 166 |
+type LocalSubjectAccessReview struct {
|
|
| 167 |
+ kapi.TypeMeta `json:",inline"` |
|
| 168 |
+ |
|
| 169 |
+ // AuthorizationAttributes describes the action being tested. The Namespace element is FORCED to the current namespace. |
|
| 170 |
+ AuthorizationAttributes `json:",inline"` |
|
| 171 |
+ // User is optional. If both User and Groups are empty, the current authenticated user is used. |
|
| 172 |
+ User string `json:"user"` |
|
| 173 |
+ // Groups is optional. Groups is the list of groups to which the User belongs. |
|
| 174 |
+ GroupsSlice []string `json:"groups"` |
|
| 175 |
+} |
|
| 176 |
+ |
|
| 177 |
+type AuthorizationAttributes struct {
|
|
| 178 |
+ // Namespace is the namespace of the action being requested. Currently, there is no distinction between no namespace and all namespaces |
|
| 179 |
+ Namespace string `json:"namespace"` |
|
| 180 |
+ // Verb is one of: get, list, watch, create, update, delete |
|
| 181 |
+ Verb string `json:"verb"` |
|
| 182 |
+ // Resource is one of the existing resource types |
|
| 183 |
+ Resource string `json:"resource"` |
|
| 159 | 184 |
// ResourceName is the name of the resource being requested for a "get" or deleted for a "delete" |
| 160 | 185 |
ResourceName string `json:"resourceName"` |
| 186 |
+ // Content is the actual content of the request for create and update |
|
| 187 |
+ Content kruntime.RawExtension `json:"content,omitempty"` |
|
| 161 | 188 |
} |
| 162 | 189 |
|
| 163 | 190 |
// PolicyList is a collection of Policies |
| ... | ... |
@@ -12,10 +12,10 @@ import ( |
| 12 | 12 |
func ValidateSubjectAccessReview(review *authorizationapi.SubjectAccessReview) fielderrors.ValidationErrorList {
|
| 13 | 13 |
allErrs := fielderrors.ValidationErrorList{}
|
| 14 | 14 |
|
| 15 |
- if len(review.Verb) == 0 {
|
|
| 15 |
+ if len(review.Action.Verb) == 0 {
|
|
| 16 | 16 |
allErrs = append(allErrs, fielderrors.NewFieldRequired("verb"))
|
| 17 | 17 |
} |
| 18 |
- if len(review.Resource) == 0 {
|
|
| 18 |
+ if len(review.Action.Resource) == 0 {
|
|
| 19 | 19 |
allErrs = append(allErrs, fielderrors.NewFieldRequired("resource"))
|
| 20 | 20 |
} |
| 21 | 21 |
|
| ... | ... |
@@ -25,10 +25,36 @@ func ValidateSubjectAccessReview(review *authorizationapi.SubjectAccessReview) f |
| 25 | 25 |
func ValidateResourceAccessReview(review *authorizationapi.ResourceAccessReview) fielderrors.ValidationErrorList {
|
| 26 | 26 |
allErrs := fielderrors.ValidationErrorList{}
|
| 27 | 27 |
|
| 28 |
- if len(review.Verb) == 0 {
|
|
| 28 |
+ if len(review.Action.Verb) == 0 {
|
|
| 29 | 29 |
allErrs = append(allErrs, fielderrors.NewFieldRequired("verb"))
|
| 30 | 30 |
} |
| 31 |
- if len(review.Resource) == 0 {
|
|
| 31 |
+ if len(review.Action.Resource) == 0 {
|
|
| 32 |
+ allErrs = append(allErrs, fielderrors.NewFieldRequired("resource"))
|
|
| 33 |
+ } |
|
| 34 |
+ |
|
| 35 |
+ return allErrs |
|
| 36 |
+} |
|
| 37 |
+ |
|
| 38 |
+func ValidateLocalSubjectAccessReview(review *authorizationapi.LocalSubjectAccessReview) fielderrors.ValidationErrorList {
|
|
| 39 |
+ allErrs := fielderrors.ValidationErrorList{}
|
|
| 40 |
+ |
|
| 41 |
+ if len(review.Action.Verb) == 0 {
|
|
| 42 |
+ allErrs = append(allErrs, fielderrors.NewFieldRequired("verb"))
|
|
| 43 |
+ } |
|
| 44 |
+ if len(review.Action.Resource) == 0 {
|
|
| 45 |
+ allErrs = append(allErrs, fielderrors.NewFieldRequired("resource"))
|
|
| 46 |
+ } |
|
| 47 |
+ |
|
| 48 |
+ return allErrs |
|
| 49 |
+} |
|
| 50 |
+ |
|
| 51 |
+func ValidateLocalResourceAccessReview(review *authorizationapi.LocalResourceAccessReview) fielderrors.ValidationErrorList {
|
|
| 52 |
+ allErrs := fielderrors.ValidationErrorList{}
|
|
| 53 |
+ |
|
| 54 |
+ if len(review.Action.Verb) == 0 {
|
|
| 55 |
+ allErrs = append(allErrs, fielderrors.NewFieldRequired("verb"))
|
|
| 56 |
+ } |
|
| 57 |
+ if len(review.Action.Resource) == 0 {
|
|
| 32 | 58 |
allErrs = append(allErrs, fielderrors.NewFieldRequired("resource"))
|
| 33 | 59 |
} |
| 34 | 60 |
|
| ... | ... |
@@ -20,6 +20,16 @@ type DefaultAuthorizationAttributes struct {
|
| 20 | 20 |
URL string |
| 21 | 21 |
} |
| 22 | 22 |
|
| 23 |
+// ToDefaultAuthorizationAttributes coerces AuthorizationAttributes to DefaultAuthorizationAttributes. Namespace is not included |
|
| 24 |
+// because the authorizer takes that information on the context |
|
| 25 |
+func ToDefaultAuthorizationAttributes(in authorizationapi.AuthorizationAttributes) DefaultAuthorizationAttributes {
|
|
| 26 |
+ return DefaultAuthorizationAttributes{
|
|
| 27 |
+ Verb: in.Verb, |
|
| 28 |
+ Resource: in.Resource, |
|
| 29 |
+ ResourceName: in.ResourceName, |
|
| 30 |
+ } |
|
| 31 |
+} |
|
| 32 |
+ |
|
| 23 | 33 |
func (a DefaultAuthorizationAttributes) RuleMatches(rule authorizationapi.PolicyRule) (bool, error) {
|
| 24 | 34 |
if a.IsNonResourceURL() {
|
| 25 | 35 |
if a.nonResourceMatches(rule) {
|
| ... | ... |
@@ -2,7 +2,7 @@ package authorizer |
| 2 | 2 |
|
| 3 | 3 |
import ( |
| 4 | 4 |
"bytes" |
| 5 |
- "errors" |
|
| 5 |
+ "fmt" |
|
| 6 | 6 |
"io/ioutil" |
| 7 | 7 |
"net/http" |
| 8 | 8 |
|
| ... | ... |
@@ -11,11 +11,24 @@ import ( |
| 11 | 11 |
) |
| 12 | 12 |
|
| 13 | 13 |
func IsPersonalAccessReview(a AuthorizationAttributes) (bool, error) {
|
| 14 |
- req, ok := a.GetRequestAttributes().(*http.Request) |
|
| 15 |
- if !ok {
|
|
| 16 |
- return false, errors.New("expected request, but did not get one")
|
|
| 14 |
+ switch extendedAttributes := a.GetRequestAttributes().(type) {
|
|
| 15 |
+ case *http.Request: |
|
| 16 |
+ return isPersonalAccessReviewFromRequest(a, extendedAttributes) |
|
| 17 |
+ |
|
| 18 |
+ case *authorizationapi.SubjectAccessReview: |
|
| 19 |
+ return isPersonalAccessReviewFromSAR(extendedAttributes), nil |
|
| 20 |
+ |
|
| 21 |
+ case *authorizationapi.LocalSubjectAccessReview: |
|
| 22 |
+ return isPersonalAccessReviewFromLocalSAR(extendedAttributes), nil |
|
| 23 |
+ |
|
| 24 |
+ default: |
|
| 25 |
+ return false, fmt.Errorf("unexpected request attributes for checking personal access review: %v", extendedAttributes)
|
|
| 26 |
+ |
|
| 17 | 27 |
} |
| 28 |
+} |
|
| 18 | 29 |
|
| 30 |
+// isPersonalAccessReviewFromRequest this variant handles the case where we have an httpRequest |
|
| 31 |
+func isPersonalAccessReviewFromRequest(a AuthorizationAttributes, req *http.Request) (bool, error) {
|
|
| 19 | 32 |
// TODO once we're integrated with the api installer, we should have direct access to the deserialized content |
| 20 | 33 |
// for now, this only happens on subjectaccessreviews with a personal check, pay the double retrieve and decode cost |
| 21 | 34 |
body, err := ioutil.ReadAll(req.Body) |
| ... | ... |
@@ -24,14 +37,36 @@ func IsPersonalAccessReview(a AuthorizationAttributes) (bool, error) {
|
| 24 | 24 |
} |
| 25 | 25 |
req.Body = ioutil.NopCloser(bytes.NewBuffer(body)) |
| 26 | 26 |
|
| 27 |
- subjectAccessReview := &authorizationapi.SubjectAccessReview{}
|
|
| 28 |
- if err := latest.Codec.DecodeInto(body, subjectAccessReview); err != nil {
|
|
| 27 |
+ obj, err := latest.Codec.Decode(body) |
|
| 28 |
+ if err != nil {
|
|
| 29 | 29 |
return false, err |
| 30 | 30 |
} |
| 31 |
+ switch castObj := obj.(type) {
|
|
| 32 |
+ case *authorizationapi.SubjectAccessReview: |
|
| 33 |
+ return isPersonalAccessReviewFromSAR(castObj), nil |
|
| 34 |
+ |
|
| 35 |
+ case *authorizationapi.LocalSubjectAccessReview: |
|
| 36 |
+ return isPersonalAccessReviewFromLocalSAR(castObj), nil |
|
| 37 |
+ |
|
| 38 |
+ default: |
|
| 39 |
+ return false, nil |
|
| 40 |
+ } |
|
| 41 |
+} |
|
| 42 |
+ |
|
| 43 |
+// isPersonalAccessReviewFromSAR this variant handles the case where we have an SAR |
|
| 44 |
+func isPersonalAccessReviewFromSAR(sar *authorizationapi.SubjectAccessReview) bool {
|
|
| 45 |
+ if len(sar.User) == 0 && len(sar.Groups) == 0 {
|
|
| 46 |
+ return true |
|
| 47 |
+ } |
|
| 48 |
+ |
|
| 49 |
+ return false |
|
| 50 |
+} |
|
| 31 | 51 |
|
| 32 |
- if (len(subjectAccessReview.User) == 0) && (len(subjectAccessReview.Groups) == 0) {
|
|
| 33 |
- return true, nil |
|
| 52 |
+// isPersonalAccessReviewFromLocalSAR this variant handles the case where we have a local SAR |
|
| 53 |
+func isPersonalAccessReviewFromLocalSAR(sar *authorizationapi.LocalSubjectAccessReview) bool {
|
|
| 54 |
+ if len(sar.User) == 0 && len(sar.Groups) == 0 {
|
|
| 55 |
+ return true |
|
| 34 | 56 |
} |
| 35 | 57 |
|
| 36 |
- return false, nil |
|
| 58 |
+ return false |
|
| 37 | 59 |
} |
| 38 | 60 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,31 @@ |
| 0 |
+package localresourceaccessreview |
|
| 1 |
+ |
|
| 2 |
+import ( |
|
| 3 |
+ api "github.com/openshift/origin/pkg/authorization/api" |
|
| 4 |
+ kapi "k8s.io/kubernetes/pkg/api" |
|
| 5 |
+ "k8s.io/kubernetes/pkg/runtime" |
|
| 6 |
+) |
|
| 7 |
+ |
|
| 8 |
+type Registry interface {
|
|
| 9 |
+ CreateLocalResourceAccessReview(ctx kapi.Context, resourceAccessReview *api.LocalResourceAccessReview) (*api.ResourceAccessReviewResponse, error) |
|
| 10 |
+} |
|
| 11 |
+ |
|
| 12 |
+type Storage interface {
|
|
| 13 |
+ Create(ctx kapi.Context, obj runtime.Object) (runtime.Object, error) |
|
| 14 |
+} |
|
| 15 |
+ |
|
| 16 |
+type storage struct {
|
|
| 17 |
+ Storage |
|
| 18 |
+} |
|
| 19 |
+ |
|
| 20 |
+func NewRegistry(s Storage) Registry {
|
|
| 21 |
+ return &storage{s}
|
|
| 22 |
+} |
|
| 23 |
+ |
|
| 24 |
+func (s *storage) CreateLocalResourceAccessReview(ctx kapi.Context, resourceAccessReview *api.LocalResourceAccessReview) (*api.ResourceAccessReviewResponse, error) {
|
|
| 25 |
+ obj, err := s.Create(ctx, resourceAccessReview) |
|
| 26 |
+ if err != nil {
|
|
| 27 |
+ return nil, err |
|
| 28 |
+ } |
|
| 29 |
+ return obj.(*api.ResourceAccessReviewResponse), nil |
|
| 30 |
+} |
| 0 | 31 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,53 @@ |
| 0 |
+package localresourceaccessreview |
|
| 1 |
+ |
|
| 2 |
+import ( |
|
| 3 |
+ "fmt" |
|
| 4 |
+ |
|
| 5 |
+ kapi "k8s.io/kubernetes/pkg/api" |
|
| 6 |
+ kapierrors "k8s.io/kubernetes/pkg/api/errors" |
|
| 7 |
+ "k8s.io/kubernetes/pkg/runtime" |
|
| 8 |
+ kutilerrors "k8s.io/kubernetes/pkg/util/errors" |
|
| 9 |
+ "k8s.io/kubernetes/pkg/util/fielderrors" |
|
| 10 |
+ |
|
| 11 |
+ authorizationapi "github.com/openshift/origin/pkg/authorization/api" |
|
| 12 |
+ authorizationvalidation "github.com/openshift/origin/pkg/authorization/api/validation" |
|
| 13 |
+ "github.com/openshift/origin/pkg/authorization/registry/resourceaccessreview" |
|
| 14 |
+) |
|
| 15 |
+ |
|
| 16 |
+// REST implements the RESTStorage interface in terms of an Registry. |
|
| 17 |
+type REST struct {
|
|
| 18 |
+ clusterRARRegistry resourceaccessreview.Registry |
|
| 19 |
+} |
|
| 20 |
+ |
|
| 21 |
+func NewREST(clusterRARRegistry resourceaccessreview.Registry) *REST {
|
|
| 22 |
+ return &REST{clusterRARRegistry}
|
|
| 23 |
+} |
|
| 24 |
+ |
|
| 25 |
+func (r *REST) New() runtime.Object {
|
|
| 26 |
+ return &authorizationapi.LocalResourceAccessReview{}
|
|
| 27 |
+} |
|
| 28 |
+ |
|
| 29 |
+// Create transforms a LocalRAR into an ClusterRAR that is requesting a namespace. That collapses the code paths. |
|
| 30 |
+// LocalResourceAccessReview exists to allow clean expression of policy. |
|
| 31 |
+func (r *REST) Create(ctx kapi.Context, obj runtime.Object) (runtime.Object, error) {
|
|
| 32 |
+ localRAR, ok := obj.(*authorizationapi.LocalResourceAccessReview) |
|
| 33 |
+ if !ok {
|
|
| 34 |
+ return nil, kapierrors.NewBadRequest(fmt.Sprintf("not a localResourceAccessReview: %#v", obj))
|
|
| 35 |
+ } |
|
| 36 |
+ if err := kutilerrors.NewAggregate(authorizationvalidation.ValidateLocalResourceAccessReview(localRAR)); err != nil {
|
|
| 37 |
+ return nil, err |
|
| 38 |
+ } |
|
| 39 |
+ if namespace := kapi.NamespaceValue(ctx); len(namespace) == 0 {
|
|
| 40 |
+ return nil, kapierrors.NewBadRequest(fmt.Sprintf("namespace is required on this type: %v", namespace))
|
|
| 41 |
+ } else if (len(localRAR.Action.Namespace) > 0) && (namespace != localRAR.Action.Namespace) {
|
|
| 42 |
+ return nil, fielderrors.NewFieldInvalid("namespace", localRAR.Action.Namespace, fmt.Sprintf("namespace must be: %v", namespace))
|
|
| 43 |
+ } |
|
| 44 |
+ |
|
| 45 |
+ // transform this into a ResourceAccessReview |
|
| 46 |
+ clusterRAR := &authorizationapi.ResourceAccessReview{
|
|
| 47 |
+ Action: localRAR.Action, |
|
| 48 |
+ } |
|
| 49 |
+ clusterRAR.Action.Namespace = kapi.NamespaceValue(ctx) |
|
| 50 |
+ |
|
| 51 |
+ return r.clusterRARRegistry.CreateResourceAccessReview(kapi.WithNamespace(ctx, ""), clusterRAR) |
|
| 52 |
+} |
| 0 | 53 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,186 @@ |
| 0 |
+package localresourceaccessreview |
|
| 1 |
+ |
|
| 2 |
+import ( |
|
| 3 |
+ "errors" |
|
| 4 |
+ "reflect" |
|
| 5 |
+ "testing" |
|
| 6 |
+ |
|
| 7 |
+ kapi "k8s.io/kubernetes/pkg/api" |
|
| 8 |
+ "k8s.io/kubernetes/pkg/util" |
|
| 9 |
+ |
|
| 10 |
+ authorizationapi "github.com/openshift/origin/pkg/authorization/api" |
|
| 11 |
+ "github.com/openshift/origin/pkg/authorization/authorizer" |
|
| 12 |
+ "github.com/openshift/origin/pkg/authorization/registry/resourceaccessreview" |
|
| 13 |
+) |
|
| 14 |
+ |
|
| 15 |
+type resourceAccessTest struct {
|
|
| 16 |
+ authorizer *testAuthorizer |
|
| 17 |
+ reviewRequest *authorizationapi.LocalResourceAccessReview |
|
| 18 |
+} |
|
| 19 |
+ |
|
| 20 |
+type testAuthorizer struct {
|
|
| 21 |
+ users util.StringSet |
|
| 22 |
+ groups util.StringSet |
|
| 23 |
+ err string |
|
| 24 |
+ |
|
| 25 |
+ actualAttributes authorizer.DefaultAuthorizationAttributes |
|
| 26 |
+} |
|
| 27 |
+ |
|
| 28 |
+func (a *testAuthorizer) Authorize(ctx kapi.Context, attributes authorizer.AuthorizationAttributes) (allowed bool, reason string, err error) {
|
|
| 29 |
+ // allow the initial check for "can I run this RAR at all" |
|
| 30 |
+ if attributes.GetResource() == "localresourceaccessreviews" {
|
|
| 31 |
+ return true, "", nil |
|
| 32 |
+ } |
|
| 33 |
+ |
|
| 34 |
+ return false, "", errors.New("Unsupported")
|
|
| 35 |
+} |
|
| 36 |
+func (a *testAuthorizer) GetAllowedSubjects(ctx kapi.Context, passedAttributes authorizer.AuthorizationAttributes) (util.StringSet, util.StringSet, error) {
|
|
| 37 |
+ attributes, ok := passedAttributes.(authorizer.DefaultAuthorizationAttributes) |
|
| 38 |
+ if !ok {
|
|
| 39 |
+ return nil, nil, errors.New("unexpected type for test")
|
|
| 40 |
+ } |
|
| 41 |
+ |
|
| 42 |
+ a.actualAttributes = attributes |
|
| 43 |
+ if len(a.err) == 0 {
|
|
| 44 |
+ return a.users, a.groups, nil |
|
| 45 |
+ } |
|
| 46 |
+ return a.users, a.groups, errors.New(a.err) |
|
| 47 |
+} |
|
| 48 |
+ |
|
| 49 |
+func TestNoNamespace(t *testing.T) {
|
|
| 50 |
+ test := &resourceAccessTest{
|
|
| 51 |
+ authorizer: &testAuthorizer{
|
|
| 52 |
+ err: "namespace is required on this type: ", |
|
| 53 |
+ }, |
|
| 54 |
+ reviewRequest: &authorizationapi.LocalResourceAccessReview{
|
|
| 55 |
+ Action: authorizationapi.AuthorizationAttributes{
|
|
| 56 |
+ Namespace: "", |
|
| 57 |
+ Verb: "get", |
|
| 58 |
+ Resource: "pods", |
|
| 59 |
+ }, |
|
| 60 |
+ }, |
|
| 61 |
+ } |
|
| 62 |
+ |
|
| 63 |
+ test.runTest(t) |
|
| 64 |
+} |
|
| 65 |
+ |
|
| 66 |
+func TestConflictingNamespace(t *testing.T) {
|
|
| 67 |
+ authorizer := &testAuthorizer{}
|
|
| 68 |
+ reviewRequest := &authorizationapi.LocalResourceAccessReview{
|
|
| 69 |
+ Action: authorizationapi.AuthorizationAttributes{
|
|
| 70 |
+ Namespace: "foo", |
|
| 71 |
+ Verb: "get", |
|
| 72 |
+ Resource: "pods", |
|
| 73 |
+ }, |
|
| 74 |
+ } |
|
| 75 |
+ |
|
| 76 |
+ storage := NewREST(resourceaccessreview.NewRegistry(resourceaccessreview.NewREST(authorizer))) |
|
| 77 |
+ ctx := kapi.WithNamespace(kapi.NewContext(), "bar") |
|
| 78 |
+ _, err := storage.Create(ctx, reviewRequest) |
|
| 79 |
+ if err == nil {
|
|
| 80 |
+ t.Fatalf("unexpected non-error: %v", err)
|
|
| 81 |
+ } |
|
| 82 |
+ if e, a := "namespace: invalid value 'foo', Details: namespace must be: bar", err.Error(); e != a {
|
|
| 83 |
+ t.Fatalf("expected %v, got %v", e, a)
|
|
| 84 |
+ } |
|
| 85 |
+} |
|
| 86 |
+ |
|
| 87 |
+func TestEmptyReturn(t *testing.T) {
|
|
| 88 |
+ test := &resourceAccessTest{
|
|
| 89 |
+ authorizer: &testAuthorizer{
|
|
| 90 |
+ users: util.StringSet{},
|
|
| 91 |
+ groups: util.StringSet{},
|
|
| 92 |
+ }, |
|
| 93 |
+ reviewRequest: &authorizationapi.LocalResourceAccessReview{
|
|
| 94 |
+ Action: authorizationapi.AuthorizationAttributes{
|
|
| 95 |
+ Namespace: "unittest", |
|
| 96 |
+ Verb: "get", |
|
| 97 |
+ Resource: "pods", |
|
| 98 |
+ }, |
|
| 99 |
+ }, |
|
| 100 |
+ } |
|
| 101 |
+ |
|
| 102 |
+ test.runTest(t) |
|
| 103 |
+} |
|
| 104 |
+ |
|
| 105 |
+func TestNoErrors(t *testing.T) {
|
|
| 106 |
+ test := &resourceAccessTest{
|
|
| 107 |
+ authorizer: &testAuthorizer{
|
|
| 108 |
+ users: util.NewStringSet("one", "two"),
|
|
| 109 |
+ groups: util.NewStringSet("three", "four"),
|
|
| 110 |
+ }, |
|
| 111 |
+ reviewRequest: &authorizationapi.LocalResourceAccessReview{
|
|
| 112 |
+ Action: authorizationapi.AuthorizationAttributes{
|
|
| 113 |
+ Namespace: "unittest", |
|
| 114 |
+ Verb: "delete", |
|
| 115 |
+ Resource: "deploymentConfig", |
|
| 116 |
+ }, |
|
| 117 |
+ }, |
|
| 118 |
+ } |
|
| 119 |
+ |
|
| 120 |
+ test.runTest(t) |
|
| 121 |
+} |
|
| 122 |
+ |
|
| 123 |
+func TestErrors(t *testing.T) {
|
|
| 124 |
+ test := &resourceAccessTest{
|
|
| 125 |
+ authorizer: &testAuthorizer{
|
|
| 126 |
+ users: util.StringSet{},
|
|
| 127 |
+ groups: util.StringSet{},
|
|
| 128 |
+ err: "some-random-failure", |
|
| 129 |
+ }, |
|
| 130 |
+ reviewRequest: &authorizationapi.LocalResourceAccessReview{
|
|
| 131 |
+ Action: authorizationapi.AuthorizationAttributes{
|
|
| 132 |
+ Namespace: "unittest", |
|
| 133 |
+ Verb: "get", |
|
| 134 |
+ Resource: "pods", |
|
| 135 |
+ }, |
|
| 136 |
+ }, |
|
| 137 |
+ } |
|
| 138 |
+ |
|
| 139 |
+ test.runTest(t) |
|
| 140 |
+} |
|
| 141 |
+ |
|
| 142 |
+func (r *resourceAccessTest) runTest(t *testing.T) {
|
|
| 143 |
+ storage := NewREST(resourceaccessreview.NewRegistry(resourceaccessreview.NewREST(r.authorizer))) |
|
| 144 |
+ |
|
| 145 |
+ expectedResponse := &authorizationapi.ResourceAccessReviewResponse{
|
|
| 146 |
+ Namespace: r.reviewRequest.Action.Namespace, |
|
| 147 |
+ Users: r.authorizer.users, |
|
| 148 |
+ Groups: r.authorizer.groups, |
|
| 149 |
+ } |
|
| 150 |
+ |
|
| 151 |
+ expectedAttributes := authorizer.ToDefaultAuthorizationAttributes(r.reviewRequest.Action) |
|
| 152 |
+ |
|
| 153 |
+ ctx := kapi.WithNamespace(kapi.NewContext(), r.reviewRequest.Action.Namespace) |
|
| 154 |
+ obj, err := storage.Create(ctx, r.reviewRequest) |
|
| 155 |
+ if err != nil && len(r.authorizer.err) == 0 {
|
|
| 156 |
+ t.Fatalf("unexpected error: %v", err)
|
|
| 157 |
+ } |
|
| 158 |
+ if len(r.authorizer.err) != 0 {
|
|
| 159 |
+ if err == nil {
|
|
| 160 |
+ t.Fatalf("unexpected non-error: %v", err)
|
|
| 161 |
+ } |
|
| 162 |
+ if e, a := r.authorizer.err, err.Error(); e != a {
|
|
| 163 |
+ t.Fatalf("expected %v, got %v", e, a)
|
|
| 164 |
+ } |
|
| 165 |
+ |
|
| 166 |
+ return |
|
| 167 |
+ } |
|
| 168 |
+ |
|
| 169 |
+ switch obj.(type) {
|
|
| 170 |
+ case *authorizationapi.ResourceAccessReviewResponse: |
|
| 171 |
+ if !reflect.DeepEqual(expectedResponse, obj) {
|
|
| 172 |
+ t.Errorf("diff %v", util.ObjectGoPrintDiff(expectedResponse, obj))
|
|
| 173 |
+ } |
|
| 174 |
+ case nil: |
|
| 175 |
+ if len(r.authorizer.err) == 0 {
|
|
| 176 |
+ t.Fatal("unexpected nil object")
|
|
| 177 |
+ } |
|
| 178 |
+ default: |
|
| 179 |
+ t.Errorf("Unexpected obj type: %v", obj)
|
|
| 180 |
+ } |
|
| 181 |
+ |
|
| 182 |
+ if !reflect.DeepEqual(expectedAttributes, r.authorizer.actualAttributes) {
|
|
| 183 |
+ t.Errorf("diff %v", util.ObjectGoPrintDiff(expectedAttributes, r.authorizer.actualAttributes))
|
|
| 184 |
+ } |
|
| 185 |
+} |
| 0 | 186 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,31 @@ |
| 0 |
+package localsubjectaccessreview |
|
| 1 |
+ |
|
| 2 |
+import ( |
|
| 3 |
+ api "github.com/openshift/origin/pkg/authorization/api" |
|
| 4 |
+ kapi "k8s.io/kubernetes/pkg/api" |
|
| 5 |
+ "k8s.io/kubernetes/pkg/runtime" |
|
| 6 |
+) |
|
| 7 |
+ |
|
| 8 |
+type Registry interface {
|
|
| 9 |
+ CreateLocalSubjectAccessReview(ctx kapi.Context, subjectAccessReview *api.LocalSubjectAccessReview) (*api.SubjectAccessReviewResponse, error) |
|
| 10 |
+} |
|
| 11 |
+ |
|
| 12 |
+type Storage interface {
|
|
| 13 |
+ Create(ctx kapi.Context, obj runtime.Object) (runtime.Object, error) |
|
| 14 |
+} |
|
| 15 |
+ |
|
| 16 |
+type storage struct {
|
|
| 17 |
+ Storage |
|
| 18 |
+} |
|
| 19 |
+ |
|
| 20 |
+func NewRegistry(s Storage) Registry {
|
|
| 21 |
+ return &storage{s}
|
|
| 22 |
+} |
|
| 23 |
+ |
|
| 24 |
+func (s *storage) CreateLocalSubjectAccessReview(ctx kapi.Context, subjectAccessReview *api.LocalSubjectAccessReview) (*api.SubjectAccessReviewResponse, error) {
|
|
| 25 |
+ obj, err := s.Create(ctx, subjectAccessReview) |
|
| 26 |
+ if err != nil {
|
|
| 27 |
+ return nil, err |
|
| 28 |
+ } |
|
| 29 |
+ return obj.(*api.SubjectAccessReviewResponse), nil |
|
| 30 |
+} |
| 0 | 31 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,55 @@ |
| 0 |
+package localsubjectaccessreview |
|
| 1 |
+ |
|
| 2 |
+import ( |
|
| 3 |
+ "fmt" |
|
| 4 |
+ |
|
| 5 |
+ kapi "k8s.io/kubernetes/pkg/api" |
|
| 6 |
+ kapierrors "k8s.io/kubernetes/pkg/api/errors" |
|
| 7 |
+ "k8s.io/kubernetes/pkg/runtime" |
|
| 8 |
+ kutilerrors "k8s.io/kubernetes/pkg/util/errors" |
|
| 9 |
+ "k8s.io/kubernetes/pkg/util/fielderrors" |
|
| 10 |
+ |
|
| 11 |
+ authorizationapi "github.com/openshift/origin/pkg/authorization/api" |
|
| 12 |
+ authorizationvalidation "github.com/openshift/origin/pkg/authorization/api/validation" |
|
| 13 |
+ "github.com/openshift/origin/pkg/authorization/registry/subjectaccessreview" |
|
| 14 |
+) |
|
| 15 |
+ |
|
| 16 |
+// REST implements the RESTStorage interface in terms of an Registry. |
|
| 17 |
+type REST struct {
|
|
| 18 |
+ clusterSARRegistry subjectaccessreview.Registry |
|
| 19 |
+} |
|
| 20 |
+ |
|
| 21 |
+func NewREST(clusterSARRegistry subjectaccessreview.Registry) *REST {
|
|
| 22 |
+ return &REST{clusterSARRegistry}
|
|
| 23 |
+} |
|
| 24 |
+ |
|
| 25 |
+func (r *REST) New() runtime.Object {
|
|
| 26 |
+ return &authorizationapi.LocalSubjectAccessReview{}
|
|
| 27 |
+} |
|
| 28 |
+ |
|
| 29 |
+// Create transforms a LocalSAR into an ClusterSAR that is requesting a namespace. That collapses the code paths. |
|
| 30 |
+// LocalSubjectAccessReview exists to allow clean expression of policy. |
|
| 31 |
+func (r *REST) Create(ctx kapi.Context, obj runtime.Object) (runtime.Object, error) {
|
|
| 32 |
+ localSAR, ok := obj.(*authorizationapi.LocalSubjectAccessReview) |
|
| 33 |
+ if !ok {
|
|
| 34 |
+ return nil, kapierrors.NewBadRequest(fmt.Sprintf("not a localSubjectAccessReview: %#v", obj))
|
|
| 35 |
+ } |
|
| 36 |
+ if err := kutilerrors.NewAggregate(authorizationvalidation.ValidateLocalSubjectAccessReview(localSAR)); err != nil {
|
|
| 37 |
+ return nil, err |
|
| 38 |
+ } |
|
| 39 |
+ if namespace := kapi.NamespaceValue(ctx); len(namespace) == 0 {
|
|
| 40 |
+ return nil, kapierrors.NewBadRequest(fmt.Sprintf("namespace is required on this type: %v", namespace))
|
|
| 41 |
+ } else if (len(localSAR.Action.Namespace) > 0) && (namespace != localSAR.Action.Namespace) {
|
|
| 42 |
+ return nil, fielderrors.NewFieldInvalid("namespace", localSAR.Action.Namespace, fmt.Sprintf("namespace must be: %v", namespace))
|
|
| 43 |
+ } |
|
| 44 |
+ |
|
| 45 |
+ // transform this into a SubjectAccessReview |
|
| 46 |
+ clusterSAR := &authorizationapi.SubjectAccessReview{
|
|
| 47 |
+ Action: localSAR.Action, |
|
| 48 |
+ User: localSAR.User, |
|
| 49 |
+ Groups: localSAR.Groups, |
|
| 50 |
+ } |
|
| 51 |
+ clusterSAR.Action.Namespace = kapi.NamespaceValue(ctx) |
|
| 52 |
+ |
|
| 53 |
+ return r.clusterSARRegistry.CreateSubjectAccessReview(kapi.WithNamespace(ctx, ""), clusterSAR) |
|
| 54 |
+} |
| 0 | 55 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,198 @@ |
| 0 |
+package localsubjectaccessreview |
|
| 1 |
+ |
|
| 2 |
+import ( |
|
| 3 |
+ "errors" |
|
| 4 |
+ "reflect" |
|
| 5 |
+ "testing" |
|
| 6 |
+ |
|
| 7 |
+ kapi "k8s.io/kubernetes/pkg/api" |
|
| 8 |
+ "k8s.io/kubernetes/pkg/util" |
|
| 9 |
+ |
|
| 10 |
+ authorizationapi "github.com/openshift/origin/pkg/authorization/api" |
|
| 11 |
+ "github.com/openshift/origin/pkg/authorization/authorizer" |
|
| 12 |
+ "github.com/openshift/origin/pkg/authorization/registry/subjectaccessreview" |
|
| 13 |
+) |
|
| 14 |
+ |
|
| 15 |
+type subjectAccessTest struct {
|
|
| 16 |
+ authorizer *testAuthorizer |
|
| 17 |
+ reviewRequest *authorizationapi.LocalSubjectAccessReview |
|
| 18 |
+} |
|
| 19 |
+ |
|
| 20 |
+type testAuthorizer struct {
|
|
| 21 |
+ allowed bool |
|
| 22 |
+ reason string |
|
| 23 |
+ err string |
|
| 24 |
+ |
|
| 25 |
+ actualAttributes authorizer.DefaultAuthorizationAttributes |
|
| 26 |
+} |
|
| 27 |
+ |
|
| 28 |
+func (a *testAuthorizer) Authorize(ctx kapi.Context, passedAttributes authorizer.AuthorizationAttributes) (allowed bool, reason string, err error) {
|
|
| 29 |
+ // allow the initial check for "can I run this SAR at all" |
|
| 30 |
+ if passedAttributes.GetResource() == "localsubjectaccessreviews" {
|
|
| 31 |
+ return true, "", nil |
|
| 32 |
+ } |
|
| 33 |
+ |
|
| 34 |
+ attributes, ok := passedAttributes.(authorizer.DefaultAuthorizationAttributes) |
|
| 35 |
+ if !ok {
|
|
| 36 |
+ return false, "ERROR", errors.New("unexpected type for test")
|
|
| 37 |
+ } |
|
| 38 |
+ |
|
| 39 |
+ a.actualAttributes = attributes |
|
| 40 |
+ |
|
| 41 |
+ if len(a.err) == 0 {
|
|
| 42 |
+ return a.allowed, a.reason, nil |
|
| 43 |
+ } |
|
| 44 |
+ return a.allowed, a.reason, errors.New(a.err) |
|
| 45 |
+} |
|
| 46 |
+func (a *testAuthorizer) GetAllowedSubjects(ctx kapi.Context, passedAttributes authorizer.AuthorizationAttributes) (util.StringSet, util.StringSet, error) {
|
|
| 47 |
+ return util.StringSet{}, util.StringSet{}, nil
|
|
| 48 |
+} |
|
| 49 |
+ |
|
| 50 |
+func TestNoNamespace(t *testing.T) {
|
|
| 51 |
+ test := &subjectAccessTest{
|
|
| 52 |
+ authorizer: &testAuthorizer{
|
|
| 53 |
+ allowed: false, |
|
| 54 |
+ err: "namespace is required on this type: ", |
|
| 55 |
+ }, |
|
| 56 |
+ reviewRequest: &authorizationapi.LocalSubjectAccessReview{
|
|
| 57 |
+ Action: authorizationapi.AuthorizationAttributes{
|
|
| 58 |
+ Namespace: "", |
|
| 59 |
+ Verb: "get", |
|
| 60 |
+ Resource: "pods", |
|
| 61 |
+ }, |
|
| 62 |
+ User: "foo", |
|
| 63 |
+ Groups: util.NewStringSet(), |
|
| 64 |
+ }, |
|
| 65 |
+ } |
|
| 66 |
+ |
|
| 67 |
+ test.runTest(t) |
|
| 68 |
+} |
|
| 69 |
+ |
|
| 70 |
+func TestConflictingNamespace(t *testing.T) {
|
|
| 71 |
+ authorizer := &testAuthorizer{
|
|
| 72 |
+ allowed: false, |
|
| 73 |
+ } |
|
| 74 |
+ reviewRequest := &authorizationapi.LocalSubjectAccessReview{
|
|
| 75 |
+ Action: authorizationapi.AuthorizationAttributes{
|
|
| 76 |
+ Namespace: "foo", |
|
| 77 |
+ Verb: "get", |
|
| 78 |
+ Resource: "pods", |
|
| 79 |
+ }, |
|
| 80 |
+ User: "foo", |
|
| 81 |
+ Groups: util.NewStringSet(), |
|
| 82 |
+ } |
|
| 83 |
+ |
|
| 84 |
+ storage := NewREST(subjectaccessreview.NewRegistry(subjectaccessreview.NewREST(authorizer))) |
|
| 85 |
+ ctx := kapi.WithNamespace(kapi.NewContext(), "bar") |
|
| 86 |
+ _, err := storage.Create(ctx, reviewRequest) |
|
| 87 |
+ if err == nil {
|
|
| 88 |
+ t.Fatalf("unexpected non-error: %v", err)
|
|
| 89 |
+ } |
|
| 90 |
+ if e, a := "namespace: invalid value 'foo', Details: namespace must be: bar", err.Error(); e != a {
|
|
| 91 |
+ t.Fatalf("expected %v, got %v", e, a)
|
|
| 92 |
+ } |
|
| 93 |
+} |
|
| 94 |
+ |
|
| 95 |
+func TestEmptyReturn(t *testing.T) {
|
|
| 96 |
+ test := &subjectAccessTest{
|
|
| 97 |
+ authorizer: &testAuthorizer{
|
|
| 98 |
+ allowed: false, |
|
| 99 |
+ reason: "because reasons", |
|
| 100 |
+ }, |
|
| 101 |
+ reviewRequest: &authorizationapi.LocalSubjectAccessReview{
|
|
| 102 |
+ Action: authorizationapi.AuthorizationAttributes{
|
|
| 103 |
+ Namespace: "unittest", |
|
| 104 |
+ Verb: "get", |
|
| 105 |
+ Resource: "pods", |
|
| 106 |
+ }, |
|
| 107 |
+ User: "foo", |
|
| 108 |
+ Groups: util.NewStringSet(), |
|
| 109 |
+ }, |
|
| 110 |
+ } |
|
| 111 |
+ |
|
| 112 |
+ test.runTest(t) |
|
| 113 |
+} |
|
| 114 |
+ |
|
| 115 |
+func TestNoErrors(t *testing.T) {
|
|
| 116 |
+ test := &subjectAccessTest{
|
|
| 117 |
+ authorizer: &testAuthorizer{
|
|
| 118 |
+ allowed: true, |
|
| 119 |
+ reason: "because good things", |
|
| 120 |
+ }, |
|
| 121 |
+ reviewRequest: &authorizationapi.LocalSubjectAccessReview{
|
|
| 122 |
+ Action: authorizationapi.AuthorizationAttributes{
|
|
| 123 |
+ Namespace: "unittest", |
|
| 124 |
+ Verb: "delete", |
|
| 125 |
+ Resource: "deploymentConfigs", |
|
| 126 |
+ }, |
|
| 127 |
+ Groups: util.NewStringSet("not-master"),
|
|
| 128 |
+ }, |
|
| 129 |
+ } |
|
| 130 |
+ |
|
| 131 |
+ test.runTest(t) |
|
| 132 |
+} |
|
| 133 |
+ |
|
| 134 |
+func TestErrors(t *testing.T) {
|
|
| 135 |
+ test := &subjectAccessTest{
|
|
| 136 |
+ authorizer: &testAuthorizer{
|
|
| 137 |
+ err: "some-random-failure", |
|
| 138 |
+ }, |
|
| 139 |
+ reviewRequest: &authorizationapi.LocalSubjectAccessReview{
|
|
| 140 |
+ Action: authorizationapi.AuthorizationAttributes{
|
|
| 141 |
+ Namespace: "unittest", |
|
| 142 |
+ Verb: "get", |
|
| 143 |
+ Resource: "pods", |
|
| 144 |
+ }, |
|
| 145 |
+ User: "foo", |
|
| 146 |
+ Groups: util.NewStringSet("first", "second"),
|
|
| 147 |
+ }, |
|
| 148 |
+ } |
|
| 149 |
+ |
|
| 150 |
+ test.runTest(t) |
|
| 151 |
+} |
|
| 152 |
+ |
|
| 153 |
+func (r *subjectAccessTest) runTest(t *testing.T) {
|
|
| 154 |
+ storage := NewREST(subjectaccessreview.NewRegistry(subjectaccessreview.NewREST(r.authorizer))) |
|
| 155 |
+ |
|
| 156 |
+ expectedResponse := &authorizationapi.SubjectAccessReviewResponse{
|
|
| 157 |
+ Namespace: r.reviewRequest.Action.Namespace, |
|
| 158 |
+ Allowed: r.authorizer.allowed, |
|
| 159 |
+ Reason: r.authorizer.reason, |
|
| 160 |
+ } |
|
| 161 |
+ |
|
| 162 |
+ expectedAttributes := authorizer.ToDefaultAuthorizationAttributes(r.reviewRequest.Action) |
|
| 163 |
+ |
|
| 164 |
+ ctx := kapi.WithNamespace(kapi.NewContext(), r.reviewRequest.Action.Namespace) |
|
| 165 |
+ obj, err := storage.Create(ctx, r.reviewRequest) |
|
| 166 |
+ if err != nil && len(r.authorizer.err) == 0 {
|
|
| 167 |
+ t.Fatalf("unexpected error: %v", err)
|
|
| 168 |
+ } |
|
| 169 |
+ if len(r.authorizer.err) != 0 {
|
|
| 170 |
+ if err == nil {
|
|
| 171 |
+ t.Fatalf("unexpected non-error: %v", err)
|
|
| 172 |
+ } |
|
| 173 |
+ if e, a := r.authorizer.err, err.Error(); e != a {
|
|
| 174 |
+ t.Fatalf("expected %v, got %v", e, a)
|
|
| 175 |
+ } |
|
| 176 |
+ |
|
| 177 |
+ return |
|
| 178 |
+ } |
|
| 179 |
+ |
|
| 180 |
+ switch obj.(type) {
|
|
| 181 |
+ case *authorizationapi.SubjectAccessReviewResponse: |
|
| 182 |
+ if !reflect.DeepEqual(expectedResponse, obj) {
|
|
| 183 |
+ t.Errorf("diff %v", util.ObjectGoPrintDiff(expectedResponse, obj))
|
|
| 184 |
+ } |
|
| 185 |
+ case nil: |
|
| 186 |
+ if len(r.authorizer.err) == 0 {
|
|
| 187 |
+ t.Fatal("unexpected nil object")
|
|
| 188 |
+ } |
|
| 189 |
+ |
|
| 190 |
+ default: |
|
| 191 |
+ t.Errorf("Unexpected obj type: %v", obj)
|
|
| 192 |
+ } |
|
| 193 |
+ |
|
| 194 |
+ if !reflect.DeepEqual(expectedAttributes, r.authorizer.actualAttributes) {
|
|
| 195 |
+ t.Errorf("diff %v", util.ObjectGoPrintDiff(expectedAttributes, r.authorizer.actualAttributes))
|
|
| 196 |
+ } |
|
| 197 |
+} |
| 0 | 198 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,31 @@ |
| 0 |
+package resourceaccessreview |
|
| 1 |
+ |
|
| 2 |
+import ( |
|
| 3 |
+ api "github.com/openshift/origin/pkg/authorization/api" |
|
| 4 |
+ kapi "k8s.io/kubernetes/pkg/api" |
|
| 5 |
+ "k8s.io/kubernetes/pkg/runtime" |
|
| 6 |
+) |
|
| 7 |
+ |
|
| 8 |
+type Registry interface {
|
|
| 9 |
+ CreateResourceAccessReview(ctx kapi.Context, resourceAccessReview *api.ResourceAccessReview) (*api.ResourceAccessReviewResponse, error) |
|
| 10 |
+} |
|
| 11 |
+ |
|
| 12 |
+type Storage interface {
|
|
| 13 |
+ Create(ctx kapi.Context, obj runtime.Object) (runtime.Object, error) |
|
| 14 |
+} |
|
| 15 |
+ |
|
| 16 |
+type storage struct {
|
|
| 17 |
+ Storage |
|
| 18 |
+} |
|
| 19 |
+ |
|
| 20 |
+func NewRegistry(s Storage) Registry {
|
|
| 21 |
+ return &storage{s}
|
|
| 22 |
+} |
|
| 23 |
+ |
|
| 24 |
+func (s *storage) CreateResourceAccessReview(ctx kapi.Context, resourceAccessReview *api.ResourceAccessReview) (*api.ResourceAccessReviewResponse, error) {
|
|
| 25 |
+ obj, err := s.Create(ctx, resourceAccessReview) |
|
| 26 |
+ if err != nil {
|
|
| 27 |
+ return nil, err |
|
| 28 |
+ } |
|
| 29 |
+ return obj.(*api.ResourceAccessReviewResponse), nil |
|
| 30 |
+} |
| ... | ... |
@@ -1,10 +1,11 @@ |
| 1 | 1 |
package resourceaccessreview |
| 2 | 2 |
|
| 3 | 3 |
import ( |
| 4 |
+ "errors" |
|
| 4 | 5 |
"fmt" |
| 5 | 6 |
|
| 6 | 7 |
kapi "k8s.io/kubernetes/pkg/api" |
| 7 |
- "k8s.io/kubernetes/pkg/api/errors" |
|
| 8 |
+ kapierrors "k8s.io/kubernetes/pkg/api/errors" |
|
| 8 | 9 |
"k8s.io/kubernetes/pkg/runtime" |
| 9 | 10 |
kutilerrors "k8s.io/kubernetes/pkg/util/errors" |
| 10 | 11 |
|
| ... | ... |
@@ -32,29 +33,53 @@ func (r *REST) New() runtime.Object {
|
| 32 | 32 |
func (r *REST) Create(ctx kapi.Context, obj runtime.Object) (runtime.Object, error) {
|
| 33 | 33 |
resourceAccessReview, ok := obj.(*authorizationapi.ResourceAccessReview) |
| 34 | 34 |
if !ok {
|
| 35 |
- return nil, errors.NewBadRequest(fmt.Sprintf("not a resourceAccessReview: %#v", obj))
|
|
| 35 |
+ return nil, kapierrors.NewBadRequest(fmt.Sprintf("not a resourceAccessReview: %#v", obj))
|
|
| 36 | 36 |
} |
| 37 | 37 |
if err := kutilerrors.NewAggregate(authorizationvalidation.ValidateResourceAccessReview(resourceAccessReview)); err != nil {
|
| 38 | 38 |
return nil, err |
| 39 | 39 |
} |
| 40 |
- |
|
| 41 |
- namespace := kapi.NamespaceValue(ctx) |
|
| 42 |
- |
|
| 43 |
- attributes := &authorizer.DefaultAuthorizationAttributes{
|
|
| 44 |
- Verb: resourceAccessReview.Verb, |
|
| 45 |
- Resource: resourceAccessReview.Resource, |
|
| 40 |
+ // if a namespace is present on the request, then the namespace on the on the RAR is overwritten. |
|
| 41 |
+ // This is to support backwards compatibility. To have gotten here in this state, it means that |
|
| 42 |
+ // the authorizer decided that a user could run an RAR against this namespace |
|
| 43 |
+ if namespace := kapi.NamespaceValue(ctx); len(namespace) > 0 {
|
|
| 44 |
+ resourceAccessReview.Action.Namespace = namespace |
|
| 45 |
+ } |
|
| 46 |
+ if err := r.isAllowed(ctx, resourceAccessReview); err != nil {
|
|
| 47 |
+ return nil, err |
|
| 46 | 48 |
} |
| 47 | 49 |
|
| 48 |
- users, groups, err := r.authorizer.GetAllowedSubjects(ctx, attributes) |
|
| 50 |
+ requestContext := kapi.WithNamespace(ctx, resourceAccessReview.Action.Namespace) |
|
| 51 |
+ attributes := authorizer.ToDefaultAuthorizationAttributes(resourceAccessReview.Action) |
|
| 52 |
+ users, groups, err := r.authorizer.GetAllowedSubjects(requestContext, attributes) |
|
| 49 | 53 |
if err != nil {
|
| 50 | 54 |
return nil, err |
| 51 | 55 |
} |
| 52 | 56 |
|
| 53 | 57 |
response := &authorizationapi.ResourceAccessReviewResponse{
|
| 54 |
- Namespace: namespace, |
|
| 58 |
+ Namespace: resourceAccessReview.Action.Namespace, |
|
| 55 | 59 |
Users: users, |
| 56 | 60 |
Groups: groups, |
| 57 | 61 |
} |
| 58 | 62 |
|
| 59 | 63 |
return response, nil |
| 60 | 64 |
} |
| 65 |
+ |
|
| 66 |
+// isAllowed checks to see if the current user has rights to issue a LocalSubjectAccessReview on the namespace they're attempting to access |
|
| 67 |
+func (r *REST) isAllowed(ctx kapi.Context, rar *authorizationapi.ResourceAccessReview) error {
|
|
| 68 |
+ localRARAttributes := authorizer.DefaultAuthorizationAttributes{
|
|
| 69 |
+ Verb: "create", |
|
| 70 |
+ Resource: "localresourceaccessreviews", |
|
| 71 |
+ } |
|
| 72 |
+ allowed, reason, err := r.authorizer.Authorize(kapi.WithNamespace(ctx, rar.Action.Namespace), localRARAttributes) |
|
| 73 |
+ |
|
| 74 |
+ if err != nil {
|
|
| 75 |
+ return kapierrors.NewForbidden(localRARAttributes.GetResource(), localRARAttributes.GetResourceName(), err) |
|
| 76 |
+ } |
|
| 77 |
+ if !allowed {
|
|
| 78 |
+ forbiddenError, _ := kapierrors.NewForbidden(localRARAttributes.GetResource(), localRARAttributes.GetResourceName(), errors.New("") /*discarded*/).(*kapierrors.StatusError)
|
|
| 79 |
+ forbiddenError.ErrStatus.Message = reason |
|
| 80 |
+ return forbiddenError |
|
| 81 |
+ } |
|
| 82 |
+ |
|
| 83 |
+ return nil |
|
| 84 |
+} |
| ... | ... |
@@ -18,18 +18,28 @@ type resourceAccessTest struct {
|
| 18 | 18 |
} |
| 19 | 19 |
|
| 20 | 20 |
type testAuthorizer struct {
|
| 21 |
- users util.StringSet |
|
| 22 |
- groups util.StringSet |
|
| 23 |
- err string |
|
| 21 |
+ users util.StringSet |
|
| 22 |
+ groups util.StringSet |
|
| 23 |
+ err string |
|
| 24 |
+ deniedNamespaces util.StringSet |
|
| 24 | 25 |
|
| 25 |
- actualAttributes *authorizer.DefaultAuthorizationAttributes |
|
| 26 |
+ actualAttributes authorizer.DefaultAuthorizationAttributes |
|
| 26 | 27 |
} |
| 27 | 28 |
|
| 28 | 29 |
func (a *testAuthorizer) Authorize(ctx kapi.Context, attributes authorizer.AuthorizationAttributes) (allowed bool, reason string, err error) {
|
| 30 |
+ // allow the initial check for "can I run this RAR at all" |
|
| 31 |
+ if attributes.GetResource() == "localresourceaccessreviews" {
|
|
| 32 |
+ if len(a.deniedNamespaces) != 0 && a.deniedNamespaces.Has(kapi.NamespaceValue(ctx)) {
|
|
| 33 |
+ return false, "denied initial check", nil |
|
| 34 |
+ } |
|
| 35 |
+ |
|
| 36 |
+ return true, "", nil |
|
| 37 |
+ } |
|
| 38 |
+ |
|
| 29 | 39 |
return false, "", errors.New("unsupported")
|
| 30 | 40 |
} |
| 31 | 41 |
func (a *testAuthorizer) GetAllowedSubjects(ctx kapi.Context, passedAttributes authorizer.AuthorizationAttributes) (util.StringSet, util.StringSet, error) {
|
| 32 |
- attributes, ok := passedAttributes.(*authorizer.DefaultAuthorizationAttributes) |
|
| 42 |
+ attributes, ok := passedAttributes.(authorizer.DefaultAuthorizationAttributes) |
|
| 33 | 43 |
if !ok {
|
| 34 | 44 |
return nil, nil, errors.New("unexpected type for test")
|
| 35 | 45 |
} |
| ... | ... |
@@ -41,6 +51,26 @@ func (a *testAuthorizer) GetAllowedSubjects(ctx kapi.Context, passedAttributes a |
| 41 | 41 |
return a.users, a.groups, errors.New(a.err) |
| 42 | 42 |
} |
| 43 | 43 |
|
| 44 |
+func TestDeniedNamespace(t *testing.T) {
|
|
| 45 |
+ test := &resourceAccessTest{
|
|
| 46 |
+ authorizer: &testAuthorizer{
|
|
| 47 |
+ users: util.StringSet{},
|
|
| 48 |
+ groups: util.StringSet{},
|
|
| 49 |
+ err: "denied initial check", |
|
| 50 |
+ deniedNamespaces: util.NewStringSet("foo"),
|
|
| 51 |
+ }, |
|
| 52 |
+ reviewRequest: &authorizationapi.ResourceAccessReview{
|
|
| 53 |
+ Action: authorizationapi.AuthorizationAttributes{
|
|
| 54 |
+ Namespace: "foo", |
|
| 55 |
+ Verb: "get", |
|
| 56 |
+ Resource: "pods", |
|
| 57 |
+ }, |
|
| 58 |
+ }, |
|
| 59 |
+ } |
|
| 60 |
+ |
|
| 61 |
+ test.runTest(t) |
|
| 62 |
+} |
|
| 63 |
+ |
|
| 44 | 64 |
func TestEmptyReturn(t *testing.T) {
|
| 45 | 65 |
test := &resourceAccessTest{
|
| 46 | 66 |
authorizer: &testAuthorizer{
|
| ... | ... |
@@ -48,8 +78,10 @@ func TestEmptyReturn(t *testing.T) {
|
| 48 | 48 |
groups: util.StringSet{},
|
| 49 | 49 |
}, |
| 50 | 50 |
reviewRequest: &authorizationapi.ResourceAccessReview{
|
| 51 |
- Verb: "get", |
|
| 52 |
- Resource: "pods", |
|
| 51 |
+ Action: authorizationapi.AuthorizationAttributes{
|
|
| 52 |
+ Verb: "get", |
|
| 53 |
+ Resource: "pods", |
|
| 54 |
+ }, |
|
| 53 | 55 |
}, |
| 54 | 56 |
} |
| 55 | 57 |
|
| ... | ... |
@@ -63,8 +95,10 @@ func TestNoErrors(t *testing.T) {
|
| 63 | 63 |
groups: util.NewStringSet("three", "four"),
|
| 64 | 64 |
}, |
| 65 | 65 |
reviewRequest: &authorizationapi.ResourceAccessReview{
|
| 66 |
- Verb: "delete", |
|
| 67 |
- Resource: "deploymentConfig", |
|
| 66 |
+ Action: authorizationapi.AuthorizationAttributes{
|
|
| 67 |
+ Verb: "delete", |
|
| 68 |
+ Resource: "deploymentConfig", |
|
| 69 |
+ }, |
|
| 68 | 70 |
}, |
| 69 | 71 |
} |
| 70 | 72 |
|
| ... | ... |
@@ -79,8 +113,10 @@ func TestErrors(t *testing.T) {
|
| 79 | 79 |
err: "some-random-failure", |
| 80 | 80 |
}, |
| 81 | 81 |
reviewRequest: &authorizationapi.ResourceAccessReview{
|
| 82 |
- Verb: "get", |
|
| 83 |
- Resource: "pods", |
|
| 82 |
+ Action: authorizationapi.AuthorizationAttributes{
|
|
| 83 |
+ Verb: "get", |
|
| 84 |
+ Resource: "pods", |
|
| 85 |
+ }, |
|
| 84 | 86 |
}, |
| 85 | 87 |
} |
| 86 | 88 |
|
| ... | ... |
@@ -88,28 +124,30 @@ func TestErrors(t *testing.T) {
|
| 88 | 88 |
} |
| 89 | 89 |
|
| 90 | 90 |
func (r *resourceAccessTest) runTest(t *testing.T) {
|
| 91 |
- const namespace = "unittest" |
|
| 92 |
- |
|
| 93 | 91 |
storage := REST{r.authorizer}
|
| 94 | 92 |
|
| 95 | 93 |
expectedResponse := &authorizationapi.ResourceAccessReviewResponse{
|
| 96 |
- Namespace: namespace, |
|
| 94 |
+ Namespace: r.reviewRequest.Action.Namespace, |
|
| 97 | 95 |
Users: r.authorizer.users, |
| 98 | 96 |
Groups: r.authorizer.groups, |
| 99 | 97 |
} |
| 100 | 98 |
|
| 101 |
- expectedAttributes := &authorizer.DefaultAuthorizationAttributes{
|
|
| 102 |
- Verb: r.reviewRequest.Verb, |
|
| 103 |
- Resource: r.reviewRequest.Resource, |
|
| 104 |
- } |
|
| 99 |
+ expectedAttributes := authorizer.ToDefaultAuthorizationAttributes(r.reviewRequest.Action) |
|
| 105 | 100 |
|
| 106 |
- ctx := kapi.WithNamespace(kapi.NewContext(), namespace) |
|
| 101 |
+ ctx := kapi.WithNamespace(kapi.NewContext(), kapi.NamespaceAll) |
|
| 107 | 102 |
obj, err := storage.Create(ctx, r.reviewRequest) |
| 108 | 103 |
if err != nil && len(r.authorizer.err) == 0 {
|
| 109 | 104 |
t.Fatalf("unexpected error: %v", err)
|
| 110 | 105 |
} |
| 111 |
- if err == nil && len(r.authorizer.err) != 0 {
|
|
| 112 |
- t.Fatalf("unexpected non-error: %v", err)
|
|
| 106 |
+ if len(r.authorizer.err) != 0 {
|
|
| 107 |
+ if err == nil {
|
|
| 108 |
+ t.Fatalf("unexpected non-error: %v", err)
|
|
| 109 |
+ } |
|
| 110 |
+ if e, a := r.authorizer.err, err.Error(); e != a {
|
|
| 111 |
+ t.Fatalf("expected %v, got %v", e, a)
|
|
| 112 |
+ } |
|
| 113 |
+ |
|
| 114 |
+ return |
|
| 113 | 115 |
} |
| 114 | 116 |
|
| 115 | 117 |
switch obj.(type) {
|
| ... | ... |
@@ -1,10 +1,11 @@ |
| 1 | 1 |
package subjectaccessreview |
| 2 | 2 |
|
| 3 | 3 |
import ( |
| 4 |
+ "errors" |
|
| 4 | 5 |
"fmt" |
| 5 | 6 |
|
| 6 | 7 |
kapi "k8s.io/kubernetes/pkg/api" |
| 7 |
- kerrors "k8s.io/kubernetes/pkg/api/errors" |
|
| 8 |
+ kapierrors "k8s.io/kubernetes/pkg/api/errors" |
|
| 8 | 9 |
"k8s.io/kubernetes/pkg/auth/user" |
| 9 | 10 |
"k8s.io/kubernetes/pkg/runtime" |
| 10 | 11 |
kutilerrors "k8s.io/kubernetes/pkg/util/errors" |
| ... | ... |
@@ -33,18 +34,27 @@ func (r *REST) New() runtime.Object {
|
| 33 | 33 |
func (r *REST) Create(ctx kapi.Context, obj runtime.Object) (runtime.Object, error) {
|
| 34 | 34 |
subjectAccessReview, ok := obj.(*authorizationapi.SubjectAccessReview) |
| 35 | 35 |
if !ok {
|
| 36 |
- return nil, kerrors.NewBadRequest(fmt.Sprintf("not a subjectAccessReview: %#v", obj))
|
|
| 36 |
+ return nil, kapierrors.NewBadRequest(fmt.Sprintf("not a subjectAccessReview: %#v", obj))
|
|
| 37 | 37 |
} |
| 38 | 38 |
if err := kutilerrors.NewAggregate(authorizationvalidation.ValidateSubjectAccessReview(subjectAccessReview)); err != nil {
|
| 39 | 39 |
return nil, err |
| 40 | 40 |
} |
| 41 |
+ // if a namespace is present on the request, then the namespace on the on the SAR is overwritten. |
|
| 42 |
+ // This is to support backwards compatibility. To have gotten here in this state, it means that |
|
| 43 |
+ // the authorizer decided that a user could run an SAR against this namespace |
|
| 44 |
+ if namespace := kapi.NamespaceValue(ctx); len(namespace) > 0 {
|
|
| 45 |
+ subjectAccessReview.Action.Namespace = namespace |
|
| 46 |
+ } |
|
| 47 |
+ if err := r.isAllowed(ctx, subjectAccessReview); err != nil {
|
|
| 48 |
+ return nil, err |
|
| 49 |
+ } |
|
| 41 | 50 |
|
| 42 | 51 |
var userToCheck user.Info |
| 43 | 52 |
if (len(subjectAccessReview.User) == 0) && (len(subjectAccessReview.Groups) == 0) {
|
| 44 | 53 |
// if no user or group was specified, use the info from the context |
| 45 | 54 |
ctxUser, exists := kapi.UserFrom(ctx) |
| 46 | 55 |
if !exists {
|
| 47 |
- return nil, kerrors.NewBadRequest("user missing from context")
|
|
| 56 |
+ return nil, kapierrors.NewBadRequest("user missing from context")
|
|
| 48 | 57 |
} |
| 49 | 58 |
userToCheck = ctxUser |
| 50 | 59 |
|
| ... | ... |
@@ -56,24 +66,39 @@ func (r *REST) Create(ctx kapi.Context, obj runtime.Object) (runtime.Object, err |
| 56 | 56 |
|
| 57 | 57 |
} |
| 58 | 58 |
|
| 59 |
- namespace := kapi.NamespaceValue(ctx) |
|
| 60 |
- requestContext := kapi.WithUser(ctx, userToCheck) |
|
| 61 |
- |
|
| 62 |
- attributes := &authorizer.DefaultAuthorizationAttributes{
|
|
| 63 |
- Verb: subjectAccessReview.Verb, |
|
| 64 |
- Resource: subjectAccessReview.Resource, |
|
| 65 |
- } |
|
| 66 |
- |
|
| 59 |
+ requestContext := kapi.WithNamespace(kapi.WithUser(ctx, userToCheck), subjectAccessReview.Action.Namespace) |
|
| 60 |
+ attributes := authorizer.ToDefaultAuthorizationAttributes(subjectAccessReview.Action) |
|
| 67 | 61 |
allowed, reason, err := r.authorizer.Authorize(requestContext, attributes) |
| 68 | 62 |
if err != nil {
|
| 69 | 63 |
return nil, err |
| 70 | 64 |
} |
| 71 | 65 |
|
| 72 | 66 |
response := &authorizationapi.SubjectAccessReviewResponse{
|
| 73 |
- Namespace: namespace, |
|
| 67 |
+ Namespace: subjectAccessReview.Action.Namespace, |
|
| 74 | 68 |
Allowed: allowed, |
| 75 | 69 |
Reason: reason, |
| 76 | 70 |
} |
| 77 | 71 |
|
| 78 | 72 |
return response, nil |
| 79 | 73 |
} |
| 74 |
+ |
|
| 75 |
+// isAllowed checks to see if the current user has rights to issue a LocalSubjectAccessReview on the namespace they're attempting to access |
|
| 76 |
+func (r *REST) isAllowed(ctx kapi.Context, sar *authorizationapi.SubjectAccessReview) error {
|
|
| 77 |
+ localSARAttributes := authorizer.DefaultAuthorizationAttributes{
|
|
| 78 |
+ Verb: "create", |
|
| 79 |
+ Resource: "localsubjectaccessreviews", |
|
| 80 |
+ RequestAttributes: sar, |
|
| 81 |
+ } |
|
| 82 |
+ allowed, reason, err := r.authorizer.Authorize(kapi.WithNamespace(ctx, sar.Action.Namespace), localSARAttributes) |
|
| 83 |
+ |
|
| 84 |
+ if err != nil {
|
|
| 85 |
+ return kapierrors.NewForbidden(localSARAttributes.GetResource(), localSARAttributes.GetResourceName(), err) |
|
| 86 |
+ } |
|
| 87 |
+ if !allowed {
|
|
| 88 |
+ forbiddenError, _ := kapierrors.NewForbidden(localSARAttributes.GetResource(), localSARAttributes.GetResourceName(), errors.New("") /*discarded*/).(*kapierrors.StatusError)
|
|
| 89 |
+ forbiddenError.ErrStatus.Message = reason |
|
| 90 |
+ return forbiddenError |
|
| 91 |
+ } |
|
| 92 |
+ |
|
| 93 |
+ return nil |
|
| 94 |
+} |
| ... | ... |
@@ -18,15 +18,25 @@ type subjectAccessTest struct {
|
| 18 | 18 |
} |
| 19 | 19 |
|
| 20 | 20 |
type testAuthorizer struct {
|
| 21 |
- allowed bool |
|
| 22 |
- reason string |
|
| 23 |
- err string |
|
| 21 |
+ allowed bool |
|
| 22 |
+ reason string |
|
| 23 |
+ err string |
|
| 24 |
+ deniedNamespaces util.StringSet |
|
| 24 | 25 |
|
| 25 |
- actualAttributes *authorizer.DefaultAuthorizationAttributes |
|
| 26 |
+ actualAttributes authorizer.DefaultAuthorizationAttributes |
|
| 26 | 27 |
} |
| 27 | 28 |
|
| 28 | 29 |
func (a *testAuthorizer) Authorize(ctx kapi.Context, passedAttributes authorizer.AuthorizationAttributes) (allowed bool, reason string, err error) {
|
| 29 |
- attributes, ok := passedAttributes.(*authorizer.DefaultAuthorizationAttributes) |
|
| 30 |
+ // allow the initial check for "can I run this SAR at all" |
|
| 31 |
+ if passedAttributes.GetResource() == "localsubjectaccessreviews" {
|
|
| 32 |
+ if len(a.deniedNamespaces) != 0 && a.deniedNamespaces.Has(kapi.NamespaceValue(ctx)) {
|
|
| 33 |
+ return false, "denied initial check", nil |
|
| 34 |
+ } |
|
| 35 |
+ |
|
| 36 |
+ return true, "", nil |
|
| 37 |
+ } |
|
| 38 |
+ |
|
| 39 |
+ attributes, ok := passedAttributes.(authorizer.DefaultAuthorizationAttributes) |
|
| 30 | 40 |
if !ok {
|
| 31 | 41 |
return false, "ERROR", errors.New("unexpected type for test")
|
| 32 | 42 |
} |
| ... | ... |
@@ -42,6 +52,27 @@ func (a *testAuthorizer) GetAllowedSubjects(ctx kapi.Context, passedAttributes a |
| 42 | 42 |
return util.StringSet{}, util.StringSet{}, nil
|
| 43 | 43 |
} |
| 44 | 44 |
|
| 45 |
+func TestDeniedNamespace(t *testing.T) {
|
|
| 46 |
+ test := &subjectAccessTest{
|
|
| 47 |
+ authorizer: &testAuthorizer{
|
|
| 48 |
+ allowed: false, |
|
| 49 |
+ err: "denied initial check", |
|
| 50 |
+ deniedNamespaces: util.NewStringSet("foo"),
|
|
| 51 |
+ }, |
|
| 52 |
+ reviewRequest: &authorizationapi.SubjectAccessReview{
|
|
| 53 |
+ Action: authorizationapi.AuthorizationAttributes{
|
|
| 54 |
+ Namespace: "foo", |
|
| 55 |
+ Verb: "get", |
|
| 56 |
+ Resource: "pods", |
|
| 57 |
+ }, |
|
| 58 |
+ User: "foo", |
|
| 59 |
+ Groups: util.NewStringSet(), |
|
| 60 |
+ }, |
|
| 61 |
+ } |
|
| 62 |
+ |
|
| 63 |
+ test.runTest(t) |
|
| 64 |
+} |
|
| 65 |
+ |
|
| 45 | 66 |
func TestEmptyReturn(t *testing.T) {
|
| 46 | 67 |
test := &subjectAccessTest{
|
| 47 | 68 |
authorizer: &testAuthorizer{
|
| ... | ... |
@@ -49,10 +80,12 @@ func TestEmptyReturn(t *testing.T) {
|
| 49 | 49 |
reason: "because reasons", |
| 50 | 50 |
}, |
| 51 | 51 |
reviewRequest: &authorizationapi.SubjectAccessReview{
|
| 52 |
- User: "foo", |
|
| 53 |
- Groups: util.NewStringSet(), |
|
| 54 |
- Verb: "get", |
|
| 55 |
- Resource: "pods", |
|
| 52 |
+ Action: authorizationapi.AuthorizationAttributes{
|
|
| 53 |
+ Verb: "get", |
|
| 54 |
+ Resource: "pods", |
|
| 55 |
+ }, |
|
| 56 |
+ User: "foo", |
|
| 57 |
+ Groups: util.NewStringSet(), |
|
| 56 | 58 |
}, |
| 57 | 59 |
} |
| 58 | 60 |
|
| ... | ... |
@@ -66,9 +99,11 @@ func TestNoErrors(t *testing.T) {
|
| 66 | 66 |
reason: "because good things", |
| 67 | 67 |
}, |
| 68 | 68 |
reviewRequest: &authorizationapi.SubjectAccessReview{
|
| 69 |
- Groups: util.NewStringSet("not-master"),
|
|
| 70 |
- Verb: "delete", |
|
| 71 |
- Resource: "deploymentConfigs", |
|
| 69 |
+ Action: authorizationapi.AuthorizationAttributes{
|
|
| 70 |
+ Verb: "delete", |
|
| 71 |
+ Resource: "deploymentConfigs", |
|
| 72 |
+ }, |
|
| 73 |
+ Groups: util.NewStringSet("not-master"),
|
|
| 72 | 74 |
}, |
| 73 | 75 |
} |
| 74 | 76 |
|
| ... | ... |
@@ -81,10 +116,12 @@ func TestErrors(t *testing.T) {
|
| 81 | 81 |
err: "some-random-failure", |
| 82 | 82 |
}, |
| 83 | 83 |
reviewRequest: &authorizationapi.SubjectAccessReview{
|
| 84 |
- User: "foo", |
|
| 85 |
- Groups: util.NewStringSet("first", "second"),
|
|
| 86 |
- Verb: "get", |
|
| 87 |
- Resource: "pods", |
|
| 84 |
+ Action: authorizationapi.AuthorizationAttributes{
|
|
| 85 |
+ Verb: "get", |
|
| 86 |
+ Resource: "pods", |
|
| 87 |
+ }, |
|
| 88 |
+ User: "foo", |
|
| 89 |
+ Groups: util.NewStringSet("first", "second"),
|
|
| 88 | 90 |
}, |
| 89 | 91 |
} |
| 90 | 92 |
|
| ... | ... |
@@ -92,28 +129,30 @@ func TestErrors(t *testing.T) {
|
| 92 | 92 |
} |
| 93 | 93 |
|
| 94 | 94 |
func (r *subjectAccessTest) runTest(t *testing.T) {
|
| 95 |
- const namespace = "unittest" |
|
| 96 |
- |
|
| 97 | 95 |
storage := REST{r.authorizer}
|
| 98 | 96 |
|
| 99 | 97 |
expectedResponse := &authorizationapi.SubjectAccessReviewResponse{
|
| 100 |
- Namespace: namespace, |
|
| 98 |
+ Namespace: r.reviewRequest.Action.Namespace, |
|
| 101 | 99 |
Allowed: r.authorizer.allowed, |
| 102 | 100 |
Reason: r.authorizer.reason, |
| 103 | 101 |
} |
| 104 | 102 |
|
| 105 |
- expectedAttributes := &authorizer.DefaultAuthorizationAttributes{
|
|
| 106 |
- Verb: r.reviewRequest.Verb, |
|
| 107 |
- Resource: r.reviewRequest.Resource, |
|
| 108 |
- } |
|
| 103 |
+ expectedAttributes := authorizer.ToDefaultAuthorizationAttributes(r.reviewRequest.Action) |
|
| 109 | 104 |
|
| 110 |
- ctx := kapi.WithNamespace(kapi.NewContext(), namespace) |
|
| 105 |
+ ctx := kapi.WithNamespace(kapi.NewContext(), kapi.NamespaceAll) |
|
| 111 | 106 |
obj, err := storage.Create(ctx, r.reviewRequest) |
| 112 | 107 |
if err != nil && len(r.authorizer.err) == 0 {
|
| 113 | 108 |
t.Fatalf("unexpected error: %v", err)
|
| 114 | 109 |
} |
| 115 |
- if err == nil && len(r.authorizer.err) != 0 {
|
|
| 116 |
- t.Fatalf("unexpected non-error: %v", err)
|
|
| 110 |
+ if len(r.authorizer.err) != 0 {
|
|
| 111 |
+ if err == nil {
|
|
| 112 |
+ t.Fatalf("unexpected non-error: %v", err)
|
|
| 113 |
+ } |
|
| 114 |
+ if e, a := r.authorizer.err, err.Error(); e != a {
|
|
| 115 |
+ t.Fatalf("expected %v, got %v", e, a)
|
|
| 116 |
+ } |
|
| 117 |
+ |
|
| 118 |
+ return |
|
| 117 | 119 |
} |
| 118 | 120 |
|
| 119 | 121 |
switch obj.(type) {
|
| ... | ... |
@@ -84,26 +84,30 @@ func resourceName(objectMeta kapi.ObjectMeta) string {
|
| 84 | 84 |
|
| 85 | 85 |
func (a *buildByStrategy) checkBuildAuthorization(build *buildapi.Build, attr admission.Attributes) error {
|
| 86 | 86 |
strategyType := build.Spec.Strategy.Type |
| 87 |
- subjectAccessReview := &authorizationapi.SubjectAccessReview{
|
|
| 88 |
- Verb: "create", |
|
| 89 |
- Resource: resourceForStrategyType(strategyType), |
|
| 90 |
- User: attr.GetUserInfo().GetName(), |
|
| 91 |
- Groups: util.NewStringSet(attr.GetUserInfo().GetGroups()...), |
|
| 92 |
- Content: runtime.EmbeddedObject{Object: build},
|
|
| 93 |
- ResourceName: resourceName(build.ObjectMeta), |
|
| 87 |
+ subjectAccessReview := &authorizationapi.LocalSubjectAccessReview{
|
|
| 88 |
+ Action: authorizationapi.AuthorizationAttributes{
|
|
| 89 |
+ Verb: "create", |
|
| 90 |
+ Resource: resourceForStrategyType(strategyType), |
|
| 91 |
+ Content: runtime.EmbeddedObject{Object: build},
|
|
| 92 |
+ ResourceName: resourceName(build.ObjectMeta), |
|
| 93 |
+ }, |
|
| 94 |
+ User: attr.GetUserInfo().GetName(), |
|
| 95 |
+ Groups: util.NewStringSet(attr.GetUserInfo().GetGroups()...), |
|
| 94 | 96 |
} |
| 95 | 97 |
return a.checkAccess(strategyType, subjectAccessReview, attr) |
| 96 | 98 |
} |
| 97 | 99 |
|
| 98 | 100 |
func (a *buildByStrategy) checkBuildConfigAuthorization(buildConfig *buildapi.BuildConfig, attr admission.Attributes) error {
|
| 99 | 101 |
strategyType := buildConfig.Spec.Strategy.Type |
| 100 |
- subjectAccessReview := &authorizationapi.SubjectAccessReview{
|
|
| 101 |
- Verb: "create", |
|
| 102 |
- Resource: resourceForStrategyType(strategyType), |
|
| 103 |
- User: attr.GetUserInfo().GetName(), |
|
| 104 |
- Groups: util.NewStringSet(attr.GetUserInfo().GetGroups()...), |
|
| 105 |
- Content: runtime.EmbeddedObject{Object: buildConfig},
|
|
| 106 |
- ResourceName: resourceName(buildConfig.ObjectMeta), |
|
| 102 |
+ subjectAccessReview := &authorizationapi.LocalSubjectAccessReview{
|
|
| 103 |
+ Action: authorizationapi.AuthorizationAttributes{
|
|
| 104 |
+ Verb: "create", |
|
| 105 |
+ Resource: resourceForStrategyType(strategyType), |
|
| 106 |
+ Content: runtime.EmbeddedObject{Object: buildConfig},
|
|
| 107 |
+ ResourceName: resourceName(buildConfig.ObjectMeta), |
|
| 108 |
+ }, |
|
| 109 |
+ User: attr.GetUserInfo().GetName(), |
|
| 110 |
+ Groups: util.NewStringSet(attr.GetUserInfo().GetGroups()...), |
|
| 107 | 111 |
} |
| 108 | 112 |
return a.checkAccess(strategyType, subjectAccessReview, attr) |
| 109 | 113 |
} |
| ... | ... |
@@ -127,8 +131,8 @@ func (a *buildByStrategy) checkBuildRequestAuthorization(req *buildapi.BuildRequ |
| 127 | 127 |
} |
| 128 | 128 |
} |
| 129 | 129 |
|
| 130 |
-func (a *buildByStrategy) checkAccess(strategyType buildapi.BuildStrategyType, subjectAccessReview *authorizationapi.SubjectAccessReview, attr admission.Attributes) error {
|
|
| 131 |
- resp, err := a.client.SubjectAccessReviews(attr.GetNamespace()).Create(subjectAccessReview) |
|
| 130 |
+func (a *buildByStrategy) checkAccess(strategyType buildapi.BuildStrategyType, subjectAccessReview *authorizationapi.LocalSubjectAccessReview, attr admission.Attributes) error {
|
|
| 131 |
+ resp, err := a.client.LocalSubjectAccessReviews(attr.GetNamespace()).Create(subjectAccessReview) |
|
| 132 | 132 |
if err != nil {
|
| 133 | 133 |
return err |
| 134 | 134 |
} |
| ... | ... |
@@ -156,14 +156,14 @@ func fakeClient(expectedResource string, reviewResponse *authorizationapi.Subjec |
| 156 | 156 |
return &testclient.Fake{
|
| 157 | 157 |
ReactFn: func(action ktestclient.Action) (runtime.Object, error) {
|
| 158 | 158 |
switch {
|
| 159 |
- case action.Matches("create", "subjectaccessreviews"):
|
|
| 160 |
- review, ok := action.(ktestclient.CreateAction).GetObject().(*authorizationapi.SubjectAccessReview) |
|
| 159 |
+ case action.Matches("create", "localsubjectaccessreviews"):
|
|
| 160 |
+ review, ok := action.(ktestclient.CreateAction).GetObject().(*authorizationapi.LocalSubjectAccessReview) |
|
| 161 | 161 |
if !ok {
|
| 162 | 162 |
return emptyResponse, fmt.Errorf("unexpected object received: %#v", review)
|
| 163 | 163 |
} |
| 164 |
- if review.Resource != expectedResource {
|
|
| 164 |
+ if review.Action.Resource != expectedResource {
|
|
| 165 | 165 |
return emptyResponse, fmt.Errorf("unexpected resource received: %s. expected: %s",
|
| 166 |
- review.Resource, expectedResource) |
|
| 166 |
+ review.Action.Resource, expectedResource) |
|
| 167 | 167 |
} |
| 168 | 168 |
return reviewResponse, nil |
| 169 | 169 |
case action.Matches("get", "buildconfigs"):
|
| ... | ... |
@@ -113,7 +113,7 @@ func validateBuildSpec(spec *buildapi.BuildSpec) fielderrors.ValidationErrorList |
| 113 | 113 |
allErrs = append(allErrs, validateOutput(&spec.Output).Prefix("output")...)
|
| 114 | 114 |
allErrs = append(allErrs, validateStrategy(&spec.Strategy).Prefix("strategy")...)
|
| 115 | 115 |
|
| 116 |
- // TODO: validate resource requirements (prereq: https://github.com/GoogleCloudPlatform/kubernetes/pull/7059) |
|
| 116 |
+ // TODO: validate resource requirements (prereq: https://k8s.io/kubernetes/pull/7059) |
|
| 117 | 117 |
return allErrs |
| 118 | 118 |
} |
| 119 | 119 |
|
| ... | ... |
@@ -34,11 +34,12 @@ type Interface interface {
|
| 34 | 34 |
UserIdentityMappingsInterface |
| 35 | 35 |
ProjectsInterface |
| 36 | 36 |
ProjectRequestsInterface |
| 37 |
- ResourceAccessReviewsNamespacer |
|
| 38 |
- ClusterResourceAccessReviews |
|
| 39 |
- SubjectAccessReviewsNamespacer |
|
| 37 |
+ LocalSubjectAccessReviewsImpersonator |
|
| 40 | 38 |
SubjectAccessReviewsImpersonator |
| 41 |
- ClusterSubjectAccessReviews |
|
| 39 |
+ LocalResourceAccessReviewsNamespacer |
|
| 40 |
+ ResourceAccessReviews |
|
| 41 |
+ SubjectAccessReviews |
|
| 42 |
+ LocalSubjectAccessReviewsNamespacer |
|
| 42 | 43 |
TemplatesNamespacer |
| 43 | 44 |
TemplateConfigsNamespacer |
| 44 | 45 |
OAuthAccessTokensInterface |
| ... | ... |
@@ -177,29 +178,34 @@ func (c *Client) RoleBindings(namespace string) RoleBindingInterface {
|
| 177 | 177 |
return newRoleBindings(c, namespace) |
| 178 | 178 |
} |
| 179 | 179 |
|
| 180 |
-// ResourceAccessReviews provides a REST client for ResourceAccessReviews |
|
| 181 |
-func (c *Client) ResourceAccessReviews(namespace string) ResourceAccessReviewInterface {
|
|
| 182 |
- return newResourceAccessReviews(c, namespace) |
|
| 180 |
+// LocalResourceAccessReviews provides a REST client for LocalResourceAccessReviews |
|
| 181 |
+func (c *Client) LocalResourceAccessReviews(namespace string) LocalResourceAccessReviewInterface {
|
|
| 182 |
+ return newLocalResourceAccessReviews(c, namespace) |
|
| 183 | 183 |
} |
| 184 | 184 |
|
| 185 | 185 |
// ClusterResourceAccessReviews provides a REST client for ClusterResourceAccessReviews |
| 186 |
-func (c *Client) ClusterResourceAccessReviews() ResourceAccessReviewInterface {
|
|
| 187 |
- return newClusterResourceAccessReviews(c) |
|
| 186 |
+func (c *Client) ResourceAccessReviews() ResourceAccessReviewInterface {
|
|
| 187 |
+ return newResourceAccessReviews(c) |
|
| 188 | 188 |
} |
| 189 | 189 |
|
| 190 |
-// SubjectAccessReviews provides a REST client for SubjectAccessReviews |
|
| 191 |
-func (c *Client) SubjectAccessReviews(namespace string) SubjectAccessReviewInterface {
|
|
| 192 |
- return newSubjectAccessReviews(c, namespace, "") |
|
| 190 |
+// ImpersonateSubjectAccessReviews provides a REST client for SubjectAccessReviews |
|
| 191 |
+func (c *Client) ImpersonateSubjectAccessReviews(token string) SubjectAccessReviewInterface {
|
|
| 192 |
+ return newImpersonatingSubjectAccessReviews(c, token) |
|
| 193 | 193 |
} |
| 194 | 194 |
|
| 195 |
-// ImpersonateSubjectAccessReviews provides a REST client for SubjectAccessReviews |
|
| 196 |
-func (c *Client) ImpersonateSubjectAccessReviews(namespace, token string) SubjectAccessReviewInterface {
|
|
| 197 |
- return newSubjectAccessReviews(c, namespace, token) |
|
| 195 |
+// ImpersonateLocalSubjectAccessReviews provides a REST client for SubjectAccessReviews |
|
| 196 |
+func (c *Client) ImpersonateLocalSubjectAccessReviews(namespace, token string) LocalSubjectAccessReviewInterface {
|
|
| 197 |
+ return newImpersonatingLocalSubjectAccessReviews(c, namespace, token) |
|
| 198 | 198 |
} |
| 199 | 199 |
|
| 200 |
-// ClusterSubjectAccessReviews provides a REST client for SubjectAccessReviews |
|
| 201 |
-func (c *Client) ClusterSubjectAccessReviews() SubjectAccessReviewInterface {
|
|
| 202 |
- return newClusterSubjectAccessReviews(c) |
|
| 200 |
+// LocalSubjectAccessReviews provides a REST client for LocalSubjectAccessReviews |
|
| 201 |
+func (c *Client) LocalSubjectAccessReviews(namespace string) LocalSubjectAccessReviewInterface {
|
|
| 202 |
+ return newLocalSubjectAccessReviews(c, namespace) |
|
| 203 |
+} |
|
| 204 |
+ |
|
| 205 |
+// SubjectAccessReviews provides a REST client for SubjectAccessReviews |
|
| 206 |
+func (c *Client) SubjectAccessReviews() SubjectAccessReviewInterface {
|
|
| 207 |
+ return newSubjectAccessReviews(c) |
|
| 203 | 208 |
} |
| 204 | 209 |
|
| 205 | 210 |
// OAuthAccessTokens provides a REST client for OAuthAccessTokens |
| 206 | 211 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,51 @@ |
| 0 |
+package client |
|
| 1 |
+ |
|
| 2 |
+import ( |
|
| 3 |
+ kapierrors "k8s.io/kubernetes/pkg/api/errors" |
|
| 4 |
+ |
|
| 5 |
+ authorizationapi "github.com/openshift/origin/pkg/authorization/api" |
|
| 6 |
+) |
|
| 7 |
+ |
|
| 8 |
+// LocalResourceAccessReviewsNamespacer has methods to work with LocalResourceAccessReview resources in a namespace |
|
| 9 |
+type LocalResourceAccessReviewsNamespacer interface {
|
|
| 10 |
+ LocalResourceAccessReviews(namespace string) LocalResourceAccessReviewInterface |
|
| 11 |
+} |
|
| 12 |
+ |
|
| 13 |
+// LocalResourceAccessReviewInterface exposes methods on LocalResourceAccessReview resources. |
|
| 14 |
+type LocalResourceAccessReviewInterface interface {
|
|
| 15 |
+ Create(policy *authorizationapi.LocalResourceAccessReview) (*authorizationapi.ResourceAccessReviewResponse, error) |
|
| 16 |
+} |
|
| 17 |
+ |
|
| 18 |
+// localResourceAccessReviews implements ResourceAccessReviewsNamespacer interface |
|
| 19 |
+type localResourceAccessReviews struct {
|
|
| 20 |
+ r *Client |
|
| 21 |
+ ns string |
|
| 22 |
+} |
|
| 23 |
+ |
|
| 24 |
+// newLocalResourceAccessReviews returns a localLocalResourceAccessReviews |
|
| 25 |
+func newLocalResourceAccessReviews(c *Client, namespace string) *localResourceAccessReviews {
|
|
| 26 |
+ return &localResourceAccessReviews{
|
|
| 27 |
+ r: c, |
|
| 28 |
+ ns: namespace, |
|
| 29 |
+ } |
|
| 30 |
+} |
|
| 31 |
+ |
|
| 32 |
+func (c *localResourceAccessReviews) Create(rar *authorizationapi.LocalResourceAccessReview) (result *authorizationapi.ResourceAccessReviewResponse, err error) {
|
|
| 33 |
+ result = &authorizationapi.ResourceAccessReviewResponse{}
|
|
| 34 |
+ err = c.r.Post().Namespace(c.ns).Resource("localResourceAccessReviews").Body(rar).Do().Into(result)
|
|
| 35 |
+ |
|
| 36 |
+ // if we get one of these failures, we may be talking to an older openshift. In that case, we need to try hitting ns/namespace-name/subjectaccessreview |
|
| 37 |
+ if kapierrors.IsForbidden(err) || kapierrors.IsNotFound(err) {
|
|
| 38 |
+ deprecatedRAR := &authorizationapi.ResourceAccessReview{
|
|
| 39 |
+ Action: rar.Action, |
|
| 40 |
+ } |
|
| 41 |
+ deprecatedResponse := &authorizationapi.ResourceAccessReviewResponse{}
|
|
| 42 |
+ deprecatedAttemptErr := c.r.Post().Namespace(c.ns).Resource("resourceAccessReviews").Body(deprecatedRAR).Do().Into(deprecatedResponse)
|
|
| 43 |
+ if deprecatedAttemptErr == nil {
|
|
| 44 |
+ err = nil |
|
| 45 |
+ result = deprecatedResponse |
|
| 46 |
+ } |
|
| 47 |
+ } |
|
| 48 |
+ |
|
| 49 |
+ return |
|
| 50 |
+} |
| 0 | 51 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,67 @@ |
| 0 |
+package client |
|
| 1 |
+ |
|
| 2 |
+import ( |
|
| 3 |
+ kapierrors "k8s.io/kubernetes/pkg/api/errors" |
|
| 4 |
+ |
|
| 5 |
+ authorizationapi "github.com/openshift/origin/pkg/authorization/api" |
|
| 6 |
+) |
|
| 7 |
+ |
|
| 8 |
+type LocalSubjectAccessReviewsImpersonator interface {
|
|
| 9 |
+ ImpersonateLocalSubjectAccessReviews(namespace, token string) LocalSubjectAccessReviewInterface |
|
| 10 |
+} |
|
| 11 |
+ |
|
| 12 |
+// LocalSubjectAccessReviewsNamespacer has methods to work with LocalSubjectAccessReview resources in a namespace |
|
| 13 |
+type LocalSubjectAccessReviewsNamespacer interface {
|
|
| 14 |
+ LocalSubjectAccessReviews(namespace string) LocalSubjectAccessReviewInterface |
|
| 15 |
+} |
|
| 16 |
+ |
|
| 17 |
+// LocalSubjectAccessReviewInterface exposes methods on LocalSubjectAccessReview resources. |
|
| 18 |
+type LocalSubjectAccessReviewInterface interface {
|
|
| 19 |
+ Create(policy *authorizationapi.LocalSubjectAccessReview) (*authorizationapi.SubjectAccessReviewResponse, error) |
|
| 20 |
+} |
|
| 21 |
+ |
|
| 22 |
+// localSubjectAccessReviews implements LocalSubjectAccessReviewsNamespacer interface |
|
| 23 |
+type localSubjectAccessReviews struct {
|
|
| 24 |
+ r *Client |
|
| 25 |
+ ns string |
|
| 26 |
+ token string |
|
| 27 |
+} |
|
| 28 |
+ |
|
| 29 |
+// newImpersonatingLocalSubjectAccessReviews returns a subjectAccessReviews |
|
| 30 |
+func newImpersonatingLocalSubjectAccessReviews(c *Client, namespace, token string) *localSubjectAccessReviews {
|
|
| 31 |
+ return &localSubjectAccessReviews{
|
|
| 32 |
+ r: c, |
|
| 33 |
+ ns: namespace, |
|
| 34 |
+ token: token, |
|
| 35 |
+ } |
|
| 36 |
+} |
|
| 37 |
+ |
|
| 38 |
+// newLocalSubjectAccessReviews returns a localSubjectAccessReviews |
|
| 39 |
+func newLocalSubjectAccessReviews(c *Client, namespace string) *localSubjectAccessReviews {
|
|
| 40 |
+ return &localSubjectAccessReviews{
|
|
| 41 |
+ r: c, |
|
| 42 |
+ ns: namespace, |
|
| 43 |
+ } |
|
| 44 |
+} |
|
| 45 |
+ |
|
| 46 |
+func (c *localSubjectAccessReviews) Create(sar *authorizationapi.LocalSubjectAccessReview) (result *authorizationapi.SubjectAccessReviewResponse, err error) {
|
|
| 47 |
+ result = &authorizationapi.SubjectAccessReviewResponse{}
|
|
| 48 |
+ err = overrideAuth(c.token, c.r.Post().Namespace(c.ns).Resource("localSubjectAccessReviews")).Body(sar).Do().Into(result)
|
|
| 49 |
+ |
|
| 50 |
+ // if we get one of these failures, we may be talking to an older openshift. In that case, we need to try hitting ns/namespace-name/subjectaccessreview |
|
| 51 |
+ if kapierrors.IsForbidden(err) || kapierrors.IsNotFound(err) {
|
|
| 52 |
+ deprecatedSAR := &authorizationapi.SubjectAccessReview{
|
|
| 53 |
+ Action: sar.Action, |
|
| 54 |
+ User: sar.User, |
|
| 55 |
+ Groups: sar.Groups, |
|
| 56 |
+ } |
|
| 57 |
+ deprecatedResponse := &authorizationapi.SubjectAccessReviewResponse{}
|
|
| 58 |
+ deprecatedAttemptErr := overrideAuth(c.token, c.r.Post().Namespace(c.ns).Resource("subjectAccessReviews")).Body(deprecatedSAR).Do().Into(deprecatedResponse)
|
|
| 59 |
+ if deprecatedAttemptErr == nil {
|
|
| 60 |
+ err = nil |
|
| 61 |
+ result = deprecatedResponse |
|
| 62 |
+ } |
|
| 63 |
+ } |
|
| 64 |
+ |
|
| 65 |
+ return |
|
| 66 |
+} |
| ... | ... |
@@ -1,17 +1,14 @@ |
| 1 | 1 |
package client |
| 2 | 2 |
|
| 3 | 3 |
import ( |
| 4 |
+ kapierrors "k8s.io/kubernetes/pkg/api/errors" |
|
| 5 |
+ |
|
| 4 | 6 |
authorizationapi "github.com/openshift/origin/pkg/authorization/api" |
| 5 | 7 |
) |
| 6 | 8 |
|
| 7 |
-// ResourceAccessReviewsNamespacer has methods to work with ResourceAccessReview resources in a namespace |
|
| 8 |
-type ResourceAccessReviewsNamespacer interface {
|
|
| 9 |
- ResourceAccessReviews(namespace string) ResourceAccessReviewInterface |
|
| 10 |
-} |
|
| 11 |
- |
|
| 12 |
-// ClusterResourceAccessReviews has methods to work with ResourceAccessReview resources in the cluster scope |
|
| 13 |
-type ClusterResourceAccessReviews interface {
|
|
| 14 |
- ClusterResourceAccessReviews() ResourceAccessReviewInterface |
|
| 9 |
+// ResourceAccessReviews has methods to work with ResourceAccessReview resources in the cluster scope |
|
| 10 |
+type ResourceAccessReviews interface {
|
|
| 11 |
+ ResourceAccessReviews() ResourceAccessReviewInterface |
|
| 15 | 12 |
} |
| 16 | 13 |
|
| 17 | 14 |
// ResourceAccessReviewInterface exposes methods on ResourceAccessReview resources. |
| ... | ... |
@@ -19,42 +16,46 @@ type ResourceAccessReviewInterface interface {
|
| 19 | 19 |
Create(policy *authorizationapi.ResourceAccessReview) (*authorizationapi.ResourceAccessReviewResponse, error) |
| 20 | 20 |
} |
| 21 | 21 |
|
| 22 |
-// resourceAccessReviews implements ResourceAccessReviewsNamespacer interface |
|
| 22 |
+// resourceAccessReviews implements ResourceAccessReviews interface |
|
| 23 | 23 |
type resourceAccessReviews struct {
|
| 24 |
- r *Client |
|
| 25 |
- ns string |
|
| 24 |
+ r *Client |
|
| 26 | 25 |
} |
| 27 | 26 |
|
| 28 | 27 |
// newResourceAccessReviews returns a resourceAccessReviews |
| 29 |
-func newResourceAccessReviews(c *Client, namespace string) *resourceAccessReviews {
|
|
| 28 |
+func newResourceAccessReviews(c *Client) *resourceAccessReviews {
|
|
| 30 | 29 |
return &resourceAccessReviews{
|
| 31 |
- r: c, |
|
| 32 |
- ns: namespace, |
|
| 30 |
+ r: c, |
|
| 33 | 31 |
} |
| 34 | 32 |
} |
| 35 | 33 |
|
| 36 |
-// Create creates new policy. Returns the server's representation of the policy and error if one occurs. |
|
| 37 |
-func (c *resourceAccessReviews) Create(policy *authorizationapi.ResourceAccessReview) (result *authorizationapi.ResourceAccessReviewResponse, err error) {
|
|
| 34 |
+func (c *resourceAccessReviews) Create(rar *authorizationapi.ResourceAccessReview) (result *authorizationapi.ResourceAccessReviewResponse, err error) {
|
|
| 38 | 35 |
result = &authorizationapi.ResourceAccessReviewResponse{}
|
| 39 |
- err = c.r.Post().Namespace(c.ns).Resource("resourceAccessReviews").Body(policy).Do().Into(result)
|
|
| 40 |
- return |
|
| 41 |
-} |
|
| 42 | 36 |
|
| 43 |
-// clusterResourceAccessReviews implements ClusterResourceAccessReviews interface |
|
| 44 |
-type clusterResourceAccessReviews struct {
|
|
| 45 |
- r *Client |
|
| 46 |
-} |
|
| 37 |
+ // if this a cluster RAR, then no special handling |
|
| 38 |
+ if len(rar.Action.Namespace) == 0 {
|
|
| 39 |
+ err = c.r.Post().Resource("resourceAccessReviews").Body(rar).Do().Into(result)
|
|
| 40 |
+ return |
|
| 41 |
+ } |
|
| 47 | 42 |
|
| 48 |
-// newClusterResourceAccessReviews returns a clusterResourceAccessReviews |
|
| 49 |
-func newClusterResourceAccessReviews(c *Client) *clusterResourceAccessReviews {
|
|
| 50 |
- return &clusterResourceAccessReviews{
|
|
| 51 |
- r: c, |
|
| 43 |
+ err = c.r.Post().Resource("resourceAccessReviews").Body(rar).Do().Into(result)
|
|
| 44 |
+ |
|
| 45 |
+ // if the namespace values don't match then we definitely hit an old server. If we got a forbidden, then we might have hit an old server |
|
| 46 |
+ // and should try the old endpoint |
|
| 47 |
+ if (rar.Action.Namespace != result.Namespace) || kapierrors.IsForbidden(err) {
|
|
| 48 |
+ deprecatedResponse := &authorizationapi.ResourceAccessReviewResponse{}
|
|
| 49 |
+ deprecatedAttemptErr := c.r.Post().Namespace(rar.Action.Namespace).Resource("resourceAccessReviews").Body(rar).Do().Into(deprecatedResponse)
|
|
| 50 |
+ |
|
| 51 |
+ // if we definitely hit an old server, then return the error and result you get from the older server. |
|
| 52 |
+ if rar.Action.Namespace != result.Namespace {
|
|
| 53 |
+ return deprecatedResponse, deprecatedAttemptErr |
|
| 54 |
+ } |
|
| 55 |
+ |
|
| 56 |
+ // if we're not certain it was an old server, success overwrites the previous error, but failure doesn't overwrite the previous error |
|
| 57 |
+ if deprecatedAttemptErr == nil {
|
|
| 58 |
+ err = nil |
|
| 59 |
+ result = deprecatedResponse |
|
| 60 |
+ } |
|
| 52 | 61 |
} |
| 53 |
-} |
|
| 54 | 62 |
|
| 55 |
-// Create creates new policy. Returns the server's representation of the policy and error if one occurs. |
|
| 56 |
-func (c *clusterResourceAccessReviews) Create(policy *authorizationapi.ResourceAccessReview) (result *authorizationapi.ResourceAccessReviewResponse, err error) {
|
|
| 57 |
- result = &authorizationapi.ResourceAccessReviewResponse{}
|
|
| 58 |
- err = c.r.Post().Resource("resourceAccessReviews").Body(policy).Do().Into(result)
|
|
| 59 | 63 |
return |
| 60 | 64 |
} |
| ... | ... |
@@ -3,23 +3,19 @@ package client |
| 3 | 3 |
import ( |
| 4 | 4 |
"fmt" |
| 5 | 5 |
|
| 6 |
+ kapierrors "k8s.io/kubernetes/pkg/api/errors" |
|
| 6 | 7 |
"k8s.io/kubernetes/pkg/client" |
| 7 | 8 |
|
| 8 | 9 |
authorizationapi "github.com/openshift/origin/pkg/authorization/api" |
| 9 | 10 |
) |
| 10 | 11 |
|
| 11 |
-// SubjectAccessReviewsNamespacer has methods to work with SubjectAccessReview resources in a namespace |
|
| 12 |
-type SubjectAccessReviewsNamespacer interface {
|
|
| 13 |
- SubjectAccessReviews(namespace string) SubjectAccessReviewInterface |
|
| 14 |
-} |
|
| 15 |
- |
|
| 16 | 12 |
type SubjectAccessReviewsImpersonator interface {
|
| 17 |
- ImpersonateSubjectAccessReviews(namespace, token string) SubjectAccessReviewInterface |
|
| 13 |
+ ImpersonateSubjectAccessReviews(token string) SubjectAccessReviewInterface |
|
| 18 | 14 |
} |
| 19 | 15 |
|
| 20 |
-// ClusterSubjectAccessReviews has methods to work with SubjectAccessReview resources in the cluster scope |
|
| 21 |
-type ClusterSubjectAccessReviews interface {
|
|
| 22 |
- ClusterSubjectAccessReviews() SubjectAccessReviewInterface |
|
| 16 |
+// SubjectAccessReviews has methods to work with SubjectAccessReview resources in the cluster scope |
|
| 17 |
+type SubjectAccessReviews interface {
|
|
| 18 |
+ SubjectAccessReviews() SubjectAccessReviewInterface |
|
| 23 | 19 |
} |
| 24 | 20 |
|
| 25 | 21 |
// SubjectAccessReviewInterface exposes methods on SubjectAccessReview resources. |
| ... | ... |
@@ -27,45 +23,56 @@ type SubjectAccessReviewInterface interface {
|
| 27 | 27 |
Create(policy *authorizationapi.SubjectAccessReview) (*authorizationapi.SubjectAccessReviewResponse, error) |
| 28 | 28 |
} |
| 29 | 29 |
|
| 30 |
-// subjectAccessReviews implements SubjectAccessReviewsNamespacer interface |
|
| 30 |
+// subjectAccessReviews implements SubjectAccessReviews interface |
|
| 31 | 31 |
type subjectAccessReviews struct {
|
| 32 | 32 |
r *Client |
| 33 |
- ns string |
|
| 34 | 33 |
token string |
| 35 | 34 |
} |
| 36 | 35 |
|
| 37 |
-// newSubjectAccessReviews returns a subjectAccessReviews |
|
| 38 |
-func newSubjectAccessReviews(c *Client, namespace, token string) *subjectAccessReviews {
|
|
| 36 |
+// newImpersonatingSubjectAccessReviews returns a subjectAccessReviews |
|
| 37 |
+func newImpersonatingSubjectAccessReviews(c *Client, token string) *subjectAccessReviews {
|
|
| 39 | 38 |
return &subjectAccessReviews{
|
| 40 | 39 |
r: c, |
| 41 |
- ns: namespace, |
|
| 42 | 40 |
token: token, |
| 43 | 41 |
} |
| 44 | 42 |
} |
| 45 | 43 |
|
| 46 |
-// Create creates new policy. Returns the server's representation of the policy and error if one occurs. |
|
| 47 |
-func (c *subjectAccessReviews) Create(policy *authorizationapi.SubjectAccessReview) (result *authorizationapi.SubjectAccessReviewResponse, err error) {
|
|
| 48 |
- result = &authorizationapi.SubjectAccessReviewResponse{}
|
|
| 49 |
- err = overrideAuth(c.token, c.r.Post().Namespace(c.ns).Resource("subjectAccessReviews")).Body(policy).Do().Into(result)
|
|
| 50 |
- return |
|
| 51 |
-} |
|
| 52 |
- |
|
| 53 |
-// clusterSubjectAccessReviews implements ClusterSubjectAccessReviews interface |
|
| 54 |
-type clusterSubjectAccessReviews struct {
|
|
| 55 |
- r *Client |
|
| 56 |
-} |
|
| 57 |
- |
|
| 58 |
-// newClusterSubjectAccessReviews returns a clusterSubjectAccessReviews |
|
| 59 |
-func newClusterSubjectAccessReviews(c *Client) *clusterSubjectAccessReviews {
|
|
| 60 |
- return &clusterSubjectAccessReviews{
|
|
| 44 |
+// newSubjectAccessReviews returns a subjectAccessReviews |
|
| 45 |
+func newSubjectAccessReviews(c *Client) *subjectAccessReviews {
|
|
| 46 |
+ return &subjectAccessReviews{
|
|
| 61 | 47 |
r: c, |
| 62 | 48 |
} |
| 63 | 49 |
} |
| 64 | 50 |
|
| 65 |
-// Create creates new policy. Returns the server's representation of the policy and error if one occurs. |
|
| 66 |
-func (c *clusterSubjectAccessReviews) Create(policy *authorizationapi.SubjectAccessReview) (result *authorizationapi.SubjectAccessReviewResponse, err error) {
|
|
| 51 |
+func (c *subjectAccessReviews) Create(sar *authorizationapi.SubjectAccessReview) (result *authorizationapi.SubjectAccessReviewResponse, err error) {
|
|
| 67 | 52 |
result = &authorizationapi.SubjectAccessReviewResponse{}
|
| 68 |
- err = c.r.Post().Resource("subjectAccessReviews").Body(policy).Do().Into(result)
|
|
| 53 |
+ |
|
| 54 |
+ // if this a cluster SAR, then no special handling |
|
| 55 |
+ if len(sar.Action.Namespace) == 0 {
|
|
| 56 |
+ err = overrideAuth(c.token, c.r.Post().Resource("subjectAccessReviews")).Body(sar).Do().Into(result)
|
|
| 57 |
+ return |
|
| 58 |
+ } |
|
| 59 |
+ |
|
| 60 |
+ err = c.r.Post().Resource("subjectAccessReviews").Body(sar).Do().Into(result)
|
|
| 61 |
+ |
|
| 62 |
+ // if the namespace values don't match then we definitely hit an old server. If we got a forbidden, then we might have hit an old server |
|
| 63 |
+ // and should try the old endpoint |
|
| 64 |
+ if (sar.Action.Namespace != result.Namespace) || kapierrors.IsForbidden(err) {
|
|
| 65 |
+ deprecatedResponse := &authorizationapi.SubjectAccessReviewResponse{}
|
|
| 66 |
+ deprecatedAttemptErr := overrideAuth(c.token, c.r.Post().Namespace(sar.Action.Namespace).Resource("subjectAccessReviews")).Body(sar).Do().Into(deprecatedResponse)
|
|
| 67 |
+ |
|
| 68 |
+ // if we definitely hit an old server, then return the error and result you get from the older server. |
|
| 69 |
+ if sar.Action.Namespace != result.Namespace {
|
|
| 70 |
+ return deprecatedResponse, deprecatedAttemptErr |
|
| 71 |
+ } |
|
| 72 |
+ |
|
| 73 |
+ // if we're not certain it was an old server, success overwrites the previous error, but failure doesn't overwrite the previous error |
|
| 74 |
+ if deprecatedAttemptErr == nil {
|
|
| 75 |
+ err = nil |
|
| 76 |
+ result = deprecatedResponse |
|
| 77 |
+ } |
|
| 78 |
+ } |
|
| 79 |
+ |
|
| 69 | 80 |
return |
| 70 | 81 |
} |
| 71 | 82 |
|
| ... | ... |
@@ -211,24 +211,24 @@ func (c *Fake) PolicyBindings(namespace string) client.PolicyBindingInterface {
|
| 211 | 211 |
return &FakePolicyBindings{Fake: c, Namespace: namespace}
|
| 212 | 212 |
} |
| 213 | 213 |
|
| 214 |
-// ResourceAccessReviews provides a fake REST client for ResourceAccessReviews |
|
| 215 |
-func (c *Fake) ResourceAccessReviews(namespace string) client.ResourceAccessReviewInterface {
|
|
| 216 |
- return &FakeResourceAccessReviews{Fake: c, Namespace: namespace}
|
|
| 214 |
+// LocalResourceAccessReviews provides a fake REST client for ResourceAccessReviews |
|
| 215 |
+func (c *Fake) LocalResourceAccessReviews(namespace string) client.LocalResourceAccessReviewInterface {
|
|
| 216 |
+ return &FakeLocalResourceAccessReviews{Fake: c}
|
|
| 217 | 217 |
} |
| 218 | 218 |
|
| 219 |
-// ClusterResourceAccessReviews provides a fake REST client for ClusterResourceAccessReviews |
|
| 220 |
-func (c *Fake) ClusterResourceAccessReviews() client.ResourceAccessReviewInterface {
|
|
| 219 |
+// ResourceAccessReviews provides a fake REST client for ClusterResourceAccessReviews |
|
| 220 |
+func (c *Fake) ResourceAccessReviews() client.ResourceAccessReviewInterface {
|
|
| 221 | 221 |
return &FakeClusterResourceAccessReviews{Fake: c}
|
| 222 | 222 |
} |
| 223 | 223 |
|
| 224 |
-// SubjectAccessReviews provides a fake REST client for SubjectAccessReviews |
|
| 225 |
-func (c *Fake) SubjectAccessReviews(namespace string) client.SubjectAccessReviewInterface {
|
|
| 226 |
- return &FakeSubjectAccessReviews{Fake: c, Namespace: namespace}
|
|
| 224 |
+// ImpersonateSubjectAccessReviews provides a fake REST client for SubjectAccessReviews |
|
| 225 |
+func (c *Fake) ImpersonateSubjectAccessReviews(token string) client.SubjectAccessReviewInterface {
|
|
| 226 |
+ return &FakeClusterSubjectAccessReviews{Fake: c}
|
|
| 227 | 227 |
} |
| 228 | 228 |
|
| 229 | 229 |
// ImpersonateSubjectAccessReviews provides a fake REST client for SubjectAccessReviews |
| 230 |
-func (c *Fake) ImpersonateSubjectAccessReviews(token, namespace string) client.SubjectAccessReviewInterface {
|
|
| 231 |
- return &FakeSubjectAccessReviews{Fake: c, Namespace: namespace}
|
|
| 230 |
+func (c *Fake) ImpersonateLocalSubjectAccessReviews(namespace, token string) client.LocalSubjectAccessReviewInterface {
|
|
| 231 |
+ return &FakeLocalSubjectAccessReviews{Fake: c, Namespace: namespace}
|
|
| 232 | 232 |
} |
| 233 | 233 |
|
| 234 | 234 |
// OAuthAccessTokens provides a fake REST client for OAuthAccessTokens |
| ... | ... |
@@ -236,8 +236,13 @@ func (c *Fake) OAuthAccessTokens() client.OAuthAccessTokenInterface {
|
| 236 | 236 |
return &FakeOAuthAccessTokens{Fake: c}
|
| 237 | 237 |
} |
| 238 | 238 |
|
| 239 |
-// ClusterSubjectAccessReviews provides a fake REST client for ClusterSubjectAccessReviews |
|
| 240 |
-func (c *Fake) ClusterSubjectAccessReviews() client.SubjectAccessReviewInterface {
|
|
| 239 |
+// LocalSubjectAccessReviews provides a fake REST client for SubjectAccessReviews |
|
| 240 |
+func (c *Fake) LocalSubjectAccessReviews(namespace string) client.LocalSubjectAccessReviewInterface {
|
|
| 241 |
+ return &FakeLocalSubjectAccessReviews{Fake: c}
|
|
| 242 |
+} |
|
| 243 |
+ |
|
| 244 |
+// SubjectAccessReviews provides a fake REST client for ClusterSubjectAccessReviews |
|
| 245 |
+func (c *Fake) SubjectAccessReviews() client.SubjectAccessReviewInterface {
|
|
| 241 | 246 |
return &FakeClusterSubjectAccessReviews{Fake: c}
|
| 242 | 247 |
} |
| 243 | 248 |
|
| 244 | 249 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,20 @@ |
| 0 |
+package testclient |
|
| 1 |
+ |
|
| 2 |
+import ( |
|
| 3 |
+ ktestclient "k8s.io/kubernetes/pkg/client/testclient" |
|
| 4 |
+ |
|
| 5 |
+ authorizationapi "github.com/openshift/origin/pkg/authorization/api" |
|
| 6 |
+) |
|
| 7 |
+ |
|
| 8 |
+type FakeLocalResourceAccessReviews struct {
|
|
| 9 |
+ Fake *Fake |
|
| 10 |
+ Namespace string |
|
| 11 |
+} |
|
| 12 |
+ |
|
| 13 |
+func (c *FakeLocalResourceAccessReviews) Create(inObj *authorizationapi.LocalResourceAccessReview) (*authorizationapi.ResourceAccessReviewResponse, error) {
|
|
| 14 |
+ obj, err := c.Fake.Invokes(ktestclient.NewCreateAction("localresourceaccessreviews", c.Namespace, inObj), &authorizationapi.ResourceAccessReviewResponse{})
|
|
| 15 |
+ if cast, ok := obj.(*authorizationapi.ResourceAccessReviewResponse); ok {
|
|
| 16 |
+ return cast, err |
|
| 17 |
+ } |
|
| 18 |
+ return nil, err |
|
| 19 |
+} |
| 0 | 20 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,20 @@ |
| 0 |
+package testclient |
|
| 1 |
+ |
|
| 2 |
+import ( |
|
| 3 |
+ ktestclient "k8s.io/kubernetes/pkg/client/testclient" |
|
| 4 |
+ |
|
| 5 |
+ authorizationapi "github.com/openshift/origin/pkg/authorization/api" |
|
| 6 |
+) |
|
| 7 |
+ |
|
| 8 |
+type FakeLocalSubjectAccessReviews struct {
|
|
| 9 |
+ Fake *Fake |
|
| 10 |
+ Namespace string |
|
| 11 |
+} |
|
| 12 |
+ |
|
| 13 |
+func (c *FakeLocalSubjectAccessReviews) Create(inObj *authorizationapi.LocalSubjectAccessReview) (*authorizationapi.SubjectAccessReviewResponse, error) {
|
|
| 14 |
+ obj, err := c.Fake.Invokes(ktestclient.NewCreateAction("localsubjectaccessreviews", c.Namespace, inObj), &authorizationapi.SubjectAccessReviewResponse{})
|
|
| 15 |
+ if cast, ok := obj.(*authorizationapi.SubjectAccessReviewResponse); ok {
|
|
| 16 |
+ return cast, err |
|
| 17 |
+ } |
|
| 18 |
+ return nil, err |
|
| 19 |
+} |
| ... | ... |
@@ -6,31 +6,14 @@ import ( |
| 6 | 6 |
authorizationapi "github.com/openshift/origin/pkg/authorization/api" |
| 7 | 7 |
) |
| 8 | 8 |
|
| 9 |
-// FakeResourceAccessReviews implements ResourceAccessReviewInterface. Meant to be embedded into a struct to get a default |
|
| 10 |
-// implementation. This makes faking out just the methods you want to test easier. |
|
| 11 |
-type FakeResourceAccessReviews struct {
|
|
| 12 |
- Fake *Fake |
|
| 13 |
- Namespace string |
|
| 14 |
-} |
|
| 15 |
- |
|
| 16 |
-func (c *FakeResourceAccessReviews) Create(inObj *authorizationapi.ResourceAccessReview) (*authorizationapi.ResourceAccessReviewResponse, error) {
|
|
| 17 |
- obj, err := c.Fake.Invokes(ktestclient.NewCreateAction("resourceaccessreviews", c.Namespace, inObj), inObj)
|
|
| 18 |
- if obj == nil {
|
|
| 19 |
- return nil, err |
|
| 20 |
- } |
|
| 21 |
- |
|
| 22 |
- return obj.(*authorizationapi.ResourceAccessReviewResponse), err |
|
| 23 |
-} |
|
| 24 |
- |
|
| 25 | 9 |
type FakeClusterResourceAccessReviews struct {
|
| 26 | 10 |
Fake *Fake |
| 27 | 11 |
} |
| 28 | 12 |
|
| 29 | 13 |
func (c *FakeClusterResourceAccessReviews) Create(inObj *authorizationapi.ResourceAccessReview) (*authorizationapi.ResourceAccessReviewResponse, error) {
|
| 30 |
- obj, err := c.Fake.Invokes(ktestclient.NewRootCreateAction("resourceaccessreviews", inObj), inObj)
|
|
| 31 |
- if obj == nil {
|
|
| 32 |
- return nil, err |
|
| 14 |
+ obj, err := c.Fake.Invokes(ktestclient.NewRootCreateAction("resourceaccessreviews", inObj), &authorizationapi.ResourceAccessReviewResponse{})
|
|
| 15 |
+ if cast, ok := obj.(*authorizationapi.ResourceAccessReviewResponse); ok {
|
|
| 16 |
+ return cast, err |
|
| 33 | 17 |
} |
| 34 |
- |
|
| 35 |
- return obj.(*authorizationapi.ResourceAccessReviewResponse), err |
|
| 18 |
+ return nil, err |
|
| 36 | 19 |
} |
| ... | ... |
@@ -6,22 +6,6 @@ import ( |
| 6 | 6 |
authorizationapi "github.com/openshift/origin/pkg/authorization/api" |
| 7 | 7 |
) |
| 8 | 8 |
|
| 9 |
-// FakeSubjectAccessReviews implements SubjectAccessReviewInterface. Meant to be embedded into a struct to get a default |
|
| 10 |
-// implementation. This makes faking out just the methods you want to test easier. |
|
| 11 |
-type FakeSubjectAccessReviews struct {
|
|
| 12 |
- Fake *Fake |
|
| 13 |
- Namespace string |
|
| 14 |
-} |
|
| 15 |
- |
|
| 16 |
-func (c *FakeSubjectAccessReviews) Create(inObj *authorizationapi.SubjectAccessReview) (*authorizationapi.SubjectAccessReviewResponse, error) {
|
|
| 17 |
- obj, err := c.Fake.Invokes(ktestclient.NewCreateAction("subjectaccessreviews", c.Namespace, inObj), inObj)
|
|
| 18 |
- if obj == nil {
|
|
| 19 |
- return nil, err |
|
| 20 |
- } |
|
| 21 |
- |
|
| 22 |
- return obj.(*authorizationapi.SubjectAccessReviewResponse), err |
|
| 23 |
-} |
|
| 24 |
- |
|
| 25 | 9 |
// FakeClusterSubjectAccessReviews implements the ClusterSubjectAccessReviews interface. |
| 26 | 10 |
// Meant to be embedded into a struct to get a default implementation. |
| 27 | 11 |
// This makes faking out just the methods you want to test easier. |
| ... | ... |
@@ -30,10 +14,9 @@ type FakeClusterSubjectAccessReviews struct {
|
| 30 | 30 |
} |
| 31 | 31 |
|
| 32 | 32 |
func (c *FakeClusterSubjectAccessReviews) Create(inObj *authorizationapi.SubjectAccessReview) (*authorizationapi.SubjectAccessReviewResponse, error) {
|
| 33 |
- obj, err := c.Fake.Invokes(ktestclient.NewRootCreateAction("subjectaccessreviews", inObj), inObj)
|
|
| 34 |
- if obj == nil {
|
|
| 35 |
- return nil, err |
|
| 33 |
+ obj, err := c.Fake.Invokes(ktestclient.NewRootCreateAction("subjectaccessreviews", inObj), &authorizationapi.SubjectAccessReviewResponse{})
|
|
| 34 |
+ if cast, ok := obj.(*authorizationapi.SubjectAccessReviewResponse); ok {
|
|
| 35 |
+ return cast, err |
|
| 36 | 36 |
} |
| 37 |
- |
|
| 38 |
- return obj.(*authorizationapi.SubjectAccessReviewResponse), err |
|
| 37 |
+ return nil, err |
|
| 39 | 38 |
} |
| ... | ... |
@@ -21,7 +21,7 @@ const WhoCanRecommendedName = "who-can" |
| 21 | 21 |
type whoCanOptions struct {
|
| 22 | 22 |
allNamespaces bool |
| 23 | 23 |
bindingNamespace string |
| 24 |
- client client.Interface |
|
| 24 |
+ client *client.Client |
|
| 25 | 25 |
|
| 26 | 26 |
verb string |
| 27 | 27 |
resource string |
| ... | ... |
@@ -68,18 +68,19 @@ func (o *whoCanOptions) complete(args []string) error {
|
| 68 | 68 |
} |
| 69 | 69 |
|
| 70 | 70 |
func (o *whoCanOptions) run() error {
|
| 71 |
- resourceAccessReview := &authorizationapi.ResourceAccessReview{}
|
|
| 72 |
- resourceAccessReview.Resource = o.resource |
|
| 73 |
- resourceAccessReview.Verb = o.verb |
|
| 71 |
+ authorizationAttributes := authorizationapi.AuthorizationAttributes{
|
|
| 72 |
+ Resource: o.resource, |
|
| 73 |
+ Verb: o.verb, |
|
| 74 |
+ } |
|
| 74 | 75 |
|
| 75 |
- var reviewInterface client.ResourceAccessReviewInterface |
|
| 76 |
+ resourceAccessReviewResponse := &authorizationapi.ResourceAccessReviewResponse{}
|
|
| 77 |
+ var err error |
|
| 76 | 78 |
if o.allNamespaces {
|
| 77 |
- reviewInterface = o.client.ClusterResourceAccessReviews() |
|
| 79 |
+ resourceAccessReviewResponse, err = o.client.ResourceAccessReviews().Create(&authorizationapi.ResourceAccessReview{Action: authorizationAttributes})
|
|
| 78 | 80 |
} else {
|
| 79 |
- reviewInterface = o.client.ResourceAccessReviews(o.bindingNamespace) |
|
| 81 |
+ resourceAccessReviewResponse, err = o.client.LocalResourceAccessReviews(o.bindingNamespace).Create(&authorizationapi.LocalResourceAccessReview{Action: authorizationAttributes})
|
|
| 80 | 82 |
} |
| 81 | 83 |
|
| 82 |
- resourceAccessReviewResponse, err := reviewInterface.Create(resourceAccessReview) |
|
| 83 | 84 |
if err != nil {
|
| 84 | 85 |
return err |
| 85 | 86 |
} |
| ... | ... |
@@ -21,7 +21,7 @@ The client stores configuration in the current user's home directory (under the |
| 21 | 21 |
config). When you login the first time, a new config file is created, and subsequent project changes with the |
| 22 | 22 |
'project' command will set the current context. These subcommands allow you to manage the config directly. |
| 23 | 23 |
|
| 24 |
-Reference: https://github.com/GoogleCloudPlatform/kubernetes/blob/master/docs/kubeconfig-file.md` |
|
| 24 |
+Reference: https://k8s.io/kubernetes/blob/master/docs/kubeconfig-file.md` |
|
| 25 | 25 |
|
| 26 | 26 |
configExample = ` // Change the config context to use |
| 27 | 27 |
%[1]s %[2]s use-context my-context |
| ... | ... |
@@ -45,6 +45,14 @@ var DescriberCoverageExceptions = []reflect.Type{
|
| 45 | 45 |
reflect.TypeOf(&deployapi.DeploymentConfigRollback{}), // normal users don't ever look at these
|
| 46 | 46 |
reflect.TypeOf(&projectapi.ProjectRequest{}), // normal users don't ever look at these
|
| 47 | 47 |
reflect.TypeOf(&authorizationapi.IsPersonalSubjectAccessReview{}), // not a top level resource
|
| 48 |
+ |
|
| 49 |
+ // these resources can't be "GET"ed, so you can't make a describer for them |
|
| 50 |
+ reflect.TypeOf(&authorizationapi.SubjectAccessReviewResponse{}),
|
|
| 51 |
+ reflect.TypeOf(&authorizationapi.ResourceAccessReviewResponse{}),
|
|
| 52 |
+ reflect.TypeOf(&authorizationapi.SubjectAccessReview{}),
|
|
| 53 |
+ reflect.TypeOf(&authorizationapi.ResourceAccessReview{}),
|
|
| 54 |
+ reflect.TypeOf(&authorizationapi.LocalSubjectAccessReview{}),
|
|
| 55 |
+ reflect.TypeOf(&authorizationapi.LocalResourceAccessReview{}),
|
|
| 48 | 56 |
} |
| 49 | 57 |
|
| 50 | 58 |
// MissingDescriberCoverageExceptions is the list of types that were missing describer methods when I started |
| ... | ... |
@@ -52,10 +60,6 @@ var DescriberCoverageExceptions = []reflect.Type{
|
| 52 | 52 |
// TODO describers should be added for these types |
| 53 | 53 |
var MissingDescriberCoverageExceptions = []reflect.Type{
|
| 54 | 54 |
reflect.TypeOf(&imageapi.ImageStreamMapping{}),
|
| 55 |
- reflect.TypeOf(&authorizationapi.SubjectAccessReviewResponse{}),
|
|
| 56 |
- reflect.TypeOf(&authorizationapi.ResourceAccessReviewResponse{}),
|
|
| 57 |
- reflect.TypeOf(&authorizationapi.SubjectAccessReview{}),
|
|
| 58 |
- reflect.TypeOf(&authorizationapi.ResourceAccessReview{}),
|
|
| 59 | 55 |
reflect.TypeOf(&oauthapi.OAuthClient{}),
|
| 60 | 56 |
reflect.TypeOf(&sdnapi.ClusterNetwork{}),
|
| 61 | 57 |
reflect.TypeOf(&sdnapi.HostSubnet{}),
|
| ... | ... |
@@ -22,16 +22,20 @@ import ( |
| 22 | 22 |
var PrinterCoverageExceptions = []reflect.Type{
|
| 23 | 23 |
reflect.TypeOf(&imageapi.DockerImage{}), // not a top level resource
|
| 24 | 24 |
reflect.TypeOf(&buildapi.BuildLog{}), // just a marker type
|
| 25 |
+ |
|
| 26 |
+ // these resources can't be "GET"ed, so we probably don't need a printer for them |
|
| 27 |
+ reflect.TypeOf(&authorizationapi.SubjectAccessReviewResponse{}),
|
|
| 28 |
+ reflect.TypeOf(&authorizationapi.ResourceAccessReviewResponse{}),
|
|
| 29 |
+ reflect.TypeOf(&authorizationapi.SubjectAccessReview{}),
|
|
| 30 |
+ reflect.TypeOf(&authorizationapi.ResourceAccessReview{}),
|
|
| 31 |
+ reflect.TypeOf(&authorizationapi.LocalSubjectAccessReview{}),
|
|
| 32 |
+ reflect.TypeOf(&authorizationapi.LocalResourceAccessReview{}),
|
|
| 25 | 33 |
} |
| 26 | 34 |
|
| 27 | 35 |
// MissingPrinterCoverageExceptions is the list of types that were missing printer methods when I started |
| 28 | 36 |
// You should never add to this list |
| 29 | 37 |
// TODO printers should be added for these types |
| 30 | 38 |
var MissingPrinterCoverageExceptions = []reflect.Type{
|
| 31 |
- reflect.TypeOf(&authorizationapi.SubjectAccessReviewResponse{}),
|
|
| 32 |
- reflect.TypeOf(&authorizationapi.ResourceAccessReviewResponse{}),
|
|
| 33 |
- reflect.TypeOf(&authorizationapi.SubjectAccessReview{}),
|
|
| 34 |
- reflect.TypeOf(&authorizationapi.ResourceAccessReview{}),
|
|
| 35 | 39 |
reflect.TypeOf(&deployapi.DeploymentConfigRollback{}),
|
| 36 | 40 |
reflect.TypeOf(&imageapi.ImageStreamMapping{}),
|
| 37 | 41 |
reflect.TypeOf(&buildapi.BuildLogOptions{}),
|
| ... | ... |
@@ -125,7 +125,7 @@ func GetBootstrapClusterRoles() []authorizationapi.ClusterRole {
|
| 125 | 125 |
{Verbs: util.NewStringSet("list"), Resources: util.NewStringSet("projectrequests")},
|
| 126 | 126 |
{Verbs: util.NewStringSet("list", "get"), Resources: util.NewStringSet("clusterroles")},
|
| 127 | 127 |
{Verbs: util.NewStringSet("list"), Resources: util.NewStringSet("projects")},
|
| 128 |
- {Verbs: util.NewStringSet("create"), Resources: util.NewStringSet("subjectaccessreviews"), AttributeRestrictions: runtime.EmbeddedObject{Object: &authorizationapi.IsPersonalSubjectAccessReview{}}},
|
|
| 128 |
+ {Verbs: util.NewStringSet("create"), Resources: util.NewStringSet("subjectaccessreviews", "localsubjectaccessreviews"), AttributeRestrictions: runtime.EmbeddedObject{Object: &authorizationapi.IsPersonalSubjectAccessReview{}}},
|
|
| 129 | 129 |
}, |
| 130 | 130 |
}, |
| 131 | 131 |
{
|
| ... | ... |
@@ -77,11 +77,13 @@ import ( |
| 77 | 77 |
clusterpolicybindingstorage "github.com/openshift/origin/pkg/authorization/registry/clusterpolicybinding/etcd" |
| 78 | 78 |
clusterrolestorage "github.com/openshift/origin/pkg/authorization/registry/clusterrole/proxy" |
| 79 | 79 |
clusterrolebindingstorage "github.com/openshift/origin/pkg/authorization/registry/clusterrolebinding/proxy" |
| 80 |
+ "github.com/openshift/origin/pkg/authorization/registry/localresourceaccessreview" |
|
| 81 |
+ "github.com/openshift/origin/pkg/authorization/registry/localsubjectaccessreview" |
|
| 80 | 82 |
policyregistry "github.com/openshift/origin/pkg/authorization/registry/policy" |
| 81 | 83 |
policyetcd "github.com/openshift/origin/pkg/authorization/registry/policy/etcd" |
| 82 | 84 |
policybindingregistry "github.com/openshift/origin/pkg/authorization/registry/policybinding" |
| 83 | 85 |
policybindingetcd "github.com/openshift/origin/pkg/authorization/registry/policybinding/etcd" |
| 84 |
- resourceaccessreviewregistry "github.com/openshift/origin/pkg/authorization/registry/resourceaccessreview" |
|
| 86 |
+ "github.com/openshift/origin/pkg/authorization/registry/resourceaccessreview" |
|
| 85 | 87 |
rolestorage "github.com/openshift/origin/pkg/authorization/registry/role/policybased" |
| 86 | 88 |
rolebindingstorage "github.com/openshift/origin/pkg/authorization/registry/rolebinding/policybased" |
| 87 | 89 |
"github.com/openshift/origin/pkg/authorization/registry/subjectaccessreview" |
| ... | ... |
@@ -359,6 +361,10 @@ func (c *MasterConfig) GetRestStorage() map[string]rest.Storage {
|
| 359 | 359 |
|
| 360 | 360 |
subjectAccessReviewStorage := subjectaccessreview.NewREST(c.Authorizer) |
| 361 | 361 |
subjectAccessReviewRegistry := subjectaccessreview.NewRegistry(subjectAccessReviewStorage) |
| 362 |
+ localSubjectAccessReviewStorage := localsubjectaccessreview.NewREST(subjectAccessReviewRegistry) |
|
| 363 |
+ resourceAccessReviewStorage := resourceaccessreview.NewREST(c.Authorizer) |
|
| 364 |
+ resourceAccessReviewRegistry := resourceaccessreview.NewRegistry(resourceAccessReviewStorage) |
|
| 365 |
+ localResourceAccessReviewStorage := localresourceaccessreview.NewREST(resourceAccessReviewRegistry) |
|
| 362 | 366 |
|
| 363 | 367 |
imageStorage := imageetcd.NewREST(c.EtcdHelper) |
| 364 | 368 |
imageRegistry := image.NewRegistry(imageStorage) |
| ... | ... |
@@ -455,8 +461,10 @@ func (c *MasterConfig) GetRestStorage() map[string]rest.Storage {
|
| 455 | 455 |
"oAuthClients": clientetcd.NewREST(c.EtcdHelper), |
| 456 | 456 |
"oAuthClientAuthorizations": clientauthetcd.NewREST(c.EtcdHelper), |
| 457 | 457 |
|
| 458 |
- "resourceAccessReviews": resourceaccessreviewregistry.NewREST(c.Authorizer), |
|
| 459 |
- "subjectAccessReviews": subjectAccessReviewStorage, |
|
| 458 |
+ "resourceAccessReviews": resourceAccessReviewStorage, |
|
| 459 |
+ "subjectAccessReviews": subjectAccessReviewStorage, |
|
| 460 |
+ "localSubjectAccessReviews": localSubjectAccessReviewStorage, |
|
| 461 |
+ "localResourceAccessReviews": localResourceAccessReviewStorage, |
|
| 460 | 462 |
|
| 461 | 463 |
"policies": policyStorage, |
| 462 | 464 |
"policyBindings": policyBindingStorage, |
| ... | ... |
@@ -87,7 +87,7 @@ func validateDeploymentStrategy(strategy *deployapi.DeploymentStrategy) fielderr |
| 87 | 87 |
} |
| 88 | 88 |
} |
| 89 | 89 |
|
| 90 |
- // TODO: validate resource requirements (prereq: https://github.com/GoogleCloudPlatform/kubernetes/pull/7059) |
|
| 90 |
+ // TODO: validate resource requirements (prereq: https://k8s.io/kubernetes/pull/7059) |
|
| 91 | 91 |
|
| 92 | 92 |
return errs |
| 93 | 93 |
} |
| ... | ... |
@@ -20,7 +20,7 @@ import ( |
| 20 | 20 |
) |
| 21 | 21 |
|
| 22 | 22 |
// TODO: This should perhaps be made public upstream. See: |
| 23 |
-// https://github.com/GoogleCloudPlatform/kubernetes/issues/7851 |
|
| 23 |
+// https://k8s.io/kubernetes/issues/7851 |
|
| 24 | 24 |
const sourceIdAnnotation = "kubectl.kubernetes.io/update-source-id" |
| 25 | 25 |
|
| 26 | 26 |
// RollingDeploymentStrategy is a Strategy which implements rolling |
| ... | ... |
@@ -35,8 +35,8 @@ const sourceIdAnnotation = "kubectl.kubernetes.io/update-source-id" |
| 35 | 35 |
// These caveats can be resolved with future upstream refactorings to |
| 36 | 36 |
// RollingUpdater[1][2]. |
| 37 | 37 |
// |
| 38 |
-// [1] https://github.com/GoogleCloudPlatform/kubernetes/pull/7183 |
|
| 39 |
-// [2] https://github.com/GoogleCloudPlatform/kubernetes/issues/7851 |
|
| 38 |
+// [1] https://k8s.io/kubernetes/pull/7183 |
|
| 39 |
+// [2] https://k8s.io/kubernetes/issues/7851 |
|
| 40 | 40 |
type RollingDeploymentStrategy struct {
|
| 41 | 41 |
// initialStrategy is used when there are no prior deployments. |
| 42 | 42 |
initialStrategy acceptingDeploymentStrategy |
| ... | ... |
@@ -56,7 +56,7 @@ type RollingDeploymentStrategy struct {
|
| 56 | 56 |
// acceptingDeploymentStrategy is a DeploymentStrategy which accepts an |
| 57 | 57 |
// injected UpdateAcceptor as part of the deploy function. This is a hack to |
| 58 | 58 |
// support using the Recreate strategy for initial deployments and should be |
| 59 |
-// removed when https://github.com/GoogleCloudPlatform/kubernetes/pull/7183 is |
|
| 59 |
+// removed when https://k8s.io/kubernetes/pull/7183 is |
|
| 60 | 60 |
// fixed. |
| 61 | 61 |
type acceptingDeploymentStrategy interface {
|
| 62 | 62 |
DeployWithAcceptor(from *kapi.ReplicationController, to *kapi.ReplicationController, desiredReplicas int, updateAcceptor kubectl.UpdateAcceptor) error |
| ... | ... |
@@ -174,7 +174,7 @@ func (s *RollingDeploymentStrategy) Deploy(from *kapi.ReplicationController, to |
| 174 | 174 |
// unless it already exists on the deployment. |
| 175 | 175 |
// |
| 176 | 176 |
// Related upstream issue: |
| 177 |
- // https://github.com/GoogleCloudPlatform/kubernetes/pull/7183 |
|
| 177 |
+ // https://k8s.io/kubernetes/pull/7183 |
|
| 178 | 178 |
to, err = s.client.GetReplicationController(to.Namespace, to.Name) |
| 179 | 179 |
if err != nil {
|
| 180 | 180 |
return fmt.Errorf("couldn't look up deployment %s: %s", deployutil.LabelForDeployment(to), err)
|
| ... | ... |
@@ -194,7 +194,7 @@ func (s *RollingDeploymentStrategy) Deploy(from *kapi.ReplicationController, to |
| 194 | 194 |
// on the RC. For now, fake it out by just setting replicas to 1. |
| 195 | 195 |
// |
| 196 | 196 |
// Related upstream issue: |
| 197 |
- // https://github.com/GoogleCloudPlatform/kubernetes/pull/7183 |
|
| 197 |
+ // https://k8s.io/kubernetes/pull/7183 |
|
| 198 | 198 |
to.Spec.Replicas = 1 |
| 199 | 199 |
|
| 200 | 200 |
// Perform a rolling update. |
| ... | ... |
@@ -250,12 +250,15 @@ func verifyOpenShiftUser(client *client.Client) error {
|
| 250 | 250 |
} |
| 251 | 251 |
|
| 252 | 252 |
func verifyImageStreamAccess(namespace, imageRepo, verb string, client *client.Client) error {
|
| 253 |
- sar := authorizationapi.SubjectAccessReview{
|
|
| 254 |
- Verb: verb, |
|
| 255 |
- Resource: "imagestreams/layers", |
|
| 256 |
- ResourceName: imageRepo, |
|
| 253 |
+ sar := authorizationapi.LocalSubjectAccessReview{
|
|
| 254 |
+ Action: authorizationapi.AuthorizationAttributes{
|
|
| 255 |
+ Verb: verb, |
|
| 256 |
+ Resource: "imagestreams/layers", |
|
| 257 |
+ ResourceName: imageRepo, |
|
| 258 |
+ }, |
|
| 257 | 259 |
} |
| 258 |
- response, err := client.SubjectAccessReviews(namespace).Create(&sar) |
|
| 260 |
+ response, err := client.LocalSubjectAccessReviews(namespace).Create(&sar) |
|
| 261 |
+ |
|
| 259 | 262 |
if err != nil {
|
| 260 | 263 |
log.Errorf("OpenShift client error: %s", err)
|
| 261 | 264 |
if kerrors.IsUnauthorized(err) || kerrors.IsForbidden(err) {
|
| ... | ... |
@@ -263,19 +266,23 @@ func verifyImageStreamAccess(namespace, imageRepo, verb string, client *client.C |
| 263 | 263 |
} |
| 264 | 264 |
return err |
| 265 | 265 |
} |
| 266 |
+ |
|
| 266 | 267 |
if !response.Allowed {
|
| 267 | 268 |
log.Errorf("OpenShift access denied: %s", response.Reason)
|
| 268 | 269 |
return ErrOpenShiftAccessDenied |
| 269 | 270 |
} |
| 271 |
+ |
|
| 270 | 272 |
return nil |
| 271 | 273 |
} |
| 272 | 274 |
|
| 273 | 275 |
func verifyPruneAccess(client *client.Client) error {
|
| 274 | 276 |
sar := authorizationapi.SubjectAccessReview{
|
| 275 |
- Verb: "delete", |
|
| 276 |
- Resource: "images", |
|
| 277 |
+ Action: authorizationapi.AuthorizationAttributes{
|
|
| 278 |
+ Verb: "delete", |
|
| 279 |
+ Resource: "images", |
|
| 280 |
+ }, |
|
| 277 | 281 |
} |
| 278 |
- response, err := client.ClusterSubjectAccessReviews().Create(&sar) |
|
| 282 |
+ response, err := client.SubjectAccessReviews().Create(&sar) |
|
| 279 | 283 |
if err != nil {
|
| 280 | 284 |
log.Errorf("OpenShift client error: %s", err)
|
| 281 | 285 |
if kerrors.IsUnauthorized(err) || kerrors.IsForbidden(err) {
|
| ... | ... |
@@ -174,9 +174,9 @@ func TestAccessController(t *testing.T) {
|
| 174 | 174 |
openshiftResponses: []response{
|
| 175 | 175 |
{500, "Uh oh"},
|
| 176 | 176 |
}, |
| 177 |
- expectedError: errors.New("an error on the server has prevented the request from succeeding (post subjectAccessReviews)"),
|
|
| 177 |
+ expectedError: errors.New("an error on the server has prevented the request from succeeding (post localSubjectAccessReviews)"),
|
|
| 178 | 178 |
expectedChallenge: false, |
| 179 |
- expectedActions: []string{"POST /oapi/v1/namespaces/foo/subjectaccessreviews"},
|
|
| 179 |
+ expectedActions: []string{"POST /oapi/v1/namespaces/foo/localsubjectaccessreviews"},
|
|
| 180 | 180 |
}, |
| 181 | 181 |
"valid openshift token but token not scoped for the given repo operation": {
|
| 182 | 182 |
access: []auth.Access{{
|
| ... | ... |
@@ -192,7 +192,7 @@ func TestAccessController(t *testing.T) {
|
| 192 | 192 |
}, |
| 193 | 193 |
expectedError: ErrOpenShiftAccessDenied, |
| 194 | 194 |
expectedChallenge: true, |
| 195 |
- expectedActions: []string{"POST /oapi/v1/namespaces/foo/subjectaccessreviews"},
|
|
| 195 |
+ expectedActions: []string{"POST /oapi/v1/namespaces/foo/localsubjectaccessreviews"},
|
|
| 196 | 196 |
}, |
| 197 | 197 |
"partially valid openshift token": {
|
| 198 | 198 |
// Check all the different resource-type/verb combinations we allow to make sure they validate and continue to validate remaining Resource requests |
| ... | ... |
@@ -212,10 +212,10 @@ func TestAccessController(t *testing.T) {
|
| 212 | 212 |
expectedError: ErrOpenShiftAccessDenied, |
| 213 | 213 |
expectedChallenge: true, |
| 214 | 214 |
expectedActions: []string{
|
| 215 |
- "POST /oapi/v1/namespaces/foo/subjectaccessreviews", |
|
| 216 |
- "POST /oapi/v1/namespaces/bar/subjectaccessreviews", |
|
| 215 |
+ "POST /oapi/v1/namespaces/foo/localsubjectaccessreviews", |
|
| 216 |
+ "POST /oapi/v1/namespaces/bar/localsubjectaccessreviews", |
|
| 217 | 217 |
"POST /oapi/v1/subjectaccessreviews", |
| 218 |
- "POST /oapi/v1/namespaces/baz/subjectaccessreviews", |
|
| 218 |
+ "POST /oapi/v1/namespaces/baz/localsubjectaccessreviews", |
|
| 219 | 219 |
}, |
| 220 | 220 |
}, |
| 221 | 221 |
"valid openshift token": {
|
| ... | ... |
@@ -232,7 +232,7 @@ func TestAccessController(t *testing.T) {
|
| 232 | 232 |
}, |
| 233 | 233 |
expectedError: nil, |
| 234 | 234 |
expectedChallenge: false, |
| 235 |
- expectedActions: []string{"POST /oapi/v1/namespaces/foo/subjectaccessreviews"},
|
|
| 235 |
+ expectedActions: []string{"POST /oapi/v1/namespaces/foo/localsubjectaccessreviews"},
|
|
| 236 | 236 |
}, |
| 237 | 237 |
"pruning": {
|
| 238 | 238 |
access: []auth.Access{
|
| ... | ... |
@@ -200,17 +200,19 @@ func NewEnviromentConfig() (*Config, error) {
|
| 200 | 200 |
if !info.Push && allowAnonymousGet {
|
| 201 | 201 |
return true, nil |
| 202 | 202 |
} |
| 203 |
- req := &authapi.SubjectAccessReview{
|
|
| 204 |
- Verb: "get", |
|
| 205 |
- Resource: "pods", |
|
| 203 |
+ req := &authapi.LocalSubjectAccessReview{
|
|
| 204 |
+ Action: authapi.AuthorizationAttributes{
|
|
| 205 |
+ Verb: "get", |
|
| 206 |
+ Resource: "pods", |
|
| 207 |
+ }, |
|
| 206 | 208 |
} |
| 207 | 209 |
if info.Push {
|
| 208 | 210 |
if !config.AllowPush {
|
| 209 | 211 |
return false, nil |
| 210 | 212 |
} |
| 211 |
- req.Verb = "create" |
|
| 213 |
+ req.Action.Verb = "create" |
|
| 212 | 214 |
} |
| 213 |
- res, err := osc.ImpersonateSubjectAccessReviews(namespace, info.Password).Create(req) |
|
| 215 |
+ res, err := osc.ImpersonateLocalSubjectAccessReviews(namespace, info.Password).Create(req) |
|
| 214 | 216 |
if err != nil {
|
| 215 | 217 |
return false, err |
| 216 | 218 |
} |
| ... | ... |
@@ -309,11 +309,13 @@ func (v *TagVerifier) Verify(old, stream *api.ImageStream, user user.Info) field |
| 309 | 309 |
} |
| 310 | 310 |
|
| 311 | 311 |
subjectAccessReview := authorizationapi.SubjectAccessReview{
|
| 312 |
- Verb: "get", |
|
| 313 |
- Resource: "imagestreams", |
|
| 314 |
- User: user.GetName(), |
|
| 315 |
- Groups: util.NewStringSet(user.GetGroups()...), |
|
| 316 |
- ResourceName: streamName, |
|
| 312 |
+ Action: authorizationapi.AuthorizationAttributes{
|
|
| 313 |
+ Verb: "get", |
|
| 314 |
+ Resource: "imagestreams", |
|
| 315 |
+ ResourceName: streamName, |
|
| 316 |
+ }, |
|
| 317 |
+ User: user.GetName(), |
|
| 318 |
+ Groups: util.NewStringSet(user.GetGroups()...), |
|
| 317 | 319 |
} |
| 318 | 320 |
ctx := kapi.WithNamespace(kapi.NewContext(), tagRef.From.Namespace) |
| 319 | 321 |
glog.V(1).Infof("Performing SubjectAccessReview for user=%s, groups=%v to %s/%s", user.GetName(), user.GetGroups(), tagRef.From.Namespace, streamName)
|
| ... | ... |
@@ -282,11 +282,13 @@ func TestTagVerifier(t *testing.T) {
|
| 282 | 282 |
t.Errorf("%s: sar namespace: expected %v, got %v", name, e, a)
|
| 283 | 283 |
} |
| 284 | 284 |
expectedSar := &authorizationapi.SubjectAccessReview{
|
| 285 |
- Verb: "get", |
|
| 286 |
- Resource: "imagestreams", |
|
| 287 |
- User: "user", |
|
| 288 |
- Groups: util.NewStringSet("group1"),
|
|
| 289 |
- ResourceName: "otherstream", |
|
| 285 |
+ Action: authorizationapi.AuthorizationAttributes{
|
|
| 286 |
+ Verb: "get", |
|
| 287 |
+ Resource: "imagestreams", |
|
| 288 |
+ ResourceName: "otherstream", |
|
| 289 |
+ }, |
|
| 290 |
+ User: "user", |
|
| 291 |
+ Groups: util.NewStringSet("group1"),
|
|
| 290 | 292 |
} |
| 291 | 293 |
if e, a := expectedSar, sar.request; !reflect.DeepEqual(e, a) {
|
| 292 | 294 |
t.Errorf("%s: unexpected SAR request: %s", name, util.ObjectDiff(e, a))
|
| ... | ... |
@@ -32,11 +32,11 @@ type Reviewer interface {
|
| 32 | 32 |
|
| 33 | 33 |
// reviewer performs access reviews for a project by name |
| 34 | 34 |
type reviewer struct {
|
| 35 |
- resourceAccessReviewsNamespacer client.ResourceAccessReviewsNamespacer |
|
| 35 |
+ resourceAccessReviewsNamespacer client.LocalResourceAccessReviewsNamespacer |
|
| 36 | 36 |
} |
| 37 | 37 |
|
| 38 | 38 |
// NewReviewer knows how to make access control reviews for a resource by name |
| 39 |
-func NewReviewer(resourceAccessReviewsNamespacer client.ResourceAccessReviewsNamespacer) Reviewer {
|
|
| 39 |
+func NewReviewer(resourceAccessReviewsNamespacer client.LocalResourceAccessReviewsNamespacer) Reviewer {
|
|
| 40 | 40 |
return &reviewer{
|
| 41 | 41 |
resourceAccessReviewsNamespacer: resourceAccessReviewsNamespacer, |
| 42 | 42 |
} |
| ... | ... |
@@ -44,13 +44,15 @@ func NewReviewer(resourceAccessReviewsNamespacer client.ResourceAccessReviewsNam |
| 44 | 44 |
|
| 45 | 45 |
// Review performs a resource access review for the given resource by name |
| 46 | 46 |
func (r *reviewer) Review(name string) (Review, error) {
|
| 47 |
- resourceAccessReview := &authorizationapi.ResourceAccessReview{
|
|
| 48 |
- Verb: "get", |
|
| 49 |
- Resource: "namespaces", |
|
| 50 |
- ResourceName: name, |
|
| 47 |
+ resourceAccessReview := &authorizationapi.LocalResourceAccessReview{
|
|
| 48 |
+ Action: authorizationapi.AuthorizationAttributes{
|
|
| 49 |
+ Verb: "get", |
|
| 50 |
+ Resource: "namespaces", |
|
| 51 |
+ ResourceName: name, |
|
| 52 |
+ }, |
|
| 51 | 53 |
} |
| 52 | 54 |
|
| 53 |
- response, err := r.resourceAccessReviewsNamespacer.ResourceAccessReviews(name).Create(resourceAccessReview) |
|
| 55 |
+ response, err := r.resourceAccessReviewsNamespacer.LocalResourceAccessReviews(name).Create(resourceAccessReview) |
|
| 54 | 56 |
|
| 55 | 57 |
if err != nil {
|
| 56 | 58 |
return nil, err |
| ... | ... |
@@ -154,12 +154,14 @@ func (r *REST) List(ctx kapi.Context, label labels.Selector, field fields.Select |
| 154 | 154 |
// the caller might not have permission to run a subject access review (he has it by default, but it could have been removed). |
| 155 | 155 |
// So we'll escalate for the subject access review to determine rights |
| 156 | 156 |
accessReview := &authorizationapi.SubjectAccessReview{
|
| 157 |
- Verb: "create", |
|
| 158 |
- Resource: "projectrequests", |
|
| 159 |
- User: userInfo.GetName(), |
|
| 160 |
- Groups: util.NewStringSet(userInfo.GetGroups()...), |
|
| 157 |
+ Action: authorizationapi.AuthorizationAttributes{
|
|
| 158 |
+ Verb: "create", |
|
| 159 |
+ Resource: "projectrequests", |
|
| 160 |
+ }, |
|
| 161 |
+ User: userInfo.GetName(), |
|
| 162 |
+ Groups: util.NewStringSet(userInfo.GetGroups()...), |
|
| 161 | 163 |
} |
| 162 |
- accessReviewResponse, err := r.openshiftClient.ClusterSubjectAccessReviews().Create(accessReview) |
|
| 164 |
+ accessReviewResponse, err := r.openshiftClient.SubjectAccessReviews().Create(accessReview) |
|
| 163 | 165 |
if err != nil {
|
| 164 | 166 |
return nil, err |
| 165 | 167 |
} |
| ... | ... |
@@ -165,6 +165,33 @@ func (test resourceAccessReviewTest) run(t *testing.T) {
|
| 165 | 165 |
} |
| 166 | 166 |
} |
| 167 | 167 |
|
| 168 |
+type localResourceAccessReviewTest struct {
|
|
| 169 |
+ clientInterface client.LocalResourceAccessReviewInterface |
|
| 170 |
+ review *authorizationapi.LocalResourceAccessReview |
|
| 171 |
+ |
|
| 172 |
+ response authorizationapi.ResourceAccessReviewResponse |
|
| 173 |
+ err string |
|
| 174 |
+} |
|
| 175 |
+ |
|
| 176 |
+func (test localResourceAccessReviewTest) run(t *testing.T) {
|
|
| 177 |
+ actualResponse, err := test.clientInterface.Create(test.review) |
|
| 178 |
+ if len(test.err) > 0 {
|
|
| 179 |
+ if err == nil {
|
|
| 180 |
+ t.Errorf("Expected error: %v", test.err)
|
|
| 181 |
+ } else if !strings.Contains(err.Error(), test.err) {
|
|
| 182 |
+ t.Errorf("expected %v, got %v", test.err, err)
|
|
| 183 |
+ } |
|
| 184 |
+ } else {
|
|
| 185 |
+ if err != nil {
|
|
| 186 |
+ t.Errorf("unexpected error: %v", err)
|
|
| 187 |
+ } |
|
| 188 |
+ } |
|
| 189 |
+ |
|
| 190 |
+ if actualResponse.Namespace != test.response.Namespace || !reflect.DeepEqual(actualResponse.Users.List(), test.response.Users.List()) || !reflect.DeepEqual(actualResponse.Groups.List(), test.response.Groups.List()) {
|
|
| 191 |
+ t.Errorf("%#v: expected %v, got %v", test.review, test.response, actualResponse)
|
|
| 192 |
+ } |
|
| 193 |
+} |
|
| 194 |
+ |
|
| 168 | 195 |
func TestAuthorizationResourceAccessReview(t *testing.T) {
|
| 169 | 196 |
_, clusterAdminKubeConfig, err := testutil.StartTestMaster() |
| 170 | 197 |
if err != nil {
|
| ... | ... |
@@ -211,12 +238,18 @@ func TestAuthorizationResourceAccessReview(t *testing.T) {
|
| 211 | 211 |
t.Fatalf("unexpected error: %v", err)
|
| 212 | 212 |
} |
| 213 | 213 |
|
| 214 |
- requestWhoCanViewDeployments := &authorizationapi.ResourceAccessReview{Verb: "get", Resource: "deployments"}
|
|
| 214 |
+ requestWhoCanViewDeployments := &authorizationapi.ResourceAccessReview{
|
|
| 215 |
+ Action: authorizationapi.AuthorizationAttributes{Verb: "get", Resource: "deployments"},
|
|
| 216 |
+ } |
|
| 217 |
+ |
|
| 218 |
+ localRequestWhoCanViewDeployments := &authorizationapi.LocalResourceAccessReview{
|
|
| 219 |
+ Action: authorizationapi.AuthorizationAttributes{Verb: "get", Resource: "deployments"},
|
|
| 220 |
+ } |
|
| 215 | 221 |
|
| 216 | 222 |
{
|
| 217 |
- test := resourceAccessReviewTest{
|
|
| 218 |
- clientInterface: haroldClient.ResourceAccessReviews("hammer-project"),
|
|
| 219 |
- review: requestWhoCanViewDeployments, |
|
| 223 |
+ test := localResourceAccessReviewTest{
|
|
| 224 |
+ clientInterface: haroldClient.LocalResourceAccessReviews("hammer-project"),
|
|
| 225 |
+ review: localRequestWhoCanViewDeployments, |
|
| 220 | 226 |
response: authorizationapi.ResourceAccessReviewResponse{
|
| 221 | 227 |
Users: util.NewStringSet("harold", "valerie"),
|
| 222 | 228 |
Groups: globalClusterAdminGroups, |
| ... | ... |
@@ -228,9 +261,9 @@ func TestAuthorizationResourceAccessReview(t *testing.T) {
|
| 228 | 228 |
test.run(t) |
| 229 | 229 |
} |
| 230 | 230 |
{
|
| 231 |
- test := resourceAccessReviewTest{
|
|
| 232 |
- clientInterface: markClient.ResourceAccessReviews("mallet-project"),
|
|
| 233 |
- review: requestWhoCanViewDeployments, |
|
| 231 |
+ test := localResourceAccessReviewTest{
|
|
| 232 |
+ clientInterface: markClient.LocalResourceAccessReviews("mallet-project"),
|
|
| 233 |
+ review: localRequestWhoCanViewDeployments, |
|
| 234 | 234 |
response: authorizationapi.ResourceAccessReviewResponse{
|
| 235 | 235 |
Users: util.NewStringSet("mark", "edgar"),
|
| 236 | 236 |
Groups: globalClusterAdminGroups, |
| ... | ... |
@@ -245,7 +278,7 @@ func TestAuthorizationResourceAccessReview(t *testing.T) {
|
| 245 | 245 |
// mark should not be able to make global access review requests |
| 246 | 246 |
{
|
| 247 | 247 |
test := resourceAccessReviewTest{
|
| 248 |
- clientInterface: markClient.ClusterResourceAccessReviews(), |
|
| 248 |
+ clientInterface: markClient.ResourceAccessReviews(), |
|
| 249 | 249 |
review: requestWhoCanViewDeployments, |
| 250 | 250 |
err: "cannot ", |
| 251 | 251 |
} |
| ... | ... |
@@ -255,7 +288,7 @@ func TestAuthorizationResourceAccessReview(t *testing.T) {
|
| 255 | 255 |
// a cluster-admin should be able to make global access review requests |
| 256 | 256 |
{
|
| 257 | 257 |
test := resourceAccessReviewTest{
|
| 258 |
- clientInterface: clusterAdminClient.ClusterResourceAccessReviews(), |
|
| 258 |
+ clientInterface: clusterAdminClient.ResourceAccessReviews(), |
|
| 259 | 259 |
review: requestWhoCanViewDeployments, |
| 260 | 260 |
response: authorizationapi.ResourceAccessReviewResponse{
|
| 261 | 261 |
Users: globalClusterAdminUsers, |
| ... | ... |
@@ -268,9 +301,11 @@ func TestAuthorizationResourceAccessReview(t *testing.T) {
|
| 268 | 268 |
} |
| 269 | 269 |
|
| 270 | 270 |
type subjectAccessReviewTest struct {
|
| 271 |
- description string |
|
| 272 |
- clientInterface client.SubjectAccessReviewInterface |
|
| 273 |
- review *authorizationapi.SubjectAccessReview |
|
| 271 |
+ description string |
|
| 272 |
+ localInterface client.LocalSubjectAccessReviewInterface |
|
| 273 |
+ clusterInterface client.SubjectAccessReviewInterface |
|
| 274 |
+ localReview *authorizationapi.LocalSubjectAccessReview |
|
| 275 |
+ clusterReview *authorizationapi.SubjectAccessReview |
|
| 274 | 276 |
|
| 275 | 277 |
response authorizationapi.SubjectAccessReviewResponse |
| 276 | 278 |
err string |
| ... | ... |
@@ -279,7 +314,13 @@ type subjectAccessReviewTest struct {
|
| 279 | 279 |
func (test subjectAccessReviewTest) run(t *testing.T) {
|
| 280 | 280 |
failMessage := "" |
| 281 | 281 |
err := wait.Poll(testutil.PolicyCachePollInterval, testutil.PolicyCachePollTimeout, func() (bool, error) {
|
| 282 |
- actualResponse, err := test.clientInterface.Create(test.review) |
|
| 282 |
+ var err error |
|
| 283 |
+ var actualResponse *authorizationapi.SubjectAccessReviewResponse |
|
| 284 |
+ if test.localReview != nil {
|
|
| 285 |
+ actualResponse, err = test.localInterface.Create(test.localReview) |
|
| 286 |
+ } else {
|
|
| 287 |
+ actualResponse, err = test.clusterInterface.Create(test.clusterReview) |
|
| 288 |
+ } |
|
| 283 | 289 |
if len(test.err) > 0 {
|
| 284 | 290 |
if err == nil {
|
| 285 | 291 |
failMessage = fmt.Sprintf("%s: Expected error: %v", test.description, test.err)
|
| ... | ... |
@@ -298,7 +339,11 @@ func (test subjectAccessReviewTest) run(t *testing.T) {
|
| 298 | 298 |
if (actualResponse.Namespace != test.response.Namespace) || |
| 299 | 299 |
(actualResponse.Allowed != test.response.Allowed) || |
| 300 | 300 |
(!strings.HasPrefix(actualResponse.Reason, test.response.Reason)) {
|
| 301 |
- failMessage = fmt.Sprintf("%s: from review\n\t%#v\nexpected\n\t%#v\ngot\n\t%#v", test.description, test.review, &test.response, actualResponse)
|
|
| 301 |
+ if test.localReview != nil {
|
|
| 302 |
+ failMessage = fmt.Sprintf("%s: from local review\n\t%#v\nexpected\n\t%#v\ngot\n\t%#v", test.description, test.localReview, &test.response, actualResponse)
|
|
| 303 |
+ } else {
|
|
| 304 |
+ failMessage = fmt.Sprintf("%s: from review\n\t%#v\nexpected\n\t%#v\ngot\n\t%#v", test.description, test.clusterReview, &test.response, actualResponse)
|
|
| 305 |
+ } |
|
| 302 | 306 |
return false, nil |
| 303 | 307 |
} |
| 304 | 308 |
|
| ... | ... |
@@ -354,11 +399,17 @@ func TestAuthorizationSubjectAccessReview(t *testing.T) {
|
| 354 | 354 |
if err := addDanny.AddRole(); err != nil {
|
| 355 | 355 |
t.Errorf("unexpected error: %v", err)
|
| 356 | 356 |
} |
| 357 |
- askCanDannyGetProject := &authorizationapi.SubjectAccessReview{User: "danny", Verb: "get", Resource: "projects"}
|
|
| 357 |
+ askCanDannyGetProject := &authorizationapi.SubjectAccessReview{
|
|
| 358 |
+ User: "danny", |
|
| 359 |
+ Action: authorizationapi.AuthorizationAttributes{Verb: "get", Resource: "projects"},
|
|
| 360 |
+ } |
|
| 358 | 361 |
subjectAccessReviewTest{
|
| 359 |
- description: "cluster admin told danny can get project default", |
|
| 360 |
- clientInterface: clusterAdminClient.SubjectAccessReviews("default"),
|
|
| 361 |
- review: askCanDannyGetProject, |
|
| 362 |
+ description: "cluster admin told danny can get project default", |
|
| 363 |
+ localInterface: clusterAdminClient.LocalSubjectAccessReviews("default"),
|
|
| 364 |
+ localReview: &authorizationapi.LocalSubjectAccessReview{
|
|
| 365 |
+ User: "danny", |
|
| 366 |
+ Action: authorizationapi.AuthorizationAttributes{Verb: "get", Resource: "projects"},
|
|
| 367 |
+ }, |
|
| 362 | 368 |
response: authorizationapi.SubjectAccessReviewResponse{
|
| 363 | 369 |
Allowed: true, |
| 364 | 370 |
Reason: "allowed by rule in default", |
| ... | ... |
@@ -366,9 +417,9 @@ func TestAuthorizationSubjectAccessReview(t *testing.T) {
|
| 366 | 366 |
}, |
| 367 | 367 |
}.run(t) |
| 368 | 368 |
subjectAccessReviewTest{
|
| 369 |
- description: "cluster admin told danny cannot get projects cluster-wide", |
|
| 370 |
- clientInterface: clusterAdminClient.ClusterSubjectAccessReviews(), |
|
| 371 |
- review: askCanDannyGetProject, |
|
| 369 |
+ description: "cluster admin told danny cannot get projects cluster-wide", |
|
| 370 |
+ clusterInterface: clusterAdminClient.SubjectAccessReviews(), |
|
| 371 |
+ clusterReview: askCanDannyGetProject, |
|
| 372 | 372 |
response: authorizationapi.SubjectAccessReviewResponse{
|
| 373 | 373 |
Allowed: false, |
| 374 | 374 |
Reason: `User "danny" cannot get projects at the cluster scope`, |
| ... | ... |
@@ -376,10 +427,10 @@ func TestAuthorizationSubjectAccessReview(t *testing.T) {
|
| 376 | 376 |
}, |
| 377 | 377 |
}.run(t) |
| 378 | 378 |
subjectAccessReviewTest{
|
| 379 |
- description: "as danny, can I make cluster subject access reviews", |
|
| 380 |
- clientInterface: dannyClient.ClusterSubjectAccessReviews(), |
|
| 381 |
- review: askCanDannyGetProject, |
|
| 382 |
- err: `User "danny" cannot create subjectaccessreviews at the cluster scope`, |
|
| 379 |
+ description: "as danny, can I make cluster subject access reviews", |
|
| 380 |
+ clusterInterface: dannyClient.SubjectAccessReviews(), |
|
| 381 |
+ clusterReview: askCanDannyGetProject, |
|
| 382 |
+ err: `User "danny" cannot create subjectaccessreviews at the cluster scope`, |
|
| 383 | 383 |
}.run(t) |
| 384 | 384 |
|
| 385 | 385 |
addValerie := &policy.RoleModificationOptions{
|
| ... | ... |
@@ -402,11 +453,14 @@ func TestAuthorizationSubjectAccessReview(t *testing.T) {
|
| 402 | 402 |
t.Fatalf("unexpected error: %v", err)
|
| 403 | 403 |
} |
| 404 | 404 |
|
| 405 |
- askCanValerieGetProject := &authorizationapi.SubjectAccessReview{User: "valerie", Verb: "get", Resource: "projects"}
|
|
| 405 |
+ askCanValerieGetProject := &authorizationapi.LocalSubjectAccessReview{
|
|
| 406 |
+ User: "valerie", |
|
| 407 |
+ Action: authorizationapi.AuthorizationAttributes{Verb: "get", Resource: "projects"},
|
|
| 408 |
+ } |
|
| 406 | 409 |
subjectAccessReviewTest{
|
| 407 |
- description: "harold told valerie can get project hammer-project", |
|
| 408 |
- clientInterface: haroldClient.SubjectAccessReviews("hammer-project"),
|
|
| 409 |
- review: askCanValerieGetProject, |
|
| 410 |
+ description: "harold told valerie can get project hammer-project", |
|
| 411 |
+ localInterface: haroldClient.LocalSubjectAccessReviews("hammer-project"),
|
|
| 412 |
+ localReview: askCanValerieGetProject, |
|
| 410 | 413 |
response: authorizationapi.SubjectAccessReviewResponse{
|
| 411 | 414 |
Allowed: true, |
| 412 | 415 |
Reason: "allowed by rule in hammer-project", |
| ... | ... |
@@ -414,9 +468,9 @@ func TestAuthorizationSubjectAccessReview(t *testing.T) {
|
| 414 | 414 |
}, |
| 415 | 415 |
}.run(t) |
| 416 | 416 |
subjectAccessReviewTest{
|
| 417 |
- description: "mark told valerie cannot get project mallet-project", |
|
| 418 |
- clientInterface: markClient.SubjectAccessReviews("mallet-project"),
|
|
| 419 |
- review: askCanValerieGetProject, |
|
| 417 |
+ description: "mark told valerie cannot get project mallet-project", |
|
| 418 |
+ localInterface: markClient.LocalSubjectAccessReviews("mallet-project"),
|
|
| 419 |
+ localReview: askCanValerieGetProject, |
|
| 420 | 420 |
response: authorizationapi.SubjectAccessReviewResponse{
|
| 421 | 421 |
Allowed: false, |
| 422 | 422 |
Reason: `User "valerie" cannot get projects in project "mallet-project"`, |
| ... | ... |
@@ -424,11 +478,14 @@ func TestAuthorizationSubjectAccessReview(t *testing.T) {
|
| 424 | 424 |
}, |
| 425 | 425 |
}.run(t) |
| 426 | 426 |
|
| 427 |
- askCanEdgarDeletePods := &authorizationapi.SubjectAccessReview{User: "edgar", Verb: "delete", Resource: "pods"}
|
|
| 427 |
+ askCanEdgarDeletePods := &authorizationapi.LocalSubjectAccessReview{
|
|
| 428 |
+ User: "edgar", |
|
| 429 |
+ Action: authorizationapi.AuthorizationAttributes{Verb: "delete", Resource: "pods"},
|
|
| 430 |
+ } |
|
| 428 | 431 |
subjectAccessReviewTest{
|
| 429 |
- description: "mark told edgar can delete pods in mallet-project", |
|
| 430 |
- clientInterface: markClient.SubjectAccessReviews("mallet-project"),
|
|
| 431 |
- review: askCanEdgarDeletePods, |
|
| 432 |
+ description: "mark told edgar can delete pods in mallet-project", |
|
| 433 |
+ localInterface: markClient.LocalSubjectAccessReviews("mallet-project"),
|
|
| 434 |
+ localReview: askCanEdgarDeletePods, |
|
| 432 | 435 |
response: authorizationapi.SubjectAccessReviewResponse{
|
| 433 | 436 |
Allowed: true, |
| 434 | 437 |
Reason: "allowed by rule in mallet-project", |
| ... | ... |
@@ -436,17 +493,20 @@ func TestAuthorizationSubjectAccessReview(t *testing.T) {
|
| 436 | 436 |
}, |
| 437 | 437 |
}.run(t) |
| 438 | 438 |
subjectAccessReviewTest{
|
| 439 |
- description: "harold denied ability to run subject access review in project mallet-project", |
|
| 440 |
- clientInterface: haroldClient.SubjectAccessReviews("mallet-project"),
|
|
| 441 |
- review: askCanEdgarDeletePods, |
|
| 442 |
- err: `User "harold" cannot create subjectaccessreviews in project "mallet-project"`, |
|
| 439 |
+ description: "harold denied ability to run subject access review in project mallet-project", |
|
| 440 |
+ localInterface: haroldClient.LocalSubjectAccessReviews("mallet-project"),
|
|
| 441 |
+ localReview: askCanEdgarDeletePods, |
|
| 442 |
+ err: `User "harold" cannot create localsubjectaccessreviews in project "mallet-project"`, |
|
| 443 | 443 |
}.run(t) |
| 444 | 444 |
|
| 445 |
- askCanHaroldUpdateProject := &authorizationapi.SubjectAccessReview{User: "harold", Verb: "update", Resource: "projects"}
|
|
| 445 |
+ askCanHaroldUpdateProject := &authorizationapi.LocalSubjectAccessReview{
|
|
| 446 |
+ User: "harold", |
|
| 447 |
+ Action: authorizationapi.AuthorizationAttributes{Verb: "update", Resource: "projects"},
|
|
| 448 |
+ } |
|
| 446 | 449 |
subjectAccessReviewTest{
|
| 447 |
- description: "harold told harold can update project hammer-project", |
|
| 448 |
- clientInterface: haroldClient.SubjectAccessReviews("hammer-project"),
|
|
| 449 |
- review: askCanHaroldUpdateProject, |
|
| 450 |
+ description: "harold told harold can update project hammer-project", |
|
| 451 |
+ localInterface: haroldClient.LocalSubjectAccessReviews("hammer-project"),
|
|
| 452 |
+ localReview: askCanHaroldUpdateProject, |
|
| 450 | 453 |
response: authorizationapi.SubjectAccessReviewResponse{
|
| 451 | 454 |
Allowed: true, |
| 452 | 455 |
Reason: "allowed by rule in hammer-project", |
| ... | ... |
@@ -454,11 +514,14 @@ func TestAuthorizationSubjectAccessReview(t *testing.T) {
|
| 454 | 454 |
}, |
| 455 | 455 |
}.run(t) |
| 456 | 456 |
|
| 457 |
- askCanClusterAdminsCreateProject := &authorizationapi.SubjectAccessReview{Groups: util.NewStringSet("system:cluster-admins"), Verb: "create", Resource: "projects"}
|
|
| 457 |
+ askCanClusterAdminsCreateProject := &authorizationapi.SubjectAccessReview{
|
|
| 458 |
+ Groups: util.NewStringSet("system:cluster-admins"),
|
|
| 459 |
+ Action: authorizationapi.AuthorizationAttributes{Verb: "create", Resource: "projects"},
|
|
| 460 |
+ } |
|
| 458 | 461 |
subjectAccessReviewTest{
|
| 459 |
- description: "cluster admin told cluster admins can create projects", |
|
| 460 |
- clientInterface: clusterAdminClient.ClusterSubjectAccessReviews(), |
|
| 461 |
- review: askCanClusterAdminsCreateProject, |
|
| 462 |
+ description: "cluster admin told cluster admins can create projects", |
|
| 463 |
+ clusterInterface: clusterAdminClient.SubjectAccessReviews(), |
|
| 464 |
+ clusterReview: askCanClusterAdminsCreateProject, |
|
| 462 | 465 |
response: authorizationapi.SubjectAccessReviewResponse{
|
| 463 | 466 |
Allowed: true, |
| 464 | 467 |
Reason: "allowed by cluster rule", |
| ... | ... |
@@ -466,28 +529,32 @@ func TestAuthorizationSubjectAccessReview(t *testing.T) {
|
| 466 | 466 |
}, |
| 467 | 467 |
}.run(t) |
| 468 | 468 |
subjectAccessReviewTest{
|
| 469 |
- description: "harold denied ability to run cluster subject access review", |
|
| 470 |
- clientInterface: haroldClient.ClusterSubjectAccessReviews(), |
|
| 471 |
- review: askCanClusterAdminsCreateProject, |
|
| 472 |
- err: `User "harold" cannot create subjectaccessreviews at the cluster scope`, |
|
| 469 |
+ description: "harold denied ability to run cluster subject access review", |
|
| 470 |
+ clusterInterface: haroldClient.SubjectAccessReviews(), |
|
| 471 |
+ clusterReview: askCanClusterAdminsCreateProject, |
|
| 472 |
+ err: `User "harold" cannot create subjectaccessreviews at the cluster scope`, |
|
| 473 | 473 |
}.run(t) |
| 474 | 474 |
|
| 475 |
- askCanICreatePods := &authorizationapi.SubjectAccessReview{Verb: "create", Resource: "pods"}
|
|
| 475 |
+ askCanICreatePods := &authorizationapi.LocalSubjectAccessReview{
|
|
| 476 |
+ Action: authorizationapi.AuthorizationAttributes{Verb: "create", Resource: "pods"},
|
|
| 477 |
+ } |
|
| 476 | 478 |
subjectAccessReviewTest{
|
| 477 |
- description: "harold told he can create pods in project hammer-project", |
|
| 478 |
- clientInterface: haroldClient.SubjectAccessReviews("hammer-project"),
|
|
| 479 |
- review: askCanICreatePods, |
|
| 479 |
+ description: "harold told he can create pods in project hammer-project", |
|
| 480 |
+ localInterface: haroldClient.LocalSubjectAccessReviews("hammer-project"),
|
|
| 481 |
+ localReview: askCanICreatePods, |
|
| 480 | 482 |
response: authorizationapi.SubjectAccessReviewResponse{
|
| 481 | 483 |
Allowed: true, |
| 482 | 484 |
Reason: "allowed by rule in hammer-project", |
| 483 | 485 |
Namespace: "hammer-project", |
| 484 | 486 |
}, |
| 485 | 487 |
}.run(t) |
| 486 |
- askCanICreatePolicyBindings := &authorizationapi.SubjectAccessReview{Verb: "create", Resource: "policybindings"}
|
|
| 488 |
+ askCanICreatePolicyBindings := &authorizationapi.LocalSubjectAccessReview{
|
|
| 489 |
+ Action: authorizationapi.AuthorizationAttributes{Verb: "create", Resource: "policybindings"},
|
|
| 490 |
+ } |
|
| 487 | 491 |
subjectAccessReviewTest{
|
| 488 |
- description: "harold told he can create policybindings in project hammer-project", |
|
| 489 |
- clientInterface: haroldClient.SubjectAccessReviews("hammer-project"),
|
|
| 490 |
- review: askCanICreatePolicyBindings, |
|
| 492 |
+ description: "harold told he can create policybindings in project hammer-project", |
|
| 493 |
+ localInterface: haroldClient.LocalSubjectAccessReviews("hammer-project"),
|
|
| 494 |
+ localReview: askCanICreatePolicyBindings, |
|
| 491 | 495 |
response: authorizationapi.SubjectAccessReviewResponse{
|
| 492 | 496 |
Allowed: false, |
| 493 | 497 |
Reason: `User "harold" cannot create policybindings in project "hammer-project"`, |
| ... | ... |
@@ -496,3 +563,161 @@ func TestAuthorizationSubjectAccessReview(t *testing.T) {
|
| 496 | 496 |
}.run(t) |
| 497 | 497 |
|
| 498 | 498 |
} |
| 499 |
+ |
|
| 500 |
+// TestOldLocalSubjectAccessReviewEndpoint checks to make sure that the old subject access review endpoint still functions properly |
|
| 501 |
+// this is needed to support old docker registry images |
|
| 502 |
+func TestOldLocalSubjectAccessReviewEndpoint(t *testing.T) {
|
|
| 503 |
+ _, clusterAdminKubeConfig, err := testutil.StartTestMaster() |
|
| 504 |
+ if err != nil {
|
|
| 505 |
+ t.Fatalf("unexpected error: %v", err)
|
|
| 506 |
+ } |
|
| 507 |
+ |
|
| 508 |
+ clusterAdminClient, err := testutil.GetClusterAdminClient(clusterAdminKubeConfig) |
|
| 509 |
+ if err != nil {
|
|
| 510 |
+ t.Fatalf("unexpected error: %v", err)
|
|
| 511 |
+ } |
|
| 512 |
+ |
|
| 513 |
+ clusterAdminClientConfig, err := testutil.GetClusterAdminClientConfig(clusterAdminKubeConfig) |
|
| 514 |
+ if err != nil {
|
|
| 515 |
+ t.Fatalf("unexpected error: %v", err)
|
|
| 516 |
+ } |
|
| 517 |
+ |
|
| 518 |
+ haroldClient, err := testutil.CreateNewProject(clusterAdminClient, *clusterAdminClientConfig, "hammer-project", "harold") |
|
| 519 |
+ if err != nil {
|
|
| 520 |
+ t.Fatalf("unexpected error: %v", err)
|
|
| 521 |
+ } |
|
| 522 |
+ |
|
| 523 |
+ namespace := "hammer-project" |
|
| 524 |
+ |
|
| 525 |
+ // simple check |
|
| 526 |
+ {
|
|
| 527 |
+ sar := &authorizationapi.SubjectAccessReview{
|
|
| 528 |
+ Action: authorizationapi.AuthorizationAttributes{
|
|
| 529 |
+ Verb: "get", |
|
| 530 |
+ Resource: "imagestreams/layers", |
|
| 531 |
+ }, |
|
| 532 |
+ } |
|
| 533 |
+ actualResponse := &authorizationapi.SubjectAccessReviewResponse{}
|
|
| 534 |
+ err := haroldClient.Post().Namespace(namespace).Resource("subjectAccessReviews").Body(sar).Do().Into(actualResponse)
|
|
| 535 |
+ if err != nil {
|
|
| 536 |
+ t.Errorf("unexpected error: %v", err)
|
|
| 537 |
+ } |
|
| 538 |
+ |
|
| 539 |
+ expectedResponse := &authorizationapi.SubjectAccessReviewResponse{
|
|
| 540 |
+ Allowed: true, |
|
| 541 |
+ Reason: `allowed by rule in hammer-project`, |
|
| 542 |
+ Namespace: namespace, |
|
| 543 |
+ } |
|
| 544 |
+ if (actualResponse.Namespace != expectedResponse.Namespace) || |
|
| 545 |
+ (actualResponse.Allowed != expectedResponse.Allowed) || |
|
| 546 |
+ (!strings.HasPrefix(actualResponse.Reason, expectedResponse.Reason)) {
|
|
| 547 |
+ t.Errorf("review\n\t%#v\nexpected\n\t%#v\ngot\n\t%#v", sar, expectedResponse, actualResponse)
|
|
| 548 |
+ } |
|
| 549 |
+ } |
|
| 550 |
+ |
|
| 551 |
+ // namespace forced to allowed namespace so we can't trick the server into leaking |
|
| 552 |
+ {
|
|
| 553 |
+ sar := &authorizationapi.SubjectAccessReview{
|
|
| 554 |
+ Action: authorizationapi.AuthorizationAttributes{
|
|
| 555 |
+ Namespace: "sneaky-user", |
|
| 556 |
+ Verb: "get", |
|
| 557 |
+ Resource: "imagestreams/layers", |
|
| 558 |
+ }, |
|
| 559 |
+ } |
|
| 560 |
+ actualResponse := &authorizationapi.SubjectAccessReviewResponse{}
|
|
| 561 |
+ err := haroldClient.Post().Namespace(namespace).Resource("subjectAccessReviews").Body(sar).Do().Into(actualResponse)
|
|
| 562 |
+ if err != nil {
|
|
| 563 |
+ t.Errorf("unexpected error: %v", err)
|
|
| 564 |
+ } |
|
| 565 |
+ |
|
| 566 |
+ expectedResponse := &authorizationapi.SubjectAccessReviewResponse{
|
|
| 567 |
+ Allowed: true, |
|
| 568 |
+ Reason: `allowed by rule in hammer-project`, |
|
| 569 |
+ Namespace: namespace, |
|
| 570 |
+ } |
|
| 571 |
+ if (actualResponse.Namespace != expectedResponse.Namespace) || |
|
| 572 |
+ (actualResponse.Allowed != expectedResponse.Allowed) || |
|
| 573 |
+ (!strings.HasPrefix(actualResponse.Reason, expectedResponse.Reason)) {
|
|
| 574 |
+ t.Errorf("review\n\t%#v\nexpected\n\t%#v\ngot\n\t%#v", sar, expectedResponse, actualResponse)
|
|
| 575 |
+ } |
|
| 576 |
+ } |
|
| 577 |
+} |
|
| 578 |
+ |
|
| 579 |
+// TestOldLocalResourceAccessReviewEndpoint checks to make sure that the old resource access review endpoint still functions properly |
|
| 580 |
+// this is needed to support old who-can client |
|
| 581 |
+func TestOldLocalResourceAccessReviewEndpoint(t *testing.T) {
|
|
| 582 |
+ _, clusterAdminKubeConfig, err := testutil.StartTestMaster() |
|
| 583 |
+ if err != nil {
|
|
| 584 |
+ t.Fatalf("unexpected error: %v", err)
|
|
| 585 |
+ } |
|
| 586 |
+ |
|
| 587 |
+ clusterAdminClient, err := testutil.GetClusterAdminClient(clusterAdminKubeConfig) |
|
| 588 |
+ if err != nil {
|
|
| 589 |
+ t.Fatalf("unexpected error: %v", err)
|
|
| 590 |
+ } |
|
| 591 |
+ |
|
| 592 |
+ clusterAdminClientConfig, err := testutil.GetClusterAdminClientConfig(clusterAdminKubeConfig) |
|
| 593 |
+ if err != nil {
|
|
| 594 |
+ t.Fatalf("unexpected error: %v", err)
|
|
| 595 |
+ } |
|
| 596 |
+ |
|
| 597 |
+ haroldClient, err := testutil.CreateNewProject(clusterAdminClient, *clusterAdminClientConfig, "hammer-project", "harold") |
|
| 598 |
+ if err != nil {
|
|
| 599 |
+ t.Fatalf("unexpected error: %v", err)
|
|
| 600 |
+ } |
|
| 601 |
+ |
|
| 602 |
+ namespace := "hammer-project" |
|
| 603 |
+ |
|
| 604 |
+ // simple check |
|
| 605 |
+ {
|
|
| 606 |
+ rar := &authorizationapi.ResourceAccessReview{
|
|
| 607 |
+ Action: authorizationapi.AuthorizationAttributes{
|
|
| 608 |
+ Verb: "get", |
|
| 609 |
+ Resource: "imagestreams/layers", |
|
| 610 |
+ }, |
|
| 611 |
+ } |
|
| 612 |
+ actualResponse := &authorizationapi.ResourceAccessReviewResponse{}
|
|
| 613 |
+ err := haroldClient.Post().Namespace(namespace).Resource("resourceAccessReviews").Body(rar).Do().Into(actualResponse)
|
|
| 614 |
+ if err != nil {
|
|
| 615 |
+ t.Errorf("unexpected error: %v", err)
|
|
| 616 |
+ } |
|
| 617 |
+ |
|
| 618 |
+ expectedResponse := &authorizationapi.ResourceAccessReviewResponse{
|
|
| 619 |
+ Namespace: namespace, |
|
| 620 |
+ Users: util.NewStringSet("harold", "system:serviceaccount:hammer-project:builder"),
|
|
| 621 |
+ Groups: util.NewStringSet("system:cluster-admins", "system:masters", "system:serviceaccounts:hammer-project"),
|
|
| 622 |
+ } |
|
| 623 |
+ if (actualResponse.Namespace != expectedResponse.Namespace) || |
|
| 624 |
+ !reflect.DeepEqual(actualResponse.Users.List(), expectedResponse.Users.List()) || |
|
| 625 |
+ !reflect.DeepEqual(actualResponse.Groups.List(), expectedResponse.Groups.List()) {
|
|
| 626 |
+ t.Errorf("review\n\t%#v\nexpected\n\t%#v\ngot\n\t%#v", rar, expectedResponse, actualResponse)
|
|
| 627 |
+ } |
|
| 628 |
+ } |
|
| 629 |
+ |
|
| 630 |
+ // namespace forced to allowed namespace so we can't trick the server into leaking |
|
| 631 |
+ {
|
|
| 632 |
+ rar := &authorizationapi.ResourceAccessReview{
|
|
| 633 |
+ Action: authorizationapi.AuthorizationAttributes{
|
|
| 634 |
+ Namespace: "sneaky-user", |
|
| 635 |
+ Verb: "get", |
|
| 636 |
+ Resource: "imagestreams/layers", |
|
| 637 |
+ }, |
|
| 638 |
+ } |
|
| 639 |
+ actualResponse := &authorizationapi.ResourceAccessReviewResponse{}
|
|
| 640 |
+ err := haroldClient.Post().Namespace(namespace).Resource("resourceAccessReviews").Body(rar).Do().Into(actualResponse)
|
|
| 641 |
+ if err != nil {
|
|
| 642 |
+ t.Errorf("unexpected error: %v", err)
|
|
| 643 |
+ } |
|
| 644 |
+ |
|
| 645 |
+ expectedResponse := &authorizationapi.ResourceAccessReviewResponse{
|
|
| 646 |
+ Namespace: namespace, |
|
| 647 |
+ Users: util.NewStringSet("harold", "system:serviceaccount:hammer-project:builder"),
|
|
| 648 |
+ Groups: util.NewStringSet("system:cluster-admins", "system:masters", "system:serviceaccounts:hammer-project"),
|
|
| 649 |
+ } |
|
| 650 |
+ if (actualResponse.Namespace != expectedResponse.Namespace) || |
|
| 651 |
+ !reflect.DeepEqual(actualResponse.Users.List(), expectedResponse.Users.List()) || |
|
| 652 |
+ !reflect.DeepEqual(actualResponse.Groups.List(), expectedResponse.Groups.List()) {
|
|
| 653 |
+ t.Errorf("review\n\t%#v\nexpected\n\t%#v\ngot\n\t%#v", rar, expectedResponse, actualResponse)
|
|
| 654 |
+ } |
|
| 655 |
+ } |
|
| 656 |
+} |
| ... | ... |
@@ -159,10 +159,12 @@ func TestBootstrapPolicySelfSubjectAccessReviews(t *testing.T) {
|
| 159 | 159 |
} |
| 160 | 160 |
|
| 161 | 161 |
// can I get a subjectaccessreview on myself even if I have no rights to do it generally |
| 162 |
- askCanICreatePolicyBindings := &authorizationapi.SubjectAccessReview{Verb: "create", Resource: "policybindings"}
|
|
| 162 |
+ askCanICreatePolicyBindings := &authorizationapi.LocalSubjectAccessReview{
|
|
| 163 |
+ Action: authorizationapi.AuthorizationAttributes{Verb: "create", Resource: "policybindings"},
|
|
| 164 |
+ } |
|
| 163 | 165 |
subjectAccessReviewTest{
|
| 164 |
- clientInterface: valerieOpenshiftClient.SubjectAccessReviews("openshift"),
|
|
| 165 |
- review: askCanICreatePolicyBindings, |
|
| 166 |
+ localInterface: valerieOpenshiftClient.LocalSubjectAccessReviews("openshift"),
|
|
| 167 |
+ localReview: askCanICreatePolicyBindings, |
|
| 166 | 168 |
response: authorizationapi.SubjectAccessReviewResponse{
|
| 167 | 169 |
Allowed: false, |
| 168 | 170 |
Reason: `User "valerie" cannot create policybindings in project "openshift"`, |
| ... | ... |
@@ -171,11 +173,14 @@ func TestBootstrapPolicySelfSubjectAccessReviews(t *testing.T) {
|
| 171 | 171 |
}.run(t) |
| 172 | 172 |
|
| 173 | 173 |
// I shouldn't be allowed to ask whether someone else can perform an action |
| 174 |
- askCanClusterAdminsCreateProject := &authorizationapi.SubjectAccessReview{Groups: util.NewStringSet("system:cluster-admins"), Verb: "create", Resource: "projects"}
|
|
| 174 |
+ askCanClusterAdminsCreateProject := &authorizationapi.LocalSubjectAccessReview{
|
|
| 175 |
+ Groups: util.NewStringSet("system:cluster-admins"),
|
|
| 176 |
+ Action: authorizationapi.AuthorizationAttributes{Verb: "create", Resource: "projects"},
|
|
| 177 |
+ } |
|
| 175 | 178 |
subjectAccessReviewTest{
|
| 176 |
- clientInterface: valerieOpenshiftClient.SubjectAccessReviews("openshift"),
|
|
| 177 |
- review: askCanClusterAdminsCreateProject, |
|
| 178 |
- err: `User "valerie" cannot create subjectaccessreviews in project "openshift"`, |
|
| 179 |
+ localInterface: valerieOpenshiftClient.LocalSubjectAccessReviews("openshift"),
|
|
| 180 |
+ localReview: askCanClusterAdminsCreateProject, |
|
| 181 |
+ err: `User "valerie" cannot create localsubjectaccessreviews in project "openshift"`, |
|
| 179 | 182 |
}.run(t) |
| 180 | 183 |
|
| 181 | 184 |
} |
| ... | ... |
@@ -17,9 +17,9 @@ const ( |
| 17 | 17 |
// WaitForPolicyUpdate checks if the given client can perform the named verb and action. |
| 18 | 18 |
// If PolicyCachePollTimeout is reached without the expected condition matching, an error is returned |
| 19 | 19 |
func WaitForPolicyUpdate(c *client.Client, namespace, verb, resource string, allowed bool) error {
|
| 20 |
- review := &authorizationapi.SubjectAccessReview{Verb: verb, Resource: resource}
|
|
| 20 |
+ review := &authorizationapi.LocalSubjectAccessReview{Action: authorizationapi.AuthorizationAttributes{Verb: verb, Resource: resource}}
|
|
| 21 | 21 |
err := wait.Poll(PolicyCachePollInterval, PolicyCachePollTimeout, func() (bool, error) {
|
| 22 |
- response, err := c.SubjectAccessReviews(namespace).Create(review) |
|
| 22 |
+ response, err := c.LocalSubjectAccessReviews(namespace).Create(review) |
|
| 23 | 23 |
if err != nil {
|
| 24 | 24 |
return false, err |
| 25 | 25 |
} |