Skip to content

DGS-21268 Add support for full payload encryption #2011

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jul 18, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
18 changes: 16 additions & 2 deletions src/confluent_kafka/schema_registry/_async/avro.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
# 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.

import io
from json import loads
from typing import Dict, Union, Optional, Callable

Expand All @@ -29,6 +29,8 @@
AsyncSchemaRegistryClient,
prefix_schema_id_serializer,
dual_schema_id_deserializer)
from confluent_kafka.schema_registry.common.schema_registry_client import \
RulePhase
from confluent_kafka.serialization import (SerializationError,
SerializationContext)
from confluent_kafka.schema_registry.rule_registry import RuleRegistry
Expand Down Expand Up @@ -348,8 +350,14 @@
with _ContextStringIO() as fo:
# write the record to the rest of the buffer
schemaless_writer(fo, parsed_schema, value)
buffer = fo.getvalue()

Check warning on line 353 in src/confluent_kafka/schema_registry/_async/avro.py

View check run for this annotation

SonarQube-Confluent / confluent-kafka-python Sonarqube Results

src/confluent_kafka/schema_registry/_async/avro.py#L353

Rename this variable; it shadows a builtin.

if latest_schema is not None:
buffer = self._execute_rules_with_phase(
ctx, subject, RulePhase.ENCODING, RuleMode.WRITE,
None, latest_schema.schema, buffer, None, None)

return self._schema_id_serializer(fo.getvalue(), ctx, self._schema_id)
return self._schema_id_serializer(buffer, ctx, self._schema_id)

async def _get_parsed_schema(self, schema: Schema) -> AvroSchema:
parsed_schema = self._parsed_schemas.get_parsed_schema(schema)
Expand Down Expand Up @@ -512,7 +520,7 @@
def __call__(self, data: bytes, ctx: Optional[SerializationContext] = None) -> Union[dict, object, None]:
return self.__deserialize(data, ctx)

async def __deserialize(

Check failure on line 523 in src/confluent_kafka/schema_registry/_async/avro.py

View check run for this annotation

SonarQube-Confluent / confluent-kafka-python Sonarqube Results

src/confluent_kafka/schema_registry/_async/avro.py#L523

Refactor this function to reduce its Cognitive Complexity from 16 to the 15 allowed.
self, data: bytes, ctx: Optional[SerializationContext] = None) -> Union[dict, object, None]:
"""
Deserialize Avro binary encoded data with Confluent Schema Registry framing to
Expand Down Expand Up @@ -557,6 +565,12 @@
if subject is not None:
latest_schema = await self._get_reader_schema(subject)

payload = self._execute_rules_with_phase(
ctx, subject, RulePhase.ENCODING, RuleMode.READ,
None, writer_schema_raw, payload, None, None)
if isinstance(payload, bytes):
payload = io.BytesIO(payload)

if latest_schema is not None:
migrations = await self._get_migrations(subject, writer_schema_raw, latest_schema, None)
reader_schema_raw = latest_schema.schema
Expand Down
28 changes: 21 additions & 7 deletions src/confluent_kafka/schema_registry/_async/json_schema.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
# 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.

import io
import json
from typing import Union, Optional, Tuple, Callable

Expand All @@ -33,6 +33,8 @@
from confluent_kafka.schema_registry.common.json_schema import (
DEFAULT_SPEC, JsonSchema, _retrieve_via_httpx, transform, _ContextStringIO, JSON_TYPE
)
from confluent_kafka.schema_registry.common.schema_registry_client import \
RulePhase
from confluent_kafka.schema_registry.rule_registry import RuleRegistry
from confluent_kafka.schema_registry.serde import AsyncBaseSerializer, AsyncBaseDeserializer, \
ParsedSchemaCache, SchemaId
Expand Down Expand Up @@ -374,8 +376,14 @@
if isinstance(encoded_value, str):
encoded_value = encoded_value.encode("utf8")
fo.write(encoded_value)
buffer = fo.getvalue()

Check warning on line 379 in src/confluent_kafka/schema_registry/_async/json_schema.py

View check run for this annotation

SonarQube-Confluent / confluent-kafka-python Sonarqube Results

src/confluent_kafka/schema_registry/_async/json_schema.py#L379

Rename this variable; it shadows a builtin.

if latest_schema is not None:
buffer = self._execute_rules_with_phase(
ctx, subject, RulePhase.ENCODING, RuleMode.WRITE,
None, latest_schema.schema, buffer, None, None)

return self._schema_id_serializer(fo.getvalue(), ctx, self._schema_id)
return self._schema_id_serializer(buffer, ctx, self._schema_id)

async def _get_parsed_schema(self, schema: Schema) -> Tuple[Optional[JsonSchema], Optional[Registry]]:
if schema is None:
Expand Down Expand Up @@ -552,9 +560,9 @@
__init__ = __init_impl

def __call__(self, data: bytes, ctx: Optional[SerializationContext] = None) -> Optional[bytes]:
return self.__serialize(data, ctx)
return self.__deserialize(data, ctx)

async def __serialize(self, data: bytes, ctx: Optional[SerializationContext] = None) -> Optional[bytes]:
async def __deserialize(self, data: bytes, ctx: Optional[SerializationContext] = None) -> Optional[bytes]:

Check failure on line 565 in src/confluent_kafka/schema_registry/_async/json_schema.py

View check run for this annotation

SonarQube-Confluent / confluent-kafka-python Sonarqube Results

src/confluent_kafka/schema_registry/_async/json_schema.py#L565

Refactor this function to reduce its Cognitive Complexity from 19 to the 15 allowed.
"""
Deserialize a JSON encoded record with Confluent Schema Registry framing to
a dict, or object instance according to from_dict if from_dict is specified.
Expand Down Expand Up @@ -583,9 +591,6 @@
schema_id = SchemaId(JSON_TYPE)
payload = self._schema_id_deserializer(data, ctx, schema_id)

