Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
31f8f76
ARROW-9947: [Python] High-level Python API for Parquet encryption of …
andersonm-ibm Jun 4, 2021
d1df129
Review feedback: Add tests with a raising KMS factory, and a KMS fact…
andersonm-ibm Jun 21, 2021
3773aaf
Throw parquetStatusException on parquet error in inner python method.
andersonm-ibm Jun 23, 2021
40d1f03
Add a Vault KMS Client example
andersonm-ibm Jul 5, 2021
46f0bf2
Change to shared_ptr for consistency
andersonm-ibm Aug 8, 2021
7dbdc21
Separate the threading issue from this PR
andersonm-ibm Aug 9, 2021
b203426
Review comments - replace cypher with a simple join between master ke…
andersonm-ibm Aug 19, 2021
62f7c1a
Fix the test_total_bytes_allocated by defining a test fixture
andersonm-ibm Sep 6, 2021
9fe580c
fix: expose concrete Python exceptions
roee88 Sep 23, 2021
b24b73f
Apply suggestions from code review
andersonm-ibm Sep 24, 2021
146077a
Update documentation and add PARQUET_REQUIRE_ENCRYPTION wherever Parq…
andersonm-ibm Sep 23, 2021
7f06a2a
Resolve a compatibility issue with PME in Spark 3.2.0.
andersonm-ibm Nov 24, 2021
696162e
Update the NoSSL version to match the API changes
andersonm-ibm Nov 30, 2021
a44388a
Update docs/source/python/parquet.rst
andersonm-ibm Dec 14, 2021
9584f87
Change exception message
andersonm-ibm Dec 15, 2021
316302b
Update docs/source/python/parquet.rst
andersonm-ibm Dec 15, 2021
6b5d8ea
Update documentation and add with nogil according to review
andersonm-ibm Dec 15, 2021
368890f
Don't expose uniform encryption yet. Will be added back with in [ARRO…
andersonm-ibm Dec 14, 2021
011c49d
Update documentation according to the review
andersonm-ibm Dec 16, 2021
5c3f68c
Separate python encryption so as not to mandate compiling parquet tog…
andersonm-ibm Jan 4, 2022
be154fd
Review comments: remove PME from minimal build,
andersonm-ibm Feb 10, 2022
4d1197e
Nits and try to fix AppVeyor link errors
pitrou Mar 1, 2022
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .github/workflows/python.yml
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,7 @@ jobs:
ARROW_JEMALLOC: ON
ARROW_ORC: ON
ARROW_PARQUET: ON
PARQUET_REQUIRE_ENCRYPTION: ON
ARROW_PLASMA: ON
ARROW_PYTHON: ON
ARROW_S3: ON
Expand Down
1 change: 1 addition & 0 deletions ci/appveyor-cpp-build.bat
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,7 @@ set PYARROW_WITH_DATASET=ON
set PYARROW_WITH_FLIGHT=%ARROW_BUILD_FLIGHT%
set PYARROW_WITH_GANDIVA=%ARROW_BUILD_GANDIVA%
set PYARROW_WITH_PARQUET=ON
set PYARROW_WITH_PARQUET_ENCRYPTION=ON
set PYARROW_WITH_S3=%ARROW_S3%
set PYARROW_WITH_STATIC_BOOST=ON

Expand Down
1 change: 1 addition & 0 deletions ci/docker/conda-python-hdfs.dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ ENV CC=gcc \
ARROW_GANDIVA=OFF \
ARROW_PLASMA=OFF \
ARROW_PARQUET=ON \
PARQUET_REQUIRE_ENCRYPTION=ON \
ARROW_ORC=OFF \
ARROW_HDFS=ON \
ARROW_PYTHON=ON \
Expand Down
1 change: 1 addition & 0 deletions ci/scripts/python_build.sh
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ export PYARROW_WITH_FLIGHT=${ARROW_FLIGHT:-OFF}
export PYARROW_WITH_PLASMA=${ARROW_PLASMA:-OFF}
export PYARROW_WITH_GANDIVA=${ARROW_GANDIVA:-OFF}
export PYARROW_WITH_PARQUET=${ARROW_PARQUET:-OFF}
export PYARROW_WITH_PARQUET_ENCRYPTION=${ARROW_PARQUET:-OFF}
export PYARROW_WITH_DATASET=${ARROW_DATASET:-OFF}

