github.com/jfrog/jfrog-cli-core/v2@v2.51.0/artifactory/commands/utils/transferconfigbase_test.go (about)

     1  package utils
     2  
     3  import (
     4  	"encoding/json"
     5  	"io"
     6  	"net/http"
     7  	"path/filepath"
     8  	"testing"
     9  
    10  	"github.com/jfrog/jfrog-cli-core/v2/artifactory/utils"
    11  	commonTests "github.com/jfrog/jfrog-cli-core/v2/common/tests"
    12  	"github.com/jfrog/jfrog-cli-core/v2/utils/config"
    13  	"github.com/jfrog/jfrog-client-go/artifactory/services"
    14  	"github.com/jfrog/jfrog-client-go/utils/io/fileutils"
    15  
    16  	"github.com/stretchr/testify/assert"
    17  )
    18  
    19  const (
    20  	repoKey    = "repoKey"
    21  	projectKey = "test-proj"
    22  )
    23  
    24  var transferConfigTestDir = filepath.Join("testdata", "transferconfig")
    25  
    26  func TestIsDefaultCredentialsDefault(t *testing.T) {
    27  	unlockCounter := 0
    28  	testServer, serverDetails, _ := commonTests.CreateRtRestsMockServer(t, func(w http.ResponseWriter, r *http.Request) {
    29  		switch r.RequestURI {
    30  		case "/api/security/lockedUsers":
    31  			w.WriteHeader(http.StatusOK)
    32  			_, err := w.Write([]byte("[]"))
    33  			assert.NoError(t, err)
    34  		case "/api/system/ping":
    35  			w.WriteHeader(http.StatusOK)
    36  			_, err := w.Write([]byte("OK"))
    37  			assert.NoError(t, err)
    38  		default:
    39  			w.WriteHeader(http.StatusOK)
    40  			_, err := w.Write([]byte("User admin was successfully unlocked"))
    41  			assert.NoError(t, err)
    42  			unlockCounter++
    43  		}
    44  	})
    45  	defer testServer.Close()
    46  
    47  	isDefaultCreds, err := createTransferConfigBase(t, serverDetails, serverDetails).IsDefaultCredentials()
    48  	assert.NoError(t, err)
    49  	assert.True(t, isDefaultCreds)
    50  	assert.Equal(t, 0, unlockCounter)
    51  }
    52  
    53  func TestIsDefaultCredentialsNotDefault(t *testing.T) {
    54  	unlockCounter := 0
    55  	testServer, serverDetails, _ := commonTests.CreateRtRestsMockServer(t, func(w http.ResponseWriter, r *http.Request) {
    56  		switch r.RequestURI {
    57  		case "/api/security/lockedUsers":
    58  			w.WriteHeader(http.StatusOK)
    59  			_, err := w.Write([]byte("[]"))
    60  			assert.NoError(t, err)
    61  		case "/api/system/ping":
    62  			w.WriteHeader(http.StatusUnauthorized)
    63  			_, err := w.Write([]byte("{\n  \"errors\" : [ {\n    \"status\" : 401,\n    \"message\" : \"Bad credentials\"\n  } ]\n}"))
    64  			assert.NoError(t, err)
    65  		default:
    66  			w.WriteHeader(http.StatusOK)
    67  			_, err := w.Write([]byte("User admin was successfully unlocked"))
    68  			assert.NoError(t, err)
    69  			unlockCounter++
    70  		}
    71  	})
    72  	defer testServer.Close()
    73  
    74  	isDefaultCreds, err := createTransferConfigBase(t, serverDetails, serverDetails).IsDefaultCredentials()
    75  	assert.NoError(t, err)
    76  	assert.False(t, isDefaultCreds)
    77  	assert.Equal(t, 1, unlockCounter)
    78  }
    79  
    80  func TestIsDefaultCredentialsLocked(t *testing.T) {
    81  	pingCounter := 0
    82  	unlockCounter := 0
    83  	testServer, serverDetails, _ := commonTests.CreateRtRestsMockServer(t, func(w http.ResponseWriter, r *http.Request) {
    84  		switch r.RequestURI {
    85  		case "/api/security/lockedUsers":
    86  			w.WriteHeader(http.StatusOK)
    87  			_, err := w.Write([]byte("[ \"admin\" ]"))
    88  			assert.NoError(t, err)
    89  		case "/api/system/ping":
    90  			w.WriteHeader(http.StatusUnauthorized)
    91  			_, err := w.Write([]byte("{\n  \"errors\" : [ {\n    \"status\" : 401,\n    \"message\" : \"Bad credentials\"\n  } ]\n}"))
    92  			assert.NoError(t, err)
    93  			pingCounter++
    94  		default:
    95  			w.WriteHeader(http.StatusOK)
    96  			_, err := w.Write([]byte("User admin was successfully unlocked"))
    97  			assert.NoError(t, err)
    98  			unlockCounter++
    99  		}
   100  	})
   101  	defer testServer.Close()
   102  
   103  	isDefaultCreds, err := createTransferConfigBase(t, serverDetails, serverDetails).IsDefaultCredentials()
   104  	assert.NoError(t, err)
   105  	assert.False(t, isDefaultCreds)
   106  	assert.Equal(t, 0, pingCounter)
   107  	assert.Equal(t, 0, unlockCounter)
   108  }
   109  
   110  func TestValidateDifferentServers(t *testing.T) {
   111  	var sourceRtVersion string
   112  	// Create transfer config command
   113  	_, sourceServerDetails, _ := commonTests.CreateRtRestsMockServer(t, func(w http.ResponseWriter, _ *http.Request) {
   114  		content, err := json.Marshal(VersionResponse{Version: sourceRtVersion})
   115  		assert.NoError(t, err)
   116  		_, err = w.Write(content)
   117  		assert.NoError(t, err)
   118  	})
   119  
   120  	t.Run("Same source and target servers", func(t *testing.T) {
   121  		err := createTransferConfigBase(t, sourceServerDetails, sourceServerDetails).ValidateDifferentServers()
   122  		assert.ErrorContains(t, err, "The source and target Artifactory servers are identical, but should be different.")
   123  	})
   124  }
   125  
   126  func TestGetSelectedRepositories(t *testing.T) {
   127  	sourceTestServer, sourceServerDetails, _ := commonTests.CreateRtRestsMockServer(t, func(w http.ResponseWriter, _ *http.Request) {
   128  		w.WriteHeader(http.StatusOK)
   129  		repositories := &[]services.RepositoryDetails{
   130  			{Key: "generic-local", Type: "local"}, {Key: "generic-local-filter", Type: "local"}, {Key: "generic-local-existed", Type: "local"},
   131  			{Key: "generic-remote", Type: "remote"}, {Key: "generic-filter-remote", Type: "remote"},
   132  			{Key: "generic-virtual", Type: "virtual"}, {Key: "filter-generic-virtual", Type: "virtual"},
   133  			{Key: "generic-federated", Type: "federated"}, {Key: "generic-federated-filter", Type: "federated"},
   134  		}
   135  		reposBytes, err := json.Marshal(repositories)
   136  		assert.NoError(t, err)
   137  		_, err = w.Write(reposBytes)
   138  		assert.NoError(t, err)
   139  	})
   140  	defer sourceTestServer.Close()
   141  	targetTestServer, targetServerDetails, _ := commonTests.CreateRtRestsMockServer(t, func(w http.ResponseWriter, _ *http.Request) {
   142  		w.WriteHeader(http.StatusOK)
   143  		repositories := &[]services.RepositoryDetails{{Key: "generic-local-existed", Type: "local"}}
   144  		reposBytes, err := json.Marshal(repositories)
   145  		assert.NoError(t, err)
   146  		_, err = w.Write(reposBytes)
   147  		assert.NoError(t, err)
   148  	})
   149  	defer targetTestServer.Close()
   150  
   151  	transferConfigBase := createTransferConfigBase(t, sourceServerDetails, targetServerDetails)
   152  	transferConfigBase.SetExcludeReposPatterns([]string{"*filter*"})
   153  	selectedRepos, err := transferConfigBase.GetSelectedRepositories()
   154  	assert.NoError(t, err)
   155  	assert.Len(t, selectedRepos, 4)
   156  	assert.Equal(t, []services.RepositoryDetails{{Key: "generic-local", Type: "local"}}, selectedRepos[utils.Local])
   157  	assert.Equal(t, []services.RepositoryDetails{{Key: "generic-remote", Type: "remote"}}, selectedRepos[utils.Remote])
   158  	assert.Equal(t, []services.RepositoryDetails{{Key: "generic-virtual", Type: "virtual"}}, selectedRepos[utils.Virtual])
   159  	assert.Equal(t, []services.RepositoryDetails{{Key: "generic-federated", Type: "federated"}}, selectedRepos[utils.Federated])
   160  }
   161  
   162  func TestTransferRepositoryToTarget(t *testing.T) {
   163  	federatedRepo := readRepoConfig(t, "federated_repo")
   164  	federatedRepoWithoutMembers := readRepoConfig(t, "federated_repo_without_members")
   165  
   166  	testServer, serverDetails, _ := commonTests.CreateRtRestsMockServer(t, func(w http.ResponseWriter, r *http.Request) {
   167  		w.WriteHeader(http.StatusOK)
   168  		switch r.Method {
   169  		case http.MethodGet:
   170  			switch r.RequestURI {
   171  			case "/api/repositories/federated-local":
   172  				_, err := w.Write(federatedRepo)
   173  				assert.NoError(t, err)
   174  			case "/api/repositories/federated-local-no-members":
   175  				_, err := w.Write(federatedRepoWithoutMembers)
   176  				assert.NoError(t, err)
   177  			default:
   178  				assert.Fail(t, "Unexpected request URI "+r.RequestURI)
   179  			}
   180  		case http.MethodPut:
   181  			body, err := io.ReadAll(r.Body)
   182  			assert.NoError(t, err)
   183  			assert.Equal(t, getRepoParamsMap(t, federatedRepoWithoutMembers), getRepoParamsMap(t, body))
   184  		default:
   185  			assert.Fail(t, "Unexpected method "+r.Method)
   186  		}
   187  	})
   188  	defer testServer.Close()
   189  
   190  	transferConfigBase := createTransferConfigBase(t, serverDetails, serverDetails)
   191  	assert.False(t, transferConfigBase.FederatedMembersRemoved)
   192  	err := transferConfigBase.transferSpecificRepositoriesToTarget([]services.RepositoryDetails{
   193  		{Key: "federated-local"}, {Key: "federated-local-no-members"}}, utils.Federated)
   194  	assert.NoError(t, err)
   195  	assert.True(t, transferConfigBase.FederatedMembersRemoved)
   196  }
   197  
   198  func TestTransferVirtualRepositoriesToTarget(t *testing.T) {
   199  	virtualRepoA := readRepoConfig(t, "virtual_repo_a")
   200  	virtualRepoB := readRepoConfig(t, "virtual_repo_b")
   201  
   202  	testServer, serverDetails, _ := commonTests.CreateRtRestsMockServer(t, func(w http.ResponseWriter, r *http.Request) {
   203  		w.WriteHeader(http.StatusOK)
   204  		switch r.Method {
   205  		case http.MethodGet:
   206  			switch r.RequestURI {
   207  			case "/api/repositories/a-virtual":
   208  				_, err := w.Write(virtualRepoA)
   209  				assert.NoError(t, err)
   210  			case "/api/repositories/b-virtual":
   211  				_, err := w.Write(virtualRepoB)
   212  				assert.NoError(t, err)
   213  			default:
   214  				assert.Fail(t, "Unexpected request URI "+r.RequestURI)
   215  			}
   216  		case http.MethodPut, http.MethodPost:
   217  			body, err := io.ReadAll(r.Body)
   218  			assert.NoError(t, err)
   219  
   220  			expectedVirtualRepoAParamsMap := getRepoParamsMap(t, virtualRepoA)
   221  			expectedVirtualRepoBParamsMap := getRepoParamsMap(t, virtualRepoB)
   222  
   223  			if r.Method == http.MethodPut {
   224  				delete(expectedVirtualRepoAParamsMap, "repositories")
   225  				delete(expectedVirtualRepoBParamsMap, "repositories")
   226  			}
   227  
   228  			switch r.RequestURI {
   229  			case "/api/repositories/a-virtual":
   230  				assert.Equal(t, expectedVirtualRepoAParamsMap, getRepoParamsMap(t, body))
   231  			case "/api/repositories/b-virtual":
   232  				assert.Equal(t, expectedVirtualRepoBParamsMap, getRepoParamsMap(t, body))
   233  			default:
   234  				assert.Fail(t, "Unexpected request URI "+r.RequestURI)
   235  			}
   236  		}
   237  	})
   238  	defer testServer.Close()
   239  
   240  	transferConfigBase := createTransferConfigBase(t, serverDetails, serverDetails)
   241  	assert.NoError(t, transferConfigBase.transferVirtualRepositoriesToTarget([]services.RepositoryDetails{{Key: "a-virtual"}, {Key: "b-virtual"}}))
   242  }
   243  
   244  func TestDeactivateKeyEncryption(t *testing.T) {
   245  	testDeactivateKeyEncryption(t, true)
   246  }
   247  
   248  func TestDeactivateKeyEncryptionNotEncrypted(t *testing.T) {
   249  	testDeactivateKeyEncryption(t, false)
   250  }
   251  
   252  func testDeactivateKeyEncryption(t *testing.T, wasEncrypted bool) {
   253  	decrypted := false
   254  	reactivated := false
   255  	testServer, serverDetails, _ := commonTests.CreateRtRestsMockServer(t, func(w http.ResponseWriter, r *http.Request) {
   256  		if r.RequestURI == "/api/system/decrypt" {
   257  			if wasEncrypted {
   258  				w.WriteHeader(http.StatusOK)
   259  			} else {
   260  				w.WriteHeader(http.StatusConflict)
   261  			}
   262  			decrypted = true
   263  		} else if r.RequestURI == "/api/system/encrypt" {
   264  			reactivated = true
   265  			w.WriteHeader(http.StatusOK)
   266  		}
   267  	})
   268  	defer testServer.Close()
   269  	transferConfigBase := createTransferConfigBase(t, serverDetails, serverDetails)
   270  
   271  	reactivate, err := transferConfigBase.DeactivateKeyEncryption()
   272  	assert.NoError(t, err)
   273  	assert.True(t, decrypted)
   274  
   275  	assert.False(t, reactivated)
   276  	assert.NoError(t, reactivate())
   277  	assert.Equal(t, reactivated, wasEncrypted)
   278  }
   279  
   280  var removeProjectKeyCases = []struct {
   281  	repoKey            string
   282  	projectKey         string
   283  	expectedProjectKey string
   284  }{
   285  	{repoKey: repoKey, projectKey: "", expectedProjectKey: ""},
   286  	{repoKey: repoKey, projectKey: "default", expectedProjectKey: ""},
   287  	{repoKey: repoKey, projectKey: projectKey, expectedProjectKey: projectKey},
   288  	{repoKey: projectKey + "-" + repoKey, projectKey: projectKey, expectedProjectKey: ""},
   289  }
   290  
   291  func TestRemoveProjectKey(t *testing.T) {
   292  	repoParams := services.NewLocalRepositoryBaseParams()
   293  
   294  	for _, testCase := range removeProjectKeyCases {
   295  		t.Run(testCase.repoKey, func(t *testing.T) {
   296  			repoParams.ProjectKey = testCase.projectKey
   297  			actualRepoParams, actualProjectKey, err := removeProjectKeyIfNeeded(repoParams, testCase.repoKey)
   298  			assert.NoError(t, err)
   299  			assert.Equal(t, testCase.expectedProjectKey, actualProjectKey)
   300  			if testCase.expectedProjectKey == "" {
   301  				assert.Equal(t, repoParams, actualRepoParams)
   302  			} else {
   303  				assert.NotEqual(t, repoParams, actualRepoParams)
   304  			}
   305  		})
   306  	}
   307  }
   308  
   309  func TestRemoveProjectKeyIllegal(t *testing.T) {
   310  	type illegalRepoParamsStruct struct {
   311  		ProjectKey int `json:"projectKey"`
   312  	}
   313  	illegalRepoParams := &illegalRepoParamsStruct{ProjectKey: 7}
   314  	actualRepoParams, projectKey, err := removeProjectKeyIfNeeded(illegalRepoParams, repoKey)
   315  	assert.ErrorContains(t, err, "couldn't parse the 'projectKey' value '7' of repository 'repoKey'")
   316  	assert.Empty(t, projectKey)
   317  	assert.Nil(t, actualRepoParams)
   318  }
   319  
   320  func TestCreateRepositoryAndAssignToProject(t *testing.T) {
   321  	projectUnassigned := false
   322  	repositoryCreated := false
   323  	projectAssigned := false
   324  	testServer, serverDetails, _ := commonTests.CreateRtRestsMockServer(t, func(w http.ResponseWriter, r *http.Request) {
   325  		switch r.RequestURI {
   326  		case "/access/api/v1/projects/_/attach/repositories/local-repo":
   327  			projectUnassigned = true
   328  		case "/api/repositories/local-repo":
   329  			repositoryCreated = true
   330  			body, err := io.ReadAll(r.Body)
   331  			assert.NoError(t, err)
   332  			_, exist := getRepoParamsMap(t, body)["projectKey"]
   333  			assert.False(t, exist)
   334  		case "/access/api/v1/projects/_/attach/repositories/local-repo/test-proj?force=true":
   335  			projectAssigned = true
   336  		default:
   337  			assert.Fail(t, "Unexpected request URI: "+r.RequestURI)
   338  		}
   339  		w.WriteHeader(http.StatusOK)
   340  	})
   341  	defer testServer.Close()
   342  	transferConfigBase := createTransferConfigBase(t, serverDetails, serverDetails)
   343  
   344  	repoParams := services.NewLocalRepositoryBaseParams()
   345  	repoParams.Key = "local-repo"
   346  	repoParams.ProjectKey = projectKey
   347  	err := transferConfigBase.createRepositoryAndAssignToProject(repoParams, services.RepositoryDetails{Key: repoParams.Key})
   348  	assert.NoError(t, err)
   349  	assert.True(t, projectUnassigned)
   350  	assert.True(t, repositoryCreated)
   351  	assert.True(t, projectAssigned)
   352  }
   353  
   354  func createTransferConfigBase(t *testing.T, sourceServerDetails, targetServerDetails *config.ServerDetails) *TransferConfigBase {
   355  	transferConfigBase := NewTransferConfigBase(sourceServerDetails, targetServerDetails)
   356  	assert.NoError(t, transferConfigBase.CreateServiceManagers(false))
   357  	return transferConfigBase
   358  }
   359  
   360  func getRepoParamsMap(t *testing.T, body []byte) map[string]interface{} {
   361  	var repoParams interface{}
   362  	assert.NoError(t, json.Unmarshal(body, &repoParams))
   363  	repoParamsMap, err := InterfaceToMap(repoParams)
   364  	assert.NoError(t, err)
   365  	return repoParamsMap
   366  }
   367  
   368  func readRepoConfig(t *testing.T, fileName string) []byte {
   369  	repoConfig, err := fileutils.ReadFile(filepath.Join(transferConfigTestDir, fileName+".json"))
   370  	assert.NoError(t, err)
   371  	return repoConfig
   372  }