github.com/argoproj/argo-cd/v3@v3.2.1/cmd/argocd/commands/admin/settings_rbac_test.go (about) 1 package admin 2 3 import ( 4 "os" 5 "testing" 6 7 "github.com/stretchr/testify/assert" 8 "github.com/stretchr/testify/require" 9 corev1 "k8s.io/api/core/v1" 10 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 11 "k8s.io/client-go/kubernetes/fake" 12 restclient "k8s.io/client-go/rest" 13 "k8s.io/client-go/tools/clientcmd" 14 clientcmdapi "k8s.io/client-go/tools/clientcmd/api" 15 16 "github.com/argoproj/argo-cd/v3/util/rbac" 17 18 "github.com/argoproj/argo-cd/v3/util/assets" 19 ) 20 21 type FakeClientConfig struct { 22 clientConfig clientcmd.ClientConfig 23 } 24 25 func NewFakeClientConfig(clientConfig clientcmd.ClientConfig) *FakeClientConfig { 26 return &FakeClientConfig{clientConfig: clientConfig} 27 } 28 29 func (f *FakeClientConfig) RawConfig() (clientcmdapi.Config, error) { 30 config, err := f.clientConfig.RawConfig() 31 return config, err 32 } 33 34 func (f *FakeClientConfig) ClientConfig() (*restclient.Config, error) { 35 return f.clientConfig.ClientConfig() 36 } 37 38 func (f *FakeClientConfig) Namespace() (string, bool, error) { 39 return f.clientConfig.Namespace() 40 } 41 42 func (f *FakeClientConfig) ConfigAccess() clientcmd.ConfigAccess { 43 return nil 44 } 45 46 func Test_validateRBACResourceAction(t *testing.T) { 47 type args struct { 48 resource string 49 action string 50 } 51 tests := []struct { 52 name string 53 args args 54 valid bool 55 }{ 56 { 57 name: "Test valid resource and action", 58 args: args{ 59 resource: rbac.ResourceApplications, 60 action: rbac.ActionCreate, 61 }, 62 valid: true, 63 }, 64 { 65 name: "Test invalid resource", 66 args: args{ 67 resource: "invalid", 68 }, 69 valid: false, 70 }, 71 { 72 name: "Test invalid action", 73 args: args{ 74 resource: rbac.ResourceApplications, 75 action: "invalid", 76 }, 77 valid: false, 78 }, 79 { 80 name: "Test invalid action for resource", 81 args: args{ 82 resource: rbac.ResourceLogs, 83 action: rbac.ActionCreate, 84 }, 85 valid: false, 86 }, 87 { 88 name: "Test valid action with path", 89 args: args{ 90 resource: rbac.ResourceApplications, 91 action: rbac.ActionAction + "/apps/Deployment/restart", 92 }, 93 valid: true, 94 }, 95 { 96 name: "Test invalid action with path", 97 args: args{ 98 resource: rbac.ResourceApplications, 99 action: rbac.ActionGet + "/apps/Deployment/restart", 100 }, 101 valid: false, 102 }, 103 } 104 105 for _, tt := range tests { 106 t.Run(tt.name, func(t *testing.T) { 107 result := validateRBACResourceAction(tt.args.resource, tt.args.action) 108 if tt.valid { 109 assert.NoError(t, result) 110 } else { 111 assert.Error(t, result) 112 } 113 }) 114 } 115 } 116 117 func Test_PolicyFromCSV(t *testing.T) { 118 ctx := t.Context() 119 120 uPol, dRole, matchMode := getPolicy(ctx, "testdata/rbac/policy.csv", nil, "") 121 require.NotEmpty(t, uPol) 122 require.Empty(t, dRole) 123 require.Empty(t, matchMode) 124 } 125 126 func Test_PolicyFromYAML(t *testing.T) { 127 ctx := t.Context() 128 129 uPol, dRole, matchMode := getPolicy(ctx, "testdata/rbac/argocd-rbac-cm.yaml", nil, "") 130 require.NotEmpty(t, uPol) 131 require.Equal(t, "role:unknown", dRole) 132 require.Empty(t, matchMode) 133 require.True(t, checkPolicy("my-org:team-qa", "update", "project", "foo", 134 "", uPol, dRole, matchMode, true)) 135 } 136 137 func Test_PolicyFromK8s(t *testing.T) { 138 data, err := os.ReadFile("testdata/rbac/policy.csv") 139 ctx := t.Context() 140 141 require.NoError(t, err) 142 kubeclientset := fake.NewClientset(&corev1.ConfigMap{ 143 ObjectMeta: metav1.ObjectMeta{ 144 Name: "argocd-rbac-cm", 145 Namespace: "argocd", 146 }, 147 Data: map[string]string{ 148 "policy.csv": string(data), 149 "policy.default": "role:unknown", 150 }, 151 }) 152 uPol, dRole, matchMode := getPolicy(ctx, "", kubeclientset, "argocd") 153 require.NotEmpty(t, uPol) 154 require.Equal(t, "role:unknown", dRole) 155 require.Empty(t, matchMode) 156 157 t.Run("get applications", func(t *testing.T) { 158 ok := checkPolicy("role:user", "get", "applications", "*/*", assets.BuiltinPolicyCSV, uPol, dRole, "", true) 159 require.True(t, ok) 160 }) 161 t.Run("get clusters", func(t *testing.T) { 162 ok := checkPolicy("role:user", "get", "clusters", "*", assets.BuiltinPolicyCSV, uPol, dRole, "", true) 163 require.True(t, ok) 164 }) 165 t.Run("get certificates", func(t *testing.T) { 166 ok := checkPolicy("role:user", "get", "certificates", "*", assets.BuiltinPolicyCSV, uPol, dRole, "", true) 167 require.False(t, ok) 168 }) 169 t.Run("get certificates by default role", func(t *testing.T) { 170 ok := checkPolicy("role:user", "get", "certificates", "*", assets.BuiltinPolicyCSV, uPol, "role:readonly", "glob", true) 171 require.True(t, ok) 172 }) 173 t.Run("get certificates by default role without builtin policy", func(t *testing.T) { 174 ok := checkPolicy("role:user", "get", "certificates", "*", "", uPol, "role:readonly", "glob", true) 175 require.False(t, ok) 176 }) 177 t.Run("use regex match mode instead of glob", func(t *testing.T) { 178 ok := checkPolicy("role:user", "get", "certificates", ".*", assets.BuiltinPolicyCSV, uPol, "role:readonly", "regex", true) 179 require.False(t, ok) 180 }) 181 t.Run("get logs", func(t *testing.T) { 182 ok := checkPolicy("role:test", "get", "logs", "*/*", assets.BuiltinPolicyCSV, uPol, dRole, "", true) 183 require.True(t, ok) 184 }) 185 t.Run("no-such-user get logs", func(t *testing.T) { 186 ok := checkPolicy("no-such-user", "get", "logs", "*/*", assets.BuiltinPolicyCSV, uPol, dRole, "", true) 187 require.False(t, ok) 188 }) 189 t.Run("log-deny-user get logs", func(t *testing.T) { 190 ok := checkPolicy("log-deny-user", "get", "logs", "*/*", assets.BuiltinPolicyCSV, uPol, dRole, "", true) 191 require.False(t, ok) 192 }) 193 t.Run("log-allow-user get logs", func(t *testing.T) { 194 ok := checkPolicy("log-allow-user", "get", "logs", "*/*", assets.BuiltinPolicyCSV, uPol, dRole, "", true) 195 require.True(t, ok) 196 }) 197 t.Run("get logs", func(t *testing.T) { 198 ok := checkPolicy("role:test", "get", "logs", "*", assets.BuiltinPolicyCSV, uPol, dRole, "", true) 199 require.True(t, ok) 200 }) 201 t.Run("get logs", func(t *testing.T) { 202 ok := checkPolicy("role:test", "get", "logs", "", assets.BuiltinPolicyCSV, uPol, dRole, "", true) 203 require.True(t, ok) 204 }) 205 t.Run("create exec", func(t *testing.T) { 206 ok := checkPolicy("role:test", "create", "exec", "*/*", assets.BuiltinPolicyCSV, uPol, dRole, "", true) 207 require.True(t, ok) 208 }) 209 t.Run("create applicationsets", func(t *testing.T) { 210 ok := checkPolicy("role:user", "create", "applicationsets", "*/*", assets.BuiltinPolicyCSV, uPol, dRole, "", true) 211 require.True(t, ok) 212 }) 213 t.Run("delete applicationsets", func(t *testing.T) { 214 ok := checkPolicy("role:user", "delete", "applicationsets", "*/*", assets.BuiltinPolicyCSV, uPol, dRole, "", true) 215 require.True(t, ok) 216 }) 217 } 218 219 func Test_PolicyFromK8sUsingRegex(t *testing.T) { 220 ctx := t.Context() 221 222 policy := ` 223 p, role:user, clusters, get, .+, allow 224 p, role:user, clusters, get, https://kubernetes.*, deny 225 p, role:user, applications, get, .*, allow 226 p, role:user, applications, create, .*/.*, allow 227 p, role:user, applicationsets, create, .*/.*, allow 228 p, role:user, applicationsets, delete, .*/.*, allow 229 p, role:user, logs, get, .*/.*, allow 230 p, role:user, exec, create, .*/.*, allow 231 ` 232 233 kubeclientset := fake.NewClientset(&corev1.ConfigMap{ 234 ObjectMeta: metav1.ObjectMeta{ 235 Name: "argocd-rbac-cm", 236 Namespace: "argocd", 237 }, 238 Data: map[string]string{ 239 "policy.csv": policy, 240 "policy.default": "role:unknown", 241 "policy.matchMode": "regex", 242 }, 243 }) 244 uPol, dRole, matchMode := getPolicy(ctx, "", kubeclientset, "argocd") 245 require.NotEmpty(t, uPol) 246 require.Equal(t, "role:unknown", dRole) 247 require.Equal(t, "regex", matchMode) 248 249 builtInPolicy := ` 250 p, role:readonly, certificates, get, .*, allow 251 p, role:, certificates, get, .*, allow` 252 253 t.Run("get applications", func(t *testing.T) { 254 ok := checkPolicy("role:user", "get", "applications", ".*/.*", builtInPolicy, uPol, dRole, "regex", true) 255 require.True(t, ok) 256 }) 257 t.Run("get clusters", func(t *testing.T) { 258 ok := checkPolicy("role:user", "get", "clusters", ".*", builtInPolicy, uPol, dRole, "regex", true) 259 require.True(t, ok) 260 }) 261 t.Run("get certificates", func(t *testing.T) { 262 ok := checkPolicy("role:user", "get", "certificates", ".*", builtInPolicy, uPol, dRole, "regex", true) 263 require.False(t, ok) 264 }) 265 t.Run("get certificates by default role", func(t *testing.T) { 266 ok := checkPolicy("role:user", "get", "certificates", ".*", builtInPolicy, uPol, "role:readonly", "regex", true) 267 require.True(t, ok) 268 }) 269 t.Run("get certificates by default role without builtin policy", func(t *testing.T) { 270 ok := checkPolicy("role:user", "get", "certificates", ".*", "", uPol, "role:readonly", "regex", true) 271 require.False(t, ok) 272 }) 273 t.Run("use glob match mode instead of regex", func(t *testing.T) { 274 ok := checkPolicy("role:user", "get", "certificates", ".+", builtInPolicy, uPol, dRole, "glob", true) 275 require.False(t, ok) 276 }) 277 t.Run("get logs via glob match mode", func(t *testing.T) { 278 ok := checkPolicy("role:user", "get", "logs", ".*/.*", builtInPolicy, uPol, dRole, "glob", true) 279 require.True(t, ok) 280 }) 281 t.Run("create exec", func(t *testing.T) { 282 ok := checkPolicy("role:user", "create", "exec", ".*/.*", builtInPolicy, uPol, dRole, "regex", true) 283 require.True(t, ok) 284 }) 285 t.Run("create applicationsets", func(t *testing.T) { 286 ok := checkPolicy("role:user", "create", "applicationsets", ".*/.*", builtInPolicy, uPol, dRole, "regex", true) 287 require.True(t, ok) 288 }) 289 t.Run("delete applicationsets", func(t *testing.T) { 290 ok := checkPolicy("role:user", "delete", "applicationsets", ".*/.*", builtInPolicy, uPol, dRole, "regex", true) 291 require.True(t, ok) 292 }) 293 } 294 295 func TestNewRBACCanCommand(t *testing.T) { 296 command := NewRBACCanCommand() 297 298 require.NotNil(t, command) 299 assert.Equal(t, "can", command.Name()) 300 assert.Equal(t, "Check RBAC permissions for a role or subject", command.Short) 301 } 302 303 func TestNewRBACValidateCommand(t *testing.T) { 304 command := NewRBACValidateCommand() 305 306 require.NotNil(t, command) 307 assert.Equal(t, "validate", command.Name()) 308 assert.Equal(t, "Validate RBAC policy", command.Short) 309 }