export PYARROW_PARALLEL=${n_jobs}
Expand Down
1 change: 1 addition & 0 deletions ci/scripts/python_sdist_test.sh
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ export PYARROW_WITH_FLIGHT=${ARROW_FLIGHT:-OFF}
export PYARROW_WITH_PLASMA=${ARROW_PLASMA:-OFF}
export PYARROW_WITH_GANDIVA=${ARROW_GANDIVA:-OFF}
export PYARROW_WITH_PARQUET=${ARROW_PARQUET:-OFF}
export PYARROW_WITH_PARQUET_ENCRYPTION=${ARROW_PARQUET:-OFF}
export PYARROW_WITH_DATASET=${ARROW_DATASET:-OFF}

# TODO: Users should not require ARROW_HOME and pkg-config to find Arrow C++.
Expand Down
3 changes: 3 additions & 0 deletions ci/scripts/python_wheel_macos_build.sh
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ echo "=== (${PYTHON_VERSION}) Building Arrow C++ libraries ==="
: ${ARROW_MIMALLOC:=ON}
: ${ARROW_ORC:=ON}
: ${ARROW_PARQUET:=ON}
: ${PARQUET_REQUIRE_ENCRYPTION:=ON}
: ${ARROW_PLASMA:=ON}
: ${ARROW_S3:=ON}
: ${ARROW_SIMD_LEVEL:="SSE4_2"}
Expand Down Expand Up @@ -106,6 +107,7 @@ cmake \
-DARROW_ORC=${ARROW_ORC} \
-DARROW_PACKAGE_KIND="python-wheel-macos" \
-DARROW_PARQUET=${ARROW_PARQUET} \
-DPARQUET_REQUIRE_ENCRYPTION=${PARQUET_REQUIRE_ENCRYPTION}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are you just adding this blindly or did you check that building the MacOS wheels still works with this option?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This can also be checked by triggering crossbow to run the wheel tasks on the PR here?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Checked the python-wheel-manylinux-2014. I expect the rest to be run with CI/CD. Should I do anything special to trigger that?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, for building and testing the wheels you need to trigger those builds manually with a comment. Will do that now.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, @jorisvandenbossche !
It seems like there is a general problem with the Mac OS build:
CMake Error: The source directory "/Users/runner/work/crossbow/crossbow/build/build" does not appear to contain CMakeLists.txt.

