Exemplo n.º 1
0
class VerificationMethodOptions(Option):
    """Container for validation options for VerificationMethods."""

    allow_type_list = 0, Schema(
        {"type": All(Switch(str, [str]), _option_allow_type_list)},
        extra=ALLOW_EXTRA)
    allow_missing_controller = 1, Schema(
        All(
            {Required("id"): All(str, DIDUrl.validate)},
            _option_allow_missing_controller,
        ),
        extra=ALLOW_EXTRA,
    )
    allow_controller_list = 2, Schema(
        {"controller": All(Switch(str, [str]), _option_allow_controller_list)},
        extra=ALLOW_EXTRA,
    )

    @property
    def priority(self):
        """Return the priority for this option."""
        return self.value[0]

    @property
    def schema(self):
        """Return the schema defined by option."""
        return self.value[1]

    @classmethod
    def apply(cls, value, options: Set["VerificationMethodOptions"]):
        """Apply options to value"""
        return All(*cls.schemas_in_application_order(options))(value)
Exemplo n.º 2
0
def _verification_methods(schema):
    return {
        "verificationMethod": [schema],
        **{
            key: [Switch(str, schema)]
            for key in (
                "authentication",
                "assertionMethod",
                "keyAgreement",
                "capabilityInvocation",
                "capabilityDelegation",
            )
        },
    }
Exemplo n.º 3
0
class Service:
    """Representation of DID Document Services."""

    _validator = Schema(
        {
            "id": All(str, DIDUrl.validate),
            "type": str,
            "serviceEndpoint": Switch(DIDUrl.validate, Url()),
        },
        extra=ALLOW_EXTRA,
        required=True,
    )

    def __init__(self, id_: DIDUrl, type_: str, endpoint: str, **extra):
        """Initialize Service."""
        self._id = id_
        self._type = type_
        self._endpoint = endpoint
        self._extra = extra

    @property
    def id(self):
        """Return id."""
        return self._id

    @property
    def type(self):
        """Return type."""
        return self._type

    @property
    def endpoint(self):
        """Return endpoint."""
        return self._endpoint

    @property
    def extra(self):
        """Return extra."""
        return self._extra

    def serialize(self):
        """Return serialized representation of Service."""
        return {
            "id": str(self.id),
            "type": self.type,
            "serviceEndpoint": self.endpoint,
            **self.extra,
        }

    @classmethod
    @wrap_validation_error(ServiceValidationError, message="Failed to validate service")
    def validate(cls, value: dict):
        """Validate object against service."""
        return cls._validator(value)

    @classmethod
    @wrap_validation_error(
        ServiceValidationError, message="Failed to deserialize service"
    )
    def deserialize(cls, value: dict):
        """Deserialize into Service."""
        value = cls.validate(value)
        deserializer = Schema(
            {
                Into("id", "id_"): DIDUrl.parse,
                Into("type", "type_"): str,
                Into("serviceEndpoint", "endpoint"): str,
            },
            extra=ALLOW_EXTRA,
        )
        value = deserializer(value)
        return cls(**value)
