membership.py 5.57 KB
Newer Older
inso's avatar
inso committed
1 2 3 4 5 6
"""
Created on 2 déc. 2014

@author: inso
"""
import re
7 8
from typing import TypeVar, Type

9
from .block_uid import BlockUID
10
from .document import Document, MalformedDocumentError
11
from ..constants import BLOCK_UID_REGEX, SIGNATURE_REGEX, PUBKEY_REGEX
12

13 14 15
# required to type hint cls in classmethod
MembershipType = TypeVar('MembershipType', bound='Membership')

inso's avatar
inso committed
16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32

class Membership(Document):
    """
.. note:: A membership document is specified by the following format :

    | Version: VERSION
    | Type: Membership
    | Currency: CURRENCY_NAME
    | Issuer: ISSUER
    | Block: NUMBER-HASH
    | Membership: MEMBERSHIP_TYPE
    | UserID: USER_ID
    | CertTS: CERTIFICATION_TS

    """

    # PUBLIC_KEY:SIGNATURE:NUMBER:HASH:TIMESTAMP:USER_ID
33 34
    re_inline = re.compile(
        "({pubkey_regex}):({signature_regex}):({ms_block_uid_regex}):({identity_block_uid_regex}):([^\n]+)\n"
35 36 37
        .format(pubkey_regex=PUBKEY_REGEX, signature_regex=SIGNATURE_REGEX,
                ms_block_uid_regex=BLOCK_UID_REGEX,
                identity_block_uid_regex=BLOCK_UID_REGEX))
inso's avatar
inso committed
38
    re_type = re.compile("Type: (Membership)")
39 40
    re_issuer = re.compile("Issuer: ({pubkey_regex})\n".format(pubkey_regex=PUBKEY_REGEX))
    re_block = re.compile("Block: ({block_uid_regex})\n".format(block_uid_regex=BLOCK_UID_REGEX))
inso's avatar
inso committed
41 42
    re_membership_type = re.compile("Membership: (IN|OUT)")
    re_userid = re.compile("UserID: ([^\n]+)\n")
43
    re_certts = re.compile("CertTS: ({block_uid_regex})\n".format(block_uid_regex=BLOCK_UID_REGEX))
inso's avatar
inso committed
44 45 46 47 48 49 50 51 52 53

    fields_parsers = {**Document.fields_parsers, **{
        "Type": re_type,
        "Issuer": re_issuer,
        "Block": re_block,
        "Membership": re_membership_type,
        "UserID": re_userid,
        "CertTS": re_certts
    }}

54 55
    def __init__(self, version: int, currency: str, issuer: str, membership_ts: BlockUID,
                 membership_type: str, uid: str, identity_ts: BlockUID, signature: str) -> None:
inso's avatar
inso committed
56
        """
Vincent Texier's avatar
Vincent Texier committed
57 58
        Create a membership document

59
        :param version: Version of the document
Vincent Texier's avatar
Vincent Texier committed
60 61
        :param currency: Name of the currency
        :param issuer: Public key of the issuer
62
        :param membership_ts: BlockUID of this membership
Vincent Texier's avatar
Vincent Texier committed
63
        :param membership_type: "IN" or "OUT" to enter or quit the community
64 65 66
        :param uid: Unique identifier of the identity
        :param identity_ts:  BlockUID of the identity
        :param signature: Signature of the document
inso's avatar
inso committed
67 68 69 70 71 72 73 74 75
        """
        super().__init__(version, currency, [signature])
        self.issuer = issuer
        self.membership_ts = membership_ts
        self.membership_type = membership_type
        self.uid = uid
        self.identity_ts = identity_ts

    @classmethod
76 77 78 79 80 81 82 83 84 85 86
    def from_inline(cls: Type[MembershipType], version: int, currency: str, membership_type: str,
                    inline: str) -> MembershipType:
        """
        Return Membership instance from inline format

        :param version: Version of the document
        :param currency: Name of the currency
        :param membership_type: "IN" or "OUT" to enter or exit membership
        :param inline: Inline string format
        :return:
        """
inso's avatar
inso committed
87 88 89 90 91 92 93 94 95 96 97
        data = Membership.re_inline.match(inline)
        if data is None:
            raise MalformedDocumentError("Inline membership ({0})".format(inline))
        issuer = data.group(1)
        signature = data.group(2)
        membership_ts = BlockUID.from_str(data.group(3))
        identity_ts = BlockUID.from_str(data.group(4))
        uid = data.group(5)
        return cls(version, currency, issuer, membership_ts, membership_type, uid, identity_ts, signature)

    @classmethod
98 99 100 101 102 103 104
    def from_signed_raw(cls: Type[MembershipType], signed_raw: str) -> MembershipType:
        """
        Return Membership instance from signed raw format

        :param signed_raw: Signed raw format string
        :return:
        """
inso's avatar
inso committed
105
        lines = signed_raw.splitlines(True)
inso's avatar
inso committed
106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137
        n = 0

        version = int(Membership.parse_field("Version", lines[n]))
        n += 1

        Membership.parse_field("Type", lines[n])
        n += 1

        currency = Membership.parse_field("Currency", lines[n])
        n += 1

        issuer = Membership.parse_field("Issuer", lines[n])
        n += 1

        membership_ts = BlockUID.from_str(Membership.parse_field("Block", lines[n]))
        n += 1

        membership_type = Membership.parse_field("Membership", lines[n])
        n += 1

        uid = Membership.parse_field("UserID", lines[n])
        n += 1

        identity_ts = BlockUID.from_str(Membership.parse_field("CertTS", lines[n]))
        n += 1

        signature = Membership.parse_field("Signature", lines[n])
        n += 1

        return cls(version, currency, issuer, membership_ts,
                   membership_type, uid, identity_ts, signature)

138 139 140 141 142 143
    def raw(self) -> str:
        """
        Return signed raw format string of the Membership instance

        :return:
        """
inso's avatar
inso committed
144 145 146 147 148 149 150 151 152
        return """Version: {0}
Type: Membership
Currency: {1}
Issuer: {2}
Block: {3}
Membership: {4}
UserID: {5}
CertTS: {6}
""".format(self.version,
153 154 155 156 157 158
           self.currency,
           self.issuer,
           self.membership_ts,
           self.membership_type,
           self.uid,
           self.identity_ts)
inso's avatar
inso committed
159

160 161 162 163 164
    def inline(self) -> str:
        """
        Return inline string format of the Membership instance
        :return:
        """
inso's avatar
inso committed
165
        return "{0}:{1}:{2}:{3}:{4}".format(self.issuer,
166 167 168 169
                                            self.signatures[0],
                                            self.membership_ts,
                                            self.identity_ts,
                                            self.uid)