diff --git a/.github/workflows/build-golang-macos.yaml b/.github/workflows/build-golang-macos.yaml index de96c34..86910bc 100644 --- a/.github/workflows/build-golang-macos.yaml +++ b/.github/workflows/build-golang-macos.yaml @@ -47,4 +47,4 @@ jobs: # - uses: ./.github/workflows/platform-integration-test.yaml # with: - # wheel: dist/otdf_python-0.2.3-py3-none-any.whl + # wheel: dist/otdf_python-0.2.4-py3-none-any.whl diff --git a/.github/workflows/build-golang-ubuntu.yaml b/.github/workflows/build-golang-ubuntu.yaml index 1241eda..b7b3122 100644 --- a/.github/workflows/build-golang-ubuntu.yaml +++ b/.github/workflows/build-golang-ubuntu.yaml @@ -8,7 +8,7 @@ jobs: uses: ./.github/workflows/lint-on-ubuntu.yaml build: - runs-on: ubuntu-latest + runs-on: ubuntu-22.04 permissions: contents: write @@ -43,12 +43,12 @@ jobs: - uses: actions/cache/restore@v4 with: - path: dist/otdf_python-0.2.3-py3-none-any.whl + path: dist/otdf_python-0.2.4-py3-none-any.whl key: ${{ runner.os }}${{ matrix.python3_version }}-data-${{ github.sha }} - uses: actions/cache/save@v4 with: - path: dist/otdf_python-0.2.3-py3-none-any.whl + path: dist/otdf_python-0.2.4-py3-none-any.whl key: ${{ runner.os }}${{ matrix.python3_version }}-data-${{ github.sha }} restore-keys: | ${{ runner.os }}${{ matrix.python3_version }}-data- @@ -61,5 +61,5 @@ jobs: needs: build uses: ./.github/workflows/platform-integration-test.yaml with: - wheel: dist/otdf_python-0.2.3-py3-none-any.whl + wheel: dist/otdf_python-0.2.4-py3-none-any.whl python_version: ${{ matrix.python3_version }} diff --git a/.github/workflows/build-python.yaml b/.github/workflows/build-python.yaml index 300628a..84f2eb1 100644 --- a/.github/workflows/build-python.yaml +++ b/.github/workflows/build-python.yaml @@ -9,7 +9,7 @@ on: jobs: build: - runs-on: ubuntu-latest + runs-on: ubuntu-22.04 strategy: matrix: go-version: [1.22.x] diff --git a/.github/workflows/lint-on-ubuntu.yaml b/.github/workflows/lint-on-ubuntu.yaml index 45e2a8a..244bd0a 100644 --- a/.github/workflows/lint-on-ubuntu.yaml +++ b/.github/workflows/lint-on-ubuntu.yaml @@ -7,7 +7,7 @@ on: jobs: lint-ubuntu: - runs-on: ubuntu-latest + runs-on: ubuntu-22.04 strategy: fail-fast: true diff --git a/.github/workflows/platform-integration-test.yaml b/.github/workflows/platform-integration-test.yaml index c707a46..b8619fb 100644 --- a/.github/workflows/platform-integration-test.yaml +++ b/.github/workflows/platform-integration-test.yaml @@ -22,14 +22,14 @@ permissions: jobs: integration_test: - runs-on: ubuntu-latest + runs-on: ubuntu-22.04 steps: - name: Checkout this repo uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 - uses: actions/cache/restore@v4 with: - path: dist/otdf_python-0.2.3-py3-none-any.whl + path: dist/otdf_python-0.2.4-py3-none-any.whl key: ${{ runner.os }}${{ inputs.python_version }}-data-${{ github.sha }} - name: Prove that the input file is available @@ -265,7 +265,7 @@ jobs: # - platform-xtest # - mavenverify # - pr - # runs-on: ubuntu-latest + # runs-on: ubuntu-22.04 # if: always() # steps: # - if: contains(needs.*.result, 'failure') diff --git a/.github/workflows/publish-test.yaml b/.github/workflows/publish-test.yaml index b0ce3f0..7cb73c3 100644 --- a/.github/workflows/publish-test.yaml +++ b/.github/workflows/publish-test.yaml @@ -84,7 +84,7 @@ jobs: build_linux_x86_64: # if: false name: Linux Python x86_64 - runs-on: ubuntu-latest + runs-on: ubuntu-22.04 steps: - uses: actions/checkout@v4 @@ -126,7 +126,7 @@ jobs: build_linux_arm: name: Linux Python ARM - runs-on: ubuntu-latest + runs-on: ubuntu-22.04 steps: - uses: actions/checkout@v4 @@ -211,7 +211,7 @@ jobs: # This permission is mandatory for PyPI's trusted publishing id-token: write needs: [build_macos, build_linux_x86_64, build_linux_arm] - runs-on: ubuntu-latest + runs-on: ubuntu-22.04 # If branch is 'develop' if: github.ref == 'refs/heads/develop' diff --git a/.github/workflows/publish.yaml b/.github/workflows/publish.yaml index dda046f..457183a 100644 --- a/.github/workflows/publish.yaml +++ b/.github/workflows/publish.yaml @@ -84,7 +84,7 @@ jobs: build_linux_x86_64: # if: false name: Linux Python x86_64 - runs-on: ubuntu-latest + runs-on: ubuntu-22.04 steps: - uses: actions/checkout@v4 @@ -126,7 +126,7 @@ jobs: build_linux_arm: name: Linux Python ARM - runs-on: ubuntu-latest + runs-on: ubuntu-22.04 steps: - uses: actions/checkout@v4 @@ -211,7 +211,7 @@ jobs: # This permission is mandatory for PyPI's trusted publishing id-token: write needs: [build_macos, build_linux_x86_64, build_linux_arm] - runs-on: ubuntu-latest + runs-on: ubuntu-22.04 # If branch is 'main' if: github.ref == 'refs/heads/main' diff --git a/README.md b/README.md index 34060dc..74c5bfd 100644 --- a/README.md +++ b/README.md @@ -27,10 +27,10 @@ Install from the [Python Package Index (PyPI)](https://pypi.org): pip install otdf_python # Install a pinned version -pip install otdf-python==0.2.3 +pip install otdf-python==0.2.4 # Install a pinned version, from test.pypi.org -pip install -i https://test.pypi.org/simple/ otdf-python==0.2.3 +pip install -i https://test.pypi.org/simple/ otdf-python==0.2.4 ``` ## Usage diff --git a/build-scripts/ci-build.sh b/build-scripts/ci-build.sh index 4cb617e..6fc7090 100755 --- a/build-scripts/ci-build.sh +++ b/build-scripts/ci-build.sh @@ -13,47 +13,63 @@ printf """ """ +echo "✨✨✨ Display Python version" echo "python -VV" python -VV +echo "✨✨✨ Display Python executable path" echo 'python -c "import sys; print(sys.executable)"' python -c "import sys; print(sys.executable)" +echo "✨✨✨ Display pip version" echo 'pip -V' pip -V +echo "✨✨✨ Install poetry" echo 'pip install poetry' pip install poetry +echo "✨✨✨ List home directory contents" # Look for go/bin (skip, we know it exists) echo '$HOME/' ls -la "$HOME/" -echo '$HOME/.local/' -ls -la "$HOME/.local/" - +echo "✨✨✨ List Go directory contents" echo '$HOME/go/' ls -la "$HOME/go/" +echo "✨✨✨ Display Go version" +go version + +echo "✨✨✨ Add Go bin directory to PATH" # Add Go bin directory to PATH echo "export PATH=$PATH:~/.local/go/bin" >> $GITHUB_ENV +echo "✨✨✨ Install dependencies with poetry" # Since we don't have our wheel build / install configured yet we use '--no-root' poetry install --no-root +echo "✨✨✨ Activate poetry environment" source $(poetry env info --path)/bin/activate +echo "✨✨✨ Add Go bin directory to PATH again" # Add Go bin directory to PATH echo "export PATH=$PATH:~/.local/go/bin" >> $GITHUB_ENV +echo "✨✨✨ Install goimports" go install golang.org/x/tools/cmd/goimports@latest +echo "✨✨✨ Install gopy" go install github.com/go-python/gopy@latest +echo "✨✨✨ Upgrade setuptools and wheel" poetry run pip install --upgrade setuptools wheel +echo "✨✨✨ Build gopy" gopy build --output=otdf_python -vm=python3 . +echo "✨✨✨ Build wheel" poetry run python3 setup.py bdist_wheel -pip install dist/otdf_python-0.2.3-py3-none-any.whl +echo "✨✨✨ Install wheel" +pip install dist/otdf_python-0.2.4-py3-none-any.whl diff --git a/build-scripts/make_and_validate_script.sh b/build-scripts/make_and_validate_script.sh index d1150b9..e6791cb 100755 --- a/build-scripts/make_and_validate_script.sh +++ b/build-scripts/make_and_validate_script.sh @@ -47,7 +47,7 @@ python3 -m pip install --upgrade setuptools wheel python3 setup.py bdist_wheel # Prove that the wheel can be installed -pip install dist/otdf_python-0.2.3-py3-none-any.whl +pip install dist/otdf_python-0.2.4-py3-none-any.whl if [[ "$SKIP_TESTS" == "-s" || "$SKIP_TESTS" == "--skip-tests" ]]; then echo "Build is complete, skipping tests." diff --git a/build-scripts/uv_make_and_validate_script.sh b/build-scripts/uv_make_and_validate_script.sh index 5dcfdb7..34f11b4 100755 --- a/build-scripts/uv_make_and_validate_script.sh +++ b/build-scripts/uv_make_and_validate_script.sh @@ -70,7 +70,7 @@ loud_print "Installing wheel" uv venv .venv-wheel --python 3.12 "$PY_TYPE" source "${BUILD_ROOT}/.venv-wheel/bin/activate" pip install pybindgen -pip install dist/otdf_python-0.2.3-py3-none-any.whl +pip install dist/otdf_python-0.2.4-py3-none-any.whl if [[ "$SKIP_TESTS" == "-s" || "$SKIP_TESTS" == "--skip-tests" ]]; then echo "Build is complete, skipping tests." diff --git a/main.go b/main.go index f74d830..d8050ec 100644 --- a/main.go +++ b/main.go @@ -17,6 +17,8 @@ import ( "fmt" "io" "os" + "path" + "path/filepath" "strings" "github.com/opentdf/platform/sdk" @@ -353,6 +355,72 @@ func EncryptFile(inputFilePath string, outputFilePath string, config OpentdfConf return outputFilePath, nil } +/* + EncryptFilesInDirNPE encrypts all files in the specified directory + +Work is performed as an NPE (Non-Person Entity). Encrypted files are placed +in the same directory as the input files, with a .tdf extension added to the file name. +*/ +func EncryptFilesInDirNPE(dirPath string, config OpentdfConfig, dataAttributes []string) ([]string, error) { + authScopes := []string{"email"} + sdkClient, err := newSdkClient(config, authScopes) + if err != nil { + return nil, err + } + + files, err := os.ReadDir(dirPath) + if err != nil { + return nil, err + } + + var outputPaths []string + for _, file := range files { + if !file.IsDir() { + inputFilePath := path.Join(dirPath, file.Name()) + outputFilePath := inputFilePath + ".tdf" + got, err := encryptFileWithClient(inputFilePath, outputFilePath, sdkClient, config, dataAttributes) + if err != nil { + return nil, fmt.Errorf("failed to encrypt file %s: %v", inputFilePath, err) + } else { + outputPaths = append(outputPaths, got) + } + } + } + return outputPaths, nil +} + +/* + EncryptFilesGlobNPE encrypts all files matching the specified glob pattern. + +Work is performed as an NPE (Non-Person Entity). Encrypted files are placed +in the same directory as the input files, with a .tdf extension added to the file name. +*/ +func EncryptFilesGlobNPE(pattern string, config OpentdfConfig, dataAttributes []string) ([]string, error) { + authScopes := []string{"email"} + sdkClient, err := newSdkClient(config, authScopes) + if err != nil { + return nil, err + } + + files, err := filepath.Glob(pattern) + if err != nil { + return nil, err + } + + var outputPaths []string + for _, inputFilePath := range files { + outputFilePath := inputFilePath + ".tdf" + got, err := encryptFileWithClient(inputFilePath, outputFilePath, sdkClient, config, dataAttributes) + if err != nil { + + return nil, fmt.Errorf("failed to encrypt file %s: %v", inputFilePath, err) + } else { + outputPaths = append(outputPaths, got) + } + } + return outputPaths, nil +} + /* Encrypts a file as a PE (Person Entity), returning a TDF manifest and the cipher text. */ @@ -480,6 +548,123 @@ func DecryptFile(inputFilePath string, outputFilePath string, config OpentdfConf return outputFilePath, nil } +/* +DecryptFilesInDirNPE decrypts all files in the specified directory +Work is performed as an NPE (Non-Person Entity). Decrypted files are placed +in the same directory as the input files, with the .tdf extension removed from the file name. +*/ +func DecryptFilesInDirNPE(dirPath string, config OpentdfConfig) ([]string, error) { + authScopes := []string{"email"} + sdkClient, err := newSdkClient(config, authScopes) + if err != nil { + return nil, err + } + + files, err := os.ReadDir(dirPath) + if err != nil { + return nil, err + } + + var outputPaths []string + for _, file := range files { + if !file.IsDir() && strings.HasSuffix(file.Name(), ".tdf") { + inputFilePath := path.Join(dirPath, file.Name()) + outputFilePath := strings.TrimSuffix(inputFilePath, ".tdf") + + bytes, err := readBytesFromFile(inputFilePath) + if err != nil { + + return nil, fmt.Errorf("failed to read file %s: %v", inputFilePath, err) + } + + decrypted, err := decryptBytesWithClient(bytes, sdkClient) + if err != nil { + return nil, fmt.Errorf("failed to decrypt file %s: %v", inputFilePath, err) + } + + tdfFile, err := os.Create(outputFilePath) + if err != nil { + return nil, fmt.Errorf("failed to write decrypted file %s: %v", outputFilePath, err) + } + defer tdfFile.Close() + + _, e := io.Copy(tdfFile, decrypted) + if e != nil { + return nil, fmt.Errorf("failed to write decrypted data to destination %s: %v", outputFilePath, err) + } + + outputPaths = append(outputPaths, outputFilePath) + } + } + return outputPaths, nil +} + +/* +DecryptFilesGlobNPE decrypts all files matching the specified glob pattern. +Work is performed as an NPE (Non-Person Entity). Decrypted files are placed +in the same directory as the input files, with the .tdf extension removed from the file name. +*/ +func DecryptFilesGlobNPE(pattern string, config OpentdfConfig) ([]string, error) { + authScopes := []string{"email"} + sdkClient, err := newSdkClient(config, authScopes) + if err != nil { + return nil, err + } + + files, err := filepath.Glob(pattern) + if err != nil { + return nil, err + } + + var outputPaths []string + for _, inputFilePath := range files { + if strings.HasSuffix(inputFilePath, ".tdf") { + outputFilePath := strings.TrimSuffix(inputFilePath, ".tdf") + + bytes, err := readBytesFromFile(inputFilePath) + if err != nil { + return nil, fmt.Errorf("failed to read file %s: %v", inputFilePath, err) + } + + decrypted, err := decryptBytesWithClient(bytes, sdkClient) + if err != nil { + return nil, fmt.Errorf("failed to decrypt file %s: %v", inputFilePath, err) + } + + tdfFile, err := os.Create(outputFilePath) + if err != nil { + return nil, fmt.Errorf("failed to write decrypted file %s", outputFilePath) + } + defer tdfFile.Close() + + _, e := io.Copy(tdfFile, decrypted) + if e != nil { + return nil, fmt.Errorf("failed to write decrypted data to destination %s: %v", outputFilePath, err) + } + + outputPaths = append(outputPaths, outputFilePath) + } + } + if len(outputPaths) == 0 { + return nil, fmt.Errorf("no files were decrypted") + } + return outputPaths, nil +} + +func decryptBytesWithClient(toDecrypt []byte, sdkClient *sdk.SDK) (*bytes.Buffer, error) { + tdfreader, err := sdkClient.LoadTDF(bytes.NewReader(toDecrypt)) + if err != nil { + return nil, err + } + + buf := new(bytes.Buffer) + _, err = io.Copy(buf, tdfreader) + if err != nil && err != io.EOF { + return nil, err + } + return buf, nil +} + func DecryptFilePE(inputFilePath string, outputFilePath string, config OpentdfConfig, token TokenAuth) (string, error) { bytes, err := readBytesFromFile(inputFilePath) if err != nil { @@ -504,3 +689,50 @@ func DecryptFilePE(inputFilePath string, outputFilePath string, config OpentdfCo return outputFilePath, nil } + +func encryptFileWithClient(inputFilePath string, outputFilePath string, sdkClient *sdk.SDK, config OpentdfConfig, dataAttributes []string) (string, error) { + bytes, err := readBytesFromFile(inputFilePath) + if err != nil { + return "", err + } + + encrypted, err := encryptBytesWithClient(bytes, sdkClient, config, dataAttributes) + if err != nil { + return "", fmt.Errorf("failed to encrypt: %w", err) + } + + var dest *os.File + if !strings.HasSuffix(outputFilePath, ".tdf") { + outputFilePath += ".tdf" + } + tdfFile, err := os.Create(outputFilePath) + if err != nil { + return "", fmt.Errorf("failed to write encrypted file %s", outputFilePath) + } + defer tdfFile.Close() + dest = tdfFile + + _, e := io.Copy(dest, encrypted) + if e != nil { + return "", errors.New("failed to write encrypted data to destination") + } + + return outputFilePath, nil +} + +func encryptBytesWithClient(b []byte, sdkClient *sdk.SDK, config OpentdfConfig, dataAttributes []string) (*bytes.Buffer, error) { + var encrypted []byte + enc := bytes.NewBuffer(encrypted) + + _, err := sdkClient.CreateTDF(enc, bytes.NewReader(b), + sdk.WithDataAttributes(dataAttributes...), + sdk.WithKasInformation(sdk.KASInfo{ + URL: config.KasUrl, + PublicKey: "", + }), + ) + if err != nil { + return nil, err + } + return enc, nil +} diff --git a/otdf_python_test.go b/otdf_python_test.go index 902d534..939e940 100644 --- a/otdf_python_test.go +++ b/otdf_python_test.go @@ -16,6 +16,13 @@ import ( "time" ) +func getEnv(key, defaultValue string) string { + if value, ok := os.LookupEnv(key); ok { + return value + } + return defaultValue +} + type TestConfiguration struct { platformEndpoint string tokenEndpoint string @@ -24,6 +31,8 @@ type TestConfiguration struct { npeClientSecret string peUsername string pePassword string + testAttribute1 string + testAttribute2 string } var config = TestConfiguration{ @@ -34,6 +43,9 @@ var config = TestConfiguration{ npeClientSecret: os.Getenv("OPENTDF_CLIENT_SECRET"), peUsername: os.Getenv("TEST_OPENTDF_SECRET_USER_ID"), pePassword: os.Getenv("TEST_OPENTDF_SECRET_USER_PASSWORD"), + // For default values, we added a helper function + testAttribute1: getEnv("TEST_OPENTDF_ATTRIBUTE_1", "https://example.com/attr/attr1/value/value1"), + testAttribute2: getEnv("TEST_OPENTDF_ATTRIBUTE_2", "https://example.com/attr/attr1/value/value2"), } func TestHello(t *testing.T) { @@ -127,11 +139,10 @@ func AuthenticateNPE() (gotdf_python.TokenAuth, error) { } func getSingleDataAttribute(config TestConfiguration) []string { - return []string{"https://example.com/attr/attr1/value/value1"} + return []string{config.testAttribute1} } func getMultiDataAttribute(config TestConfiguration) []string { - return []string{"https://example.com/attr/attr1/value/value1", "https://example.com/attr/attr1/value/value2"} - + return []string{config.testAttribute1, config.testAttribute2} } func doEncryptString(t *testing.T, dataAttributes []string) { @@ -285,36 +296,6 @@ func Test_NPE_Encrypt_File_Multi_Attributes(t *testing.T) { encrypt_file_NPE(t, attrValues) } -func e2e_test_as_NPE(t *testing.T, dataAttributes []string) { - - tdfPath := encrypt_file_NPE(t, dataAttributes) - tmpOutputFile, err := os.CreateTemp("", "output-file-*.txt") - if err != nil { - t.Fatal(err) - } - tokenAuth, err := AuthenticatePE() - if err != nil { - t.Error(err) - } - - got, err := gotdf_python.DecryptFilePE(tdfPath, tmpOutputFile.Name(), gotdf_python.OpentdfConfig{ - ClientId: config.npeClientId, - ClientSecret: config.npeClientSecret, - PlatformEndpoint: config.platformEndpoint, - TokenEndpoint: config.tokenEndpoint, - KasUrl: config.kasEndpoint, - }, tokenAuth) - - if err != nil { - t.Fatal(err) - } - if got == "" { - t.Error("Unexpected value") - } else { - fmt.Println("Successfully decrypted TDF") - } -} - func e2e_test_as_PE(t *testing.T, dataAttributes []string) { token_for_encrypt, err := AuthenticatePE() if err != nil { @@ -364,3 +345,232 @@ func Test_PE_E2E_File_Multi_Attributes(t *testing.T) { attrValues := getMultiDataAttribute(config) e2e_test_as_PE(t, attrValues) } + +func Test_NPE_Encrypt_Files_In_Dir_Nil_Attributes(t *testing.T) { + // Create a temporary directory + tmpDir, err := os.MkdirTemp("", "input-dir") + if err != nil { + t.Fatal("Could not create temporary directory", err) + } + defer os.RemoveAll(tmpDir) + + // Create a temporary file in the directory + tmpFile1, err := os.CreateTemp(tmpDir, "input-file1-*.txt") + if err != nil { + t.Fatal("Could not create input file", err) + } + defer tmpFile1.Close() + + // Write some data to the file + if _, err = tmpFile1.WriteString("test data"); err != nil { + t.Fatal("Unable to write to temporary file", err) + } + + // Create a temporary file in the directory + tmpFile2, err := os.CreateTemp(tmpDir, "input-file2-*.txt") + if err != nil { + t.Fatal("Could not create input file", err) + } + defer tmpFile2.Close() + + // Write some data to the file + if _, err = tmpFile2.WriteString("test data"); err != nil { + t.Fatal("Unable to write to temporary file", err) + } + + // Call the EncryptFilesInDirNPE function + got, err := gotdf_python.EncryptFilesInDirNPE(tmpDir, gotdf_python.OpentdfConfig{ + ClientId: config.npeClientId, + ClientSecret: config.npeClientSecret, + PlatformEndpoint: config.platformEndpoint, + TokenEndpoint: config.tokenEndpoint, + KasUrl: config.kasEndpoint, + }, nil) + if err != nil { + t.Fatal("Failed to EncryptFilesInDirNPE()!", err) + } + + if len(got) == 0 { + t.Fatal("EncryptFilesInDirNPE returned empty value, but didn't error!") + } + + fmt.Println("Successfully encrypted files in directory") +} + +func Test_NPE_Encrypt_Files_Glob_Nil_Attributes(t *testing.T) { + // Create a temporary directory + tmpDir, err := os.MkdirTemp("", "input-dir") + if err != nil { + t.Fatal("Could not create temporary directory", err) + } + defer os.RemoveAll(tmpDir) + + // Create a temporary file in the directory + tmpFile1, err := os.CreateTemp(tmpDir, "input-file1-*.txt") + if err != nil { + t.Fatal("Could not create input file", err) + } + defer tmpFile1.Close() + + // Write some data to the file + if _, err = tmpFile1.WriteString("test data"); err != nil { + t.Fatal("Unable to write to temporary file", err) + } + + // Create a temporary file in the directory + tmpFile2, err := os.CreateTemp(tmpDir, "input-file2-*.txt") + if err != nil { + t.Fatal("Could not create input file", err) + } + defer tmpFile2.Close() + + // Write some data to the file + if _, err = tmpFile2.WriteString("test data"); err != nil { + t.Fatal("Unable to write to temporary file", err) + } + + // Call the EncryptFilesGlobNPE function + got, err := gotdf_python.EncryptFilesGlobNPE(tmpDir+"/*.txt", gotdf_python.OpentdfConfig{ + ClientId: config.npeClientId, + ClientSecret: config.npeClientSecret, + PlatformEndpoint: config.platformEndpoint, + TokenEndpoint: config.tokenEndpoint, + KasUrl: config.kasEndpoint, + }, nil) + if err != nil { + t.Fatal("Failed to EncryptFilesGlobNPE()!", err) + } + + if len(got) == 0 { + t.Fatal("EncryptFilesGlobNPE returned empty value, but didn't error!") + } + + fmt.Println("Successfully encrypted files using glob pattern") +} + +// Call the DecryptFilesInDirNPE function +func Test_NPE_Decrypt_Files_In_Dir_Nil_Attributes(t *testing.T) { + // Create a temporary directory + tmpDir, err := os.MkdirTemp("", "input-dir") + if err != nil { + t.Fatal("Could not create temporary directory", err) + } + defer os.RemoveAll(tmpDir) + + // Create a temporary file in the directory + tmpFile1, err := os.CreateTemp(tmpDir, "input-file1-*.txt") + if err != nil { + t.Fatal("Could not create input file", err) + } + defer tmpFile1.Close() + + // Write some data to the file + if _, err = tmpFile1.WriteString("test data"); err != nil { + t.Fatal("Unable to write to temporary file", err) + } + + // Create a temporary file in the directory + tmpFile2, err := os.CreateTemp(tmpDir, "input-file2-*.txt") + if err != nil { + t.Fatal("Could not create input file", err) + } + defer tmpFile2.Close() + + // Write some data to the file + if _, err = tmpFile2.WriteString("test data"); err != nil { + t.Fatal("Unable to write to temporary file", err) + } + + // Encrypt the file + _, err = gotdf_python.EncryptFilesInDirNPE(tmpDir, gotdf_python.OpentdfConfig{ + ClientId: config.npeClientId, + ClientSecret: config.npeClientSecret, + PlatformEndpoint: config.platformEndpoint, + TokenEndpoint: config.tokenEndpoint, + KasUrl: config.kasEndpoint, + }, nil) + if err != nil { + t.Fatal("Failed to EncryptFilesInDirNPE()!", err) + } + + // Call the DecryptFilesInDirNPE function + got, err := gotdf_python.DecryptFilesInDirNPE(tmpDir, gotdf_python.OpentdfConfig{ + ClientId: config.npeClientId, + ClientSecret: config.npeClientSecret, + PlatformEndpoint: config.platformEndpoint, + TokenEndpoint: config.tokenEndpoint, + KasUrl: config.kasEndpoint, + }) + if err != nil { + t.Fatal("Failed to DecryptFilesInDirNPE()!", err) + } + + if len(got) == 0 { + t.Fatal("DecryptFilesInDirNPE returned empty value, but didn't error!") + } + + fmt.Println("Successfully decrypted files in directory") +} + +func Test_NPE_Decrypt_Files_Glob_Nil_Attributes(t *testing.T) { + // Create a temporary directory + tmpDir, err := os.MkdirTemp("", "input-dir") + if err != nil { + t.Fatal("Could not create temporary directory", err) + } + defer os.RemoveAll(tmpDir) + + // Create a temporary file in the directory + tmpFile1, err := os.CreateTemp(tmpDir, "input-file1-*.txt") + if err != nil { + t.Fatal("Could not create input file", err) + } + defer tmpFile1.Close() + + // Write some data to the file + if _, err = tmpFile1.WriteString("test data"); err != nil { + t.Fatal("Unable to write to temporary file", err) + } + + // Create a temporary file in the directory + tmpFile2, err := os.CreateTemp(tmpDir, "input-file2-*.txt") + if err != nil { + t.Fatal("Could not create input file", err) + } + defer tmpFile2.Close() + + // Write some data to the file + if _, err = tmpFile2.WriteString("test data"); err != nil { + t.Fatal("Unable to write to temporary file", err) + } + + // Encrypt the file + _, err = gotdf_python.EncryptFilesGlobNPE(tmpDir+"/*.txt", gotdf_python.OpentdfConfig{ + ClientId: config.npeClientId, + ClientSecret: config.npeClientSecret, + PlatformEndpoint: config.platformEndpoint, + TokenEndpoint: config.tokenEndpoint, + KasUrl: config.kasEndpoint, + }, nil) + if err != nil { + t.Fatal("Failed to EncryptFilesGlobNPE()!", err) + } + + // Call the DecryptFilesGlobNPE function + got, err := gotdf_python.DecryptFilesGlobNPE(tmpDir+"/*.tdf", gotdf_python.OpentdfConfig{ + ClientId: config.npeClientId, + ClientSecret: config.npeClientSecret, + PlatformEndpoint: config.platformEndpoint, + TokenEndpoint: config.tokenEndpoint, + KasUrl: config.kasEndpoint, + }) + if err != nil { + t.Fatal("Failed to DecryptFilesGlobNPE()!", err) + } + + if len(got) == 0 { + t.Fatal("DecryptFilesGlobNPE returned empty value, but didn't error!") + } + + fmt.Println("Successfully decrypted files using glob pattern") +} diff --git a/poetry.lock b/poetry.lock index c51f402..6002809 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,4 +1,4 @@ -# This file is automatically @generated by Poetry 1.8.4 and should not be changed by hand. +# This file is automatically @generated by Poetry 2.0.1 and should not be changed by hand. [[package]] name = "colorama" @@ -6,6 +6,8 @@ version = "0.4.6" description = "Cross-platform colored terminal text." optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" +groups = ["dev"] +markers = "sys_platform == \"win32\"" files = [ {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"}, {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, @@ -17,6 +19,7 @@ version = "2.0.0" description = "brain-dead simple config-ini parsing" optional = false python-versions = ">=3.7" +groups = ["dev"] files = [ {file = "iniconfig-2.0.0-py3-none-any.whl", hash = "sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374"}, {file = "iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3"}, @@ -28,6 +31,7 @@ version = "24.2" description = "Core utilities for Python packages" optional = false python-versions = ">=3.8" +groups = ["dev"] files = [ {file = "packaging-24.2-py3-none-any.whl", hash = "sha256:09abb1bccd265c01f4a3aa3f7a7db064b36514d2cba19a2f694fe6150451a759"}, {file = "packaging-24.2.tar.gz", hash = "sha256:c228a6dc5e932d346bc5739379109d49e8853dd8223571c7c5b55260edc0b97f"}, @@ -39,6 +43,7 @@ version = "1.5.0" description = "plugin and hook calling mechanisms for python" optional = false python-versions = ">=3.8" +groups = ["dev"] files = [ {file = "pluggy-1.5.0-py3-none-any.whl", hash = "sha256:44e1ad92c8ca002de6377e165f3e0f1be63266ab4d554740532335b9d75ea669"}, {file = "pluggy-1.5.0.tar.gz", hash = "sha256:2cffa88e94fdc978c4c574f15f9e59b7f4201d439195c3715ca9e2486f1d0cf1"}, @@ -54,6 +59,7 @@ version = "0.22.1" description = "Python Bindings Generator" optional = false python-versions = "*" +groups = ["main"] files = [ {file = "PyBindGen-0.22.1-py2.py3-none-any.whl", hash = "sha256:e9c2dad3c3e9da7811c271b5d6dcbb59ee59a2fe7877b4277556f95a9aea296e"}, {file = "PyBindGen-0.22.1.tar.gz", hash = "sha256:8c7f22391a49a84518f5a2ad06e3a5b1e839d10e34da7631519c8a28fcba3764"}, @@ -61,13 +67,14 @@ files = [ [[package]] name = "pytest" -version = "8.3.3" +version = "8.3.4" description = "pytest: simple powerful testing with Python" optional = false python-versions = ">=3.8" +groups = ["dev"] files = [ - {file = "pytest-8.3.3-py3-none-any.whl", hash = "sha256:a6853c7375b2663155079443d2e45de913a911a11d669df02a50814944db57b2"}, - {file = "pytest-8.3.3.tar.gz", hash = "sha256:70b98107bd648308a7952b06e6ca9a50bc660be218d53c257cc1fc94fda10181"}, + {file = "pytest-8.3.4-py3-none-any.whl", hash = "sha256:50e16d954148559c9a74109af1eaf0c945ba2d8f30f0a3d3335edde19788b6f6"}, + {file = "pytest-8.3.4.tar.gz", hash = "sha256:965370d062bce11e73868e0335abac31b4d3de0e82f4007408d242b4f8610761"}, ] [package.dependencies] @@ -85,6 +92,7 @@ version = "0.45.1" description = "A built-package format for Python" optional = false python-versions = ">=3.8" +groups = ["main"] files = [ {file = "wheel-0.45.1-py3-none-any.whl", hash = "sha256:708e7481cc80179af0e556bbf0cc00b8444c7321e2700b8d8580231d13017248"}, {file = "wheel-0.45.1.tar.gz", hash = "sha256:661e1abd9198507b1409a20c02106d9670b2576e916d58f520316666abca6729"}, @@ -94,6 +102,6 @@ files = [ test = ["pytest (>=6.0.0)", "setuptools (>=65)"] [metadata] -lock-version = "2.0" +lock-version = "2.1" python-versions = ">=3.11,<3.14" -content-hash = "2ce122d5f23a9bd0d885da6a0d4e5ecf37f5f071f327b2c26f14ac50845ca54e" +content-hash = "6bbe14f2edb0469e90b8acaffbc2061a108122c58e6cee30f7b9f28387107be0" diff --git a/pyproject.toml b/pyproject.toml index d9d3e77..77e5e7e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,7 +1,7 @@ [project] name = "otdf-python" # Should match 'setup.py' version number (used for gopy/pybindgen) -version = "0.2.3" +version = "0.2.4" description = "Unofficial OpenTDF SDK for Python." authors = [ {name="b-long", email="b-long@users.noreply.github.com"} @@ -19,6 +19,7 @@ pybindgen = "^0.22.1" [tool.poetry] package-mode = false +version = "0.2.4" [tool.poetry.dependencies] python = ">=3.11,<3.14" diff --git a/setup.py b/setup.py index f188bf2..ad39f39 100644 --- a/setup.py +++ b/setup.py @@ -24,7 +24,7 @@ url="https://github.com/b-long/opentdf-python-sdk", package_data={"otdf_python": ["*.so"]}, # Should match 'pyproject.toml' version number - version="0.2.3", + version="0.2.4", author_email="b-long@users.noreply.github.com", include_package_data=True, ) diff --git a/setup_ci.py b/setup_ci.py index f0a3f75..5dddaa2 100644 --- a/setup_ci.py +++ b/setup_ci.py @@ -81,7 +81,7 @@ def build_extension(self, ext: Extension): setuptools.setup( name="otdf_python", - version="0.2.3", + version="0.2.4", author="b-long", description="Unofficial OpenTDF SDK for Python.", long_description_content_type="text/markdown", diff --git a/uv.lock b/uv.lock index 957c832..4c21e9b 100644 --- a/uv.lock +++ b/uv.lock @@ -3,5 +3,5 @@ requires-python = ">=3.11" [[package]] name = "otdf-python" -version = "0.2.3" +version = "0.2.4" source = { editable = "." }