Exemplo n.º 4
0
class DIDDocument:
    """Representation of DID Document."""

    properties = Properties(extra=ALLOW_EXTRA)

    def __init__(self,
                 id: Union[str, DID],
                 context: List[Any],
                 *,
                 also_known_as: List[str] = None,
                 controller: List[str] = None,
                 verification_method: List[VerificationMethod] = None,
                 authentication: VerificationRelationship = None,
                 assertion_method: VerificationRelationship = None,
                 key_agreement: VerificationRelationship = None,
                 capability_invocation: VerificationRelationship = None,
                 capability_delegation: VerificationRelationship = None,
                 service: List[Service] = None,
                 **extra):
        """Create DIDDocument."""
        self._id = id
        self._context = context
        self._also_known_as = also_known_as
        self._controller = controller
        self._verification_method = verification_method
        self._authentication = authentication
        self._assertion_method = assertion_method
        self._key_agreement = key_agreement
        self._capability_invocation = capability_invocation
        self._capability_delegation = capability_delegation
        self._service = service
        self.extra = extra

        self._index = {}
        self._index_resources()

    def _index_resources(self):
        """Index resources by ID.

        IDs are not guaranteed to be unique within the document.
        The first instance is stored in the index and subsequent id collisions
        are checked against the original. If they do not match, an error will
        be thrown.
        """
        def _indexer(item):
            if not item:
                # Attribute isn't set
                return
            if isinstance(item, DIDUrl):
                # We don't index references
                return
            if isinstance(item, list):
                for subitem in item:
                    _indexer(subitem)
                return
            if isinstance(item, VerificationRelationship):
                for subitem in item.items:
                    _indexer(subitem)
                return

            assert isinstance(item, (VerificationMethod, Service))
            if item.id in self._index and item != self._index[item.id]:
                raise IdentifiedResourceMismatch(
                    "ID {} already found in Index and Items do not match".
                    format(item.id))

            self._index[item.id] = item

        for item in (
                self.verification_method,
                self.authentication,
                self.assertion_method,
                self.key_agreement,
                self.capability_invocation,
                self.capability_delegation,
                self.service,
        ):
            _indexer(item)

    @property
    @properties.add(
        data_key="@context",
        required=True,
        validate=Switch(Url(), [Url()], dict, [dict]),
        serialize=unwrap_if_list_of_one,
        deserialize=single_to_list,
    )
    def context(self):
        """Return context."""
        return self._context

    @property
    @properties.add(
        required=True,
        validate=All(str, DID.validate),
        serialize=Coerce(str),
        deserialize=Coerce(DID),
    )
    def id(self):
        """Return id."""
        return self._id

    @property
    @properties.add(data_key="alsoKnownAs", validate=[str])
    def also_known_as(self):
        """Return also_known_as."""
        return self._also_known_as

    @property
    @properties.add(
        validate=Switch(All(str, DID.validate), [DID.validate]),
        serialize=All([Coerce(str)], unwrap_if_list_of_one),
        deserialize=All(single_to_list, [Coerce(DID)]),
    )
    def controller(self):
        """Return controller."""
        return self._controller

    @property
    @properties.add(
        data_key="verificationMethod",
        validate=[VerificationMethod.validate],
        serialize=[serialize],
        deserialize=[VerificationMethod.deserialize],
    )
    def verification_method(self):
        """Return verification_method."""
        return self._verification_method

    @property
    @properties.add(
        validate=VerificationRelationship.validate,
        serialize=serialize,
        deserialize=VerificationRelationship.deserialize,
    )
    def authentication(self):
        """Return authentication."""
        return self._authentication

    @property
    @properties.add(
        data_key="assertionMethod",
        validate=VerificationRelationship.validate,
        serialize=serialize,
        deserialize=VerificationRelationship.deserialize,
    )
    def assertion_method(self):
        """Return assertion_method."""
        return self._assertion_method

    @property
    @properties.add(
        data_key="keyAgreement",
        validate=VerificationRelationship.validate,
        serialize=serialize,
        deserialize=VerificationRelationship.deserialize,
    )
    def key_agreement(self):
        """Return key_agreement."""
        return self._key_agreement

    @property
    @properties.add(
        data_key="capabilityInvocation",
        validate=VerificationRelationship.validate,
        serialize=serialize,
        deserialize=VerificationRelationship.deserialize,
    )
    def capability_invocation(self):
        """Return capability_invocation."""
        return self._capability_invocation

    @property
    @properties.add(
        data_key="capabilityDelegation",
        validate=VerificationRelationship.validate,
        serialize=serialize,
        deserialize=VerificationRelationship.deserialize,
    )
    def capability_delegation(self):
        """Return capability_delegation."""
        return self._capability_delegation

    @property
    @properties.add(
        validate=[Service.validate],
        serialize=[serialize],
        deserialize=[Service.deserialize],
    )
    def service(self):
        """Return service."""
        return self._service

    def dereference(self, reference: Union[str, DIDUrl]):
        """Dereference a DID URL to a document resource."""
        if isinstance(reference, str):
            reference = DIDUrl.parse(reference)

        if reference not in self._index:
            raise ResourceIDNotFound(
                "ID {} not found in document".format(reference))
        return self._index[reference]

    @classmethod
    @wrap_validation_error(DIDDocumentValidationError,
                           message="Failed to validate DID Document")
    def validate(cls, value):
        """Validate against expected schema."""
        return cls.properties.validate(value)

    @wrap_validation_error(DIDDocumentError,
                           message="Failed to serialize DID Document")
    def serialize(self):
        """Serialize DID Document."""
        value = self.properties.serialize(self)
        return {**value, **self.extra}

    @classmethod
    @wrap_validation_error(DIDDocumentValidationError,
                           message="Failed to deserialize DID Document")
    def deserialize(cls, value: dict, options: Set[Option] = None):
        """Deserialize DID Document."""
        if options:
            value = DIDDocumentOption.apply(value, options)
        value = cls.validate(value)
        value = cls.properties.deserialize(value)
        return cls(**value)