-DARROW_PLASMA=${ARROW_PLASMA} \
-DARROW_PYTHON=ON \
-DARROW_RPATH_ORIGIN=ON \
Expand Down Expand Up @@ -147,6 +149,7 @@ export PYARROW_WITH_GANDIVA=${ARROW_GANDIVA}
export PYARROW_WITH_HDFS=${ARROW_HDFS}
export PYARROW_WITH_ORC=${ARROW_ORC}
export PYARROW_WITH_PARQUET=${ARROW_PARQUET}
export PYARROW_WITH_PARQUET_ENCRYPTION=${PARQUET_REQUIRE_ENCRYPTION}
export PYARROW_WITH_PLASMA=${ARROW_PLASMA}
export PYARROW_WITH_S3=${ARROW_S3}
export PYARROW_CMAKE_OPTIONS="-DCMAKE_OSX_ARCHITECTURES=${CMAKE_OSX_ARCHITECTURES} -DARROW_SIMD_LEVEL=${ARROW_SIMD_LEVEL}"
Expand Down
3 changes: 3 additions & 0 deletions ci/scripts/python_wheel_manylinux_build.sh
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ echo "=== (${PYTHON_VERSION}) Building Arrow C++ libraries ==="
: ${ARROW_MIMALLOC:=ON}
: ${ARROW_ORC:=ON}
: ${ARROW_PARQUET:=ON}
: ${PARQUET_REQUIRE_ENCRYPTION:=ON}
: ${ARROW_PLASMA:=ON}
: ${ARROW_S3:=ON}
: ${ARROW_TENSORFLOW:=ON}
Expand Down Expand Up @@ -100,6 +101,7 @@ cmake \
-DARROW_ORC=${ARROW_ORC} \
-DARROW_PACKAGE_KIND="python-wheel-manylinux${MANYLINUX_VERSION}" \
-DARROW_PARQUET=${ARROW_PARQUET} \
-DPARQUET_REQUIRE_ENCRYPTION=${PARQUET_REQUIRE_ENCRYPTION} \

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same question here: did you check that this works?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I created a python-wheel-manylinux-2014 with PARQUET_REQUIRE_ENCRYPTION=ON and it worked fine.
I expect the CI/CD to check it on the rest of the platforms.

-DARROW_PLASMA=${ARROW_PLASMA} \
-DARROW_PYTHON=ON \
-DARROW_RPATH_ORIGIN=ON \
Expand Down Expand Up @@ -143,6 +145,7 @@ export PYARROW_WITH_GANDIVA=${ARROW_GANDIVA}
export PYARROW_WITH_HDFS=${ARROW_HDFS}
export PYARROW_WITH_ORC=${ARROW_ORC}
export PYARROW_WITH_PARQUET=${ARROW_PARQUET}
export PYARROW_WITH_PARQUET_ENCRYPTION=${PARQUET_REQUIRE_ENCRYPTION}
export PYARROW_WITH_PLASMA=${ARROW_PLASMA}
export PYARROW_WITH_S3=${ARROW_S3}
# PyArrow build configuration
Expand Down
3 changes: 3 additions & 0 deletions ci/scripts/python_wheel_windows_build.bat
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ set ARROW_GANDIVA=OFF
set ARROW_HDFS=ON
set ARROW_ORC=OFF
set ARROW_PARQUET=ON
set PARQUET_REQUIRE_ENCRYPTION=ON
set ARROW_MIMALLOC=ON
set ARROW_S3=ON
set ARROW_TENSORFLOW=ON
Expand Down Expand Up @@ -68,6 +69,7 @@ cmake ^
-DARROW_ORC=%ARROW_ORC% ^
-DARROW_PACKAGE_KIND="python-wheel-windows" ^
-DARROW_PARQUET=%ARROW_PARQUET% ^
-DPARQUET_REQUIRE_ENCRYPTION=%PARQUET_REQUIRE_ENCRYPTION% ^

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same question...

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Checked the python-wheel-manylinux-2014. I expect the rest of the platforms to be run with CI/CD. Should I do anything special to trigger that?

-DARROW_PYTHON=ON ^
-DARROW_S3=%ARROW_S3% ^
-DARROW_TENSORFLOW=%ARROW_TENSORFLOW% ^
Expand Down Expand Up @@ -102,6 +104,7 @@ set PYARROW_WITH_GANDIVA=%ARROW_GANDIVA%
set PYARROW_WITH_HDFS=%ARROW_HDFS%
set PYARROW_WITH_ORC=%ARROW_ORC%
set PYARROW_WITH_PARQUET=%ARROW_PARQUET%
set PYARROW_WITH_PARQUET_ENCRYPTION=%PARQUET_REQUIRE_ENCRYPTION%
set PYARROW_WITH_S3=%ARROW_S3%
set ARROW_HOME=C:\arrow-dist

