github.com/argoproj/argo-cd/v3@v3.2.1/server/rbacpolicy/rbacpolicy_test.go (about)

     1  package rbacpolicy
     2  
     3  import (
     4  	"fmt"
     5  	"testing"
     6  
     7  	"github.com/golang-jwt/jwt/v5"
     8  	"github.com/stretchr/testify/assert"
     9  	"github.com/stretchr/testify/require"
    10  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    11  	"k8s.io/client-go/kubernetes/fake"
    12  
    13  	"github.com/argoproj/argo-cd/v3/common"
    14  	argoappv1 "github.com/argoproj/argo-cd/v3/pkg/apis/application/v1alpha1"
    15  	"github.com/argoproj/argo-cd/v3/test"
    16  	"github.com/argoproj/argo-cd/v3/util/rbac"
    17  )
    18  
    19  func newFakeProj() *argoappv1.AppProject {
    20  	jwtTokenByRole := make(map[string]argoappv1.JWTTokens)
    21  	jwtTokenByRole["my-role"] = argoappv1.JWTTokens{Items: []argoappv1.JWTToken{{IssuedAt: 1234}}}
    22  
    23  	return &argoappv1.AppProject{
    24  		ObjectMeta: metav1.ObjectMeta{
    25  			Name:      "my-proj",
    26  			Namespace: test.FakeArgoCDNamespace,
    27  		},
    28  		Spec: argoappv1.AppProjectSpec{
    29  			Roles: []argoappv1.ProjectRole{
    30  				{
    31  					Name: "my-role",
    32  					Policies: []string{
    33  						"p, proj:my-proj:my-role, applications, create, my-proj/*, allow",
    34  						"p, proj:my-proj:my-role, logs, get, my-proj/*, allow",
    35  						"p, proj:my-proj:my-role, exec, create, my-proj/*, allow",
    36  					},
    37  					Groups: []string{
    38  						"my-org:my-team",
    39  					},
    40  					JWTTokens: []argoappv1.JWTToken{
    41  						{
    42  							IssuedAt: 1234,
    43  						},
    44  					},
    45  				},
    46  			},
    47  		},
    48  		Status: argoappv1.AppProjectStatus{JWTTokensByRole: jwtTokenByRole},
    49  	}
    50  }
    51  
    52  func TestEnforceAllPolicies(t *testing.T) {
    53  	kubeclientset := fake.NewClientset(test.NewFakeConfigMap())
    54  	projLister := test.NewFakeProjLister(newFakeProj())
    55  	enf := rbac.NewEnforcer(kubeclientset, test.FakeArgoCDNamespace, common.ArgoCDConfigMapName, nil)
    56  	enf.EnableLog(true)
    57  	_ = enf.SetBuiltinPolicy(`p, alice, applications, create, my-proj/*, allow` + "\n" + `p, alice, logs, get, my-proj/*, allow` + "\n" + `p, alice, exec, create, my-proj/*, allow`)
    58  	_ = enf.SetUserPolicy(`p, bob, applications, create, my-proj/*, allow` + "\n" + `p, bob, logs, get, my-proj/*, allow` + "\n" + `p, bob, exec, create, my-proj/*, allow`)
    59  	rbacEnf := NewRBACPolicyEnforcer(enf, projLister)
    60  	enf.SetClaimsEnforcerFunc(rbacEnf.EnforceClaims)
    61  
    62  	claims := jwt.MapClaims{"sub": "alice"}
    63  	assert.True(t, enf.Enforce(claims, "applications", "create", "my-proj/my-app"))
    64  	assert.True(t, enf.Enforce(claims, "logs", "get", "my-proj/my-app"))
    65  	assert.True(t, enf.Enforce(claims, "exec", "create", "my-proj/my-app"))
    66  
    67  	claims = jwt.MapClaims{"sub": "bob"}
    68  	assert.True(t, enf.Enforce(claims, "applications", "create", "my-proj/my-app"))
    69  	assert.True(t, enf.Enforce(claims, "logs", "get", "my-proj/my-app"))
    70  	assert.True(t, enf.Enforce(claims, "exec", "create", "my-proj/my-app"))
    71  
    72  	claims = jwt.MapClaims{"sub": "qwertyuiop", "federated_claims": map[string]any{"user_id": "bob"}}
    73  	assert.True(t, enf.Enforce(claims, "applications", "create", "my-proj/my-app"))
    74  	assert.True(t, enf.Enforce(claims, "logs", "get", "my-proj/my-app"))
    75  	assert.True(t, enf.Enforce(claims, "exec", "create", "my-proj/my-app"))
    76  
    77  	claims = jwt.MapClaims{"sub": "proj:my-proj:my-role", "iat": 1234}
    78  	assert.True(t, enf.Enforce(claims, "applications", "create", "my-proj/my-app"))
    79  	assert.True(t, enf.Enforce(claims, "logs", "get", "my-proj/my-app"))
    80  	assert.True(t, enf.Enforce(claims, "exec", "create", "my-proj/my-app"))
    81  
    82  	claims = jwt.MapClaims{"groups": []string{"my-org:my-team"}}
    83  	assert.True(t, enf.Enforce(claims, "applications", "create", "my-proj/my-app"))
    84  	assert.True(t, enf.Enforce(claims, "logs", "get", "my-proj/my-app"))
    85  	assert.True(t, enf.Enforce(claims, "exec", "create", "my-proj/my-app"))
    86  
    87  	claims = jwt.MapClaims{"sub": "cathy"}
    88  	assert.False(t, enf.Enforce(claims, "applications", "create", "my-proj/my-app"))
    89  	assert.False(t, enf.Enforce(claims, "logs", "get", "my-proj/my-app"))
    90  	assert.False(t, enf.Enforce(claims, "exec", "create", "my-proj/my-app"))
    91  
    92  	// AWS cognito returns its groups in  cognito:groups
    93  	rbacEnf.SetScopes([]string{"cognito:groups"})
    94  	claims = jwt.MapClaims{"cognito:groups": []string{"my-org:my-team"}}
    95  	assert.True(t, enf.Enforce(claims, "applications", "create", "my-proj/my-app"))
    96  }
    97  
    98  func TestEnforceActionActions(t *testing.T) {
    99  	kubeclientset := fake.NewClientset(test.NewFakeConfigMap())
   100  	projLister := test.NewFakeProjLister(newFakeProj())
   101  	enf := rbac.NewEnforcer(kubeclientset, test.FakeArgoCDNamespace, common.ArgoCDConfigMapName, nil)
   102  	enf.EnableLog(true)
   103  	_ = enf.SetBuiltinPolicy(fmt.Sprintf(`p, alice, applications, %s/*, my-proj/*, allow
   104  p, bob, applications, %s/argoproj.io/Rollout/*, my-proj/*, allow
   105  p, cam, applications, %s/argoproj.io/Rollout/resume, my-proj/*, allow
   106  `, rbac.ActionAction, rbac.ActionAction, rbac.ActionAction))
   107  	rbacEnf := NewRBACPolicyEnforcer(enf, projLister)
   108  	enf.SetClaimsEnforcerFunc(rbacEnf.EnforceClaims)
   109  
   110  	// Alice has wild-card approval for all actions
   111  	claims := jwt.MapClaims{"sub": "alice"}
   112  	assert.True(t, enf.Enforce(claims, "applications", rbac.ActionAction+"/argoproj.io/Rollout/resume", "my-proj/my-app"))
   113  	claims = jwt.MapClaims{"sub": "alice"}
   114  	assert.True(t, enf.Enforce(claims, "applications", rbac.ActionAction+"/argoproj.io/NewCrd/abort", "my-proj/my-app"))
   115  	// Bob has wild-card approval for all actions under argoproj.io/Rollout
   116  	claims = jwt.MapClaims{"sub": "bob"}
   117  	assert.True(t, enf.Enforce(claims, "applications", rbac.ActionAction+"/argoproj.io/Rollout/resume", "my-proj/my-app"))
   118  	claims = jwt.MapClaims{"sub": "bob"}
   119  	assert.False(t, enf.Enforce(claims, "applications", rbac.ActionAction+"/argoproj.io/NewCrd/abort", "my-proj/my-app"))
   120  	// Cam only has approval for actions/argoproj.io/Rollout:resume
   121  	claims = jwt.MapClaims{"sub": "cam"}
   122  	assert.True(t, enf.Enforce(claims, "applications", rbac.ActionAction+"/argoproj.io/Rollout/resume", "my-proj/my-app"))
   123  	claims = jwt.MapClaims{"sub": "cam"}
   124  	assert.False(t, enf.Enforce(claims, "applications", rbac.ActionAction+"/argoproj.io/Rollout/abort", "my-proj/my-app"))
   125  
   126  	// Eve does not have approval for any actions
   127  	claims = jwt.MapClaims{"sub": "eve"}
   128  	assert.False(t, enf.Enforce(claims, "applications", rbac.ActionAction+"/argoproj.io/Rollout/resume", "my-proj/my-app"))
   129  }
   130  
   131  func TestInvalidatedCache(t *testing.T) {
   132  	kubeclientset := fake.NewClientset(test.NewFakeConfigMap())
   133  	projLister := test.NewFakeProjLister(newFakeProj())
   134  	enf := rbac.NewEnforcer(kubeclientset, test.FakeArgoCDNamespace, common.ArgoCDConfigMapName, nil)
   135  	enf.EnableLog(true)
   136  	_ = enf.SetBuiltinPolicy(`p, alice, applications, create, my-proj/*, allow` + "\n" + `p, alice, logs, get, my-proj/*, allow` + "\n" + `p, alice, exec, create, my-proj/*, allow`)
   137  	_ = enf.SetUserPolicy(`p, bob, applications, create, my-proj/*, allow` + "\n" + `p, bob, logs, get, my-proj/*, allow` + "\n" + `p, bob, exec, create, my-proj/*, allow`)
   138  	rbacEnf := NewRBACPolicyEnforcer(enf, projLister)
   139  	enf.SetClaimsEnforcerFunc(rbacEnf.EnforceClaims)
   140  
   141  	claims := jwt.MapClaims{"sub": "alice"}
   142  	assert.True(t, enf.Enforce(claims, "applications", "create", "my-proj/my-app"))
   143  	assert.True(t, enf.Enforce(claims, "logs", "get", "my-proj/my-app"))
   144  	assert.True(t, enf.Enforce(claims, "exec", "create", "my-proj/my-app"))
   145  
   146  	claims = jwt.MapClaims{"sub": "bob"}
   147  	assert.True(t, enf.Enforce(claims, "applications", "create", "my-proj/my-app"))
   148  	assert.True(t, enf.Enforce(claims, "logs", "get", "my-proj/my-app"))
   149  	assert.True(t, enf.Enforce(claims, "exec", "create", "my-proj/my-app"))
   150  
   151  	_ = enf.SetBuiltinPolicy(`p, alice, applications, create, my-proj2/*, allow` + "\n" + `p, alice, logs, get, my-proj2/*, allow` + "\n" + `p, alice, exec, create, my-proj2/*, allow`)
   152  	_ = enf.SetUserPolicy(`p, bob, applications, create, my-proj2/*, allow` + "\n" + `p, bob, logs, get, my-proj2/*, allow` + "\n" + `p, bob, exec, create, my-proj2/*, allow`)
   153  	claims = jwt.MapClaims{"sub": "alice"}
   154  	assert.True(t, enf.Enforce(claims, "applications", "create", "my-proj2/my-app"))
   155  	assert.True(t, enf.Enforce(claims, "logs", "get", "my-proj2/my-app"))
   156  	assert.True(t, enf.Enforce(claims, "exec", "create", "my-proj2/my-app"))
   157  
   158  	claims = jwt.MapClaims{"sub": "bob"}
   159  	assert.True(t, enf.Enforce(claims, "applications", "create", "my-proj2/my-app"))
   160  	assert.True(t, enf.Enforce(claims, "logs", "get", "my-proj2/my-app"))
   161  	assert.True(t, enf.Enforce(claims, "exec", "create", "my-proj2/my-app"))
   162  
   163  	claims = jwt.MapClaims{"sub": "alice"}
   164  	assert.False(t, enf.Enforce(claims, "applications", "create", "my-proj/my-app"))
   165  	assert.False(t, enf.Enforce(claims, "logs", "get", "my-proj/my-app"))
   166  	assert.False(t, enf.Enforce(claims, "exec", "create", "my-proj/my-app"))
   167  
   168  	claims = jwt.MapClaims{"sub": "bob"}
   169  	assert.False(t, enf.Enforce(claims, "applications", "create", "my-proj/my-app"))
   170  	assert.False(t, enf.Enforce(claims, "logs", "get", "my-proj/my-app"))
   171  	assert.False(t, enf.Enforce(claims, "exec", "create", "my-proj/my-app"))
   172  }
   173  
   174  func TestGetScopes_DefaultScopes(t *testing.T) {
   175  	rbacEnforcer := NewRBACPolicyEnforcer(nil, nil)
   176  
   177  	scopes := rbacEnforcer.GetScopes()
   178  	assert.Equal(t, scopes, rbac.DefaultScopes)
   179  }
   180  
   181  func TestGetScopes_CustomScopes(t *testing.T) {
   182  	rbacEnforcer := NewRBACPolicyEnforcer(nil, nil)
   183  	customScopes := []string{"custom"}
   184  	rbacEnforcer.SetScopes(customScopes)
   185  
   186  	scopes := rbacEnforcer.GetScopes()
   187  	assert.Equal(t, scopes, customScopes)
   188  }
   189  
   190  func Test_getProjectFromRequest(t *testing.T) {
   191  	tests := []struct {
   192  		name     string
   193  		resource string
   194  		action   string
   195  		arg      string
   196  	}{
   197  		{
   198  			name:     "valid project/repo string",
   199  			resource: "repositories",
   200  			action:   "create",
   201  			arg:      newFakeProj().Name + "/https://github.com/argoproj/argocd-example-apps",
   202  		},
   203  		{
   204  			name:     "applicationsets with project/repo string",
   205  			resource: "applicationsets",
   206  			action:   "create",
   207  			arg:      newFakeProj().Name + "/https://github.com/argoproj/argocd-example-apps",
   208  		},
   209  		{
   210  			name:     "applicationsets with project/repo string",
   211  			resource: "applicationsets",
   212  			action:   "*",
   213  			arg:      newFakeProj().Name + "/https://github.com/argoproj/argocd-example-apps",
   214  		},
   215  		{
   216  			name:     "applicationsets with project/repo string",
   217  			resource: "applicationsets",
   218  			action:   "get",
   219  			arg:      newFakeProj().Name + "/https://github.com/argoproj/argocd-example-apps",
   220  		},
   221  	}
   222  
   223  	for _, tt := range tests {
   224  		t.Run(tt.name, func(t *testing.T) {
   225  			fp := newFakeProj()
   226  			projLister := test.NewFakeProjLister(fp)
   227  			rbacEnforcer := NewRBACPolicyEnforcer(nil, projLister)
   228  
   229  			project := rbacEnforcer.getProjectFromRequest("", tt.resource, tt.action, tt.arg)
   230  			require.Equal(t, fp.Name, project.Name)
   231  		})
   232  	}
   233  }