Exemplo n.º 5
0
class DIDDocumentBuilder:
    """Builder for constructing DID Documents programmatically."""

    DEFAULT_CONTEXT = ["https://www.w3.org/ns/did/v1"]

    @validate_init(id_=Switch(All(str, Coerce(DID)), DID))
    def __init__(self,
                 id_: DID,
                 context: List[str] = None,
                 *,
                 also_known_as: List[str] = None,
                 controller: List[str] = None):
        """Initliaze builder."""
        self.id = id_
        self.context = context or self.DEFAULT_CONTEXT
        self.also_known_as = also_known_as
        self.controller = controller
        self.verification_methods = VerificationMethodBuilder(self.id)
        self.authentication = RelationshipBuilder(self.id, "auth")
        self.assertion_method = RelationshipBuilder(self.id, "assert")
        self.key_agreement = RelationshipBuilder(self.id, "key-agreement")
        self.capability_invocation = RelationshipBuilder(
            self.id, "capability-invocation")
        self.capability_delegation = RelationshipBuilder(
            self.id, "capability-delegation")
        self.services = ServiceBuilder(self.id)
        self.extra = {}

    @classmethod
    def from_doc(cls, doc: DIDDocument) -> "DIDDocumentBuilder":
        """Create a Builder from an existing DIDDocument."""
        builder = cls(
            id_=doc.did,
            context=doc.context,
            also_known_as=doc.also_known_as,
            controller=doc.controller,
        )
        builder.verification_methods = VerificationMethodBuilder(
            doc.did, methods=doc.verification_method)
        builder.authentication = RelationshipBuilder(
            doc.did, "auth", methods=doc.authentication)
        builder.assertion_method = RelationshipBuilder(
            doc.did, "assert", methods=doc.assertion_method)
        builder.key_agreement = RelationshipBuilder(doc.did,
                                                    "key-agreement",
                                                    methods=doc.key_agreement)
        builder.capability_invocation = RelationshipBuilder(
            doc.did,
            "capability-invocation",
            methods=doc.capability_invocation)
        builder.capability_delegation = RelationshipBuilder(
            doc.did,
            "capability-delegation",
            methods=doc.capability_delegation)
        builder.services = ServiceBuilder(doc.did, services=doc.service)
        return builder

    def build(self) -> DIDDocument:
        """Build document."""
        return DIDDocument(
            id=str(self.id),
            context=self.context,
            also_known_as=self.also_known_as,
            controller=self.controller,
            verification_method=self.verification_methods.methods or None,
            authentication=VerificationRelationship(
                self.authentication.methods) or None,
            assertion_method=VerificationRelationship(
                self.assertion_method.methods) or None,
            key_agreement=VerificationRelationship(self.key_agreement.methods)
            or None,
            capability_invocation=VerificationRelationship(
                self.capability_invocation.methods) or None,
            capability_delegation=VerificationRelationship(
                self.capability_delegation.methods) or None,
            service=self.services.services or None,
            **self.extra)