# Path: federation/entities/diaspora/utils.py
# def add_element_to_doc(doc, tag, value):
#     """Set text value of an etree.Element of tag, appending a new element with given tag if it doesn't exist."""
#     element = doc.find(".//%s" % tag)
#     if element is None:
#         element = etree.SubElement(doc, tag)
#     element.text = value
# 
# Path: federation/entities/utils.py
# def get_base_attributes(entity):
#     """Build a dict of attributes of an entity.
# 
#     Returns attributes and their values, ignoring any properties, functions and anything that starts
#     with an underscore.
#     """
#     attributes = {}
#     cls = entity.__class__
#     for attr, _ in inspect.getmembers(cls, lambda o: not isinstance(o, property) and not inspect.isroutine(o)):
#         if not attr.startswith("_"):
#             attributes[attr] = getattr(entity, attr)
#     return attributes
# 
# Path: federation/exceptions.py
# class SignatureVerificationError(Exception):
#     """Authenticity of the signature could not be verified given the key."""
#     pass
# 
# Path: federation/protocols/diaspora/signatures.py
# def verify_relayable_signature(public_key, doc, signature):
#     """
#     Verify the signed XML elements to have confidence that the claimed
#     author did actually generate this message.
#     """
#     sig_hash = _create_signature_hash(doc)
#     cipher = PKCS1_v1_5.new(RSA.importKey(public_key))
#     return cipher.verify(sig_hash, b64decode(signature))
# 
# def create_relayable_signature(private_key: RsaKey, doc):
#     sig_hash = _create_signature_hash(doc)
#     cipher = PKCS1_v1_5.new(private_key)
#     return b64encode(cipher.sign(sig_hash)).decode("ascii")


from Crypto.PublicKey import RSA
from lxml import etree
from federation.entities.diaspora.utils import add_element_to_doc
from federation.entities.mixins import BaseEntity
from federation.entities.utils import get_base_attributes
from federation.exceptions import SignatureVerificationError
from federation.protocols.diaspora.signatures import verify_relayable_signature, create_relayable_signature

    @staticmethod
    def fill_extra_attributes(attributes):
        """Implement in subclasses to fill extra attributes when an XML is transformed to an object.

        This is called just before initializing the entity.

        Args:
            attributes (dict) - Already transformed attributes that will be passed to entity create.

        Returns:
            Must return the attributes dictionary, possibly with changed or additional values.
        """
        return attributes


class DiasporaRelayableMixin(DiasporaEntityMixin):
    _xml_tags = []
    parent_signature = ""

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self._required += ["signature"]

    def _validate_signatures(self):
        super()._validate_signatures()
        if not self._sender_key:
            raise SignatureVerificationError("Cannot verify entity signature - no sender key available")
        source_doc = etree.fromstring(self._source_object)
        if not verify_relayable_signature(self._sender_key, source_doc, self.signature):

# Path: federation/entities/diaspora/utils.py
# def add_element_to_doc(doc, tag, value):
#     """Set text value of an etree.Element of tag, appending a new element with given tag if it doesn't exist."""
#     element = doc.find(".//%s" % tag)
#     if element is None:
#         element = etree.SubElement(doc, tag)
#     element.text = value
# 
# Path: federation/entities/utils.py
# def get_base_attributes(entity):
#     """Build a dict of attributes of an entity.
# 
#     Returns attributes and their values, ignoring any properties, functions and anything that starts
#     with an underscore.
#     """
#     attributes = {}
#     cls = entity.__class__
#     for attr, _ in inspect.getmembers(cls, lambda o: not isinstance(o, property) and not inspect.isroutine(o)):
#         if not attr.startswith("_"):
#             attributes[attr] = getattr(entity, attr)
#     return attributes
# 
# Path: federation/exceptions.py
# class SignatureVerificationError(Exception):
#     """Authenticity of the signature could not be verified given the key."""
#     pass
# 
# Path: federation/protocols/diaspora/signatures.py
# def verify_relayable_signature(public_key, doc, signature):
#     """
#     Verify the signed XML elements to have confidence that the claimed
#     author did actually generate this message.
#     """
#     sig_hash = _create_signature_hash(doc)
#     cipher = PKCS1_v1_5.new(RSA.importKey(public_key))
#     return cipher.verify(sig_hash, b64decode(signature))
# 
# def create_relayable_signature(private_key: RsaKey, doc):
#     sig_hash = _create_signature_hash(doc)
#     cipher = PKCS1_v1_5.new(private_key)
#     return b64encode(cipher.sign(sig_hash)).decode("ascii")


# Path: federation/entities/diaspora/mixins.py
from Crypto.PublicKey import RSA
from lxml import etree
from federation.entities.diaspora.utils import add_element_to_doc
from federation.entities.mixins import BaseEntity
from federation.entities.utils import get_base_attributes
from federation.exceptions import SignatureVerificationError
from federation.protocols.diaspora.signatures import verify_relayable_signature, create_relayable_signature


    @staticmethod
    def fill_extra_attributes(attributes):
        """Implement in subclasses to fill extra attributes when an XML is transformed to an object.

        This is called just before initializing the entity.

        Args:
            attributes (dict) - Already transformed attributes that will be passed to entity create.

        Returns:
            Must return the attributes dictionary, possibly with changed or additional values.
        """
        return attributes


class DiasporaRelayableMixin(DiasporaEntityMixin):
    _xml_tags = []
    parent_signature = ""

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self._required += ["signature"]

    def _validate_signatures(self):
        super()._validate_signatures()
        if not self._sender_key:
            raise SignatureVerificationError("Cannot verify entity signature - no sender key available")
        source_doc = etree.fromstring(self._source_object)
        if not verify_relayable_signature(self._sender_key, source_doc, self.signature):

            raise SignatureVerificationError("Signature verification failed.")