class BaseManager:
    def __init__(self):
        """Objects and methods shared across *_manager libraries"""
        self.batch = Batcher()
        self.client = ClientSync()

    def send(self,
             signer_keypair,
             object_id,
             payload,
             do_send=True,
             do_get=False):
        """Sends a payload to the transaction processor"""
        if not isinstance(signer_keypair, Key):
            raise TypeError("Expected signer_keypair to be a Key")
        if not isinstance(payload, RBACPayload):
            raise TypeError("Expected payload to be an RBACPayload")

        transaction, batch, batch_list, batch_request = self.batch.make(
            payload=payload, signer_keypair=signer_keypair)

        if not do_send:
            return None, None, transaction, batch, batch_list, batch_request

        status = self.client.send_batches_get_status(batch_list=batch_list)
        if not do_get:
            return None, status, transaction, batch, batch_list, batch_request

        got = self.get(object_id=object_id)
        return got, status, transaction, batch, batch_list, batch_request
class BaseMessage:
    def __init__(self):
        """Objects and methods shared across message libraries"""
        self.batch = Batcher()
        self.client = ClientSync()
        self.state = StateClient()

    def getattr(self, item, attribute):
        """A version of getattr that will return None if attributes
        is not found on the item"""
        if hasattr(item, attribute):
            return getattr(item, attribute)
        return None

    @property
    def name(self):
        raise NotImplementedError("Class must implement this property")

    @property
    def names(self):
        return self.name + "s"

    @property
    def message_type(self):
        raise NotImplementedError("Class must implement this method")

    @property
    def message_proto(self):
        raise NotImplementedError("Class must implement this method")

    @property
    def container_proto(self):
        raise NotImplementedError("Class must implement this method")

    @property
    def state_proto(self):
        raise NotImplementedError("Class must implement this method")

    @property
    def message_fields_not_in_state(self):
        """Fields that are on the message but not stored on the state object"""
        return []

    def address(self, object_id, target_id):
        raise NotImplementedError("Class must implement this method")

    def make(self, object_id):
        raise NotImplementedError("Class must implement this method")

    def make_addresses(self, message, signer_keypair):
        raise NotImplementedError("Class must implement this method")

    def base_validate(self, message, signer=None):
        if not isinstance(message, self.message_proto):
            raise TypeError("Expected message to be {}".format(
                self.message_proto))
        if (signer is not None and not isinstance(signer, Key)
                and not (isinstance(signer, str)
                         and PUBLIC_KEY_PATTERN.match(signer))):
            raise TypeError("Expected signer to be a keypair or a public key")
        if isinstance(signer, Key):
            signer = signer.public_key
        return signer

    def base_validate_state(self, state, message, signer):
        if signer is None:
            raise ValueError("Signer is required")
        if message is None:
            raise ValueError("Message is required")
        if not isinstance(signer, str) and PUBLIC_KEY_PATTERN.match(signer):
            raise TypeError("Expected signer to be a public key")
        if state is None:
            raise ValueError("State is required")

    def validate(self, message, signer=None):
        signer = self.base_validate(message=message, signer=signer)

    def make_payload(self, message, signer_keypair=None):
        """Make a payload for the given message type"""
        self.validate(message=message, signer=signer_keypair)

        message_type = self.message_type
        inputs, outputs = self.make_addresses(message=message,
                                              signer_keypair=signer_keypair)
        return self.batch.make_payload(message=message,
                                       message_type=message_type,
                                       inputs=inputs,
                                       outputs=outputs)

    def create(self, signer_keypair, message, object_id=None, target_id=None):
        """Send a message to the blockchain"""
        self.validate(message=message, signer=signer_keypair)

        return self.send(
            signer_keypair=signer_keypair,
            payload=self.make_payload(message=message,
                                      signer_keypair=signer_keypair),
            object_id=object_id,
            target_id=target_id,
        )

    def send(self, signer_keypair, payload, object_id=None, target_id=None):
        """Sends a payload to the transaction processor"""
        if not isinstance(signer_keypair, Key):
            raise TypeError("Expected signer_keypair to be a Key")
        if not isinstance(payload, protobuf.rbac_payload_pb2.RBACPayload):
            raise TypeError("Expected payload to be an RBACPayload")

        _, _, batch_list, _ = self.batch.make(payload=payload,
                                              signer_keypair=signer_keypair)
        got = None

        status = self.client.send_batches_get_status(batch_list=batch_list)

        if object_id is not None:
            got = self.get(object_id=object_id, target_id=target_id)

        return got, status

    def _find_in_container(self,
                           container,
                           address,
                           object_id,
                           target_id=None):
        items = list(getattr(container, self.names))
        if not items:
            return None
        if len(items) > 1:
            LOGGER.warning(
                "%s container for %s target %s has more than one record at address %s",
                self.name,
                object_id,
                target_id,
                address,
            )
        for item in items:
            if (self.getattr(item, "object_id") == object_id
                    and self.getattr(item, "target_id") == target_id):
                return item
            if self.getattr(item, self.name +
                            "_id") == object_id and target_id is None:
                return item
        LOGGER.warning(
            "%s not found in container for %s target %s at address %s",
            self.name,
            object_id,
            target_id,
            address,
        )
        return None

    def get(self, object_id, target_id=None):
        """Gets an address from the blockchain from the API"""
        address = self.address(object_id=object_id, target_id=target_id)
        container = self.container_proto()
        container.ParseFromString(self.client.get_address(address=address))
        return self._find_in_container(
            container=container,
            address=address,
            object_id=object_id,
            target_id=target_id,
        )

    def get_state(self, state, object_id, target_id=None):
        """Gets an address from the blockchain state from the state object"""
        address = self.address(object_id=object_id, target_id=target_id)
        container = self.container_proto()

        results = self.state.get_address(state=state, address=address)
        if not list(results):
            return None

        container.ParseFromString(results[0].data)
        return self._find_in_container(
            container=container,
            address=address,
            object_id=object_id,
            target_id=target_id,
        )

    def exists_state(self, state, object_id, target_id=None):
        """Checks an object exists in the blockchain"""
        got = self.get_state(state=state,
                             object_id=object_id,
                             target_id=target_id)
        return bool(got is not None)