# JSON documents are self-describing; no need to query schema
obj_dict = self._json_decode(payload.read())

if self._registry is not None:
writer_schema_raw = await self._get_writer_schema(schema_id, subject)
writer_schema, writer_ref_registry = await self._get_parsed_schema(writer_schema_raw)
Expand All @@ -597,6 +602,15 @@
writer_schema_raw = None
writer_schema, writer_ref_registry = None, None

payload = self._execute_rules_with_phase(
ctx, subject, RulePhase.ENCODING, RuleMode.READ,
None, writer_schema_raw, payload, None, None)
if isinstance(payload, bytes):
payload = io.BytesIO(payload)

# JSON documents are self-describing; no need to query schema
obj_dict = self._json_decode(payload.read())

if latest_schema is not None:
migrations = await self._get_migrations(subject, writer_schema_raw, latest_schema, None)
reader_schema_raw = latest_schema.schema
Expand Down
21 changes: 18 additions & 3 deletions src/confluent_kafka/schema_registry/_async/protobuf.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@
from confluent_kafka.schema_registry import (reference_subject_name_strategy,
topic_subject_name_strategy,
prefix_schema_id_serializer, dual_schema_id_deserializer)
from confluent_kafka.schema_registry.common.schema_registry_client import \
RulePhase
from confluent_kafka.schema_registry.schema_registry_client import AsyncSchemaRegistryClient
from confluent_kafka.schema_registry.common.protobuf import _bytes, _create_index_array, \
_init_pool, _is_builtin, _schema_to_str, _str_to_proto, transform, _ContextStringIO, PROTOBUF_TYPE
Expand Down Expand Up @@ -426,7 +428,14 @@
with _ContextStringIO() as fo:
fo.write(message.SerializeToString())
self._schema_id.message_indexes = self._index_array
return self._schema_id_serializer(fo.getvalue(), ctx, self._schema_id)
buffer = fo.getvalue()

Check warning on line 431 in src/confluent_kafka/schema_registry/_async/protobuf.py

View check run for this annotation

SonarQube-Confluent / confluent-kafka-python Sonarqube Results

src/confluent_kafka/schema_registry/_async/protobuf.py#L431

Rename this variable; it shadows a builtin.

if latest_schema is not None:
buffer = self._execute_rules_with_phase(
ctx, subject, RulePhase.ENCODING, RuleMode.WRITE,
None, latest_schema.schema, buffer, None, None)

return self._schema_id_serializer(buffer, ctx, self._schema_id)

