github.com/argoproj/argo-cd@v1.8.7/server/project/project_test.go (about)

     1  package project
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"strings"
     7  	"testing"
     8  
     9  	"github.com/argoproj/pkg/sync"
    10  	"github.com/dgrijalva/jwt-go/v4"
    11  	"github.com/google/uuid"
    12  	"github.com/stretchr/testify/assert"
    13  	"google.golang.org/grpc/codes"
    14  	"google.golang.org/grpc/status"
    15  	corev1 "k8s.io/api/core/v1"
    16  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    17  	v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    18  	"k8s.io/client-go/kubernetes/fake"
    19  	k8scache "k8s.io/client-go/tools/cache"
    20  
    21  	"github.com/argoproj/argo-cd/common"
    22  	"github.com/argoproj/argo-cd/pkg/apiclient/project"
    23  	"github.com/argoproj/argo-cd/pkg/apis/application/v1alpha1"
    24  	apps "github.com/argoproj/argo-cd/pkg/client/clientset/versioned/fake"
    25  	informer "github.com/argoproj/argo-cd/pkg/client/informers/externalversions"
    26  	"github.com/argoproj/argo-cd/server/rbacpolicy"
    27  	"github.com/argoproj/argo-cd/test"
    28  	"github.com/argoproj/argo-cd/util/assets"
    29  	jwtutil "github.com/argoproj/argo-cd/util/jwt"
    30  	"github.com/argoproj/argo-cd/util/rbac"
    31  	"github.com/argoproj/argo-cd/util/session"
    32  	"github.com/argoproj/argo-cd/util/settings"
    33  )
    34  
    35  const testNamespace = "default"
    36  
    37  func TestProjectServer(t *testing.T) {
    38  	kubeclientset := fake.NewSimpleClientset(&corev1.ConfigMap{
    39  		ObjectMeta: v1.ObjectMeta{
    40  			Namespace: testNamespace,
    41  			Name:      "argocd-cm",
    42  			Labels: map[string]string{
    43  				"app.kubernetes.io/part-of": "argocd",
    44  			},
    45  		},
    46  	}, &corev1.Secret{
    47  		ObjectMeta: v1.ObjectMeta{
    48  			Name:      "argocd-secret",
    49  			Namespace: testNamespace,
    50  		},
    51  		Data: map[string][]byte{
    52  			"admin.password":   []byte("test"),
    53  			"server.secretkey": []byte("test"),
    54  		},
    55  	})
    56  	settingsMgr := settings.NewSettingsManager(context.Background(), kubeclientset, testNamespace)
    57  	enforcer := newEnforcer(kubeclientset)
    58  	existingProj := v1alpha1.AppProject{
    59  		ObjectMeta: v1.ObjectMeta{Name: "test", Namespace: testNamespace},
    60  		Spec: v1alpha1.AppProjectSpec{
    61  			Destinations: []v1alpha1.ApplicationDestination{
    62  				{Namespace: "ns1", Server: "https://server1"},
    63  				{Namespace: "ns2", Server: "https://server2"},
    64  			},
    65  			SourceRepos: []string{"https://github.com/argoproj/argo-cd.git"},
    66  		},
    67  	}
    68  	existingApp := v1alpha1.Application{
    69  		ObjectMeta: v1.ObjectMeta{Name: "test", Namespace: "default"},
    70  		Spec:       v1alpha1.ApplicationSpec{Project: "test", Destination: v1alpha1.ApplicationDestination{Namespace: "ns3", Server: "https://server3"}},
    71  	}
    72  
    73  	policyTemplate := "p, proj:%s:%s, applications, %s, %s/%s, %s"
    74  
    75  	ctx := context.Background()
    76  	fakeAppsClientset := apps.NewSimpleClientset()
    77  	factory := informer.NewFilteredSharedInformerFactory(fakeAppsClientset, 0, "", func(options *metav1.ListOptions) {})
    78  	projInformer := factory.Argoproj().V1alpha1().AppProjects().Informer()
    79  	go projInformer.Run(ctx.Done())
    80  	if !k8scache.WaitForCacheSync(ctx.Done(), projInformer.HasSynced) {
    81  		panic("Timed out waiting forfff caches to sync")
    82  	}
    83  
    84  	t.Run("TestNormalizeProj", func(t *testing.T) {
    85  		sessionMgr := session.NewSessionManager(settingsMgr, test.NewFakeProjLister(), "", session.NewInMemoryUserStateStorage())
    86  		projectWithRole := existingProj.DeepCopy()
    87  		roleName := "roleName"
    88  		role1 := v1alpha1.ProjectRole{Name: roleName, JWTTokens: []v1alpha1.JWTToken{{IssuedAt: 1}}}
    89  		projectWithRole.Spec.Roles = append(projectWithRole.Spec.Roles, role1)
    90  
    91  		projectServer := NewServer("default", fake.NewSimpleClientset(), apps.NewSimpleClientset(projectWithRole), enforcer, sync.NewKeyLock(), sessionMgr, nil, projInformer, settingsMgr)
    92  		err := projectServer.NormalizeProjs()
    93  		assert.NoError(t, err)
    94  
    95  		appList, err := projectServer.appclientset.ArgoprojV1alpha1().AppProjects(projectWithRole.Namespace).List(context.Background(), v1.ListOptions{})
    96  		assert.NoError(t, err)
    97  		assert.Equal(t, appList.Items[0].Status.JWTTokensByRole[roleName].Items[0].IssuedAt, int64(1))
    98  		assert.ElementsMatch(t, appList.Items[0].Status.JWTTokensByRole[roleName].Items, appList.Items[0].Spec.Roles[0].JWTTokens)
    99  
   100  	})
   101  
   102  	t.Run("TestClusterUpdateDenied", func(t *testing.T) {
   103  
   104  		enforcer.SetDefaultRole("role:projects")
   105  		_ = enforcer.SetBuiltinPolicy("p, role:projects, projects, update, *, allow")
   106  		projectServer := NewServer("default", fake.NewSimpleClientset(), apps.NewSimpleClientset(&existingProj, &existingApp), enforcer, sync.NewKeyLock(), nil, nil, projInformer, settingsMgr)
   107  
   108  		updatedProj := existingProj.DeepCopy()
   109  		updatedProj.Spec.Destinations = nil
   110  
   111  		_, err := projectServer.Update(context.Background(), &project.ProjectUpdateRequest{Project: updatedProj})
   112  
   113  		assert.Equal(t, status.Error(codes.PermissionDenied, "permission denied: clusters, update, https://server1"), err)
   114  	})
   115  
   116  	t.Run("TestReposUpdateDenied", func(t *testing.T) {
   117  
   118  		enforcer.SetDefaultRole("role:projects")
   119  		_ = enforcer.SetBuiltinPolicy("p, role:projects, projects, update, *, allow")
   120  		projectServer := NewServer("default", fake.NewSimpleClientset(), apps.NewSimpleClientset(&existingProj, &existingApp), enforcer, sync.NewKeyLock(), nil, nil, projInformer, settingsMgr)
   121  
   122  		updatedProj := existingProj.DeepCopy()
   123  		updatedProj.Spec.SourceRepos = nil
   124  
   125  		_, err := projectServer.Update(context.Background(), &project.ProjectUpdateRequest{Project: updatedProj})
   126  
   127  		assert.Equal(t, status.Error(codes.PermissionDenied, "permission denied: repositories, update, https://github.com/argoproj/argo-cd.git"), err)
   128  	})
   129  
   130  	t.Run("TestClusterResourceWhitelistUpdateDenied", func(t *testing.T) {
   131  
   132  		enforcer.SetDefaultRole("role:projects")
   133  		_ = enforcer.SetBuiltinPolicy("p, role:projects, projects, update, *, allow")
   134  		projectServer := NewServer("default", fake.NewSimpleClientset(), apps.NewSimpleClientset(&existingProj, &existingApp), enforcer, sync.NewKeyLock(), nil, nil, projInformer, settingsMgr)
   135  
   136  		updatedProj := existingProj.DeepCopy()
   137  		updatedProj.Spec.ClusterResourceWhitelist = []metav1.GroupKind{{}}
   138  
   139  		_, err := projectServer.Update(context.Background(), &project.ProjectUpdateRequest{Project: updatedProj})
   140  
   141  		assert.Equal(t, status.Error(codes.PermissionDenied, "permission denied: clusters, update, https://server1"), err)
   142  	})
   143  
   144  	t.Run("TestNamespaceResourceBlacklistUpdateDenied", func(t *testing.T) {
   145  
   146  		enforcer.SetDefaultRole("role:projects")
   147  		_ = enforcer.SetBuiltinPolicy("p, role:projects, projects, update, *, allow")
   148  		projectServer := NewServer("default", fake.NewSimpleClientset(), apps.NewSimpleClientset(&existingProj, &existingApp), enforcer, sync.NewKeyLock(), nil, nil, projInformer, settingsMgr)
   149  
   150  		updatedProj := existingProj.DeepCopy()
   151  		updatedProj.Spec.NamespaceResourceBlacklist = []metav1.GroupKind{{}}
   152  
   153  		_, err := projectServer.Update(context.Background(), &project.ProjectUpdateRequest{Project: updatedProj})
   154  
   155  		assert.Equal(t, status.Error(codes.PermissionDenied, "permission denied: clusters, update, https://server1"), err)
   156  	})
   157  
   158  	enforcer = newEnforcer(kubeclientset)
   159  
   160  	t.Run("TestRemoveDestinationSuccessful", func(t *testing.T) {
   161  		existingApp := v1alpha1.Application{
   162  			ObjectMeta: v1.ObjectMeta{Name: "test", Namespace: "default"},
   163  			Spec:       v1alpha1.ApplicationSpec{Project: "test", Destination: v1alpha1.ApplicationDestination{Namespace: "ns3", Server: "https://server3"}},
   164  		}
   165  
   166  		projectServer := NewServer("default", fake.NewSimpleClientset(), apps.NewSimpleClientset(&existingProj, &existingApp), enforcer, sync.NewKeyLock(), nil, nil, projInformer, settingsMgr)
   167  
   168  		updatedProj := existingProj.DeepCopy()
   169  		updatedProj.Spec.Destinations = updatedProj.Spec.Destinations[1:]
   170  
   171  		_, err := projectServer.Update(context.Background(), &project.ProjectUpdateRequest{Project: updatedProj})
   172  
   173  		assert.Nil(t, err)
   174  	})
   175  
   176  	t.Run("TestRemoveDestinationUsedByApp", func(t *testing.T) {
   177  		existingApp := v1alpha1.Application{
   178  			ObjectMeta: v1.ObjectMeta{Name: "test", Namespace: "default"},
   179  			Spec:       v1alpha1.ApplicationSpec{Project: "test", Destination: v1alpha1.ApplicationDestination{Namespace: "ns1", Server: "https://server1"}},
   180  		}
   181  
   182  		projectServer := NewServer("default", fake.NewSimpleClientset(), apps.NewSimpleClientset(&existingProj, &existingApp), enforcer, sync.NewKeyLock(), nil, nil, projInformer, settingsMgr)
   183  
   184  		updatedProj := existingProj.DeepCopy()
   185  		updatedProj.Spec.Destinations = updatedProj.Spec.Destinations[1:]
   186  
   187  		_, err := projectServer.Update(context.Background(), &project.ProjectUpdateRequest{Project: updatedProj})
   188  
   189  		assert.NotNil(t, err)
   190  		statusCode, _ := status.FromError(err)
   191  		assert.Equal(t, codes.InvalidArgument, statusCode.Code())
   192  	})
   193  
   194  	t.Run("TestRemoveSourceSuccessful", func(t *testing.T) {
   195  		existingApp := v1alpha1.Application{
   196  			ObjectMeta: v1.ObjectMeta{Name: "test", Namespace: "default"},
   197  			Spec:       v1alpha1.ApplicationSpec{Project: "test"},
   198  		}
   199  
   200  		projectServer := NewServer("default", fake.NewSimpleClientset(), apps.NewSimpleClientset(&existingProj, &existingApp), enforcer, sync.NewKeyLock(), nil, nil, projInformer, settingsMgr)
   201  
   202  		updatedProj := existingProj.DeepCopy()
   203  		updatedProj.Spec.SourceRepos = []string{}
   204  
   205  		_, err := projectServer.Update(context.Background(), &project.ProjectUpdateRequest{Project: updatedProj})
   206  
   207  		assert.Nil(t, err)
   208  	})
   209  
   210  	t.Run("TestRemoveSourceUsedByApp", func(t *testing.T) {
   211  		existingApp := v1alpha1.Application{
   212  			ObjectMeta: v1.ObjectMeta{Name: "test", Namespace: "default"},
   213  			Spec:       v1alpha1.ApplicationSpec{Project: "test", Source: v1alpha1.ApplicationSource{RepoURL: "https://github.com/argoproj/argo-cd.git"}},
   214  		}
   215  
   216  		projectServer := NewServer("default", fake.NewSimpleClientset(), apps.NewSimpleClientset(&existingProj, &existingApp), enforcer, sync.NewKeyLock(), nil, nil, projInformer, settingsMgr)
   217  
   218  		updatedProj := existingProj.DeepCopy()
   219  		updatedProj.Spec.SourceRepos = []string{}
   220  
   221  		_, err := projectServer.Update(context.Background(), &project.ProjectUpdateRequest{Project: updatedProj})
   222  
   223  		assert.NotNil(t, err)
   224  		statusCode, _ := status.FromError(err)
   225  		assert.Equal(t, codes.InvalidArgument, statusCode.Code())
   226  	})
   227  
   228  	t.Run("TestRemoveSourceUsedByAppSuccessfulIfPermittedByAnotherSrc", func(t *testing.T) {
   229  		proj := existingProj.DeepCopy()
   230  		proj.Spec.SourceRepos = []string{"https://github.com/argoproj/argo-cd.git", "https://github.com/argoproj/*"}
   231  		existingApp := v1alpha1.Application{
   232  			ObjectMeta: v1.ObjectMeta{Name: "test", Namespace: "default"},
   233  			Spec:       v1alpha1.ApplicationSpec{Project: "test", Source: v1alpha1.ApplicationSource{RepoURL: "https://github.com/argoproj/argo-cd.git"}},
   234  		}
   235  
   236  		projectServer := NewServer("default", fake.NewSimpleClientset(), apps.NewSimpleClientset(proj, &existingApp), enforcer, sync.NewKeyLock(), nil, nil, projInformer, settingsMgr)
   237  
   238  		updatedProj := proj.DeepCopy()
   239  		updatedProj.Spec.SourceRepos = []string{"https://github.com/argoproj/*"}
   240  
   241  		res, err := projectServer.Update(context.Background(), &project.ProjectUpdateRequest{Project: updatedProj})
   242  
   243  		assert.NoError(t, err)
   244  		assert.ElementsMatch(t, res.Spec.SourceRepos, updatedProj.Spec.SourceRepos)
   245  	})
   246  
   247  	t.Run("TestRemoveDestinationUsedByAppSuccessfulIfPermittedByAnotherDestination", func(t *testing.T) {
   248  		proj := existingProj.DeepCopy()
   249  		proj.Spec.Destinations = []v1alpha1.ApplicationDestination{
   250  			{Namespace: "org1-team1", Server: "https://server1"},
   251  			{Namespace: "org1-*", Server: "https://server1"},
   252  		}
   253  		existingApp := v1alpha1.Application{
   254  			ObjectMeta: v1.ObjectMeta{Name: "test", Namespace: "default"},
   255  			Spec: v1alpha1.ApplicationSpec{Project: "test", Destination: v1alpha1.ApplicationDestination{
   256  				Server:    "https://server1",
   257  				Namespace: "org1-team1",
   258  			}},
   259  		}
   260  
   261  		projectServer := NewServer("default", fake.NewSimpleClientset(), apps.NewSimpleClientset(proj, &existingApp), enforcer, sync.NewKeyLock(), nil, nil, projInformer, settingsMgr)
   262  
   263  		updatedProj := proj.DeepCopy()
   264  		updatedProj.Spec.Destinations = []v1alpha1.ApplicationDestination{
   265  			{Namespace: "org1-*", Server: "https://server1"},
   266  		}
   267  
   268  		res, err := projectServer.Update(context.Background(), &project.ProjectUpdateRequest{Project: updatedProj})
   269  
   270  		assert.NoError(t, err)
   271  		assert.ElementsMatch(t, res.Spec.Destinations, updatedProj.Spec.Destinations)
   272  	})
   273  
   274  	t.Run("TestDeleteProjectSuccessful", func(t *testing.T) {
   275  		projectServer := NewServer("default", fake.NewSimpleClientset(), apps.NewSimpleClientset(&existingProj), enforcer, sync.NewKeyLock(), nil, nil, projInformer, settingsMgr)
   276  
   277  		_, err := projectServer.Delete(context.Background(), &project.ProjectQuery{Name: "test"})
   278  
   279  		assert.Nil(t, err)
   280  	})
   281  
   282  	t.Run("TestDeleteDefaultProjectFailure", func(t *testing.T) {
   283  		defaultProj := v1alpha1.AppProject{
   284  			ObjectMeta: v1.ObjectMeta{Name: "default", Namespace: "default"},
   285  			Spec:       v1alpha1.AppProjectSpec{},
   286  		}
   287  		projectServer := NewServer("default", fake.NewSimpleClientset(), apps.NewSimpleClientset(&defaultProj), enforcer, sync.NewKeyLock(), nil, nil, projInformer, settingsMgr)
   288  
   289  		_, err := projectServer.Delete(context.Background(), &project.ProjectQuery{Name: defaultProj.Name})
   290  		statusCode, _ := status.FromError(err)
   291  		assert.Equal(t, codes.InvalidArgument, statusCode.Code())
   292  	})
   293  
   294  	t.Run("TestDeleteProjectReferencedByApp", func(t *testing.T) {
   295  		existingApp := v1alpha1.Application{
   296  			ObjectMeta: v1.ObjectMeta{Name: "test", Namespace: "default"},
   297  			Spec:       v1alpha1.ApplicationSpec{Project: "test"},
   298  		}
   299  
   300  		projectServer := NewServer("default", fake.NewSimpleClientset(), apps.NewSimpleClientset(&existingProj, &existingApp), enforcer, sync.NewKeyLock(), nil, nil, projInformer, settingsMgr)
   301  
   302  		_, err := projectServer.Delete(context.Background(), &project.ProjectQuery{Name: "test"})
   303  
   304  		assert.NotNil(t, err)
   305  		statusCode, _ := status.FromError(err)
   306  		assert.Equal(t, codes.InvalidArgument, statusCode.Code())
   307  	})
   308  
   309  	// configure a user named "admin" which is denied by default
   310  	enforcer = newEnforcer(kubeclientset)
   311  	_ = enforcer.SetBuiltinPolicy(`p, *, *, *, *, deny`)
   312  	enforcer.SetClaimsEnforcerFunc(nil)
   313  	// nolint:staticcheck
   314  	ctx = context.WithValue(context.Background(), "claims", &jwt.MapClaims{"groups": []string{"my-group"}})
   315  	policyEnf := rbacpolicy.NewRBACPolicyEnforcer(enforcer, nil)
   316  	policyEnf.SetScopes([]string{"groups"})
   317  
   318  	tokenName := "testToken"
   319  	id := "testId"
   320  
   321  	t.Run("TestCreateTokenDenied", func(t *testing.T) {
   322  		sessionMgr := session.NewSessionManager(settingsMgr, test.NewFakeProjLister(), "", session.NewInMemoryUserStateStorage())
   323  		projectWithRole := existingProj.DeepCopy()
   324  		projectWithRole.Spec.Roles = []v1alpha1.ProjectRole{{Name: tokenName}}
   325  		projectServer := NewServer("default", fake.NewSimpleClientset(), apps.NewSimpleClientset(projectWithRole), enforcer, sync.NewKeyLock(), sessionMgr, policyEnf, projInformer, settingsMgr)
   326  		_, err := projectServer.CreateToken(ctx, &project.ProjectTokenCreateRequest{Project: projectWithRole.Name, Role: tokenName, ExpiresIn: 1})
   327  		assert.EqualError(t, err, "rpc error: code = PermissionDenied desc = permission denied: projects, update, test")
   328  	})
   329  
   330  	t.Run("TestCreateTokenSuccessfullyUsingGroup", func(t *testing.T) {
   331  		sessionMgr := session.NewSessionManager(settingsMgr, test.NewFakeProjLister(), "", session.NewInMemoryUserStateStorage())
   332  		projectWithRole := existingProj.DeepCopy()
   333  		projectWithRole.Spec.Roles = []v1alpha1.ProjectRole{{Name: tokenName, Groups: []string{"my-group"}}}
   334  		projectServer := NewServer("default", fake.NewSimpleClientset(), apps.NewSimpleClientset(projectWithRole), enforcer, sync.NewKeyLock(), sessionMgr, policyEnf, projInformer, settingsMgr)
   335  		_, err := projectServer.CreateToken(ctx, &project.ProjectTokenCreateRequest{Project: projectWithRole.Name, Role: tokenName, ExpiresIn: 1})
   336  		assert.NoError(t, err)
   337  	})
   338  
   339  	_ = enforcer.SetBuiltinPolicy(`p, role:admin, projects, update, *, allow`)
   340  
   341  	t.Run("TestCreateTokenSuccessfully", func(t *testing.T) {
   342  		projectWithRole := existingProj.DeepCopy()
   343  		projectWithRole.Spec.Roles = []v1alpha1.ProjectRole{{Name: tokenName}}
   344  		clientset := apps.NewSimpleClientset(projectWithRole)
   345  
   346  		sessionMgr := session.NewSessionManager(settingsMgr, test.NewFakeProjListerFromInterface(clientset.ArgoprojV1alpha1().AppProjects("default")), "", session.NewInMemoryUserStateStorage())
   347  		projectServer := NewServer("default", fake.NewSimpleClientset(), clientset, enforcer, sync.NewKeyLock(), sessionMgr, policyEnf, projInformer, settingsMgr)
   348  		tokenResponse, err := projectServer.CreateToken(context.Background(), &project.ProjectTokenCreateRequest{Project: projectWithRole.Name, Role: tokenName, ExpiresIn: 100})
   349  		assert.NoError(t, err)
   350  		claims, err := sessionMgr.Parse(tokenResponse.Token)
   351  		assert.NoError(t, err)
   352  
   353  		mapClaims, err := jwtutil.MapClaims(claims)
   354  		subject, ok := mapClaims["sub"].(string)
   355  		assert.True(t, ok)
   356  		expectedSubject := fmt.Sprintf(JWTTokenSubFormat, projectWithRole.Name, tokenName)
   357  		assert.Equal(t, expectedSubject, subject)
   358  		assert.NoError(t, err)
   359  	})
   360  
   361  	t.Run("TestCreateTokenWithIDSuccessfully", func(t *testing.T) {
   362  		projectWithRole := existingProj.DeepCopy()
   363  		projectWithRole.Spec.Roles = []v1alpha1.ProjectRole{{Name: tokenName}}
   364  		clientset := apps.NewSimpleClientset(projectWithRole)
   365  
   366  		sessionMgr := session.NewSessionManager(settingsMgr, test.NewFakeProjListerFromInterface(clientset.ArgoprojV1alpha1().AppProjects("default")), "", session.NewInMemoryUserStateStorage())
   367  		projectServer := NewServer("default", fake.NewSimpleClientset(), clientset, enforcer, sync.NewKeyLock(), sessionMgr, policyEnf, projInformer, settingsMgr)
   368  		tokenResponse, err := projectServer.CreateToken(context.Background(), &project.ProjectTokenCreateRequest{Project: projectWithRole.Name, Role: tokenName, ExpiresIn: 1, Id: id})
   369  		assert.NoError(t, err)
   370  		claims, err := sessionMgr.Parse(tokenResponse.Token)
   371  		assert.NoError(t, err)
   372  
   373  		mapClaims, err := jwtutil.MapClaims(claims)
   374  		subject, ok := mapClaims["sub"].(string)
   375  		assert.True(t, ok)
   376  		expectedSubject := fmt.Sprintf(JWTTokenSubFormat, projectWithRole.Name, tokenName)
   377  		assert.Equal(t, expectedSubject, subject)
   378  		assert.NoError(t, err)
   379  	})
   380  
   381  	t.Run("TestCreateTokenWithSameIdDeny", func(t *testing.T) {
   382  		projectWithRole := existingProj.DeepCopy()
   383  		projectWithRole.Spec.Roles = []v1alpha1.ProjectRole{{Name: tokenName}}
   384  		clientset := apps.NewSimpleClientset(projectWithRole)
   385  
   386  		sessionMgr := session.NewSessionManager(settingsMgr, test.NewFakeProjListerFromInterface(clientset.ArgoprojV1alpha1().AppProjects("default")), "", session.NewInMemoryUserStateStorage())
   387  		projectServer := NewServer("default", fake.NewSimpleClientset(), clientset, enforcer, sync.NewKeyLock(), sessionMgr, policyEnf, projInformer, settingsMgr)
   388  		tokenResponse, err := projectServer.CreateToken(context.Background(), &project.ProjectTokenCreateRequest{Project: projectWithRole.Name, Role: tokenName, ExpiresIn: 1, Id: id})
   389  
   390  		assert.NoError(t, err)
   391  		claims, err := sessionMgr.Parse(tokenResponse.Token)
   392  		assert.NoError(t, err)
   393  
   394  		mapClaims, err := jwtutil.MapClaims(claims)
   395  		subject, ok := mapClaims["sub"].(string)
   396  		assert.True(t, ok)
   397  		expectedSubject := fmt.Sprintf(JWTTokenSubFormat, projectWithRole.Name, tokenName)
   398  		assert.Equal(t, expectedSubject, subject)
   399  		assert.NoError(t, err)
   400  
   401  		_, err1 := projectServer.CreateToken(context.Background(), &project.ProjectTokenCreateRequest{Project: projectWithRole.Name, Role: tokenName, ExpiresIn: 1, Id: id})
   402  		expectedErr := fmt.Sprintf("rpc error: code = InvalidArgument desc = rpc error: code = InvalidArgument desc = Token id '%s' has been used. ", id)
   403  		assert.EqualError(t, err1, expectedErr)
   404  	})
   405  
   406  	_ = enforcer.SetBuiltinPolicy(`p, *, *, *, *, deny`)
   407  
   408  	t.Run("TestDeleteTokenDenied", func(t *testing.T) {
   409  		sessionMgr := session.NewSessionManager(settingsMgr, test.NewFakeProjLister(), "", session.NewInMemoryUserStateStorage())
   410  		projWithToken := existingProj.DeepCopy()
   411  		issuedAt := int64(1)
   412  		secondIssuedAt := issuedAt + 1
   413  		token := v1alpha1.ProjectRole{Name: tokenName, JWTTokens: []v1alpha1.JWTToken{{IssuedAt: issuedAt}, {IssuedAt: secondIssuedAt}}}
   414  		projWithToken.Spec.Roles = append(projWithToken.Spec.Roles, token)
   415  
   416  		projectServer := NewServer("default", fake.NewSimpleClientset(), apps.NewSimpleClientset(projWithToken), enforcer, sync.NewKeyLock(), sessionMgr, policyEnf, projInformer, settingsMgr)
   417  		_, err := projectServer.DeleteToken(ctx, &project.ProjectTokenDeleteRequest{Project: projWithToken.Name, Role: tokenName, Iat: issuedAt})
   418  		assert.EqualError(t, err, "rpc error: code = PermissionDenied desc = permission denied: projects, update, test")
   419  	})
   420  
   421  	t.Run("TestDeleteTokenSuccessfullyWithGroup", func(t *testing.T) {
   422  		sessionMgr := session.NewSessionManager(settingsMgr, test.NewFakeProjLister(), "", session.NewInMemoryUserStateStorage())
   423  		projWithToken := existingProj.DeepCopy()
   424  		issuedAt := int64(1)
   425  		secondIssuedAt := issuedAt + 1
   426  		token := v1alpha1.ProjectRole{Name: tokenName, Groups: []string{"my-group"}, JWTTokens: []v1alpha1.JWTToken{{IssuedAt: issuedAt}, {IssuedAt: secondIssuedAt}}}
   427  		projWithToken.Spec.Roles = append(projWithToken.Spec.Roles, token)
   428  
   429  		projectServer := NewServer("default", fake.NewSimpleClientset(), apps.NewSimpleClientset(projWithToken), enforcer, sync.NewKeyLock(), sessionMgr, policyEnf, projInformer, settingsMgr)
   430  		_, err := projectServer.DeleteToken(ctx, &project.ProjectTokenDeleteRequest{Project: projWithToken.Name, Role: tokenName, Iat: issuedAt})
   431  		assert.NoError(t, err)
   432  	})
   433  
   434  	_ = enforcer.SetBuiltinPolicy(`p, role:admin, projects, get, *, allow
   435  p, role:admin, projects, update, *, allow`)
   436  
   437  	t.Run("TestDeleteTokenSuccessfully", func(t *testing.T) {
   438  		sessionMgr := session.NewSessionManager(settingsMgr, test.NewFakeProjLister(), "", session.NewInMemoryUserStateStorage())
   439  		projWithToken := existingProj.DeepCopy()
   440  		issuedAt := int64(1)
   441  		secondIssuedAt := issuedAt + 1
   442  		token := v1alpha1.ProjectRole{Name: tokenName, JWTTokens: []v1alpha1.JWTToken{{IssuedAt: issuedAt}, {IssuedAt: secondIssuedAt}}}
   443  		projWithToken.Spec.Roles = append(projWithToken.Spec.Roles, token)
   444  
   445  		projectServer := NewServer("default", fake.NewSimpleClientset(), apps.NewSimpleClientset(projWithToken), enforcer, sync.NewKeyLock(), sessionMgr, policyEnf, projInformer, settingsMgr)
   446  		_, err := projectServer.DeleteToken(ctx, &project.ProjectTokenDeleteRequest{Project: projWithToken.Name, Role: tokenName, Iat: issuedAt})
   447  		assert.NoError(t, err)
   448  		projWithoutToken, err := projectServer.Get(context.Background(), &project.ProjectQuery{Name: projWithToken.Name})
   449  		assert.NoError(t, err)
   450  		assert.Len(t, projWithoutToken.Spec.Roles, 1)
   451  		assert.Len(t, projWithoutToken.Spec.Roles[0].JWTTokens, 1)
   452  		assert.Equal(t, projWithoutToken.Spec.Roles[0].JWTTokens[0].IssuedAt, secondIssuedAt)
   453  	})
   454  
   455  	_ = enforcer.SetBuiltinPolicy(`p, role:admin, projects, get, *, allow
   456  p, role:admin, projects, update, *, allow`)
   457  
   458  	t.Run("TestDeleteTokenByIdSuccessfully", func(t *testing.T) {
   459  		sessionMgr := session.NewSessionManager(settingsMgr, test.NewFakeProjLister(), "", session.NewInMemoryUserStateStorage())
   460  		projWithToken := existingProj.DeepCopy()
   461  		issuedAt := int64(1)
   462  		secondIssuedAt := issuedAt + 1
   463  		id := "testId"
   464  		uniqueId, _ := uuid.NewRandom()
   465  		secondId := uniqueId.String()
   466  		token := v1alpha1.ProjectRole{Name: tokenName, JWTTokens: []v1alpha1.JWTToken{{IssuedAt: issuedAt, ID: id}, {IssuedAt: secondIssuedAt, ID: secondId}}}
   467  		projWithToken.Spec.Roles = append(projWithToken.Spec.Roles, token)
   468  
   469  		projectServer := NewServer("default", fake.NewSimpleClientset(), apps.NewSimpleClientset(projWithToken), enforcer, sync.NewKeyLock(), sessionMgr, policyEnf, projInformer, settingsMgr)
   470  		_, err := projectServer.DeleteToken(ctx, &project.ProjectTokenDeleteRequest{Project: projWithToken.Name, Role: tokenName, Iat: secondIssuedAt, Id: id})
   471  		assert.NoError(t, err)
   472  		projWithoutToken, err := projectServer.Get(context.Background(), &project.ProjectQuery{Name: projWithToken.Name})
   473  		assert.NoError(t, err)
   474  		assert.Len(t, projWithoutToken.Spec.Roles, 1)
   475  		assert.Len(t, projWithoutToken.Spec.Roles[0].JWTTokens, 1)
   476  		assert.Equal(t, projWithoutToken.Spec.Roles[0].JWTTokens[0].IssuedAt, secondIssuedAt)
   477  	})
   478  
   479  	enforcer = newEnforcer(kubeclientset)
   480  
   481  	t.Run("TestCreateTwoTokensInRoleSuccess", func(t *testing.T) {
   482  		sessionMgr := session.NewSessionManager(settingsMgr, test.NewFakeProjLister(), "", session.NewInMemoryUserStateStorage())
   483  		projWithToken := existingProj.DeepCopy()
   484  		tokenName := "testToken"
   485  		token := v1alpha1.ProjectRole{Name: tokenName, JWTTokens: []v1alpha1.JWTToken{{IssuedAt: 1}}}
   486  		projWithToken.Spec.Roles = append(projWithToken.Spec.Roles, token)
   487  		projectServer := NewServer("default", fake.NewSimpleClientset(), apps.NewSimpleClientset(projWithToken), enforcer, sync.NewKeyLock(), sessionMgr, policyEnf, projInformer, settingsMgr)
   488  		_, err := projectServer.CreateToken(context.Background(), &project.ProjectTokenCreateRequest{Project: projWithToken.Name, Role: tokenName})
   489  		assert.Nil(t, err)
   490  		projWithTwoTokens, err := projectServer.Get(context.Background(), &project.ProjectQuery{Name: projWithToken.Name})
   491  		assert.Nil(t, err)
   492  		assert.Len(t, projWithTwoTokens.Spec.Roles, 1)
   493  		assert.Len(t, projWithTwoTokens.Spec.Roles[0].JWTTokens, 2)
   494  	})
   495  
   496  	t.Run("TestAddWildcardSource", func(t *testing.T) {
   497  
   498  		proj := existingProj.DeepCopy()
   499  		wildSourceRepo := "*"
   500  		proj.Spec.SourceRepos = append(proj.Spec.SourceRepos, wildSourceRepo)
   501  
   502  		projectServer := NewServer("default", fake.NewSimpleClientset(), apps.NewSimpleClientset(proj), enforcer, sync.NewKeyLock(), nil, policyEnf, projInformer, settingsMgr)
   503  		request := &project.ProjectUpdateRequest{Project: proj}
   504  		updatedProj, err := projectServer.Update(context.Background(), request)
   505  		assert.Nil(t, err)
   506  		assert.Equal(t, wildSourceRepo, updatedProj.Spec.SourceRepos[1])
   507  	})
   508  
   509  	t.Run("TestCreateRolePolicySuccessfully", func(t *testing.T) {
   510  		action := "create"
   511  		object := "testApplication"
   512  		roleName := "testRole"
   513  		effect := "allow"
   514  
   515  		projWithRole := existingProj.DeepCopy()
   516  		role := v1alpha1.ProjectRole{Name: roleName, JWTTokens: []v1alpha1.JWTToken{{IssuedAt: 1}}}
   517  		policy := fmt.Sprintf(policyTemplate, projWithRole.Name, roleName, action, projWithRole.Name, object, effect)
   518  		role.Policies = append(role.Policies, policy)
   519  		projWithRole.Spec.Roles = append(projWithRole.Spec.Roles, role)
   520  
   521  		projectServer := NewServer("default", fake.NewSimpleClientset(), apps.NewSimpleClientset(projWithRole), enforcer, sync.NewKeyLock(), nil, policyEnf, projInformer, settingsMgr)
   522  		request := &project.ProjectUpdateRequest{Project: projWithRole}
   523  		_, err := projectServer.Update(context.Background(), request)
   524  		assert.Nil(t, err)
   525  		t.Log(projWithRole.Spec.Roles[0].Policies[0])
   526  		expectedPolicy := fmt.Sprintf(policyTemplate, projWithRole.Name, role.Name, action, projWithRole.Name, object, effect)
   527  		assert.Equal(t, projWithRole.Spec.Roles[0].Policies[0], expectedPolicy)
   528  	})
   529  
   530  	t.Run("TestValidatePolicyDuplicatePolicyFailure", func(t *testing.T) {
   531  		action := "create"
   532  		object := "testApplication"
   533  		roleName := "testRole"
   534  		effect := "allow"
   535  
   536  		projWithRole := existingProj.DeepCopy()
   537  		role := v1alpha1.ProjectRole{Name: roleName, JWTTokens: []v1alpha1.JWTToken{{IssuedAt: 1}}}
   538  		policy := fmt.Sprintf(policyTemplate, projWithRole.Name, roleName, action, projWithRole.Name, object, effect)
   539  		role.Policies = append(role.Policies, policy)
   540  		role.Policies = append(role.Policies, policy)
   541  		projWithRole.Spec.Roles = append(projWithRole.Spec.Roles, role)
   542  
   543  		projectServer := NewServer("default", fake.NewSimpleClientset(), apps.NewSimpleClientset(projWithRole), enforcer, sync.NewKeyLock(), nil, nil, projInformer, settingsMgr)
   544  		request := &project.ProjectUpdateRequest{Project: projWithRole}
   545  		_, err := projectServer.Update(context.Background(), request)
   546  		expectedErr := fmt.Sprintf("rpc error: code = AlreadyExists desc = policy '%s' already exists for role '%s'", policy, roleName)
   547  		assert.EqualError(t, err, expectedErr)
   548  	})
   549  
   550  	t.Run("TestValidateProjectAccessToSeparateProjectObjectFailure", func(t *testing.T) {
   551  		action := "create"
   552  		object := "testApplication"
   553  		roleName := "testRole"
   554  		otherProject := "other-project"
   555  		effect := "allow"
   556  
   557  		projWithRole := existingProj.DeepCopy()
   558  		role := v1alpha1.ProjectRole{Name: roleName, JWTTokens: []v1alpha1.JWTToken{{IssuedAt: 1}}}
   559  		policy := fmt.Sprintf(policyTemplate, projWithRole.Name, roleName, action, otherProject, object, effect)
   560  		role.Policies = append(role.Policies, policy)
   561  		projWithRole.Spec.Roles = append(projWithRole.Spec.Roles, role)
   562  
   563  		projectServer := NewServer("default", fake.NewSimpleClientset(), apps.NewSimpleClientset(projWithRole), enforcer, sync.NewKeyLock(), nil, nil, projInformer, settingsMgr)
   564  		request := &project.ProjectUpdateRequest{Project: projWithRole}
   565  		_, err := projectServer.Update(context.Background(), request)
   566  		assert.Contains(t, err.Error(), "object must be of form 'test/*' or 'test/<APPNAME>'")
   567  	})
   568  
   569  	t.Run("TestValidateProjectIncorrectProjectInRoleFailure", func(t *testing.T) {
   570  		action := "create"
   571  		object := "testApplication"
   572  		roleName := "testRole"
   573  		otherProject := "other-project"
   574  		effect := "allow"
   575  
   576  		projWithRole := existingProj.DeepCopy()
   577  		role := v1alpha1.ProjectRole{Name: roleName, JWTTokens: []v1alpha1.JWTToken{{IssuedAt: 1}}}
   578  		invalidPolicy := fmt.Sprintf(policyTemplate, otherProject, roleName, action, projWithRole.Name, object, effect)
   579  		role.Policies = append(role.Policies, invalidPolicy)
   580  		projWithRole.Spec.Roles = append(projWithRole.Spec.Roles, role)
   581  
   582  		projectServer := NewServer("default", fake.NewSimpleClientset(), apps.NewSimpleClientset(projWithRole), enforcer, sync.NewKeyLock(), nil, nil, projInformer, settingsMgr)
   583  		request := &project.ProjectUpdateRequest{Project: projWithRole}
   584  		_, err := projectServer.Update(context.Background(), request)
   585  		assert.Contains(t, err.Error(), "policy subject must be: 'proj:test:testRole'")
   586  	})
   587  
   588  	t.Run("TestValidateProjectIncorrectTokenInRoleFailure", func(t *testing.T) {
   589  		action := "create"
   590  		object := "testApplication"
   591  		roleName := "testRole"
   592  		otherToken := "other-token"
   593  		effect := "allow"
   594  
   595  		projWithRole := existingProj.DeepCopy()
   596  		role := v1alpha1.ProjectRole{Name: roleName, JWTTokens: []v1alpha1.JWTToken{{IssuedAt: 1}}}
   597  		invalidPolicy := fmt.Sprintf(policyTemplate, projWithRole.Name, otherToken, action, projWithRole.Name, object, effect)
   598  		role.Policies = append(role.Policies, invalidPolicy)
   599  		projWithRole.Spec.Roles = append(projWithRole.Spec.Roles, role)
   600  
   601  		projectServer := NewServer("default", fake.NewSimpleClientset(), apps.NewSimpleClientset(projWithRole), enforcer, sync.NewKeyLock(), nil, nil, projInformer, settingsMgr)
   602  		request := &project.ProjectUpdateRequest{Project: projWithRole}
   603  		_, err := projectServer.Update(context.Background(), request)
   604  		assert.Contains(t, err.Error(), "policy subject must be: 'proj:test:testRole'")
   605  	})
   606  
   607  	t.Run("TestValidateProjectInvalidEffectFailure", func(t *testing.T) {
   608  		action := "create"
   609  		object := "testApplication"
   610  		roleName := "testRole"
   611  		effect := "testEffect"
   612  
   613  		projWithRole := existingProj.DeepCopy()
   614  		role := v1alpha1.ProjectRole{Name: roleName, JWTTokens: []v1alpha1.JWTToken{{IssuedAt: 1}}}
   615  		invalidPolicy := fmt.Sprintf(policyTemplate, projWithRole.Name, roleName, action, projWithRole.Name, object, effect)
   616  		role.Policies = append(role.Policies, invalidPolicy)
   617  		projWithRole.Spec.Roles = append(projWithRole.Spec.Roles, role)
   618  
   619  		projectServer := NewServer("default", fake.NewSimpleClientset(), apps.NewSimpleClientset(projWithRole), enforcer, sync.NewKeyLock(), nil, nil, projInformer, settingsMgr)
   620  		request := &project.ProjectUpdateRequest{Project: projWithRole}
   621  		_, err := projectServer.Update(context.Background(), request)
   622  		assert.Contains(t, err.Error(), "effect must be: 'allow' or 'deny'")
   623  	})
   624  
   625  	t.Run("TestNormalizeProjectRolePolicies", func(t *testing.T) {
   626  		action := "create"
   627  		object := "testApplication"
   628  		roleName := "testRole"
   629  		effect := "allow"
   630  
   631  		projWithRole := existingProj.DeepCopy()
   632  		role := v1alpha1.ProjectRole{Name: roleName, JWTTokens: []v1alpha1.JWTToken{{IssuedAt: 1}}}
   633  		noSpacesPolicyTemplate := strings.Replace(policyTemplate, " ", "", -1)
   634  		invalidPolicy := fmt.Sprintf(noSpacesPolicyTemplate, projWithRole.Name, roleName, action, projWithRole.Name, object, effect)
   635  		role.Policies = append(role.Policies, invalidPolicy)
   636  		projWithRole.Spec.Roles = append(projWithRole.Spec.Roles, role)
   637  
   638  		projectServer := NewServer("default", fake.NewSimpleClientset(), apps.NewSimpleClientset(projWithRole), enforcer, sync.NewKeyLock(), nil, nil, projInformer, settingsMgr)
   639  		request := &project.ProjectUpdateRequest{Project: projWithRole}
   640  		updateProj, err := projectServer.Update(context.Background(), request)
   641  		assert.Nil(t, err)
   642  		expectedPolicy := fmt.Sprintf(policyTemplate, projWithRole.Name, roleName, action, projWithRole.Name, object, effect)
   643  		assert.Equal(t, expectedPolicy, updateProj.Spec.Roles[0].Policies[0])
   644  	})
   645  
   646  	t.Run("TestSyncWindowsActive", func(t *testing.T) {
   647  		sessionMgr := session.NewSessionManager(settingsMgr, test.NewFakeProjLister(), "", session.NewInMemoryUserStateStorage())
   648  		projectWithSyncWindows := existingProj.DeepCopy()
   649  		projectWithSyncWindows.Spec.SyncWindows = v1alpha1.SyncWindows{}
   650  		win := &v1alpha1.SyncWindow{Kind: "allow", Schedule: "* * * * *", Duration: "1h"}
   651  		projectWithSyncWindows.Spec.SyncWindows = append(projectWithSyncWindows.Spec.SyncWindows, win)
   652  
   653  		projectServer := NewServer("default", fake.NewSimpleClientset(), apps.NewSimpleClientset(projectWithSyncWindows), enforcer, sync.NewKeyLock(), sessionMgr, nil, projInformer, settingsMgr)
   654  		res, err := projectServer.GetSyncWindowsState(ctx, &project.SyncWindowsQuery{Name: projectWithSyncWindows.Name})
   655  		assert.NoError(t, err)
   656  		assert.Equal(t, 1, len(res.Windows))
   657  	})
   658  
   659  	t.Run("TestGetSyncWindowsStateCannotGetProjectDetails", func(t *testing.T) {
   660  		sessionMgr := session.NewSessionManager(settingsMgr, test.NewFakeProjLister(), "", session.NewInMemoryUserStateStorage())
   661  		projectWithSyncWindows := existingProj.DeepCopy()
   662  		projectWithSyncWindows.Spec.SyncWindows = v1alpha1.SyncWindows{}
   663  		win := &v1alpha1.SyncWindow{Kind: "allow", Schedule: "* * * * *", Duration: "1h"}
   664  		projectWithSyncWindows.Spec.SyncWindows = append(projectWithSyncWindows.Spec.SyncWindows, win)
   665  
   666  		projectServer := NewServer("default", fake.NewSimpleClientset(), apps.NewSimpleClientset(projectWithSyncWindows), enforcer, sync.NewKeyLock(), sessionMgr, nil, projInformer, settingsMgr)
   667  		res, err := projectServer.GetSyncWindowsState(ctx, &project.SyncWindowsQuery{Name: "incorrect"})
   668  		assert.Contains(t, err.Error(), "not found")
   669  		assert.Nil(t, res)
   670  	})
   671  
   672  	t.Run("TestGetSyncWindowsStateDenied", func(t *testing.T) {
   673  		enforcer = newEnforcer(kubeclientset)
   674  		_ = enforcer.SetBuiltinPolicy(`p, *, *, *, *, deny`)
   675  		enforcer.SetClaimsEnforcerFunc(nil)
   676  		// nolint:staticcheck
   677  		ctx := context.WithValue(context.Background(), "claims", &jwt.MapClaims{"groups": []string{"my-group"}})
   678  
   679  		sessionMgr := session.NewSessionManager(settingsMgr, test.NewFakeProjLister(), "", session.NewInMemoryUserStateStorage())
   680  		projectWithSyncWindows := existingProj.DeepCopy()
   681  		win := &v1alpha1.SyncWindow{Kind: "allow", Schedule: "* * * * *", Duration: "1h"}
   682  		projectWithSyncWindows.Spec.SyncWindows = append(projectWithSyncWindows.Spec.SyncWindows, win)
   683  
   684  		projectServer := NewServer("default", fake.NewSimpleClientset(), apps.NewSimpleClientset(projectWithSyncWindows), enforcer, sync.NewKeyLock(), sessionMgr, nil, projInformer, settingsMgr)
   685  		_, err := projectServer.GetSyncWindowsState(ctx, &project.SyncWindowsQuery{Name: projectWithSyncWindows.Name})
   686  		assert.EqualError(t, err, "rpc error: code = PermissionDenied desc = permission denied: projects, get, test")
   687  	})
   688  
   689  }
   690  
   691  func newEnforcer(kubeclientset *fake.Clientset) *rbac.Enforcer {
   692  	enforcer := rbac.NewEnforcer(kubeclientset, testNamespace, common.ArgoCDRBACConfigMapName, nil)
   693  	_ = enforcer.SetBuiltinPolicy(assets.BuiltinPolicyCSV)
   694  	enforcer.SetDefaultRole("role:admin")
   695  	enforcer.SetClaimsEnforcerFunc(func(claims jwt.Claims, rvals ...interface{}) bool {
   696  		return true
   697  	})
   698  	return enforcer
   699  }