Beispiel #3
0
class UserManager:
    def __init__(self):
        self.batch = Batcher()
        self.client = ClientSync()

    def make(self,
             user_id,
             name,
             user_name=None,
             email=None,
             metadata=None,
             manager_id=None):
        return user_state_pb2.User(
            user_id=user_id,
            name=name,
            # user_name=user_name,
            # email=email,
            metadata=metadata,
            manager_id=manager_id,
        )

    def make_with_key(
        self,
        name,
        user_id=None,
        user_name=None,
        email=None,
        metadata=None,
        manager_id=None,
    ):
        keypair = Key()
        if user_id is None:
            user_id = keypair.public_key
        user = self.make(
            user_id=user_id,
            name=name,
            user_name=user_name,
            email=email,
            metadata=metadata,
            manager_id=manager_id,
        )
        return user, keypair

    def create(self,
               signer_keypair,
               user,
               do_batch=True,
               do_send=True,
               do_get=False):
        if not isinstance(signer_keypair, Key):
            raise TypeError("Expected signer_keypair to be a Key")
        if not isinstance(user, user_state_pb2.User):
            raise TypeError(
                "Expected user to be a user_state_pb2.User, use make first")

        message = user_transaction_pb2.CreateUser(
            user_id=user.user_id,
            # user_name=user_name,
            name=user.name,
            metadata=user.metadata,
        )
        inputs = [make_user_address(user_id=user.user_id)]

        if user.manager_id:
            message.manager_id = user.manager_id
            inputs.append(make_user_address(user_id=user.manager_id))

        transaction = self.batch.make_transaction(
            message=message,
            message_type=RBACPayload.CREATE_USER,
            inputs=inputs,
            outputs=inputs,
            signer_keypair=signer_keypair,
        )

        if not do_batch:
            return transaction

        batch = self.batch.make_batch(transaction=transaction)
        if not do_send:
            return batch

        batch_list = self.batch.batch_to_list(batch)
        status = self.client.send_batches_get_status(batch_list=batch_list)
        if not do_get:
            return status

        return self.get(user_id=user.user_id)

    def get(self, user_id):
        address = make_user_address(user_id=user_id)
        user_container = user_state_pb2.UserContainer()
        user_container.ParseFromString(
            self.client.get_address(address=address))
        users = list(user_container.users)
        if len(users) == 0:
            return None
        elif len(users) > 1:
            LOGGER.warning(
                "user container at address %s has more than one record, looking for %s",
                address,
                user_id,
            )
        for user in users:
            if user.user_id == user_id:
                return user
        LOGGER.warning("user %s not found in container address %s", user_id,
                       address)
        return None