async def _get_parsed_schema(self, schema: Schema) -> Tuple[descriptor_pb2.FileDescriptorProto, DescriptorPool]:
result = self._parsed_schemas.get_parsed_schema(schema)
Expand Down Expand Up @@ -549,9 +558,9 @@
__init__ = __init_impl

def __call__(self, data: bytes, ctx: Optional[SerializationContext] = None) -> Optional[bytes]:
return self.__serialize(data, ctx)
return self.__deserialize(data, ctx)

async def __serialize(self, data: bytes, ctx: Optional[SerializationContext] = None) -> Optional[bytes]:
async def __deserialize(self, data: bytes, ctx: Optional[SerializationContext] = None) -> Optional[bytes]:

Check failure on line 563 in src/confluent_kafka/schema_registry/_async/protobuf.py

View check run for this annotation

SonarQube-Confluent / confluent-kafka-python Sonarqube Results

src/confluent_kafka/schema_registry/_async/protobuf.py#L563

Refactor this function to reduce its Cognitive Complexity from 20 to the 15 allowed.
"""
Deserialize a serialized protobuf message with Confluent Schema Registry
framing.
Expand Down Expand Up @@ -596,6 +605,12 @@
writer_schema_raw = None
writer_schema = None

payload = self._execute_rules_with_phase(
ctx, subject, RulePhase.ENCODING, RuleMode.READ,
None, writer_schema_raw, payload, None, None)
if isinstance(payload, bytes):
payload = io.BytesIO(payload)

if latest_schema is not None:
migrations = await self._get_migrations(subject, writer_schema_raw, latest_schema, None)
reader_schema_raw = latest_schema.schema
Expand Down
44 changes: 34 additions & 10 deletions src/confluent_kafka/schema_registry/_async/serde.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@
from typing import List, Optional, Set, Dict, Any

from confluent_kafka.schema_registry import RegisteredSchema
from confluent_kafka.schema_registry.common.schema_registry_client import \
RulePhase
from confluent_kafka.schema_registry.common.serde import ErrorAction, \
FieldTransformer, Migration, NoneAction, RuleAction, \
RuleConditionError, RuleContext, RuleError, SchemaId
Expand Down Expand Up @@ -59,6 +61,17 @@
source: Optional[Schema], target: Optional[Schema],
message: Any, inline_tags: Optional[Dict[str, Set[str]]],
field_transformer: Optional[FieldTransformer]
) -> Any:
return self._execute_rules_with_phase(
ser_ctx, subject, RulePhase.DOMAIN, rule_mode,
source, target, message, inline_tags, field_transformer)

def _execute_rules_with_phase(

Check failure on line 69 in src/confluent_kafka/schema_registry/_async/serde.py

View check run for this annotation

SonarQube-Confluent / confluent-kafka-python Sonarqube Results

src/confluent_kafka/schema_registry/_async/serde.py#L69

Refactor this function to reduce its Cognitive Complexity from 60 to the 15 allowed.
self, ser_ctx: SerializationContext, subject: str,
rule_phase: RulePhase, rule_mode: RuleMode,
source: Optional[Schema], target: Optional[Schema],
message: Any, inline_tags: Optional[Dict[str, Set[str]]],
field_transformer: Optional[FieldTransformer]
) -> Any:
if message is None or target is None:
return message
Expand All @@ -73,7 +86,10 @@
rules.reverse()
else:
if target is not None and target.rule_set is not None:
rules = target.rule_set.domain_rules
if rule_phase == RulePhase.ENCODING:
rules = target.rule_set.encoding_rules
else:
rules = target.rule_set.domain_rules
if rule_mode == RuleMode.READ:
# Execute read rules in reverse order for symmetry
rules = rules[:] if rules else []
Expand Down Expand Up @@ -197,19 +213,25 @@
else:
raise SerializationError("Schema ID or GUID is not set")

def _has_rules(self, rule_set: RuleSet, mode: RuleMode) -> bool:
def _has_rules(self, rule_set: RuleSet, phase: RulePhase, mode: RuleMode) -> bool:
if rule_set is None:
return False
if phase == RulePhase.MIGRATION:
rules = rule_set.migration_rules
elif phase == RulePhase.DOMAIN:
rules = rule_set.domain_rules
elif phase == RulePhase.ENCODING:
rules = rule_set.encoding_rules
if mode in (RuleMode.UPGRADE, RuleMode.DOWNGRADE):
return any(rule.mode == mode or rule.mode == RuleMode.UPDOWN
for rule in rule_set.migration_rules or [])
for rule in rules or [])
elif mode == RuleMode.UPDOWN:
return any(rule.mode == mode for rule in rule_set.migration_rules or [])
return any(rule.mode == mode for rule in rules or [])
elif mode in (RuleMode.WRITE, RuleMode.READ):
return any(rule.mode == mode or rule.mode == RuleMode.WRITEREAD
for rule in rule_set.domain_rules or [])
for rule in rules or [])
elif mode == RuleMode.WRITEREAD:
return any(rule.mode == mode for rule in rule_set.migration_rules or [])
return any(rule.mode == mode for rule in rules or [])
return False

async def _get_migrations(
Expand All @@ -235,7 +257,8 @@
if i == 0:
previous = version
continue
if version.schema.rule_set is not None and self._has_rules(version.schema.rule_set, migration_mode):
if version.schema.rule_set is not None and self._has_rules(
version.schema.rule_set, RulePhase.MIGRATION, migration_mode):
if migration_mode == RuleMode.UPGRADE:
migration = Migration(migration_mode, previous, version)
else:
Expand Down Expand Up @@ -265,7 +288,8 @@
migrations: List[Migration], message: Any
) -> Any:
for migration in migrations:
message = self._execute_rules(ser_ctx, subject, migration.rule_mode,
migration.source.schema, migration.target.schema,
message, None, None)
message = self._execute_rules_with_phase(
ser_ctx, subject, RulePhase.MIGRATION, migration.rule_mode,
migration.source.schema, migration.target.schema,
message, None, None)
return message
18 changes: 16 additions & 2 deletions src/confluent_kafka/schema_registry/_sync/avro.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
# 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.

import io
from json import loads
from typing import Dict, Union, Optional, Callable

Expand All @@ -29,6 +29,8 @@
SchemaRegistryClient,
prefix_schema_id_serializer,
dual_schema_id_deserializer)
from confluent_kafka.schema_registry.common.schema_registry_client import \
RulePhase
from confluent_kafka.serialization import (SerializationError,
SerializationContext)
from confluent_kafka.schema_registry.rule_registry import RuleRegistry
Expand Down Expand Up @@ -348,8 +350,14 @@
with _ContextStringIO() as fo:
# write the record to the rest of the buffer
schemaless_writer(fo, parsed_schema, value)
buffer = fo.getvalue()

Check warning on line 353 in src/confluent_kafka/schema_registry/_sync/avro.py

View check run for this annotation

SonarQube-Confluent / confluent-kafka-python Sonarqube Results

src/confluent_kafka/schema_registry/_sync/avro.py#L353

Rename this variable; it shadows a builtin.

if latest_schema is not None:
buffer = self._execute_rules_with_phase(
ctx, subject, RulePhase.ENCODING, RuleMode.WRITE,
None, latest_schema.schema, buffer, None, None)

return self._schema_id_serializer(fo.getvalue(), ctx, self._schema_id)
return self._schema_id_serializer(buffer, ctx, self._schema_id)

def _get_parsed_schema(self, schema: Schema) -> AvroSchema:
parsed_schema = self._parsed_schemas.get_parsed_schema(schema)
Expand Down Expand Up @@ -512,7 +520,7 @@
def __call__(self, data: bytes, ctx: Optional[SerializationContext] = None) -> Union[dict, object, None]:
return self.__deserialize(data, ctx)

def __deserialize(

Check failure on line 523 in src/confluent_kafka/schema_registry/_sync/avro.py

View check run for this annotation

SonarQube-Confluent / confluent-kafka-python Sonarqube Results

src/confluent_kafka/schema_registry/_sync/avro.py#L523

Refactor this function to reduce its Cognitive Complexity from 16 to the 15 allowed.
self, data: bytes, ctx: Optional[SerializationContext] = None) -> Union[dict, object, None]:
"""
Deserialize Avro binary encoded data with Confluent Schema Registry framing to
Expand Down Expand Up @@ -557,6 +565,12 @@
if subject is not None:
latest_schema = self._get_reader_schema(subject)