Expand Down
8 changes: 8 additions & 0 deletions cpp/src/arrow/python/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,10 @@ if(ARROW_FILESYSTEM)
list(APPEND ARROW_PYTHON_SRCS filesystem.cc)
endif()

if(PARQUET_REQUIRE_ENCRYPTION)
list(APPEND ARROW_PYTHON_SRCS parquet_encryption.cc)
endif()

set(ARROW_PYTHON_DEPENDENCIES arrow_dependencies)

if(CMAKE_CXX_COMPILER_ID STREQUAL "AppleClang" OR CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
Expand All @@ -70,6 +74,10 @@ if(WIN32)
list(APPEND ARROW_PYTHON_SHARED_LINK_LIBS ${PYTHON_LIBRARIES} ${PYTHON_OTHER_LIBS})
endif()

if(PARQUET_REQUIRE_ENCRYPTION)
list(APPEND ARROW_PYTHON_SHARED_LINK_LIBS parquet_shared)
endif()

set(ARROW_PYTHON_INCLUDES ${NUMPY_INCLUDE_DIRS} ${PYTHON_INCLUDE_DIRS})

add_arrow_lib(arrow_python
Expand Down
98 changes: 98 additions & 0 deletions cpp/src/arrow/python/parquet_encryption.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.

#include "arrow/python/parquet_encryption.h"
#include "parquet/exception.h"

namespace arrow {
namespace py {
namespace parquet {
namespace encryption {

PyKmsClient::PyKmsClient(PyObject* handler, PyKmsClientVtable vtable)
: handler_(handler), vtable_(std::move(vtable)) {
Py_INCREF(handler);
}

PyKmsClient::~PyKmsClient() {}

std::string PyKmsClient::WrapKey(const std::string& key_bytes,
const std::string& master_key_identifier) {
std::string wrapped;
auto st = SafeCallIntoPython([&]() -> Status {
vtable_.wrap_key(handler_.obj(), key_bytes, master_key_identifier, &wrapped);
return CheckPyError();
});
if (!st.ok()) {
throw ::parquet::ParquetStatusException(st);
}
return wrapped;
}

std::string PyKmsClient::UnwrapKey(const std::string& wrapped_key,
const std::string& master_key_identifier) {
std::string unwrapped;
auto st = SafeCallIntoPython([&]() -> Status {
vtable_.unwrap_key(handler_.obj(), wrapped_key, master_key_identifier, &unwrapped);
return CheckPyError();
});
if (!st.ok()) {
throw ::parquet::ParquetStatusException(st);
}
return unwrapped;
}

PyKmsClientFactory::PyKmsClientFactory(PyObject* handler, PyKmsClientFactoryVtable vtable)
: handler_(handler), vtable_(std::move(vtable)) {
Py_INCREF(handler);
}

PyKmsClientFactory::~PyKmsClientFactory() {}

std::shared_ptr<::parquet::encryption::KmsClient> PyKmsClientFactory::CreateKmsClient(
const ::parquet::encryption::KmsConnectionConfig& kms_connection_config) {
std::shared_ptr<::parquet::encryption::KmsClient> kms_client;
auto st = SafeCallIntoPython([&]() -> Status {
vtable_.create_kms_client(handler_.obj(), kms_connection_config, &kms_client);
return CheckPyError();
});
if (!st.ok()) {
throw ::parquet::ParquetStatusException(st);
}
return kms_client;
}

arrow::Result<std::shared_ptr<::parquet::FileEncryptionProperties>>
PyCryptoFactory::SafeGetFileEncryptionProperties(
const ::parquet::encryption::KmsConnectionConfig& kms_connection_config,
const ::parquet::encryption::EncryptionConfiguration& encryption_config) {
PARQUET_CATCH_AND_RETURN(
this->GetFileEncryptionProperties(kms_connection_config, encryption_config));
}

arrow::Result<std::shared_ptr<::parquet::FileDecryptionProperties>>
PyCryptoFactory::SafeGetFileDecryptionProperties(
const ::parquet::encryption::KmsConnectionConfig& kms_connection_config,
const ::parquet::encryption::DecryptionConfiguration& decryption_config) {
PARQUET_CATCH_AND_RETURN(
this->GetFileDecryptionProperties(kms_connection_config, decryption_config));
}

} // namespace encryption
} // namespace parquet
} // namespace py
} // namespace arrow
109 changes: 109 additions & 0 deletions cpp/src/arrow/python/parquet_encryption.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.

#pragma once

#include <string>

#include "arrow/python/common.h"
#include "arrow/python/visibility.h"
#include "arrow/util/macros.h"
#include "parquet/encryption/crypto_factory.h"
#include "parquet/encryption/kms_client.h"
#include "parquet/encryption/kms_client_factory.h"

namespace arrow {
namespace py {
namespace parquet {
namespace encryption {

/// \brief A table of function pointers for calling from C++ into
/// Python.
class ARROW_PYTHON_EXPORT PyKmsClientVtable {
public:
std::function<void(PyObject*, const std::string& key_bytes,
const std::string& master_key_identifier, std::string* out)>
wrap_key;
std::function<void(PyObject*, const std::string& wrapped_key,
const std::string& master_key_identifier, std::string* out)>
unwrap_key;
};

/// \brief A helper for KmsClient implementation in Python.
class ARROW_PYTHON_EXPORT PyKmsClient : public ::parquet::encryption::KmsClient {
public:
PyKmsClient(PyObject* handler, PyKmsClientVtable vtable);
~PyKmsClient() override;

std::string WrapKey(const std::string& key_bytes,
const std::string& master_key_identifier) override;

std::string UnwrapKey(const std::string& wrapped_key,
const std::string& master_key_identifier) override;

private:
OwnedRefNoGIL handler_;
PyKmsClientVtable vtable_;
};

/// \brief A table of function pointers for calling from C++ into
/// Python.
class ARROW_PYTHON_EXPORT PyKmsClientFactoryVtable {
public:
std::function<void(
PyObject*, const ::parquet::encryption::KmsConnectionConfig& kms_connection_config,
std::shared_ptr<::parquet::encryption::KmsClient>* out)>
create_kms_client;
};

/// \brief A helper for KmsClientFactory implementation in Python.
class ARROW_PYTHON_EXPORT PyKmsClientFactory
: public ::parquet::encryption::KmsClientFactory {
public:
PyKmsClientFactory(PyObject* handler, PyKmsClientFactoryVtable vtable);
~PyKmsClientFactory() override;

std::shared_ptr<::parquet::encryption::KmsClient> CreateKmsClient(
const ::parquet::encryption::KmsConnectionConfig& kms_connection_config) override;

private:
OwnedRefNoGIL handler_;
PyKmsClientFactoryVtable vtable_;
};

/// \brief A CryptoFactory that returns Results instead of throwing exceptions.
class ARROW_PYTHON_EXPORT PyCryptoFactory : public ::parquet::encryption::CryptoFactory {
public:
arrow::Result<std::shared_ptr<::parquet::FileEncryptionProperties>>
SafeGetFileEncryptionProperties(
const ::parquet::encryption::KmsConnectionConfig& kms_connection_config,
const ::parquet::encryption::EncryptionConfiguration& encryption_config);

/// The returned FileDecryptionProperties object will use the cache inside this
/// CryptoFactory object, so please keep this
/// CryptoFactory object alive along with the returned
/// FileDecryptionProperties object.
arrow::Result<std::shared_ptr<::parquet::FileDecryptionProperties>>
SafeGetFileDecryptionProperties(
const ::parquet::encryption::KmsConnectionConfig& kms_connection_config,
const ::parquet::encryption::DecryptionConfiguration& decryption_config);
};

} // namespace encryption
} // namespace parquet
} // namespace py
} // namespace arrow
Loading