def process(rec, conn):
    """ Process inbound queue records
    """
    try:
        # Changes members from distinguished name to next_id for roles
        if "members" in rec["data"]:
            rec = translate_field_to_next(rec, "members")
        if "owners" in rec["data"]:
            rec = translate_field_to_next(rec, "owners")

        add_transaction(rec)
        if "batch" not in rec or not rec["batch"]:
            r.table("inbound_queue").get(rec["id"]).delete().run(conn)
            rec["sync_direction"] = "inbound"
            r.table("sync_errors").insert(rec).run(conn)
            return

        batch = batch_pb2.Batch()
        batch.ParseFromString(rec["batch"])
        batch_list = batch_to_list(batch=batch)
        client = ClientSync()
        status = client.send_batches_get_status(batch_list=batch_list)
        while status[0]["status"] == "PENDING":
            LOGGER.info("Batch status is %s", status)
            status = client.status_recheck(batch_list)
        if status[0]["status"] == "COMMITTED":
            if rec["data_type"] == "user":
                insert_to_user_mapping(rec)
            if "metadata" in rec and rec["metadata"]:
                data = {
                    "address": rec["address"],
                    "object_type": rec["object_type"],
                    "object_id": rec["object_id"],
                    "provider_id": rec["provider_id"],
                    "created_at": r.now(),
                    "updated_at": r.now(),
                    **rec["metadata"],
                }

                query = (
                    r.table("metadata").get(
                        rec["address"]).replace(lambda doc: r.branch(
                            # pylint: disable=singleton-comparison
                            (doc == None),  # noqa
                            r.expr(data),
                            doc.merge({
                                "metadata": rec["metadata"],
                                "updated_at": r.now()
                            }),
                        )))
                result = query.run(conn)
                if (not result["inserted"]
                        and not result["replaced"]) or result["errors"] > 0:
                    LOGGER.warning("error updating metadata record:\n%s\n%s",
                                   result, query)
            rec["sync_direction"] = "inbound"
            r.table("changelog").insert(rec).run(conn)
            r.table("inbound_queue").get(rec["id"]).delete().run(conn)
        else:
            rec["error"] = get_status_error(status)
            rec["sync_direction"] = "inbound"
            r.table("sync_errors").insert(rec).run(conn)
            r.table("inbound_queue").get(rec["id"]).delete().run(conn)

    except Exception as err:  # pylint: disable=broad-except
        LOGGER.exception("%s exception processing inbound record:\n%s",
                         type(err).__name__, rec)
        LOGGER.exception(err)