payload = self._execute_rules_with_phase(
ctx, subject, RulePhase.ENCODING, RuleMode.READ,
None, writer_schema_raw, payload, None, None)
if isinstance(payload, bytes):
payload = io.BytesIO(payload)

if latest_schema is not None:
migrations = self._get_migrations(subject, writer_schema_raw, latest_schema, None)
reader_schema_raw = latest_schema.schema
Expand Down
28 changes: 21 additions & 7 deletions src/confluent_kafka/schema_registry/_sync/json_schema.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
# 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.

import io
import json
from typing import Union, Optional, Tuple, Callable

Expand All @@ -33,6 +33,8 @@
from confluent_kafka.schema_registry.common.json_schema import (
DEFAULT_SPEC, JsonSchema, _retrieve_via_httpx, transform, _ContextStringIO, JSON_TYPE
)
from confluent_kafka.schema_registry.common.schema_registry_client import \
RulePhase
from confluent_kafka.schema_registry.rule_registry import RuleRegistry
from confluent_kafka.schema_registry.serde import BaseSerializer, BaseDeserializer, \
ParsedSchemaCache, SchemaId
Expand Down Expand Up @@ -374,8 +376,14 @@
if isinstance(encoded_value, str):
encoded_value = encoded_value.encode("utf8")
fo.write(encoded_value)
buffer = fo.getvalue()

