helpers_test.go 9.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307
  1. /*
  2. Copyright 2017 The Kubernetes Authors.
  3. Licensed under the Apache License, Version 2.0 (the "License");
  4. you may not use this file except in compliance with the License.
  5. You may obtain a copy of the License at
  6. http://www.apache.org/licenses/LICENSE-2.0
  7. Unless required by applicable law or agreed to in writing, software
  8. distributed under the License is distributed on an "AS IS" BASIS,
  9. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  10. See the License for the specific language governing permissions and
  11. limitations under the License.
  12. */
  13. package rbac_test
  14. import (
  15. "reflect"
  16. "testing"
  17. "k8s.io/apimachinery/pkg/runtime"
  18. "k8s.io/apimachinery/pkg/util/diff"
  19. "k8s.io/kubernetes/pkg/api/legacyscheme"
  20. "k8s.io/kubernetes/pkg/apis/rbac"
  21. "k8s.io/kubernetes/pkg/apis/rbac/v1"
  22. // install RBAC types
  23. _ "k8s.io/kubernetes/pkg/apis/rbac/install"
  24. )
  25. // TestHelpersRoundTrip confirms that the rbac.New* helper functions produce RBAC objects that match objects
  26. // that have gone through conversion and defaulting. This is required because these helper functions are
  27. // used to create the bootstrap RBAC policy which is used during reconciliation. If they produced objects
  28. // that did not match, reconciliation would incorrectly add duplicate data to the cluster's RBAC policy.
  29. func TestHelpersRoundTrip(t *testing.T) {
  30. rb := rbac.NewRoleBinding("role", "ns").Groups("g").SAs("ns", "sa").Users("u").BindingOrDie()
  31. rbcr := rbac.NewRoleBindingForClusterRole("role", "ns").Groups("g").SAs("ns", "sa").Users("u").BindingOrDie()
  32. crb := rbac.NewClusterBinding("role").Groups("g").SAs("ns", "sa").Users("u").BindingOrDie()
  33. role := &rbac.Role{
  34. Rules: []rbac.PolicyRule{
  35. rbac.NewRule("verb").Groups("g").Resources("foo").RuleOrDie(),
  36. rbac.NewRule("verb").URLs("/foo").RuleOrDie(),
  37. },
  38. }
  39. clusterRole := &rbac.ClusterRole{
  40. Rules: []rbac.PolicyRule{
  41. rbac.NewRule("verb").Groups("g").Resources("foo").RuleOrDie(),
  42. rbac.NewRule("verb").URLs("/foo").RuleOrDie(),
  43. },
  44. }
  45. for _, internalObj := range []runtime.Object{&rb, &rbcr, &crb, role, clusterRole} {
  46. v1Obj, err := legacyscheme.Scheme.ConvertToVersion(internalObj, v1.SchemeGroupVersion)
  47. if err != nil {
  48. t.Errorf("err on %T: %v", internalObj, err)
  49. continue
  50. }
  51. legacyscheme.Scheme.Default(v1Obj)
  52. roundTrippedObj, err := legacyscheme.Scheme.ConvertToVersion(v1Obj, rbac.SchemeGroupVersion)
  53. if err != nil {
  54. t.Errorf("err on %T: %v", internalObj, err)
  55. continue
  56. }
  57. if !reflect.DeepEqual(internalObj, roundTrippedObj) {
  58. t.Errorf("err on %T: got difference:\n%s", internalObj, diff.ObjectDiff(internalObj, roundTrippedObj))
  59. continue
  60. }
  61. }
  62. }
  63. func TestResourceMatches(t *testing.T) {
  64. tests := []struct {
  65. name string
  66. ruleResources []string
  67. combinedRequestedResource string
  68. requestedSubresource string
  69. expected bool
  70. }{
  71. {
  72. name: "all matches 01",
  73. ruleResources: []string{"*"},
  74. combinedRequestedResource: "foo",
  75. expected: true,
  76. },
  77. {
  78. name: "checks all rules",
  79. ruleResources: []string{"doesn't match", "*"},
  80. combinedRequestedResource: "foo",
  81. expected: true,
  82. },
  83. {
  84. name: "matches exact rule",
  85. ruleResources: []string{"foo/bar"},
  86. combinedRequestedResource: "foo/bar",
  87. requestedSubresource: "bar",
  88. expected: true,
  89. },
  90. {
  91. name: "matches exact rule 02",
  92. ruleResources: []string{"foo/bar"},
  93. combinedRequestedResource: "foo",
  94. expected: false,
  95. },
  96. {
  97. name: "matches subresource",
  98. ruleResources: []string{"*/scale"},
  99. combinedRequestedResource: "foo/scale",
  100. requestedSubresource: "scale",
  101. expected: true,
  102. },
  103. {
  104. name: "doesn't match partial subresource hit",
  105. ruleResources: []string{"foo/bar", "*/other"},
  106. combinedRequestedResource: "foo/other/segment",
  107. requestedSubresource: "other/segment",
  108. expected: false,
  109. },
  110. {
  111. name: "matches subresource with multiple slashes",
  112. ruleResources: []string{"*/other/segment"},
  113. combinedRequestedResource: "foo/other/segment",
  114. requestedSubresource: "other/segment",
  115. expected: true,
  116. },
  117. {
  118. name: "doesn't fail on empty",
  119. ruleResources: []string{""},
  120. combinedRequestedResource: "foo/other/segment",
  121. requestedSubresource: "other/segment",
  122. expected: false,
  123. },
  124. {
  125. name: "doesn't fail on slash",
  126. ruleResources: []string{"/"},
  127. combinedRequestedResource: "foo/other/segment",
  128. requestedSubresource: "other/segment",
  129. expected: false,
  130. },
  131. {
  132. name: "doesn't fail on missing subresource",
  133. ruleResources: []string{"*/"},
  134. combinedRequestedResource: "foo/other/segment",
  135. requestedSubresource: "other/segment",
  136. expected: false,
  137. },
  138. {
  139. name: "doesn't match on not star",
  140. ruleResources: []string{"*something/other/segment"},
  141. combinedRequestedResource: "foo/other/segment",
  142. requestedSubresource: "other/segment",
  143. expected: false,
  144. },
  145. {
  146. name: "doesn't match on something else",
  147. ruleResources: []string{"something/other/segment"},
  148. combinedRequestedResource: "foo/other/segment",
  149. requestedSubresource: "other/segment",
  150. expected: false,
  151. },
  152. }
  153. for _, tc := range tests {
  154. t.Run(tc.name, func(t *testing.T) {
  155. rule := &rbac.PolicyRule{
  156. Resources: tc.ruleResources,
  157. }
  158. actual := rbac.ResourceMatches(rule, tc.combinedRequestedResource, tc.requestedSubresource)
  159. if tc.expected != actual {
  160. t.Errorf("expected %v, got %v", tc.expected, actual)
  161. }
  162. })
  163. }
  164. }
  165. func TestPolicyRuleBuilder(t *testing.T) {
  166. tests := []struct {
  167. testName string
  168. verbs []string
  169. groups []string
  170. resources []string
  171. names []string
  172. urls []string
  173. expected bool
  174. policyRule rbac.PolicyRule
  175. }{
  176. {
  177. testName: "all empty",
  178. verbs: nil,
  179. groups: nil,
  180. resources: nil,
  181. names: nil,
  182. urls: nil,
  183. expected: false,
  184. policyRule: rbac.PolicyRule{},
  185. },
  186. {
  187. testName: "normal resource case",
  188. verbs: []string{"get"},
  189. groups: []string{""},
  190. resources: []string{"pod"},
  191. names: []string{"gakki"},
  192. urls: nil,
  193. expected: true,
  194. policyRule: rbac.PolicyRule{
  195. Verbs: []string{"get"},
  196. APIGroups: []string{""},
  197. Resources: []string{"pod"},
  198. ResourceNames: []string{"gakki"},
  199. NonResourceURLs: []string{},
  200. },
  201. },
  202. {
  203. testName: "normal noResourceURLs case",
  204. verbs: []string{"get"},
  205. groups: nil,
  206. resources: nil,
  207. names: nil,
  208. urls: []string{"/api/registry/healthz"},
  209. expected: true,
  210. policyRule: rbac.PolicyRule{
  211. Verbs: []string{"get"},
  212. APIGroups: []string{},
  213. Resources: []string{},
  214. ResourceNames: []string{},
  215. NonResourceURLs: []string{"/api/registry/healthz"},
  216. },
  217. },
  218. {
  219. testName: "nonResourceURLs with no-empty groups",
  220. verbs: []string{"get"},
  221. groups: []string{""},
  222. resources: nil,
  223. names: nil,
  224. urls: []string{"/api/registry/healthz"},
  225. expected: false,
  226. policyRule: rbac.PolicyRule{},
  227. },
  228. {
  229. testName: "nonResourceURLs with no-empty resources",
  230. verbs: []string{"get"},
  231. groups: nil,
  232. resources: []string{"deployments", "secrets"},
  233. names: nil,
  234. urls: []string{"/api/registry/healthz"},
  235. expected: false,
  236. policyRule: rbac.PolicyRule{},
  237. },
  238. {
  239. testName: "nonResourceURLs with no-empty resourceNames",
  240. verbs: []string{"get"},
  241. groups: nil,
  242. resources: nil,
  243. names: []string{"gakki"},
  244. urls: []string{"/api/registry/healthz"},
  245. expected: false,
  246. policyRule: rbac.PolicyRule{},
  247. },
  248. {
  249. testName: "resource without apiGroups",
  250. verbs: []string{"get"},
  251. groups: nil,
  252. resources: []string{"pod"},
  253. names: []string{""},
  254. urls: nil,
  255. expected: false,
  256. policyRule: rbac.PolicyRule{},
  257. },
  258. {
  259. testName: "resourceNames with illegal verb",
  260. verbs: []string{"list", "watch", "create", "deletecollection"},
  261. groups: []string{""},
  262. resources: []string{"pod"},
  263. names: []string{"gakki"},
  264. urls: nil,
  265. expected: false,
  266. policyRule: rbac.PolicyRule{},
  267. },
  268. {
  269. testName: "no nonResourceURLs nor resources",
  270. verbs: []string{"get"},
  271. groups: []string{"rbac.authorization.k8s.io"},
  272. resources: nil,
  273. names: []string{"gakki"},
  274. urls: nil,
  275. expected: false,
  276. policyRule: rbac.PolicyRule{},
  277. },
  278. }
  279. for _, tc := range tests {
  280. actual, err := rbac.NewRule(tc.verbs...).Groups(tc.groups...).Resources(tc.resources...).Names(tc.names...).URLs(tc.urls...).Rule()
  281. if err != nil {
  282. if tc.expected {
  283. t.Error(err)
  284. } else {
  285. continue
  286. }
  287. }
  288. if !reflect.DeepEqual(actual, tc.policyRule) {
  289. t.Errorf("Expected %s got %s.", tc.policyRule, actual)
  290. }
  291. }
  292. }