mirror of
https://github.com/argoproj/argo-cd.git
synced 2026-02-20 01:28:45 +01:00
feat: syncing to a different revision requires override privilege (#22858)
Signed-off-by: Andreas Schramm <andreas.jabs@gmail.com>
This commit is contained in:
@@ -2113,22 +2113,36 @@ func (s *Server) Sync(ctx context.Context, syncReq *application.ApplicationSyncR
|
||||
}
|
||||
|
||||
func (s *Server) resolveSourceRevisions(ctx context.Context, a *v1alpha1.Application, syncReq *application.ApplicationSyncRequest) (string, string, []string, []string, error) {
|
||||
requireOverridePrivilegeForRevisionSync, err := s.settingsMgr.RequireOverridePrivilegeForRevisionSync()
|
||||
if err != nil {
|
||||
// give up, and return the error
|
||||
return "", "", nil, nil,
|
||||
fmt.Errorf("error getting setting 'RequireOverridePrivilegeForRevisionSync' from configmap: : %w", err)
|
||||
}
|
||||
if a.Spec.HasMultipleSources() {
|
||||
numOfSources := int64(len(a.Spec.GetSources()))
|
||||
sourceRevisions := make([]string, numOfSources)
|
||||
displayRevisions := make([]string, numOfSources)
|
||||
|
||||
sources := a.Spec.GetSources()
|
||||
desiredRevisions := make([]string, numOfSources)
|
||||
for i, pos := range syncReq.SourcePositions {
|
||||
if pos <= 0 || pos > numOfSources {
|
||||
return "", "", nil, nil, errors.New("source position is out of range")
|
||||
}
|
||||
sources[pos-1].TargetRevision = syncReq.Revisions[i]
|
||||
desiredRevisions[pos-1] = syncReq.Revisions[i]
|
||||
}
|
||||
for index, source := range sources {
|
||||
if a.Spec.SyncPolicy != nil && a.Spec.SyncPolicy.IsAutomatedSyncEnabled() && !syncReq.GetDryRun() {
|
||||
if text.FirstNonEmpty(a.Spec.GetSources()[index].TargetRevision, "HEAD") != text.FirstNonEmpty(source.TargetRevision, "HEAD") {
|
||||
return "", "", nil, nil, status.Errorf(codes.FailedPrecondition, "Cannot sync source %s to %s: auto-sync currently set to %s", source.RepoURL, source.TargetRevision, a.Spec.Sources[index].TargetRevision)
|
||||
for index, desiredRevision := range desiredRevisions {
|
||||
if desiredRevision != "" && desiredRevision != text.FirstNonEmpty(a.Spec.GetSources()[index].TargetRevision, "HEAD") {
|
||||
// User is trying to sync to a different revision than the ones specified in the app sources
|
||||
// Enforce that they have the 'override' privilege if the setting is enabled
|
||||
if requireOverridePrivilegeForRevisionSync {
|
||||
if err := s.enf.EnforceErr(ctx.Value("claims"), rbac.ResourceApplications, rbac.ActionOverride, a.RBACName(s.ns)); err != nil {
|
||||
return "", "", nil, nil, err
|
||||
}
|
||||
}
|
||||
if a.Spec.SyncPolicy != nil && a.Spec.SyncPolicy.IsAutomatedSyncEnabled() && !syncReq.GetDryRun() {
|
||||
return "", "", nil, nil, status.Errorf(codes.FailedPrecondition,
|
||||
"Cannot sync source %s to %s: auto-sync currently set to %s",
|
||||
a.Spec.GetSources()[index].RepoURL, desiredRevision, a.Spec.Sources[index].TargetRevision)
|
||||
}
|
||||
}
|
||||
revision, displayRevision, err := s.resolveRevision(ctx, a, syncReq, index)
|
||||
@@ -2141,8 +2155,18 @@ func (s *Server) resolveSourceRevisions(ctx context.Context, a *v1alpha1.Applica
|
||||
return "", "", sourceRevisions, displayRevisions, nil
|
||||
}
|
||||
source := a.Spec.GetSource()
|
||||
if a.Spec.SyncPolicy != nil && a.Spec.SyncPolicy.IsAutomatedSyncEnabled() && !syncReq.GetDryRun() {
|
||||
if syncReq.GetRevision() != "" && syncReq.GetRevision() != text.FirstNonEmpty(source.TargetRevision, "HEAD") {
|
||||
if syncReq.GetRevision() != "" &&
|
||||
syncReq.GetRevision() != text.FirstNonEmpty(source.TargetRevision, "HEAD") {
|
||||
// User is trying to sync to a different revision than the one specified in the app spec
|
||||
// Enforce that they have the 'override' privilege if the setting is enabled
|
||||
if requireOverridePrivilegeForRevisionSync {
|
||||
if err := s.enf.EnforceErr(ctx.Value("claims"), rbac.ResourceApplications, rbac.ActionOverride, a.RBACName(s.ns)); err != nil {
|
||||
return "", "", nil, nil, err
|
||||
}
|
||||
}
|
||||
if a.Spec.SyncPolicy != nil &&
|
||||
a.Spec.SyncPolicy.IsAutomatedSyncEnabled() && !syncReq.GetDryRun() {
|
||||
// If the app has auto-sync enabled, we cannot allow syncing to a different revision
|
||||
return "", "", nil, nil, status.Errorf(codes.FailedPrecondition, "Cannot sync to %s: auto-sync currently set to %s", syncReq.GetRevision(), source.TargetRevision)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -548,6 +548,30 @@ func newTestApp(opts ...func(app *v1alpha1.Application)) *v1alpha1.Application {
|
||||
return createTestApp(fakeApp, opts...)
|
||||
}
|
||||
|
||||
func newMultiSourceTestApp(opts ...func(app *v1alpha1.Application)) *v1alpha1.Application {
|
||||
multiSourceApp := newTestApp(opts...)
|
||||
multiSourceApp.Name = "multi-source-app"
|
||||
multiSourceApp.Spec = v1alpha1.ApplicationSpec{
|
||||
Sources: []v1alpha1.ApplicationSource{
|
||||
{
|
||||
RepoURL: "https://github.com/argoproj/argocd-example-apps.git",
|
||||
Path: "helm-guestbook",
|
||||
TargetRevision: "appbranch1",
|
||||
},
|
||||
{
|
||||
RepoURL: "https://github.com/argoproj/argocd-example-apps.git",
|
||||
Path: "kustomize-guestbook",
|
||||
TargetRevision: "appbranch2",
|
||||
},
|
||||
},
|
||||
Destination: v1alpha1.ApplicationDestination{
|
||||
Server: "https://cluster-api.example.com",
|
||||
Namespace: test.FakeDestNamespace,
|
||||
},
|
||||
}
|
||||
return multiSourceApp
|
||||
}
|
||||
|
||||
func newTestAppWithAnnotations(opts ...func(app *v1alpha1.Application)) *v1alpha1.Application {
|
||||
return createTestApp(fakeAppWithAnnotations, opts...)
|
||||
}
|
||||
@@ -2039,6 +2063,545 @@ p, test-user, applications, update/fake.io/PodTest/*, default/test-app, deny
|
||||
})
|
||||
}
|
||||
|
||||
func TestSyncRBACOverrideRequired_DiffRevDenied(t *testing.T) {
|
||||
ctx := t.Context()
|
||||
//nolint:staticcheck
|
||||
ctx = context.WithValue(ctx, "claims", &jwt.RegisteredClaims{Subject: "test-user"})
|
||||
|
||||
f := func(enf *rbac.Enforcer) {
|
||||
_ = enf.SetBuiltinPolicy(assets.BuiltinPolicyCSV)
|
||||
enf.SetDefaultRole("role:admin")
|
||||
}
|
||||
appServer := newTestAppServerWithEnforcerConfigure(t, f,
|
||||
map[string]string{"application.sync.requireOverridePrivilegeForRevisionSync": "true"})
|
||||
|
||||
_ = appServer.enf.SetBuiltinPolicy(`
|
||||
p, test-user, applications, get, default/*, allow
|
||||
p, test-user, applications, create, default/*, allow
|
||||
p, test-user, applications, sync, default/*, allow
|
||||
p, test-user, applications, override, default/*, deny
|
||||
`)
|
||||
|
||||
testApp := newTestApp()
|
||||
app, err := appServer.Create(ctx,
|
||||
&application.ApplicationCreateRequest{Application: testApp})
|
||||
require.NoError(t, err)
|
||||
syncReq := &application.ApplicationSyncRequest{
|
||||
Name: &app.Name,
|
||||
Revision: ptr.To("revisionbranch"),
|
||||
}
|
||||
_, err = appServer.Sync(ctx, syncReq)
|
||||
assert.Equal(t, codes.PermissionDenied.String(), status.Code(err).String(),
|
||||
"should not be able to sync to different revision without override permission")
|
||||
|
||||
// same for multi-source app
|
||||
multiSourceApp := newMultiSourceTestApp()
|
||||
app, err = appServer.Create(ctx,
|
||||
&application.ApplicationCreateRequest{Application: multiSourceApp})
|
||||
require.NoError(t, err)
|
||||
syncReq = &application.ApplicationSyncRequest{
|
||||
Name: &app.Name,
|
||||
SourcePositions: []int64{1},
|
||||
Revisions: []string{"revisionbranch1"},
|
||||
}
|
||||
_, err = appServer.Sync(ctx, syncReq)
|
||||
assert.Equal(t, codes.PermissionDenied.String(), status.Code(err).String(),
|
||||
"should not be able to sync to different revision without override permission, multi-source app")
|
||||
}
|
||||
|
||||
func TestSyncRBACOverrideRequired_SameRevisionAllowed(t *testing.T) {
|
||||
ctx := t.Context()
|
||||
//nolint:staticcheck
|
||||
ctx = context.WithValue(ctx, "claims", &jwt.RegisteredClaims{Subject: "test-user"})
|
||||
|
||||
f := func(enf *rbac.Enforcer) {
|
||||
_ = enf.SetBuiltinPolicy(assets.BuiltinPolicyCSV)
|
||||
enf.SetDefaultRole("role:admin")
|
||||
}
|
||||
appServer := newTestAppServerWithEnforcerConfigure(t, f,
|
||||
map[string]string{"application.sync.requireOverridePrivilegeForRevisionSync": "true"})
|
||||
_ = appServer.enf.SetBuiltinPolicy(`
|
||||
p, test-user, applications, get, default/*, allow
|
||||
p, test-user, applications, create, default/*, allow
|
||||
p, test-user, applications, sync, default/*, allow
|
||||
p, test-user, applications, override, default/*, deny
|
||||
`)
|
||||
|
||||
// create an app and sync to the same revision
|
||||
testApp := newTestApp()
|
||||
app, err := appServer.Create(ctx,
|
||||
&application.ApplicationCreateRequest{Application: testApp})
|
||||
require.NoError(t, err)
|
||||
syncReq := &application.ApplicationSyncRequest{
|
||||
Name: &app.Name,
|
||||
Revision: ptr.To("HEAD"),
|
||||
}
|
||||
syncedApp, err := appServer.Sync(ctx, syncReq)
|
||||
require.NoError(t, err,
|
||||
"should be able to sync to the same revision without override permission")
|
||||
assert.NotNil(t, syncedApp)
|
||||
|
||||
// same for multi-source app
|
||||
multiSourceApp := newMultiSourceTestApp()
|
||||
app, err = appServer.Create(ctx,
|
||||
&application.ApplicationCreateRequest{Application: multiSourceApp})
|
||||
require.NoError(t, err)
|
||||
syncReq = &application.ApplicationSyncRequest{
|
||||
Name: &app.Name,
|
||||
SourcePositions: []int64{1, 2},
|
||||
Revisions: []string{"appbranch1", "appbranch2"},
|
||||
}
|
||||
syncedApp, err = appServer.Sync(ctx, syncReq)
|
||||
require.NoError(t, err,
|
||||
"should be able to sync to the same revision without override permission, multi-source app")
|
||||
assert.NotNil(t, syncedApp)
|
||||
}
|
||||
|
||||
func TestSyncRBACOverrideRequired_WithoutRevisionAllowed(t *testing.T) {
|
||||
ctx := t.Context()
|
||||
//nolint:staticcheck
|
||||
ctx = context.WithValue(ctx, "claims", &jwt.RegisteredClaims{Subject: "test-user"})
|
||||
|
||||
f := func(enf *rbac.Enforcer) {
|
||||
_ = enf.SetBuiltinPolicy(assets.BuiltinPolicyCSV)
|
||||
enf.SetDefaultRole("role:admin")
|
||||
}
|
||||
appServer := newTestAppServerWithEnforcerConfigure(t, f,
|
||||
map[string]string{"application.sync.requireOverridePrivilegeForRevisionSync": "true"})
|
||||
|
||||
_ = appServer.enf.SetBuiltinPolicy(`
|
||||
p, test-user, applications, get, default/*, allow
|
||||
p, test-user, applications, create, default/*, allow
|
||||
p, test-user, applications, sync, default/*, allow
|
||||
p, test-user, applications, override, default/*, deny
|
||||
`)
|
||||
|
||||
testApp := newTestApp()
|
||||
app, err := appServer.Create(ctx,
|
||||
&application.ApplicationCreateRequest{Application: testApp})
|
||||
require.NoError(t, err)
|
||||
syncReq := &application.ApplicationSyncRequest{
|
||||
Name: &app.Name,
|
||||
}
|
||||
syncedApp, err := appServer.Sync(ctx, syncReq)
|
||||
require.NoError(t, err)
|
||||
assert.NotNil(t, syncedApp)
|
||||
assert.Equal(t, app.Spec, testApp.Spec)
|
||||
|
||||
// same for multi-source app
|
||||
multiSourceApp := newMultiSourceTestApp()
|
||||
app, err = appServer.Create(ctx,
|
||||
&application.ApplicationCreateRequest{Application: multiSourceApp})
|
||||
require.NoError(t, err)
|
||||
syncReq = &application.ApplicationSyncRequest{
|
||||
Name: &app.Name,
|
||||
}
|
||||
syncedApp, err = appServer.Sync(ctx, syncReq)
|
||||
require.NoError(t, err)
|
||||
assert.NotNil(t, syncedApp)
|
||||
}
|
||||
|
||||
func TestSyncRBACOverrideGranted_DiffRevisionAllowed(t *testing.T) {
|
||||
ctx := t.Context()
|
||||
//nolint:staticcheck
|
||||
ctx = context.WithValue(ctx, "claims", &jwt.RegisteredClaims{Subject: "test-user"})
|
||||
|
||||
f := func(enf *rbac.Enforcer) {
|
||||
_ = enf.SetBuiltinPolicy(assets.BuiltinPolicyCSV)
|
||||
enf.SetDefaultRole("role:admin")
|
||||
}
|
||||
appServer := newTestAppServerWithEnforcerConfigure(t, f,
|
||||
map[string]string{"application.sync.requireOverridePrivilegeForRevisionSync": "true"})
|
||||
|
||||
_ = appServer.enf.SetBuiltinPolicy(`
|
||||
p, test-user, applications, get, default/*, allow
|
||||
p, test-user, applications, create, default/*, allow
|
||||
p, test-user, applications, sync, default/*, allow
|
||||
p, test-user, applications, override, default/*, allow
|
||||
`)
|
||||
|
||||
testApp := newTestApp()
|
||||
|
||||
app, err := appServer.Create(ctx,
|
||||
&application.ApplicationCreateRequest{Application: testApp})
|
||||
require.NoError(t, err)
|
||||
syncReq := &application.ApplicationSyncRequest{
|
||||
Name: &app.Name,
|
||||
Revision: ptr.To("revisionbranch"),
|
||||
}
|
||||
syncedApp, err := appServer.Sync(ctx, syncReq)
|
||||
require.NoError(t, err,
|
||||
"should be able to sync to different revision with override permission")
|
||||
assert.NotNil(t, syncedApp)
|
||||
assert.Equal(t, "HEAD", syncedApp.Spec.Source.TargetRevision)
|
||||
|
||||
// same for multi-source app
|
||||
multiSourceApp := newMultiSourceTestApp()
|
||||
app, err = appServer.Create(ctx,
|
||||
&application.ApplicationCreateRequest{Application: multiSourceApp})
|
||||
require.NoError(t, err)
|
||||
syncReq = &application.ApplicationSyncRequest{
|
||||
Name: &app.Name,
|
||||
SourcePositions: []int64{1, 2},
|
||||
Revisions: []string{"appbranch1", "appbranch2"},
|
||||
}
|
||||
syncedApp, err = appServer.Sync(ctx, syncReq)
|
||||
require.NoError(t, err,
|
||||
"should be able to sync to different revision with override permission, multi-source app")
|
||||
assert.NotNil(t, syncedApp)
|
||||
}
|
||||
|
||||
func TestSyncMultiSource_PosTooLarge(t *testing.T) {
|
||||
ctx := t.Context()
|
||||
//nolint:staticcheck
|
||||
ctx = context.WithValue(ctx, "claims", &jwt.RegisteredClaims{Subject: "test-user"})
|
||||
appServer := newTestAppServer(t)
|
||||
|
||||
_ = appServer.enf.SetBuiltinPolicy(`
|
||||
p, test-user, applications, get, default/*, allow
|
||||
p, test-user, applications, create, default/*, allow
|
||||
p, test-user, applications, sync, default/*, allow
|
||||
`)
|
||||
// create a new app
|
||||
multiSourceApp := newMultiSourceTestApp()
|
||||
app, err := appServer.Create(ctx,
|
||||
&application.ApplicationCreateRequest{Application: multiSourceApp})
|
||||
require.NoError(t, err)
|
||||
// sync with source position greater than the number of sources
|
||||
syncReq := &application.ApplicationSyncRequest{
|
||||
Name: &app.Name,
|
||||
SourcePositions: []int64{3, 2},
|
||||
Revisions: []string{"appbranch1", "appbranch2"},
|
||||
}
|
||||
_, err = appServer.Sync(ctx, syncReq)
|
||||
require.EqualError(t, err, "source position is out of range",
|
||||
"should fail because source position is greater than the number of sources")
|
||||
}
|
||||
|
||||
func TestSyncMultiSource_PosTooSmall(t *testing.T) {
|
||||
ctx := t.Context()
|
||||
//nolint:staticcheck
|
||||
ctx = context.WithValue(ctx, "claims", &jwt.RegisteredClaims{Subject: "test-user"})
|
||||
appServer := newTestAppServer(t)
|
||||
|
||||
_ = appServer.enf.SetBuiltinPolicy(`
|
||||
p, test-user, applications, get, default/*, allow
|
||||
p, test-user, applications, create, default/*, allow
|
||||
p, test-user, applications, sync, default/*, allow
|
||||
`)
|
||||
// create a new app
|
||||
multiSourceApp := newMultiSourceTestApp()
|
||||
app, err := appServer.Create(ctx,
|
||||
&application.ApplicationCreateRequest{Application: multiSourceApp})
|
||||
require.NoError(t, err)
|
||||
// sync with source position less than 1
|
||||
// this should fail because source positions are 1-based
|
||||
syncReq := &application.ApplicationSyncRequest{
|
||||
Name: &app.Name,
|
||||
SourcePositions: []int64{0, 2},
|
||||
Revisions: []string{"appbranch1", "appbranch2"},
|
||||
}
|
||||
_, err = appServer.Sync(ctx, syncReq)
|
||||
require.EqualError(t, err, "source position is out of range",
|
||||
"should fail because source position is less than 1")
|
||||
}
|
||||
|
||||
func TestSync_SyncWithoutSyncPermissionShouldFail(t *testing.T) {
|
||||
ctx := t.Context()
|
||||
//nolint:staticcheck
|
||||
ctx = context.WithValue(ctx, "claims", &jwt.RegisteredClaims{Subject: "test-user"})
|
||||
appServer := newTestAppServer(t)
|
||||
_ = appServer.enf.SetBuiltinPolicy(`
|
||||
p, test-user, applications, get, default/*, allow
|
||||
p, test-user, applications, create, default/*, allow
|
||||
p, test-user, applications, sync, default/*, deny`)
|
||||
|
||||
testApp := newTestApp()
|
||||
app, err := appServer.Create(ctx,
|
||||
&application.ApplicationCreateRequest{Application: testApp})
|
||||
require.NoError(t, err)
|
||||
syncReq := &application.ApplicationSyncRequest{
|
||||
Name: &app.Name,
|
||||
}
|
||||
_, err = appServer.Sync(ctx, syncReq)
|
||||
assert.Equal(t, codes.PermissionDenied.String(), status.Code(err).String())
|
||||
|
||||
// same for multi-source app
|
||||
multiSourceApp := newMultiSourceTestApp()
|
||||
app, err = appServer.Create(ctx,
|
||||
&application.ApplicationCreateRequest{Application: multiSourceApp})
|
||||
require.NoError(t, err)
|
||||
syncReq = &application.ApplicationSyncRequest{
|
||||
Name: &app.Name,
|
||||
}
|
||||
_, err = appServer.Sync(ctx, syncReq)
|
||||
assert.Equal(t, codes.PermissionDenied.String(), status.Code(err).String())
|
||||
}
|
||||
|
||||
func TestSyncRBACSettingsError(t *testing.T) {
|
||||
ctx := t.Context()
|
||||
//nolint:staticcheck
|
||||
ctx = context.WithValue(ctx, "claims", &jwt.RegisteredClaims{Subject: "test-user"})
|
||||
appServer := newTestAppServer(t)
|
||||
_ = appServer.enf.SetBuiltinPolicy(`
|
||||
p, test-user, applications, get, default/*, allow
|
||||
p, test-user, applications, create, default/*, allow
|
||||
p, test-user, applications, sync, default/*, allow
|
||||
p, test-user, applications, override, default/*, deny
|
||||
`)
|
||||
|
||||
// create a new app
|
||||
testApp := newTestApp()
|
||||
app, err := appServer.Create(ctx,
|
||||
&application.ApplicationCreateRequest{Application: testApp})
|
||||
require.NoError(t, err)
|
||||
|
||||
// override settings manager to return error
|
||||
brokenclientset := fake.NewClientset(&corev1.Secret{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "argocd-secret",
|
||||
Namespace: testNamespace,
|
||||
},
|
||||
Data: map[string][]byte{
|
||||
"admin.password": []byte("test"),
|
||||
"server.secretkey": []byte("test"),
|
||||
},
|
||||
})
|
||||
appServer.settingsMgr = settings.NewSettingsManager(ctx, brokenclientset, testNamespace)
|
||||
// and sync to different revision
|
||||
syncReq := &application.ApplicationSyncRequest{
|
||||
Name: &app.Name,
|
||||
Revision: ptr.To("revisionbranch"),
|
||||
}
|
||||
|
||||
_, err2 := appServer.Sync(ctx, syncReq)
|
||||
require.Error(t, err2)
|
||||
require.ErrorContains(t, err2, "error getting setting")
|
||||
}
|
||||
|
||||
func TestSyncRBACOverrideFalse_DiffRevNoOverrideAllowed(t *testing.T) {
|
||||
ctx := t.Context()
|
||||
//nolint:staticcheck
|
||||
ctx = context.WithValue(ctx, "claims", &jwt.RegisteredClaims{Subject: "test-user"})
|
||||
appServer := newTestAppServer(t)
|
||||
_ = appServer.enf.SetBuiltinPolicy(`
|
||||
p, test-user, applications, get, default/*, allow
|
||||
p, test-user, applications, create, default/*, allow
|
||||
p, test-user, applications, sync, default/*, allow
|
||||
p, test-user, applications, override, default/*, deny
|
||||
`)
|
||||
|
||||
// create a new app
|
||||
testApp := newTestApp()
|
||||
app, err := appServer.Create(ctx,
|
||||
&application.ApplicationCreateRequest{Application: testApp})
|
||||
require.NoError(t, err)
|
||||
// and sync to different revision
|
||||
syncReq := &application.ApplicationSyncRequest{
|
||||
Name: &app.Name,
|
||||
Revision: ptr.To("revisionbranch"),
|
||||
}
|
||||
|
||||
syncedApp, err := appServer.Sync(ctx, syncReq)
|
||||
require.NoError(t, err)
|
||||
assert.NotNil(t, syncedApp)
|
||||
assert.Equal(t, "HEAD", syncedApp.Spec.Source.TargetRevision)
|
||||
|
||||
// same for multi-source app
|
||||
|
||||
multiSourceApp := newMultiSourceTestApp()
|
||||
app, err = appServer.Create(ctx,
|
||||
&application.ApplicationCreateRequest{Application: multiSourceApp})
|
||||
require.NoError(t, err)
|
||||
syncReq = &application.ApplicationSyncRequest{
|
||||
Name: &app.Name,
|
||||
SourcePositions: []int64{1},
|
||||
Revisions: []string{"revisionbranch1"},
|
||||
}
|
||||
syncedApp, err = appServer.Sync(ctx, syncReq)
|
||||
require.NoError(t, err)
|
||||
assert.NotNil(t, syncedApp)
|
||||
// Sync to different revision must not change app spec
|
||||
assert.Equal(t, "appbranch1", syncedApp.Spec.Sources[0].TargetRevision)
|
||||
assert.Equal(t, "appbranch2", syncedApp.Spec.Sources[1].TargetRevision)
|
||||
}
|
||||
|
||||
func TestSyncRBACOverrideNotRequired_SameRevisionAllowed(t *testing.T) {
|
||||
ctx := t.Context()
|
||||
//nolint:staticcheck
|
||||
ctx = context.WithValue(ctx, "claims", &jwt.RegisteredClaims{Subject: "test-user"})
|
||||
appServer := newTestAppServer(t)
|
||||
_ = appServer.enf.SetBuiltinPolicy(`
|
||||
p, test-user, applications, get, default/*, allow
|
||||
p, test-user, applications, create, default/*, allow
|
||||
p, test-user, applications, sync, default/*, allow
|
||||
p, test-user, applications, override, default/*, deny`)
|
||||
|
||||
// create a new app
|
||||
testApp := newTestApp()
|
||||
app, err := appServer.Create(ctx, &application.ApplicationCreateRequest{Application: testApp})
|
||||
require.NoError(t, err)
|
||||
syncReq := &application.ApplicationSyncRequest{
|
||||
Name: &app.Name,
|
||||
Revision: ptr.To("HEAD"),
|
||||
}
|
||||
|
||||
syncedApp, err := appServer.Sync(ctx, syncReq)
|
||||
require.NoError(t, err,
|
||||
"should be able to sync to the same revision without override permission")
|
||||
assert.NotNil(t, syncedApp)
|
||||
|
||||
// same for multi-source app
|
||||
multiSourceApp := newMultiSourceTestApp()
|
||||
app, err = appServer.Create(ctx, &application.ApplicationCreateRequest{Application: multiSourceApp})
|
||||
require.NoError(t, err)
|
||||
syncReq = &application.ApplicationSyncRequest{
|
||||
Name: &app.Name,
|
||||
SourcePositions: []int64{1, 2},
|
||||
Revisions: []string{"appbranch1", "appbranch2"},
|
||||
}
|
||||
syncedApp, err = appServer.Sync(ctx, syncReq)
|
||||
require.NoError(t, err,
|
||||
"should be able to sync to the same revision without override permission, multi-source app")
|
||||
assert.NotNil(t, syncedApp)
|
||||
}
|
||||
|
||||
func TestSyncRBACOverrideNotRequired_EmptyRevisionAllowed(t *testing.T) {
|
||||
ctx := t.Context()
|
||||
//nolint:staticcheck
|
||||
ctx = context.WithValue(ctx, "claims", &jwt.RegisteredClaims{Subject: "test-user"})
|
||||
appServer := newTestAppServer(t)
|
||||
_ = appServer.enf.SetBuiltinPolicy(`
|
||||
p, test-user, applications, get, default/*, allow
|
||||
p, test-user, applications, create, default/*, allow
|
||||
p, test-user, applications, sync, default/*, allow
|
||||
p, test-user, applications, override, default/*, deny
|
||||
`)
|
||||
|
||||
// create a new app
|
||||
testApp := newTestApp()
|
||||
app, err := appServer.Create(ctx, &application.ApplicationCreateRequest{Application: testApp})
|
||||
require.NoError(t, err)
|
||||
syncReq := &application.ApplicationSyncRequest{
|
||||
Name: &app.Name,
|
||||
}
|
||||
|
||||
syncedApp, err := appServer.Sync(ctx, syncReq)
|
||||
require.NoError(t, err,
|
||||
"should be able to sync with empty revision without override permission")
|
||||
assert.NotNil(t, app)
|
||||
assert.Equal(t, "HEAD", syncedApp.Spec.Source.TargetRevision)
|
||||
|
||||
// same for multi-source app
|
||||
multiSourceApp := newMultiSourceTestApp()
|
||||
app, err = appServer.Create(ctx, &application.ApplicationCreateRequest{Application: multiSourceApp})
|
||||
require.NoError(t, err)
|
||||
syncReq = &application.ApplicationSyncRequest{
|
||||
Name: &app.Name,
|
||||
}
|
||||
syncedApp, err = appServer.Sync(ctx, syncReq)
|
||||
require.NoError(t, err,
|
||||
"should be able to sync with empty revision without override permission, multi-source app")
|
||||
assert.NotNil(t, syncedApp)
|
||||
assert.Equal(t, "appbranch1", syncedApp.Spec.Sources[0].TargetRevision)
|
||||
assert.Equal(t, "appbranch2", syncedApp.Spec.Sources[1].TargetRevision)
|
||||
}
|
||||
|
||||
func TestSyncRBACOverrideNotRequired_DiffRevisionAllowed(t *testing.T) {
|
||||
ctx := t.Context()
|
||||
//nolint:staticcheck
|
||||
ctx = context.WithValue(ctx, "claims", &jwt.RegisteredClaims{Subject: "test-user"})
|
||||
appServer := newTestAppServer(t)
|
||||
_ = appServer.enf.SetBuiltinPolicy(`
|
||||
p, test-user, applications, get, default/*, allow
|
||||
p, test-user, applications, create, default/*, allow
|
||||
p, test-user, applications, sync, default/*, allow
|
||||
p, test-user, applications, override, default/*, allow
|
||||
`)
|
||||
// create a new app
|
||||
testApp := newTestApp()
|
||||
app, err := appServer.Create(ctx, &application.ApplicationCreateRequest{Application: testApp})
|
||||
require.NoError(t, err)
|
||||
|
||||
syncReq := &application.ApplicationSyncRequest{
|
||||
Name: &app.Name,
|
||||
Revision: ptr.To("revisionbranch"),
|
||||
}
|
||||
|
||||
syncedApp, err := appServer.Sync(ctx, syncReq)
|
||||
require.NoError(t, err,
|
||||
"should be able to sync to different revision with override permission")
|
||||
assert.NotNil(t, syncedApp)
|
||||
// Sync must not change app spec
|
||||
assert.Equal(t, "HEAD", syncedApp.Spec.Source.TargetRevision)
|
||||
|
||||
// same for multi-source app
|
||||
multiSourceApp := newMultiSourceTestApp()
|
||||
app, err = appServer.Create(ctx, &application.ApplicationCreateRequest{Application: multiSourceApp})
|
||||
require.NoError(t, err)
|
||||
syncReq = &application.ApplicationSyncRequest{
|
||||
Name: &app.Name,
|
||||
}
|
||||
syncedApp, err = appServer.Sync(ctx, syncReq)
|
||||
require.NoError(t, err,
|
||||
"should be able to sync to different revision with override permission, multi-source app")
|
||||
assert.NotNil(t, syncedApp)
|
||||
assert.Equal(t, "appbranch1", syncedApp.Spec.Sources[0].TargetRevision)
|
||||
assert.Equal(t, "appbranch2", syncedApp.Spec.Sources[1].TargetRevision)
|
||||
}
|
||||
|
||||
func TestSyncRBACOverrideNotRequired_DiffRevisionWithAutosyncPrevented(t *testing.T) {
|
||||
ctx := t.Context()
|
||||
//nolint:staticcheck
|
||||
ctx = context.WithValue(ctx, "claims", &jwt.RegisteredClaims{Subject: "test-user"})
|
||||
appServer := newTestAppServer(t)
|
||||
_ = appServer.enf.SetBuiltinPolicy(`
|
||||
p, test-user, applications, get, default/*, allow
|
||||
p, test-user, applications, create, default/*, allow
|
||||
p, test-user, applications, sync, default/*, allow
|
||||
p, test-user, applications, override, default/*, allow
|
||||
`)
|
||||
|
||||
// create a new app
|
||||
testApp := newTestApp()
|
||||
testApp.Spec.SyncPolicy = &v1alpha1.SyncPolicy{
|
||||
Automated: &v1alpha1.SyncPolicyAutomated{
|
||||
Prune: true,
|
||||
SelfHeal: true,
|
||||
},
|
||||
}
|
||||
app, err := appServer.Create(ctx, &application.ApplicationCreateRequest{Application: testApp})
|
||||
require.NoError(t, err)
|
||||
syncReq := &application.ApplicationSyncRequest{
|
||||
Name: &app.Name,
|
||||
Revision: ptr.To("revisionbranch"),
|
||||
}
|
||||
|
||||
_, err = appServer.Sync(ctx, syncReq)
|
||||
require.EqualError(t, err, "rpc error: code = FailedPrecondition desc = Cannot sync to revisionbranch: auto-sync currently set to HEAD",
|
||||
"should not be able to sync to different revision with auto-sync enabled")
|
||||
|
||||
// same for multi-source app
|
||||
multiSourceApp := newMultiSourceTestApp()
|
||||
multiSourceApp.Spec.SyncPolicy = &v1alpha1.SyncPolicy{
|
||||
Automated: &v1alpha1.SyncPolicyAutomated{
|
||||
Prune: true,
|
||||
SelfHeal: true,
|
||||
},
|
||||
}
|
||||
|
||||
app, err = appServer.Create(ctx, &application.ApplicationCreateRequest{Application: multiSourceApp})
|
||||
require.NoError(t, err)
|
||||
syncReq = &application.ApplicationSyncRequest{
|
||||
Name: &app.Name,
|
||||
SourcePositions: []int64{1},
|
||||
Revisions: []string{"revisionbranch1"},
|
||||
}
|
||||
_, err = appServer.Sync(ctx, syncReq)
|
||||
assert.EqualError(t, err, "rpc error: code = FailedPrecondition desc = Cannot sync source https://github.com/argoproj/argocd-example-apps.git to revisionbranch1: auto-sync currently set to appbranch1",
|
||||
"should not be able to sync to different revision with auto-sync enabled, multi-source app")
|
||||
}
|
||||
|
||||
func TestSyncAndTerminate(t *testing.T) {
|
||||
ctx := t.Context()
|
||||
appServer := newTestAppServer(t)
|
||||
|
||||
Reference in New Issue
Block a user