Check warning on line 379 in src/confluent_kafka/schema_registry/_sync/json_schema.py

View check run for this annotation

SonarQube-Confluent / confluent-kafka-python Sonarqube Results

src/confluent_kafka/schema_registry/_sync/json_schema.py#L379

Rename this variable; it shadows a builtin.

if latest_schema is not None:
buffer = self._execute_rules_with_phase(
ctx, subject, RulePhase.ENCODING, RuleMode.WRITE,
None, latest_schema.schema, buffer, None, None)

return self._schema_id_serializer(fo.getvalue(), ctx, self._schema_id)
return self._schema_id_serializer(buffer, ctx, self._schema_id)

def _get_parsed_schema(self, schema: Schema) -> Tuple[Optional[JsonSchema], Optional[Registry]]:
if schema is None:
Expand Down Expand Up @@ -552,9 +560,9 @@
__init__ = __init_impl

def __call__(self, data: bytes, ctx: Optional[SerializationContext] = None) -> Optional[bytes]:
return self.__serialize(data, ctx)
return self.__deserialize(data, ctx)

def __serialize(self, data: bytes, ctx: Optional[SerializationContext] = None) -> Optional[bytes]:
def __deserialize(self, data: bytes, ctx: Optional[SerializationContext] = None) -> Optional[bytes]:

Check failure on line 565 in src/confluent_kafka/schema_registry/_sync/json_schema.py

View check run for this annotation

SonarQube-Confluent / confluent-kafka-python Sonarqube Results

src/confluent_kafka/schema_registry/_sync/json_schema.py#L565

Refactor this function to reduce its Cognitive Complexity from 19 to the 15 allowed.
"""
Deserialize a JSON encoded record with Confluent Schema Registry framing to
a dict, or object instance according to from_dict if from_dict is specified.
Expand Down Expand Up @@ -583,9 +591,6 @@
schema_id = SchemaId(JSON_TYPE)
payload = self._schema_id_deserializer(data, ctx, schema_id)

# JSON documents are self-describing; no need to query schema
obj_dict = self._json_decode(payload.read())

if self._registry is not None:
writer_schema_raw = self._get_writer_schema(schema_id, subject)
writer_schema, writer_ref_registry = self._get_parsed_schema(writer_schema_raw)
Expand All @@ -597,6 +602,15 @@
writer_schema_raw = None
writer_schema, writer_ref_registry = None, None

payload = self._execute_rules_with_phase(
ctx, subject, RulePhase.ENCODING, RuleMode.READ,
None, writer_schema_raw, payload, None, None)
if isinstance(payload, bytes):
payload = io.BytesIO(payload)

# JSON documents are self-describing; no need to query schema
obj_dict = self._json_decode(payload.read())

if latest_schema is not None:
migrations = self._get_migrations(subject, writer_schema_raw, latest_schema, None)
reader_schema_raw = latest_schema.schema
Expand Down
Loading
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy