github.com/argoproj/argo-cd@v1.8.7/util/rbac/rbac_test.go (about) 1 package rbac 2 3 import ( 4 "context" 5 "fmt" 6 "testing" 7 "time" 8 9 "github.com/dgrijalva/jwt-go/v4" 10 log "github.com/sirupsen/logrus" 11 "github.com/stretchr/testify/assert" 12 apiv1 "k8s.io/api/core/v1" 13 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 14 "k8s.io/client-go/kubernetes/fake" 15 16 "github.com/argoproj/argo-cd/util/assets" 17 ) 18 19 const ( 20 fakeConfigMapName = "fake-cm" 21 fakeNamespace = "fake-ns" 22 ) 23 24 var ( 25 noOpUpdate = func(cm *apiv1.ConfigMap) error { 26 return nil 27 } 28 ) 29 30 func fakeConfigMap() *apiv1.ConfigMap { 31 cm := apiv1.ConfigMap{ 32 TypeMeta: metav1.TypeMeta{ 33 Kind: "ConfigMap", 34 APIVersion: "v1", 35 }, 36 ObjectMeta: metav1.ObjectMeta{ 37 Name: fakeConfigMapName, 38 Namespace: fakeNamespace, 39 }, 40 Data: make(map[string]string), 41 } 42 return &cm 43 } 44 45 // TestBuiltinPolicyEnforcer tests the builtin policy rules 46 func TestBuiltinPolicyEnforcer(t *testing.T) { 47 kubeclientset := fake.NewSimpleClientset() 48 enf := NewEnforcer(kubeclientset, fakeNamespace, fakeConfigMapName, nil) 49 err := enf.syncUpdate(fakeConfigMap(), noOpUpdate) 50 assert.Nil(t, err) 51 52 // Without setting builtin policy, this should fail 53 assert.False(t, enf.Enforce("admin", "applications", "get", "foo/bar")) 54 55 // now set builtin policy 56 _ = enf.SetBuiltinPolicy(assets.BuiltinPolicyCSV) 57 58 allowed := [][]interface{}{ 59 {"admin", "applications", "get", "foo/bar"}, 60 {"admin", "applications", "delete", "foo/bar"}, 61 {"role:readonly", "applications", "get", "foo/bar"}, 62 {"role:admin", "applications", "get", "foo/bar"}, 63 {"role:admin", "applications", "delete", "foo/bar"}, 64 } 65 for _, a := range allowed { 66 if !assert.True(t, enf.Enforce(a...)) { 67 log.Errorf("%s: expected true, got false", a) 68 } 69 } 70 71 disallowed := [][]interface{}{ 72 {"role:readonly", "applications", "create", "foo/bar"}, 73 {"role:readonly", "applications", "delete", "foo/bar"}, 74 } 75 for _, a := range disallowed { 76 if !assert.False(t, enf.Enforce(a...)) { 77 log.Errorf("%s: expected false, got true", a) 78 } 79 } 80 } 81 82 // TestProjectIsolationEnforcement verifies the ability to create Project specific policies 83 func TestProjectIsolationEnforcement(t *testing.T) { 84 kubeclientset := fake.NewSimpleClientset(fakeConfigMap()) 85 enf := NewEnforcer(kubeclientset, fakeNamespace, fakeConfigMapName, nil) 86 policy := ` 87 p, role:foo-admin, *, *, foo/*, allow 88 p, role:bar-admin, *, *, bar/*, allow 89 g, alice, role:foo-admin 90 g, bob, role:bar-admin 91 ` 92 _ = enf.SetBuiltinPolicy(policy) 93 94 // verify alice can only affect objects in foo and not bar, 95 // and that bob can only affect objects in bar and not foo 96 assert.True(t, enf.Enforce("bob", "applications", "delete", "bar/obj")) 97 assert.False(t, enf.Enforce("bob", "applications", "delete", "foo/obj")) 98 assert.True(t, enf.Enforce("alice", "applications", "delete", "foo/obj")) 99 assert.False(t, enf.Enforce("alice", "applications", "delete", "bar/obj")) 100 } 101 102 // TestProjectReadOnly verifies the ability to have a read only role in a Project 103 func TestProjectReadOnly(t *testing.T) { 104 kubeclientset := fake.NewSimpleClientset(fakeConfigMap()) 105 enf := NewEnforcer(kubeclientset, fakeNamespace, fakeConfigMapName, nil) 106 policy := ` 107 p, role:foo-readonly, *, get, foo/*, allow 108 g, alice, role:foo-readonly 109 ` 110 _ = enf.SetBuiltinPolicy(policy) 111 112 assert.True(t, enf.Enforce("alice", "applications", "get", "foo/obj")) 113 assert.False(t, enf.Enforce("alice", "applications", "delete", "bar/obj")) 114 assert.False(t, enf.Enforce("alice", "applications", "get", "bar/obj")) 115 assert.False(t, enf.Enforce("bob", "applications", "get", "foo/obj")) 116 } 117 118 // TestDefaultRole tests the ability to set a default role 119 func TestDefaultRole(t *testing.T) { 120 kubeclientset := fake.NewSimpleClientset() 121 enf := NewEnforcer(kubeclientset, fakeNamespace, fakeConfigMapName, nil) 122 err := enf.syncUpdate(fakeConfigMap(), noOpUpdate) 123 assert.Nil(t, err) 124 _ = enf.SetBuiltinPolicy(assets.BuiltinPolicyCSV) 125 126 assert.False(t, enf.Enforce("bob", "applications", "get", "foo/bar")) 127 // after setting the default role to be the read-only role, this should now pass 128 enf.SetDefaultRole("role:readonly") 129 assert.True(t, enf.Enforce("bob", "applications", "get", "foo/bar")) 130 } 131 132 // TestURLAsObjectName tests the ability to have a URL as an object name 133 func TestURLAsObjectName(t *testing.T) { 134 kubeclientset := fake.NewSimpleClientset() 135 enf := NewEnforcer(kubeclientset, fakeNamespace, fakeConfigMapName, nil) 136 err := enf.syncUpdate(fakeConfigMap(), noOpUpdate) 137 assert.Nil(t, err) 138 policy := ` 139 p, alice, repositories, *, foo/*, allow 140 p, bob, repositories, *, foo/https://github.com/argoproj/argo-cd.git, allow 141 p, cathy, repositories, *, foo/*, allow 142 ` 143 _ = enf.SetUserPolicy(policy) 144 145 assert.True(t, enf.Enforce("alice", "repositories", "delete", "foo/https://github.com/argoproj/argo-cd.git")) 146 assert.True(t, enf.Enforce("alice", "repositories", "delete", "foo/https://github.com/golang/go.git")) 147 148 assert.True(t, enf.Enforce("bob", "repositories", "delete", "foo/https://github.com/argoproj/argo-cd.git")) 149 assert.False(t, enf.Enforce("bob", "repositories", "delete", "foo/https://github.com/golang/go.git")) 150 151 } 152 153 func TestEnableDisableEnforce(t *testing.T) { 154 kubeclientset := fake.NewSimpleClientset(fakeConfigMap()) 155 enf := NewEnforcer(kubeclientset, fakeNamespace, fakeConfigMapName, nil) 156 policy := ` 157 p, alice, *, get, foo/obj, allow 158 p, mike, *, get, foo/obj, deny 159 ` 160 _ = enf.SetUserPolicy(policy) 161 enf.SetClaimsEnforcerFunc(func(claims jwt.Claims, rvals ...interface{}) bool { 162 return false 163 }) 164 165 assert.True(t, enf.Enforce("alice", "applications", "get", "foo/obj")) 166 assert.False(t, enf.Enforce("alice", "applications/resources", "delete", "foo/obj")) 167 assert.False(t, enf.Enforce("mike", "applications", "get", "foo/obj")) 168 assert.False(t, enf.Enforce("mike", "applications/resources", "delete", "foo/obj")) 169 assert.False(t, enf.Enforce(nil, "applications/resources", "delete", "foo/obj")) 170 assert.False(t, enf.Enforce(&jwt.StandardClaims{}, "applications/resources", "delete", "foo/obj")) 171 172 enf.EnableEnforce(false) 173 assert.True(t, enf.Enforce("alice", "applications", "get", "foo/obj")) 174 assert.True(t, enf.Enforce("alice", "applications/resources", "delete", "foo/obj")) 175 assert.True(t, enf.Enforce("mike", "applications", "get", "foo/obj")) 176 assert.True(t, enf.Enforce("mike", "applications/resources", "delete", "foo/obj")) 177 assert.True(t, enf.Enforce(nil, "applications/resources", "delete", "foo/obj")) 178 assert.True(t, enf.Enforce(&jwt.StandardClaims{}, "applications/resources", "delete", "foo/obj")) 179 } 180 181 func TestUpdatePolicy(t *testing.T) { 182 kubeclientset := fake.NewSimpleClientset(fakeConfigMap()) 183 enf := NewEnforcer(kubeclientset, fakeNamespace, fakeConfigMapName, nil) 184 185 _ = enf.SetUserPolicy("p, alice, *, get, foo/obj, allow") 186 assert.True(t, enf.Enforce("alice", "applications", "get", "foo/obj")) 187 assert.False(t, enf.Enforce("bob", "applications", "get", "foo/obj")) 188 189 _ = enf.SetUserPolicy("p, bob, *, get, foo/obj, allow") 190 assert.False(t, enf.Enforce("alice", "applications", "get", "foo/obj")) 191 assert.True(t, enf.Enforce("bob", "applications", "get", "foo/obj")) 192 193 _ = enf.SetUserPolicy("") 194 assert.False(t, enf.Enforce("alice", "applications", "get", "foo/obj")) 195 assert.False(t, enf.Enforce("bob", "applications", "get", "foo/obj")) 196 197 _ = enf.SetBuiltinPolicy("p, alice, *, get, foo/obj, allow") 198 assert.True(t, enf.Enforce("alice", "applications", "get", "foo/obj")) 199 assert.False(t, enf.Enforce("bob", "applications", "get", "foo/obj")) 200 201 _ = enf.SetBuiltinPolicy("p, bob, *, get, foo/obj, allow") 202 assert.False(t, enf.Enforce("alice", "applications", "get", "foo/obj")) 203 assert.True(t, enf.Enforce("bob", "applications", "get", "foo/obj")) 204 205 _ = enf.SetBuiltinPolicy("") 206 assert.False(t, enf.Enforce("alice", "applications", "get", "foo/obj")) 207 assert.False(t, enf.Enforce("bob", "applications", "get", "foo/obj")) 208 } 209 210 func TestNoPolicy(t *testing.T) { 211 cm := fakeConfigMap() 212 kubeclientset := fake.NewSimpleClientset(cm) 213 enf := NewEnforcer(kubeclientset, fakeNamespace, fakeConfigMapName, nil) 214 assert.False(t, enf.Enforce("admin", "applications", "delete", "foo/bar")) 215 } 216 217 // TestClaimsEnforcerFunc tests 218 func TestClaimsEnforcerFunc(t *testing.T) { 219 kubeclientset := fake.NewSimpleClientset() 220 enf := NewEnforcer(kubeclientset, fakeNamespace, fakeConfigMapName, nil) 221 claims := jwt.StandardClaims{ 222 Subject: "foo", 223 } 224 assert.False(t, enf.Enforce(&claims, "applications", "get", "foo/bar")) 225 enf.SetClaimsEnforcerFunc(func(claims jwt.Claims, rvals ...interface{}) bool { 226 return true 227 }) 228 assert.True(t, enf.Enforce(&claims, "applications", "get", "foo/bar")) 229 } 230 231 // TestDefaultRoleWithRuntimePolicy tests the ability for a default role to still take affect when 232 // enforcing a runtime policy 233 func TestDefaultRoleWithRuntimePolicy(t *testing.T) { 234 kubeclientset := fake.NewSimpleClientset() 235 enf := NewEnforcer(kubeclientset, fakeNamespace, fakeConfigMapName, nil) 236 err := enf.syncUpdate(fakeConfigMap(), noOpUpdate) 237 assert.Nil(t, err) 238 runtimePolicy := assets.BuiltinPolicyCSV 239 assert.False(t, enf.EnforceRuntimePolicy(runtimePolicy, "bob", "applications", "get", "foo/bar")) 240 enf.SetDefaultRole("role:readonly") 241 assert.True(t, enf.EnforceRuntimePolicy(runtimePolicy, "bob", "applications", "get", "foo/bar")) 242 } 243 244 // TestClaimsEnforcerFuncWithRuntimePolicy tests the ability for claims enforcer function to still 245 // take effect when enforcing a runtime policy 246 func TestClaimsEnforcerFuncWithRuntimePolicy(t *testing.T) { 247 kubeclientset := fake.NewSimpleClientset() 248 enf := NewEnforcer(kubeclientset, fakeNamespace, fakeConfigMapName, nil) 249 err := enf.syncUpdate(fakeConfigMap(), noOpUpdate) 250 assert.Nil(t, err) 251 runtimePolicy := assets.BuiltinPolicyCSV 252 claims := jwt.StandardClaims{ 253 Subject: "foo", 254 } 255 assert.False(t, enf.EnforceRuntimePolicy(runtimePolicy, claims, "applications", "get", "foo/bar")) 256 enf.SetClaimsEnforcerFunc(func(claims jwt.Claims, rvals ...interface{}) bool { 257 return true 258 }) 259 assert.True(t, enf.EnforceRuntimePolicy(runtimePolicy, claims, "applications", "get", "foo/bar")) 260 } 261 262 // TestInvalidRuntimePolicy tests when an invalid policy is supplied, it falls back to normal enforcement 263 func TestInvalidRuntimePolicy(t *testing.T) { 264 cm := fakeConfigMap() 265 kubeclientset := fake.NewSimpleClientset(cm) 266 enf := NewEnforcer(kubeclientset, fakeNamespace, fakeConfigMapName, nil) 267 err := enf.syncUpdate(fakeConfigMap(), noOpUpdate) 268 assert.Nil(t, err) 269 _ = enf.SetBuiltinPolicy(assets.BuiltinPolicyCSV) 270 assert.True(t, enf.EnforceRuntimePolicy("", "admin", "applications", "update", "foo/bar")) 271 assert.False(t, enf.EnforceRuntimePolicy("", "role:readonly", "applications", "update", "foo/bar")) 272 badPolicy := "this, is, not, a, good, policy" 273 assert.True(t, enf.EnforceRuntimePolicy(badPolicy, "admin", "applications", "update", "foo/bar")) 274 assert.False(t, enf.EnforceRuntimePolicy(badPolicy, "role:readonly", "applications", "update", "foo/bar")) 275 } 276 277 func TestValidatePolicy(t *testing.T) { 278 goodPolicies := []string{ 279 "p, role:admin, projects, delete, *, allow", 280 "", 281 "#", 282 `p, "role,admin", projects, delete, *, allow`, 283 ` p, role:admin, projects, delete, *, allow `, 284 } 285 for _, good := range goodPolicies { 286 assert.Nil(t, ValidatePolicy(good)) 287 } 288 badPolicies := []string{ 289 "this, is, not, a, good, policy", 290 "this\ttoo", 291 } 292 for _, bad := range badPolicies { 293 assert.Error(t, ValidatePolicy(bad)) 294 } 295 } 296 297 // TestEnforceErrorMessage ensures we give descriptive error message 298 func TestEnforceErrorMessage(t *testing.T) { 299 kubeclientset := fake.NewSimpleClientset() 300 enf := NewEnforcer(kubeclientset, fakeNamespace, fakeConfigMapName, nil) 301 err := enf.syncUpdate(fakeConfigMap(), noOpUpdate) 302 assert.Nil(t, err) 303 304 err = enf.EnforceErr("admin", "applications", "get", "foo/bar") 305 assert.Error(t, err) 306 assert.Equal(t, "rpc error: code = PermissionDenied desc = permission denied: applications, get, foo/bar", err.Error()) 307 308 err = enf.EnforceErr() 309 assert.Error(t, err) 310 assert.Equal(t, "rpc error: code = PermissionDenied desc = permission denied", err.Error()) 311 312 // nolint:staticcheck 313 ctx := context.WithValue(context.Background(), "claims", &jwt.StandardClaims{Subject: "proj:default:admin"}) 314 err = enf.EnforceErr(ctx.Value("claims"), "project") 315 assert.Error(t, err) 316 assert.Equal(t, "rpc error: code = PermissionDenied desc = permission denied: project, sub: proj:default:admin", err.Error()) 317 318 iat := time.Unix(int64(1593035962), 0).Format(time.RFC3339) 319 exp := fmt.Sprintf("rpc error: code = PermissionDenied desc = permission denied: project, sub: proj:default:admin, iat: %s", iat) 320 // nolint:staticcheck 321 ctx = context.WithValue(context.Background(), "claims", &jwt.StandardClaims{Subject: "proj:default:admin", IssuedAt: jwt.NewTime(1593035962)}) 322 err = enf.EnforceErr(ctx.Value("claims"), "project") 323 assert.Error(t, err) 324 assert.Equal(t, exp, err.Error()) 325 326 // nolint:staticcheck 327 ctx = context.WithValue(context.Background(), "claims", &jwt.StandardClaims{ExpiresAt: jwt.NewTime(1)}) 328 err = enf.EnforceErr(ctx.Value("claims"), "project") 329 assert.Error(t, err) 330 assert.Equal(t, "rpc error: code = PermissionDenied desc = permission denied: project", err.Error()) 331 332 // nolint:staticcheck 333 ctx = context.WithValue(context.Background(), "claims", &jwt.StandardClaims{Subject: "proj:default:admin", IssuedAt: nil}) 334 err = enf.EnforceErr(ctx.Value("claims"), "project") 335 assert.Error(t, err) 336 assert.Equal(t, "rpc error: code = PermissionDenied desc = permission denied: project, sub: proj:default:admin", err.Error()) 337 338 }