From cc4d1f48c70adaf9df78ff9469d5fe2a40242da6 Mon Sep 17 00:00:00 2001 From: mnsboev Date: Sun, 12 Apr 2026 09:47:02 +0300 Subject: [PATCH] Remove e2e attachment tests which required a custom user --- .../e2e/tests/e2e_create_attachment_suite.go | 486 +++--------------- tests/e2e/tests/e2e_create_suite.go | 4 +- tests/e2e/tests/e2e_get_suite.go | 94 +--- tests/e2e/tests/e2e_verify_suite.go | 89 +--- 4 files changed, 103 insertions(+), 570 deletions(-) diff --git a/tests/e2e/tests/e2e_create_attachment_suite.go b/tests/e2e/tests/e2e_create_attachment_suite.go index ed7605d..4c218da 100644 --- a/tests/e2e/tests/e2e_create_attachment_suite.go +++ b/tests/e2e/tests/e2e_create_attachment_suite.go @@ -1,253 +1,101 @@ package tests import ( - "bytes" "encoding/json" "fmt" "io" "net/http" "os" - "os/exec" "path/filepath" - "runtime" "strings" "testing" - "time" - "github.com/jfrog/jfrog-cli-evidence/tests/e2e" "github.com/jfrog/jfrog-cli-evidence/tests/e2e/utils" clientutils "github.com/jfrog/jfrog-client-go/utils" - "github.com/jfrog/jfrog-client-go/utils/errorutils" "github.com/stretchr/testify/require" ) -func (r *EvidenceE2ETestsRunner) RunCreateEvidenceWithAttachmentPermissions(t *testing.T) { - t.Log("=== Create Evidence - Attachment Permissions Test ===") +type attachmentEvidenceFixture struct { + SubjectRepoPath string + AttachmentRepoPath string + PredicatePath string +} + +func (r *EvidenceE2ETestsRunner) RunCreateEvidenceWithAttachment(t *testing.T) { + t.Log("=== Create Evidence - With Attachment Test ===") require.NotEmpty(t, SharedPrivateKeyPath, "shared key pair not initialized") + ensureAttachmentSupportedArtifactoryVersion(t, r) ensureAttachmentSupportedEvidenceVersion(t, r) - t.Run("LocalAttachment_UserHasSubjectAndTempWritePermissions", func(t *testing.T) { - r.runAttachmentCase(t, attachmentCase{ - name: "local-attachment-write-allowed", - useLocalAttachment: true, - grantTempWrite: true, - expectError: false, - }) - }) - - t.Run("LocalAttachment_UserHasSubjectButNoTempWritePermissions", func(t *testing.T) { - r.runAttachmentCase(t, attachmentCase{ - name: "local-attachment-write-denied", - useLocalAttachment: true, - grantTempWrite: false, - expectError: true, - errorContains: []string{ - "failed to upload --attach-local file", - "403 Forbidden", - }, - }) - }) - - t.Run("ArtifactoryAttachment_UserHasSubjectAndAttachmentReadPermissions", func(t *testing.T) { - r.runAttachmentCase(t, attachmentCase{ - name: "artifactory-attachment-read-allowed", - useArtifactoryAttachment: true, - grantArtifactoryAttachmentRead: true, - expectError: false, - }) - }) + fixture := prepareAttachmentEvidenceFixture(t, r, "create-attachments") - t.Run("ArtifactoryAttachment_UserHasSubjectButNoAttachmentReadPermissions", func(t *testing.T) { - r.runAttachmentCase(t, attachmentCase{ - name: "artifactory-attachment-read-denied", - useArtifactoryAttachment: true, - grantArtifactoryAttachmentRead: false, - expectError: true, - errorContains: []string{ - "failed to resolve --attach-artifactory-path", - "403 Forbidden", - }, - }) - }) -} + createOutput := r.EvidenceUserCLI.RunCliCmdWithOutput(t, + "create", + "--predicate", fixture.PredicatePath, + "--predicate-type", "https://slsa.dev/provenance/v1", + "--subject-repo-path", fixture.SubjectRepoPath, + "--attach-artifactory-path", fixture.AttachmentRepoPath, + "--key", SharedPrivateKeyPath, + "--key-alias", SharedKeyAlias, + ) + t.Logf("Evidence creation output: %s", createOutput) + require.NotContains(t, createOutput, "Error", "evidence create with attachment should succeed") + require.NotContains(t, createOutput, "Failed", "evidence create with attachment should succeed") -type attachmentCase struct { - name string - useLocalAttachment bool - useArtifactoryAttachment bool - grantTempWrite bool - grantArtifactoryAttachmentRead bool - expectError bool - errorContains []string + getOutput := r.EvidenceAdminCLI.RunCliCmdWithOutput(t, + "get", + "--subject-repo-path", fixture.SubjectRepoPath, + "--format", "json", + ) + assertGetOutputContainsAttachment(t, getOutput) + t.Log("=== ✅ Create Evidence with Attachment completed ===") } -func (r *EvidenceE2ETestsRunner) runAttachmentCase(t *testing.T, tc attachmentCase) { +func prepareAttachmentEvidenceFixture(t *testing.T, r *EvidenceE2ETestsRunner, predicateKind string) attachmentEvidenceFixture { t.Helper() - adminToken := mustGetAdminToken(t, r) - baseURL := getJfrogBaseURL(t, r) - - // Track access resources for cleanup. Populated after repo creation, - // but cleaned up AFTER repo deletion (LIFO: registered first, runs last). - var permissionNames []string - var username, groupName string - - t.Cleanup(func() { - for _, perm := range permissionNames { - if err := deleteAccessPermission(baseURL, adminToken, perm); err != nil { - t.Logf("Warning: failed to delete permission %s: %v", perm, err) - } - } - if groupName != "" { - if err := deleteAccessGroup(baseURL, adminToken, groupName); err != nil { - t.Logf("Warning: failed to delete group %s: %v", groupName, err) - } - } - if username != "" { - if err := deleteAccessUser(baseURL, adminToken, username); err != nil { - t.Logf("Warning: failed to delete user %s: %v", username, err) - } - } - }) - - // Repos register their own t.Cleanup (LIFO: registered after access cleanup, so repos are deleted first). - // This prevents 500 errors: repo deletion cascade-updates permissions while they still exist, - // then the access cleanup above deletes the (now repo-free) permissions cleanly. subjectRepo := utils.CreateTestRepositoryWithName(t, r.ServicesManager, "generic") - subjectArtifactPath := utils.CreateTestArtifact(t, "subject artifact for attachment permissions case") - subjectArtifactName := filepath.Base(subjectArtifactPath) - subjectRepoPath := fmt.Sprintf("%s/%s", subjectRepo, subjectArtifactName) + subjectArtifactPath := utils.CreateTestArtifact(t, fmt.Sprintf("subject artifact for %s", predicateKind)) + subjectName := filepath.Base(subjectArtifactPath) + subjectRepoPath := fmt.Sprintf("%s/%s", subjectRepo, subjectName) require.NoError(t, utils.UploadArtifact(r.ServicesManager, subjectArtifactPath, subjectRepoPath)) - tmpRepo := utils.CreateTestRepositoryWithName(t, r.ServicesManager, "generic") - tmpTarget := fmt.Sprintf("%s/tmp/", tmpRepo) - localAttachmentPath := utils.CreateTestArtifact(t, "local attachment content") - attachmentRepo := utils.CreateTestRepositoryWithName(t, r.ServicesManager, "generic") - attachmentRepoPath := fmt.Sprintf("%s/attachments/preuploaded.txt", attachmentRepo) - require.NoError(t, utils.UploadArtifact(r.ServicesManager, localAttachmentPath, attachmentRepoPath)) + attachmentPath := utils.CreateTestArtifact(t, fmt.Sprintf("attachment for %s", predicateKind)) + attachmentRepoPath := fmt.Sprintf("%s/attachments/report.txt", attachmentRepo) + require.NoError(t, utils.UploadArtifact(r.ServicesManager, attachmentPath, attachmentRepoPath)) tempDir := t.TempDir() predicatePath := filepath.Join(tempDir, "predicate.json") - require.NoError(t, os.WriteFile(predicatePath, []byte(`{"type":"attachment-permissions-e2e"}`), 0644)) - - username = fmt.Sprintf("att-e2e-%d", time.Now().UnixNano()) - password := "EvidenceTest123!" - email := fmt.Sprintf("%s@jfrog.local", username) - - require.NoError(t, createAccessUser(baseURL, adminToken, username, password, email)) - - groupName = fmt.Sprintf("att-e2e-group-%d", time.Now().UnixNano()) - require.NoError(t, createAccessGroup(baseURL, adminToken, groupName, username)) - - subjectPermissionName := fmt.Sprintf("perm-subject-%d", time.Now().UnixNano()) - require.NoError(t, createAccessPermissionForGroup( - baseURL, - adminToken, - subjectPermissionName, - groupName, - []string{"READ", "ANNOTATE"}, - []string{subjectRepo}, - )) - permissionNames = append(permissionNames, subjectPermissionName) + require.NoError(t, os.WriteFile(predicatePath, []byte(fmt.Sprintf(`{"type":"%s"}`, predicateKind)), 0644)) - if tc.grantTempWrite { - tmpPermissionName := fmt.Sprintf("perm-tmp-%d", time.Now().UnixNano()) - require.NoError(t, createAccessPermissionForGroup( - baseURL, - adminToken, - tmpPermissionName, - groupName, - []string{"READ", "WRITE", "DELETE"}, - []string{tmpRepo}, - )) - permissionNames = append(permissionNames, tmpPermissionName) + return attachmentEvidenceFixture{ + SubjectRepoPath: subjectRepoPath, + AttachmentRepoPath: attachmentRepoPath, + PredicatePath: predicatePath, } - - if tc.grantArtifactoryAttachmentRead { - attachmentPermissionName := fmt.Sprintf("perm-att-read-%d", time.Now().UnixNano()) - require.NoError(t, createAccessPermissionForGroup( - baseURL, - adminToken, - attachmentPermissionName, - groupName, - []string{"READ"}, - []string{attachmentRepo}, - )) - permissionNames = append(permissionNames, attachmentPermissionName) - } - - userToken, err := createGroupScopedUserToken(baseURL, adminToken, username, groupName) - require.NoError(t, err) - require.NotEmpty(t, userToken) - - createArgs := []string{ - "create", - "--predicate", predicatePath, - "--predicate-type", "https://slsa.dev/provenance/v1", - "--subject-repo-path", subjectRepoPath, - "--key", SharedPrivateKeyPath, - "--key-alias", SharedKeyAlias, - } - if tc.useLocalAttachment { - createArgs = append(createArgs, "--attach-local", localAttachmentPath, "--attach-artifactory-temp-path", tmpTarget) - } - if tc.useArtifactoryAttachment { - createArgs = append(createArgs, "--attach-artifactory-path", attachmentRepoPath) - } - - output, runErr := runEvidenceCommandWithToken(t, r, userToken, createArgs...) - combined := output - if runErr != nil { - combined = combined + "\n" + runErr.Error() - } - - if tc.expectError { - require.Error(t, runErr, "attachment case should fail") - for _, msg := range tc.errorContains { - require.Contains(t, combined, msg) - } - return - } - - require.NoError(t, runErr, "attachment case should succeed") - require.NotContains(t, combined, "Error") - require.NotContains(t, combined, "Failed") } -func runEvidenceCommandWithToken(t *testing.T, r *EvidenceE2ETestsRunner, token string, args ...string) (string, error) { +func assertGetOutputContainsAttachment(t *testing.T, getOutput string) { t.Helper() - require.NotEmpty(t, token, "access token is required") - - _, currentFile, _, _ := runtime.Caller(0) - binaryPath := filepath.Join(filepath.Dir(currentFile), "..", "..", "..", "build", "jfrog-evidence") - cmdArgs := append([]string{}, args...) - cmdArgs = append(cmdArgs, fmt.Sprintf("--url=%s", getJfrogBaseURL(t, r)), fmt.Sprintf("--access-token=%s", token)) - cmd := exec.Command(binaryPath, cmdArgs...) - output, err := cmd.CombinedOutput() - return string(output), err -} -func getJfrogBaseURL(t *testing.T, r *EvidenceE2ETestsRunner) string { - t.Helper() - require.NotNil(t, r) - require.NotNil(t, r.ServicesManager, "services manager not initialized") + var jsonData map[string]any + require.NoError(t, json.Unmarshal([]byte(getOutput), &jsonData), "get output should be valid JSON") - config := r.ServicesManager.GetConfig() - require.NotNil(t, config, "services manager config not initialized") + resultMap, ok := jsonData["result"].(map[string]any) + require.True(t, ok, "get output must include result") - serviceDetails := config.GetServiceDetails() - require.NotNil(t, serviceDetails, "service details not initialized") + evidences, ok := resultMap["evidence"].([]any) + require.True(t, ok, "get output must include evidence list") + require.NotEmpty(t, evidences, "get output must include at least one evidence") - baseURL := normalizePlatformURL(serviceDetails.GetUrl()) - require.NotEmpty(t, baseURL, "jfrog base url not configured") - return baseURL -} + firstEvidence, ok := evidences[0].(map[string]any) + require.True(t, ok, "first evidence must be an object") -func normalizePlatformURL(rawURL string) string { - baseURL := strings.TrimRight(rawURL, "/") - return strings.TrimSuffix(baseURL, "/artifactory") + attachments, exists := firstEvidence["attachments"] + require.True(t, exists, "attachments should be present for evidence with attachment") + require.NotEmpty(t, attachments, "attachments should not be empty") } func ensureAttachmentSupportedEvidenceVersion(t *testing.T, r *EvidenceE2ETestsRunner) { @@ -259,11 +107,13 @@ func ensureAttachmentSupportedEvidenceVersion(t *testing.T, r *EvidenceE2ETestsR t.Skipf("skipping attachment e2e: failed to query evidence version: %v", err) } defer func() { _ = resp.Body.Close() }() + body, err := io.ReadAll(resp.Body) require.NoError(t, err) if resp.StatusCode != http.StatusOK { t.Skipf("skipping attachment e2e: evidence version endpoint returned %s", resp.Status) } + version := strings.TrimSpace(string(body)) if err = clientutils.ValidateMinimumVersion("JFrog Evidence", version, "7.646.1"); err != nil { t.Skipf("skipping attachment e2e: evidence version %q does not support attachments", version) @@ -285,7 +135,7 @@ func ensureAttachmentSupportedArtifactoryVersion(t *testing.T, r *EvidenceE2ETes } } -func mustGetAdminToken(t *testing.T, r *EvidenceE2ETestsRunner) string { +func getJfrogBaseURL(t *testing.T, r *EvidenceE2ETestsRunner) string { t.Helper() require.NotNil(t, r) require.NotNil(t, r.ServicesManager, "services manager not initialized") @@ -296,222 +146,12 @@ func mustGetAdminToken(t *testing.T, r *EvidenceE2ETestsRunner) string { serviceDetails := config.GetServiceDetails() require.NotNil(t, serviceDetails, "service details not initialized") - if token := strings.TrimSpace(serviceDetails.GetAccessToken()); token != "" { - return token - } - if e2e.ArtifactoryAdminToken != nil && strings.TrimSpace(*e2e.ArtifactoryAdminToken) != "" { - return strings.TrimSpace(*e2e.ArtifactoryAdminToken) - } - _, currentFile, _, _ := runtime.Caller(0) - tokenPath := filepath.Join(filepath.Dir(currentFile), "..", "local", ".admin_token") - data, err := os.ReadFile(tokenPath) - require.NoError(t, err) - token := strings.TrimSpace(string(data)) - require.NotEmpty(t, token) - return token -} - -func createAccessUser(baseURL, adminToken, username, password, email string) error { - payload := map[string]any{ - "username": username, - "password": password, - "email": email, - "groups": []string{}, - "profile_updatable": true, - "admin": false, - } - content, err := json.Marshal(payload) - if err != nil { - return err - } - req, err := http.NewRequest(http.MethodPost, baseURL+"/access/api/v2/users", bytes.NewReader(content)) - if err != nil { - return err - } - req.Header.Set("Authorization", "Bearer "+adminToken) - req.Header.Set("Content-Type", "application/json") - resp, err := http.DefaultClient.Do(req) - if err != nil { - return err - } - defer func() { _ = resp.Body.Close() }() - if resp.StatusCode == http.StatusCreated || resp.StatusCode == http.StatusConflict { - return nil - } - body, _ := io.ReadAll(resp.Body) - return errorutils.CheckErrorf("failed to create user '%s': %s (%s)", username, resp.Status, string(body)) -} - -func createGroupScopedUserToken(baseURL, adminToken, username, groupName string) (string, error) { - form := fmt.Sprintf("username=%s&scope=applied-permissions/groups:%s&expires_in=0", username, groupName) - req, err := http.NewRequest(http.MethodPost, baseURL+"/access/api/v1/tokens", strings.NewReader(form)) - if err != nil { - return "", err - } - req.Header.Set("Authorization", "Bearer "+adminToken) - req.Header.Set("Content-Type", "application/x-www-form-urlencoded") - resp, err := http.DefaultClient.Do(req) - if err != nil { - return "", err - } - defer func() { _ = resp.Body.Close() }() - body, _ := io.ReadAll(resp.Body) - if resp.StatusCode != http.StatusOK { - return "", errorutils.CheckErrorf("failed to create group-scoped token for user '%s': %s (%s)", username, resp.Status, string(body)) - } - var parsed struct { - AccessToken string `json:"access_token"` - } - if err = json.Unmarshal(body, &parsed); err != nil { - return "", err - } - if strings.TrimSpace(parsed.AccessToken) == "" { - return "", errorutils.CheckErrorf("empty access_token in token response for user '%s' and group '%s'", username, groupName) - } - return parsed.AccessToken, nil -} - -func deleteAccessUser(baseURL, adminToken, username string) error { - req, err := http.NewRequest(http.MethodDelete, fmt.Sprintf("%s/access/api/v2/users/%s", baseURL, username), nil) - if err != nil { - return err - } - req.Header.Set("Authorization", "Bearer "+adminToken) - resp, err := http.DefaultClient.Do(req) - if err != nil { - return err - } - defer func() { _ = resp.Body.Close() }() - if resp.StatusCode == http.StatusNoContent || resp.StatusCode == http.StatusNotFound { - return nil - } - body, _ := io.ReadAll(resp.Body) - return errorutils.CheckErrorf("failed to delete user '%s': %s (%s)", username, resp.Status, string(body)) -} - -func deleteAccessPermission(baseURL, adminToken, permissionName string) error { - req, err := http.NewRequest(http.MethodDelete, fmt.Sprintf("%s/access/api/v2/permissions/%s", baseURL, permissionName), nil) - if err != nil { - return err - } - req.Header.Set("Authorization", "Bearer "+adminToken) - resp, err := http.DefaultClient.Do(req) - if err != nil { - return err - } - defer func() { _ = resp.Body.Close() }() - if resp.StatusCode == http.StatusNoContent || resp.StatusCode == http.StatusNotFound { - return nil - } - body, _ := io.ReadAll(resp.Body) - return errorutils.CheckErrorf("failed to delete permission '%s': %s (%s)", permissionName, resp.Status, string(body)) -} - -func createAccessGroup(baseURL, adminToken, groupName, username string) error { - payload := map[string]any{ - "name": groupName, - "description": "attachment e2e isolated group", - "admin_privileges": false, - "auto_join": false, - "members": []string{username}, - } - content, err := json.Marshal(payload) - if err != nil { - return err - } - req, err := http.NewRequest(http.MethodPost, baseURL+"/access/api/v2/groups", bytes.NewReader(content)) - if err != nil { - return err - } - req.Header.Set("Authorization", "Bearer "+adminToken) - req.Header.Set("Content-Type", "application/json") - resp, err := http.DefaultClient.Do(req) - if err != nil { - return err - } - defer func() { _ = resp.Body.Close() }() - if resp.StatusCode == http.StatusCreated || resp.StatusCode == http.StatusOK || resp.StatusCode == http.StatusConflict { - return nil - } - body, _ := io.ReadAll(resp.Body) - return errorutils.CheckErrorf("failed to create group '%s': %s (%s)", groupName, resp.Status, string(body)) -} - -func deleteAccessGroup(baseURL, adminToken, groupName string) error { - req, err := http.NewRequest(http.MethodDelete, fmt.Sprintf("%s/access/api/v2/groups/%s", baseURL, groupName), nil) - if err != nil { - return err - } - req.Header.Set("Authorization", "Bearer "+adminToken) - resp, err := http.DefaultClient.Do(req) - if err != nil { - return err - } - defer func() { _ = resp.Body.Close() }() - if resp.StatusCode == http.StatusNoContent || resp.StatusCode == http.StatusNotFound { - return nil - } - body, _ := io.ReadAll(resp.Body) - return errorutils.CheckErrorf("failed to delete group '%s': %s (%s)", groupName, resp.Status, string(body)) -} - -func createAccessPermissionForGroup(baseURL, adminToken, permissionName, groupName string, actions []string, repos []string) error { - targets := map[string]map[string][]string{} - for _, repo := range repos { - targets[repo] = map[string][]string{ - "include_patterns": {"**"}, - "exclude_patterns": {}, - } - } - payload := map[string]any{ - "name": permissionName, - "resources": map[string]any{ - "artifact": map[string]any{ - "actions": map[string]any{ - "users": map[string][]string{}, - "groups": map[string][]string{groupName: actions}, - }, - "targets": targets, - }, - "repository": map[string]any{ - "actions": map[string]any{ - "users": map[string][]string{}, - "groups": map[string][]string{groupName: actions}, - }, - "targets": targets, - }, - }, - } - content, err := json.Marshal(payload) - if err != nil { - return err - } - err = postAccessPermission(baseURL, adminToken, content) - if err == nil { - return nil - } - deleteErr := deleteAccessPermission(baseURL, adminToken, permissionName) - if deleteErr != nil { - return err - } - return postAccessPermission(baseURL, adminToken, content) + baseURL := normalizePlatformURL(serviceDetails.GetUrl()) + require.NotEmpty(t, baseURL, "jfrog base url not configured") + return baseURL } -func postAccessPermission(baseURL, adminToken string, content []byte) error { - req, err := http.NewRequest(http.MethodPost, baseURL+"/access/api/v2/permissions", bytes.NewReader(content)) - if err != nil { - return err - } - req.Header.Set("Authorization", "Bearer "+adminToken) - req.Header.Set("Content-Type", "application/json") - resp, err := http.DefaultClient.Do(req) - if err != nil { - return err - } - defer func() { _ = resp.Body.Close() }() - if resp.StatusCode == http.StatusCreated || resp.StatusCode == http.StatusOK { - return nil - } - body, _ := io.ReadAll(resp.Body) - return errorutils.CheckErrorf("failed to create permission target: %s (%s)", resp.Status, string(body)) +func normalizePlatformURL(rawURL string) string { + baseURL := strings.TrimRight(rawURL, "/") + return strings.TrimSuffix(baseURL, "/artifactory") } diff --git a/tests/e2e/tests/e2e_create_suite.go b/tests/e2e/tests/e2e_create_suite.go index 9fa0826..b8c42f1 100644 --- a/tests/e2e/tests/e2e_create_suite.go +++ b/tests/e2e/tests/e2e_create_suite.go @@ -42,8 +42,8 @@ func (r *EvidenceE2ETestsRunner) RunCreateEvidenceSuite(t *testing.T) { t.Run("WithSubjectSha256", func(t *testing.T) { r.RunCreateEvidenceWithSubjectSha256(t) }) - t.Run("WithAttachmentPermissions", func(t *testing.T) { - r.RunCreateEvidenceWithAttachmentPermissions(t) + t.Run("WithAttachment", func(t *testing.T) { + r.RunCreateEvidenceWithAttachment(t) }) } diff --git a/tests/e2e/tests/e2e_get_suite.go b/tests/e2e/tests/e2e_get_suite.go index 00c8de4..ab1c64d 100644 --- a/tests/e2e/tests/e2e_get_suite.go +++ b/tests/e2e/tests/e2e_get_suite.go @@ -5,7 +5,6 @@ import ( "fmt" "os" "path/filepath" - "strings" "testing" "time" @@ -37,8 +36,8 @@ func (r *EvidenceE2ETestsRunner) RunGetEvidenceSuite(t *testing.T) { t.Run("ForReleaseBundle", func(t *testing.T) { r.RunGetEvidenceForReleaseBundle(t) }) - t.Run("WithAttachmentAndSubjectOnlyPermissions", func(t *testing.T) { - r.RunGetEvidenceWithAttachmentAndSubjectOnlyPermissions(t) + t.Run("WithAttachment", func(t *testing.T) { + r.RunGetEvidenceWithAttachment(t) }) } @@ -527,98 +526,33 @@ func (r *EvidenceE2ETestsRunner) RunGetEvidenceForReleaseBundle(t *testing.T) { t.Log("=== ✅ Get Evidence for Release Bundle Test Completed Successfully! ===") } -// RunGetEvidenceWithAttachmentAndSubjectOnlyPermissions verifies get works for evidence with attachments -// when the user has read permission only on the subject repository. -func (r *EvidenceE2ETestsRunner) RunGetEvidenceWithAttachmentAndSubjectOnlyPermissions(t *testing.T) { - t.Log("=== Get Evidence - Attachment with Subject-only permissions ===") +// RunGetEvidenceWithAttachment verifies get works for evidence with attachments using the standard CLI harness. +func (r *EvidenceE2ETestsRunner) RunGetEvidenceWithAttachment(t *testing.T) { + t.Log("=== Get Evidence - With Attachment ===") require.NotEmpty(t, SharedPrivateKeyPath, "shared key pair not initialized") ensureAttachmentSupportedArtifactoryVersion(t, r) ensureAttachmentSupportedEvidenceVersion(t, r) - baseURL := getJfrogBaseURL(t, r) - adminToken := mustGetAdminToken(t, r) - - var permissionName, userName, groupName string - - t.Cleanup(func() { - if permissionName != "" { - if err := deleteAccessPermission(baseURL, adminToken, permissionName); err != nil { - t.Logf("Warning: failed to delete permission %s: %v", permissionName, err) - } - } - if groupName != "" { - if err := deleteAccessGroup(baseURL, adminToken, groupName); err != nil { - t.Logf("Warning: failed to delete group %s: %v", groupName, err) - } - } - if userName != "" { - if err := deleteAccessUser(baseURL, adminToken, userName); err != nil { - t.Logf("Warning: failed to delete user %s: %v", userName, err) - } - } - }) - - subjectRepo := utils.CreateTestRepositoryWithName(t, r.ServicesManager, "generic") - attachmentRepo := utils.CreateTestRepositoryWithName(t, r.ServicesManager, "generic") - - subjectArtifactPath := utils.CreateTestArtifact(t, "subject artifact for get attachment permissions") - subjectName := filepath.Base(subjectArtifactPath) - subjectRepoPath := fmt.Sprintf("%s/%s", subjectRepo, subjectName) - require.NoError(t, utils.UploadArtifact(r.ServicesManager, subjectArtifactPath, subjectRepoPath)) - - attachmentPath := utils.CreateTestArtifact(t, "attachment for get") - attachmentRepoPath := fmt.Sprintf("%s/attachments/report.txt", attachmentRepo) - require.NoError(t, utils.UploadArtifact(r.ServicesManager, attachmentPath, attachmentRepoPath)) - - tempDir := t.TempDir() - predicatePath := filepath.Join(tempDir, "predicate.json") - require.NoError(t, os.WriteFile(predicatePath, []byte(`{"type":"get-attachments-subject-only"}`), 0644)) + fixture := prepareAttachmentEvidenceFixture(t, r, "get-attachments") createOutput := r.EvidenceAdminCLI.RunCliCmdWithOutput(t, "create", - "--predicate", predicatePath, + "--predicate", fixture.PredicatePath, "--predicate-type", "https://slsa.dev/provenance/v1", - "--subject-repo-path", subjectRepoPath, - "--attach-artifactory-path", attachmentRepoPath, + "--subject-repo-path", fixture.SubjectRepoPath, + "--attach-artifactory-path", fixture.AttachmentRepoPath, "--key", SharedPrivateKeyPath, "--key-alias", SharedKeyAlias, ) require.NotContains(t, createOutput, "Error", "evidence create with attachment should succeed") + require.NotContains(t, createOutput, "Failed", "evidence create with attachment should succeed") - userName = fmt.Sprintf("get-att-user-%d", time.Now().UnixNano()) - groupName = fmt.Sprintf("get-att-group-%d", time.Now().UnixNano()) - require.NoError(t, createAccessUser(baseURL, adminToken, userName, "Password!234", fmt.Sprintf("%s@test.local", userName))) - - require.NoError(t, createAccessGroup(baseURL, adminToken, groupName, userName)) - - permissionName = fmt.Sprintf("perm-get-subject-only-%d", time.Now().UnixNano()) - require.NoError(t, createAccessPermissionForGroup(baseURL, adminToken, permissionName, groupName, []string{"READ"}, []string{subjectRepo})) - - token, err := createGroupScopedUserToken(baseURL, adminToken, userName, groupName) - require.NoError(t, err) - require.NotEmpty(t, token) - - getOutput, getErr := runEvidenceCommandWithToken(t, r, token, + getOutput := r.EvidenceUserCLI.RunCliCmdWithOutput(t, "get", - "--subject-repo-path", subjectRepoPath, + "--subject-repo-path", fixture.SubjectRepoPath, "--format", "json", ) - require.NoError(t, getErr, "get should succeed with subject-only read permissions") - require.NotContains(t, strings.ToLower(getOutput), "permission", "get output should not include permission errors") - - var jsonData map[string]any - require.NoError(t, json.Unmarshal([]byte(getOutput), &jsonData), "get output should be valid JSON") - resultMap, ok := jsonData["result"].(map[string]any) - require.True(t, ok, "get output must include result") - evidences, ok := resultMap["evidence"].([]any) - require.True(t, ok) - require.NotEmpty(t, evidences) - - firstEvidence, ok := evidences[0].(map[string]any) - require.True(t, ok) - attachments, exists := firstEvidence["attachments"] - require.True(t, exists, "attachments should be present for evidence with attachment") - require.NotEmpty(t, attachments) - t.Log("=== ✅ Get Evidence with Attachment and Subject-only permissions completed ===") + assertGetOutputContainsAttachment(t, getOutput) + t.Log("=== ✅ Get Evidence with Attachment completed ===") } diff --git a/tests/e2e/tests/e2e_verify_suite.go b/tests/e2e/tests/e2e_verify_suite.go index a8f68e5..78cd634 100644 --- a/tests/e2e/tests/e2e_verify_suite.go +++ b/tests/e2e/tests/e2e_verify_suite.go @@ -5,7 +5,6 @@ import ( "fmt" "os" "path/filepath" - "strings" "testing" "time" @@ -43,8 +42,8 @@ func (r *EvidenceE2ETestsRunner) RunVerifyEvidenceSuite(t *testing.T) { t.Run("WithJsonFormat", func(t *testing.T) { r.RunVerifyEvidenceWithJsonFormat(t) }) - t.Run("WithAttachmentAndSubjectOnlyPermissions", func(t *testing.T) { - r.RunVerifyEvidenceWithAttachmentAndSubjectOnlyPermissions(t) + t.Run("WithAttachment", func(t *testing.T) { + r.RunVerifyEvidenceWithAttachment(t) }) } @@ -676,89 +675,49 @@ func (r *EvidenceE2ETestsRunner) RunVerifyEvidenceWithJsonFormat(t *testing.T) { t.Log("=== ✅ Verify Evidence with JSON Format Test Completed Successfully! ===") } -// RunVerifyEvidenceWithAttachmentAndSubjectOnlyPermissions ensures verify works when user can read only the subject repo. -func (r *EvidenceE2ETestsRunner) RunVerifyEvidenceWithAttachmentAndSubjectOnlyPermissions(t *testing.T) { - t.Log("=== Verify Evidence - Attachment with Subject-only permissions ===") +// RunVerifyEvidenceWithAttachment ensures verify works for evidence with attachments using the standard CLI harness. +func (r *EvidenceE2ETestsRunner) RunVerifyEvidenceWithAttachment(t *testing.T) { + t.Log("=== Verify Evidence - With Attachment ===") require.NotEmpty(t, SharedPrivateKeyPath, "shared key pair not initialized") require.NotEmpty(t, SharedPublicKeyPath, "shared key pair not initialized") ensureAttachmentSupportedArtifactoryVersion(t, r) ensureAttachmentSupportedEvidenceVersion(t, r) - baseURL := getJfrogBaseURL(t, r) - adminToken := mustGetAdminToken(t, r) - - var permissionName, userName, groupName string - - t.Cleanup(func() { - if permissionName != "" { - if err := deleteAccessPermission(baseURL, adminToken, permissionName); err != nil { - t.Logf("Warning: failed to delete permission %s: %v", permissionName, err) - } - } - if groupName != "" { - if err := deleteAccessGroup(baseURL, adminToken, groupName); err != nil { - t.Logf("Warning: failed to delete group %s: %v", groupName, err) - } - } - if userName != "" { - if err := deleteAccessUser(baseURL, adminToken, userName); err != nil { - t.Logf("Warning: failed to delete user %s: %v", userName, err) - } - } - }) - - subjectRepo := utils.CreateTestRepositoryWithName(t, r.ServicesManager, "generic") - attachmentRepo := utils.CreateTestRepositoryWithName(t, r.ServicesManager, "generic") - - subjectArtifactPath := utils.CreateTestArtifact(t, "subject artifact for verify attachment permissions") - subjectName := filepath.Base(subjectArtifactPath) - subjectRepoPath := fmt.Sprintf("%s/%s", subjectRepo, subjectName) - require.NoError(t, utils.UploadArtifact(r.ServicesManager, subjectArtifactPath, subjectRepoPath)) - - attachmentPath := utils.CreateTestArtifact(t, "attachment for verify") - attachmentRepoPath := fmt.Sprintf("%s/attachments/report.txt", attachmentRepo) - require.NoError(t, utils.UploadArtifact(r.ServicesManager, attachmentPath, attachmentRepoPath)) - - tempDir := t.TempDir() - predicatePath := filepath.Join(tempDir, "predicate.json") - require.NoError(t, os.WriteFile(predicatePath, []byte(`{"type":"verify-attachments-subject-only"}`), 0644)) + fixture := prepareAttachmentEvidenceFixture(t, r, "verify-attachments") createOutput := r.EvidenceAdminCLI.RunCliCmdWithOutput(t, "create", - "--predicate", predicatePath, + "--predicate", fixture.PredicatePath, "--predicate-type", "https://slsa.dev/provenance/v1", - "--subject-repo-path", subjectRepoPath, - "--attach-artifactory-path", attachmentRepoPath, + "--subject-repo-path", fixture.SubjectRepoPath, + "--attach-artifactory-path", fixture.AttachmentRepoPath, "--key", SharedPrivateKeyPath, "--key-alias", SharedKeyAlias, ) require.NotContains(t, createOutput, "Error", "evidence create with attachment should succeed") + require.NotContains(t, createOutput, "Failed", "evidence create with attachment should succeed") - userName = fmt.Sprintf("verify-att-user-%d", time.Now().UnixNano()) - groupName = fmt.Sprintf("verify-att-group-%d", time.Now().UnixNano()) - require.NoError(t, createAccessUser(baseURL, adminToken, userName, "Password!234", fmt.Sprintf("%s@test.local", userName))) - - require.NoError(t, createAccessGroup(baseURL, adminToken, groupName, userName)) - - permissionName = fmt.Sprintf("perm-verify-subject-only-%d", time.Now().UnixNano()) - require.NoError(t, createAccessPermissionForGroup(baseURL, adminToken, permissionName, groupName, []string{"READ"}, []string{subjectRepo})) - - token, err := createGroupScopedUserToken(baseURL, adminToken, userName, groupName) - require.NoError(t, err) - require.NotEmpty(t, token) - - verifyOutput, verifyErr := runEvidenceCommandWithToken(t, r, token, + verifyOutput := r.EvidenceUserCLI.RunCliCmdWithOutput(t, "verify", - "--subject-repo-path", subjectRepoPath, + "--subject-repo-path", fixture.SubjectRepoPath, "--public-keys", SharedPublicKeyPath, "--format", "json", ) - require.NoError(t, verifyErr, "verify should succeed with subject-only read permissions") - require.NotContains(t, strings.ToLower(verifyOutput), "permission", "verify output should not include permission errors") var jsonData map[string]any require.NoError(t, json.Unmarshal([]byte(verifyOutput), &jsonData), "verify output should be valid JSON") require.Equal(t, "success", jsonData["overallVerificationStatus"]) - t.Log("=== ✅ Verify Evidence with Attachment and Subject-only permissions completed ===") + + evidenceVerifications, ok := jsonData["evidenceVerifications"].([]any) + require.True(t, ok, "verify output should include evidenceVerifications") + require.NotEmpty(t, evidenceVerifications, "verify output should include at least one evidence verification") + + firstVerification, ok := evidenceVerifications[0].(map[string]any) + require.True(t, ok, "first evidence verification must be an object") + + verificationResult, ok := firstVerification["verificationResult"].(map[string]any) + require.True(t, ok, "verificationResult should be present") + require.Equal(t, "success", verificationResult["attachmentsVerificationStatus"]) + t.Log("=== ✅ Verify Evidence with Attachment completed ===") }