class RoleManager:
    def __init__(self):
        self.batch = Batcher()
        self.client = ClientSync()

    def make(self, role_id, name, metadata=None, admins=None, owners=None):
        return role_transaction_pb2.CreateRole(
            role_id=role_id, name=name, metadata=metadata, admins=admins, owners=owners
        )

    def create(self, signer_keypair, role, do_batch=True, do_send=True, do_get=False):
        if not isinstance(signer_keypair, Key):
            raise TypeError("Expected signer_keypair to be a Key")
        if not isinstance(role, role_transaction_pb2.CreateRole):
            raise TypeError(
                "Expected role to be a role_transaction_pb2.CreateRole, use make first"
            )

        inputs = [
            addresser.make_sysadmin_members_address(signer_keypair.public_key),
            addresser.make_role_attributes_address(role.role_id),
        ]
        inputs.extend([addresser.make_user_address(u) for u in role.admins])
        inputs.extend([addresser.make_user_address(u) for u in role.owners])
        inputs.extend(
            [
                addresser.make_role_admins_address(role_id=role.role_id, user_id=a)
                for a in role.admins
            ]
        )
        inputs.extend(
            [
                addresser.make_role_owners_address(role_id=role.role_id, user_id=o)
                for o in role.owners
            ]
        )

        transaction = self.batch.make_transaction(
            message=role,
            message_type=RBACPayload.CREATE_ROLE,
            inputs=inputs,
            outputs=inputs,
            signer_keypair=signer_keypair,
        )

        if not do_batch:
            return transaction

        batch = self.batch.make_batch(transaction=transaction)
        if not do_send:
            return batch

        batch_list = self.batch.batch_to_list(batch)
        status = self.client.send_batches_get_status(batch_list=batch_list)
        if not do_get:
            return status

        return self.get(role_id=role.role_id)

    def get(self, role_id):
        container = role_state_pb2.RoleAttributesContainer()
        address = addresser.make_role_attributes_address(role_id=role_id)
        container.ParseFromString(self.client.get_address(address=address))
        items = list(container.role_attributes)
        if len(items) == 0:
            return None
        elif len(items) > 1:
            LOGGER.warning(
                "role container for %s at address %s has more than one role record",
                role_id,
                address,
            )
        return items[0]

    def check_owner(self, role_id, user_id):
        container = role_state_pb2.RoleRelationshipContainer()
        address = addresser.make_role_owners_address(role_id=role_id, user_id=user_id)
        container.ParseFromString(self.client.get_address(address=address))
        items = list(container.relationships)
        if len(items) == 0:
            return False
        elif len(items) > 1:
            LOGGER.warning(
                "role %s owners container for user %s at address %s has more than one record",
                role_id,
                user_id,
                address,
            )
        item = items[0]
        identifiers = list(item.identifiers)
        if len(identifiers) == 0:
            LOGGER.warning(
                "role %s owners container for user %s at address %s has no identifiers",
                role_id,
                user_id,
                address,
            )
            return False
        if len(identifiers) > 1:
            LOGGER.warning(
                "role %s owners container for user %s at address %s has more than one identifier",
                role_id,
                user_id,
                address,
            )
        return bool(user_id in item.identifiers)

    def check_admin(self, role_id, user_id):
        container = role_state_pb2.RoleRelationshipContainer()
        address = addresser.make_role_admins_address(role_id=role_id, user_id=user_id)
        container.ParseFromString(self.client.get_address(address=address))
        items = list(container.relationships)
        if len(items) == 0:
            return False
        elif len(items) > 1:
            LOGGER.warning(
                "role %s admins container for user %s at address %s has more than one record",
                role_id,
                user_id,
                address,
            )
        item = items[0]
        identifiers = list(item.identifiers)
        if len(identifiers) == 0:
            LOGGER.warning(
                "role %s admins container for user %s at address %s has no identifiers",
                role_id,
                user_id,
                address,
            )
            return False
        if len(identifiers) > 1:
            LOGGER.warning(
                "role %s admins container for user %s at address %s has more than one identifier",
                role_id,
                user_id,
                address,
            )
        return bool(user_id in item.identifiers)