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  }