PYTHON-1341 Impl of client-side column-level encryption/decryption by absurdfarce · Pull Request #1150 · apache/cassandra-python-driver · GitHub
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
e3c2f2e
Just getting started...
Mar 15, 2023
e4a1ce1
Fleshing some things out
Mar 15, 2023
284a60e
A few utility methods
Mar 16, 2023
4dd8dbe
Very initial stabs on integrating CLE functionality into message seri…
Mar 27, 2023
dfa7e4e
Merge branch 'master' into cle
Apr 3, 2023
f76a4cb
Can write pickled bits to a Cassandra cluster at this point
Apr 7, 2023
6b681e3
Shift to explicit arg at column add time to identify type; let's us a…
Apr 8, 2023
f202017
Shift to using typename from cqltypes rather than actual class name. …
Apr 8, 2023
6aaad11
Merge branch 'master' into cle
Apr 8, 2023
de91285
Simple integration test (prepared insert + prepared select) working i…
Apr 18, 2023
0391198
Simple integration test actually _wasn't_ working on the prior commit…
Apr 18, 2023
8d34464
Looks like Cython is working now, or at least the base path is
Apr 21, 2023
2b7c860
Incorporate end-to-end integration test
Apr 22, 2023
027e20a
Trying to fix tox to make TravisCI happy
Apr 22, 2023
2bc8c4b
Explicit maxsize param for LRU cache; required for Python 3.7.x
Apr 22, 2023
1811112
Trying to determine whether PyPy 3.7 is supported for us on TravisCI yet
Apr 22, 2023
67740cf
Another try to fix the PyPy build
Apr 22, 2023
e21f437
Fix integration test to account for extra args to recv_results_rows
Apr 24, 2023
bb5ec26
Another attempt to fix the TravisCI PyPy build
Apr 24, 2023
03a6f09
Let's try this one more time
Apr 24, 2023
3311fae
Revert to the TravisCI build used for 3.26.0. PYTHON-1342 has been f…
Apr 25, 2023
0af7a4a
Reverting to old versions of files is hard
Apr 25, 2023
c88ca70
Temporarily enabling both Cython and non-Cython smoke builds while wo…
Apr 25, 2023
44260f4
Avoid PyPy for now. We'll fix this in PYTHON-1342.
Apr 25, 2023
72746b7
Review feedback
Apr 26, 2023
28fca76
Add helper for using CLE with simple statements
Apr 27, 2023
0cd9af7
PYTHON-1341 Documentation for CLE functionality (#1152)
absurdfarce May 1, 2023
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: 0 additions & 1 deletion .travis.yml
2 changes: 1 addition & 1 deletion Jenkinsfile
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ matrices = [
"SMOKE": [
"SERVER": ['3.11', '4.0', 'dse-6.8.30'],
"RUNTIME": ['3.7.7', '3.8.3'],
"CYTHON": ["False"]
"CYTHON": ["True", "False"]
]
]

Expand Down
20 changes: 18 additions & 2 deletions cassandra/cluster.py
Original file line number Diff line number Diff line change
Expand Up @@ -1003,6 +1003,12 @@ def default_retry_policy(self, policy):
load the configuration and certificates.
"""

column_encryption_policy = None
"""
An instance of :class:`cassandra.policies.ColumnEncryptionPolicy` specifying encryption materials to be
used for columns in this cluster.
"""

@property
def schema_metadata_enabled(self):
"""
Expand Down Expand Up @@ -1104,7 +1110,8 @@ def __init__(self,
monitor_reporting_enabled=True,
monitor_reporting_interval=30,
client_id=None,
cloud=None):
cloud=None,
column_encryption_policy=None):
"""
``executor_threads`` defines the number of threads in a pool for handling asynchronous tasks such as
extablishing connection pools or refreshing metadata.
Expand Down Expand Up @@ -1152,6 +1159,9 @@ def __init__(self,

self.port = port

if column_encryption_policy is not None:
self.column_encryption_policy = column_encryption_policy

self.endpoint_factory = endpoint_factory or DefaultEndPointFactory(port=self.port)
self.endpoint_factory.configure(self)

Expand Down Expand Up @@ -2535,6 +2545,12 @@ def __init__(self, cluster, hosts, keyspace=None):

self.encoder = Encoder()

if self.cluster.column_encryption_policy is not None:
try:
self.client_protocol_handler.column_encryption_policy = self.cluster.column_encryption_policy
except AttributeError:
log.info("Unable to set column encryption policy for session")

# create connection pools in parallel
self._initial_connect_futures = set()
for host in hosts:
Expand Down Expand Up @@ -3074,7 +3090,7 @@ def prepare(self, query, custom_payload=None, keyspace=None):
prepared_keyspace = keyspace if keyspace else None
prepared_statement = PreparedStatement.from_message(
response.query_id, response.bind_metadata, response.pk_indexes, self.cluster.metadata, query, prepared_keyspace,
self._protocol_version, response.column_metadata, response.result_metadata_id)
self._protocol_version, response.column_metadata, response.result_metadata_id, self.cluster.column_encryption_policy)
prepared_statement.custom_payload = future.custom_payload

self.cluster.add_prepared(response.query_id, prepared_statement)
Expand Down
16 changes: 15 additions & 1 deletion cassandra/obj_parser.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,12 @@ include "ioutils.pyx"
from cassandra import DriverException
from cassandra.bytesio cimport BytesIOReader
from cassandra.deserializers cimport Deserializer, from_binary
from cassandra.deserializers import find_deserializer
from cassandra.parsing cimport ParseDesc, ColumnParser, RowParser
from cassandra.tuple cimport tuple_new, tuple_set

from cpython.bytes cimport PyBytes_AsStringAndSize


cdef class ListParser(ColumnParser):
"""Decode a ResultMessage into a list of tuples (or other objects)"""
Expand Down Expand Up @@ -58,18 +61,29 @@ cdef class TupleRowParser(RowParser):
assert desc.rowsize >= 0

cdef Buffer buf
cdef Buffer newbuf
cdef Py_ssize_t i, rowsize = desc.rowsize
cdef Deserializer deserializer
cdef tuple res = tuple_new(desc.rowsize)

ce_policy = desc.column_encryption_policy
for i in range(rowsize):
# Read the next few bytes
get_buf(reader, &buf)

# Deserialize bytes to python object
deserializer = desc.deserializers[i]
coldesc = desc.coldescs[i]
uses_ce = ce_policy and ce_policy.contains_column(coldesc)
try:
val = from_binary(deserializer, &buf, desc.protocol_version)
if uses_ce:
col_type = ce_policy.column_type(coldesc)
decrypted_bytes = ce_policy.decrypt(coldesc, to_bytes(&buf))
PyBytes_AsStringAndSize(decrypted_bytes, &newbuf.ptr, &newbuf.size)
deserializer = find_deserializer(ce_policy.column_type(coldesc))
val = from_binary(deserializer, &newbuf, desc.protocol_version)
else:
val = from_binary(deserializer, &buf, desc.protocol_version)
except Exception as e:
raise DriverException('Failed decoding result column "%s" of type %s: %s' % (desc.colnames[i],
desc.coltypes[i].cql_parameterized_type(),
Expand Down
2 changes: 2 additions & 0 deletions cassandra/parsing.pxd
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ from cassandra.deserializers cimport Deserializer
cdef class ParseDesc:
cdef public object colnames
cdef public object coltypes
cdef public object column_encryption_policy
cdef public list coldescs
cdef Deserializer[::1] deserializers
cdef public int protocol_version
cdef Py_ssize_t rowsize
Expand Down
4 changes: 3 additions & 1 deletion cassandra/parsing.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,11 @@ Module containing the definitions and declarations (parsing.pxd) for parsers.
cdef class ParseDesc:
"""Description of what structure to parse"""

def __init__(self, colnames, coltypes, deserializers, protocol_version):
def __init__(self, colnames, coltypes, column_encryption_policy, coldescs, deserializers, protocol_version):
self.colnames = colnames
self.coltypes = coltypes
self.column_encryption_policy = column_encryption_policy
self.coldescs = coldescs
self.deserializers = deserializers
self.protocol_version = protocol_version
self.rowsize = len(colnames)
Expand Down
181 changes: 173 additions & 8 deletions cassandra/policies.py
Loading