def create_role(txn_key, batch_key, role_name, role_id, metadata, admins, owners): """Create a BatchList with a CreateRole transaction in it. Args: txn_key (Key): The transaction signer's key pair. batch_key (Key): The batch signer's key pair. role_name (str): The name of the Role. role_id (str): A uuid that identifies this Role. metadata (str): Client supplied information that is not parsed. admins (list): A list of User ids of the Users who are admins of this Role. owners (list): A list of User ids of the Users who are owners of this Role. Returns: tuple BatchList, batch header_signature tuple """ create_role_payload = role_transaction_pb2.CreateRole(role_id=role_id, name=role_name, metadata=metadata, admins=admins, owners=owners) inputs = [ addresser.make_sysadmin_members_address(txn_key.public_key), addresser.make_role_attributes_address(role_id) ] inputs.extend([addresser.make_user_address(u) for u in admins]) inputs.extend([addresser.make_user_address(u) for u in owners]) inputs.extend([ addresser.make_role_admins_address(role_id=role_id, user_id=a) for a in admins ]) inputs.extend([ addresser.make_role_owners_address(role_id=role_id, user_id=o) for o in owners ]) outputs = [addresser.make_role_attributes_address(role_id)] outputs.extend([ addresser.make_role_admins_address(role_id=role_id, user_id=a) for a in admins ]) outputs.extend([ addresser.make_role_owners_address(role_id=role_id, user_id=o) for o in owners ]) rbac_payload = rbac_payload_pb2.RBACPayload( content=create_role_payload.SerializeToString(), message_type=rbac_payload_pb2.RBACPayload.CREATE_ROLE) return make_header_and_batch(rbac_payload, inputs, outputs, txn_key, batch_key)
def _handle_role_state_set(create_role, state): role_container = role_state_pb2.RoleAttributesContainer() role = role_container.role_attributes.add() role.role_id = create_role.role_id role.name = create_role.name role.metadata = create_role.metadata entries_to_set = [ StateEntry(address=addresser.make_role_attributes_address( create_role.role_id), data=role_container.SerializeToString()) ] pubkeys_by_address = {} for admin in list(create_role.admins): admin_address = addresser.make_role_admins_address( role_id=create_role.role_id, user_id=admin) if admin_address in pubkeys_by_address: pubkeys_by_address[admin_address].append(admin) else: pubkeys_by_address[admin_address] = [admin] for owner in list(create_role.owners): owner_address = addresser.make_role_owners_address( role_id=create_role.role_id, user_id=owner) if owner_address in pubkeys_by_address: pubkeys_by_address[owner_address].append(owner) else: pubkeys_by_address[owner_address] = [owner] state_returns = get_state(state, [ addresser.make_role_admins_address(role_id=create_role.role_id, user_id=a) for a in create_role.admins ] + [ addresser.make_role_owners_address(role_id=create_role.role_id, user_id=o) for o in create_role.owners ]) for addr, pubkeys in pubkeys_by_address.items(): try: state_entry = get_state_entry(state_returns, addr) container = role_state_pb2.RoleRelationshipContainer() container.ParseFromString(state_entry.data) except KeyError: container = role_state_pb2.RoleRelationshipContainer() _add_role_rel_to_container(container, create_role.role_id, pubkeys) entries_to_set.append( StateEntry(address=addr, data=container.SerializeToString())) set_state(state, entries_to_set)
def test_generated_role_owner_addr(self): """Tests the role owner address creation function as well as the address_is function. """ role_id = uuid4().hex owner_id = uuid4().hex address = addresser.make_role_owners_address(role_id, owner_id) self.assertEqual(len(address), addresser.ADDRESS_LENGTH, "The address is 70 characters") self.assertTrue(addresser.is_address(address), "The address is 70 character hexidecimal") self.assertTrue(addresser.namespace_ok(address), "The address has correct namespace prefix") self.assertTrue( addresser.is_family_address(address), "The address is 70 character hexidecimal with family prefix") self.assertEqual( addresser.address_is(address), AddressSpace.ROLES_OWNERS, "The address created must be a Role Attributes address.")
def test_determine_role_owner_addr(self): """Tests that a specific role_id and owner_id generates the expected role owner address, and thus is probably deterministic. """ role_id = '99968acb8f1a48b3a4bc21e2cd252e67' owner_id = '966ab67317234df489adb4bc1f517b88' expected_address = '9f444809326a1713a905b26359fc8d\ a2817c1a5f67de6f464701f0c10042da345d2893' address = addresser.make_role_owners_address(role_id, owner_id) self.assertEqual(len(address), addresser.ADDRESS_LENGTH, "The address is 70 characters") self.assertTrue(addresser.is_address(address), "The address is 70 character hexidecimal") self.assertTrue(addresser.namespace_ok(address), "The address has correct namespace prefix") self.assertTrue( addresser.is_family_address(address), "The address is 70 character hexidecimal with family prefix") self.assertEqual(address, expected_address, "The address is the one we expected it to be") self.assertEqual( addresser.address_is(address), AddressSpace.ROLES_OWNERS, "The address created must be a Role Attributes address.")
def propose_add_role_tasks(txn_key, batch_key, proposal_id, role_id, task_id, reason, metadata): propose_payload = role_transaction_pb2.ProposeAddRoleTask( proposal_id=proposal_id, role_id=role_id, task_id=task_id, reason=reason, metadata=metadata) inputs = [ addresser.make_proposal_address(role_id, task_id), addresser.make_role_tasks_address(role_id, task_id), addresser.make_role_owners_address(role_id, txn_key.public_key), addresser.make_role_attributes_address(role_id), addresser.make_task_attributes_address(task_id) ] outputs = [addresser.make_proposal_address(role_id, task_id)] rbac_payload = rbac_payload_pb2.RBACPayload( content=propose_payload.SerializeToString(), message_type=rbac_payload_pb2.RBACPayload.PROPOSE_ADD_ROLE_TASKS) return make_header_and_batch(rbac_payload, inputs, outputs, txn_key, batch_key)
def confirm_remove_role_members(txn_key, batch_key, proposal_id, role_id, user_id, reason): confirm_payload = role_transaction_pb2.ConfirmRemoveRoleMember( proposal_id=proposal_id, role_id=role_id, user_id=user_id, reason=reason) inputs = [ addresser.make_proposal_address(role_id, user_id), addresser.make_role_owners_address(role_id, txn_key.public_key) ] outputs = [ addresser.make_proposal_address(role_id, user_id), addresser.make_role_members_address(role_id, user_id) ] rbac_payload = rbac_payload_pb2.RBACPayload( content=confirm_payload.SerializeToString(), message_type=rbac_payload_pb2.RBACPayload.CONFIRM_REMOVE_ROLE_MEMBERS) return make_header_and_batch(rbac_payload, inputs, outputs, txn_key, batch_key)
def apply_propose_remove(header, payload, state): proposal_payload = role_transaction_pb2.ProposeRemoveRoleMember() proposal_payload.ParseFromString(payload.content) role_owners_address = addresser.make_role_owners_address( role_id=proposal_payload.role_id, user_id=proposal_payload.user_id) proposal_address = addresser.make_proposal_address( object_id=proposal_payload.role_id, related_id=proposal_payload.user_id) state_entries = validate_role_rel_proposal(header, proposal_payload, role_owners_address, state) if not no_open_proposal( state_entries=state_entries, object_id=proposal_payload.role_id, related_id=proposal_payload.user_id, proposal_address=proposal_address, proposal_type=proposal_state_pb2.Proposal.REMOVE_ROLE_OWNERS): raise InvalidTransaction( "There is already an open proposal for REMOVE_ROLE_OWNERS " "with role id {} and user id {}".format(proposal_payload.role_id, proposal_payload.user_id)) handle_propose_state_set( state_entries=state_entries, header=header, payload=proposal_payload, address=proposal_address, proposal_type=proposal_state_pb2.Proposal.REMOVE_ROLE_OWNERS, state=state)
def apply_reject(header, payload, state): reject_payload = role_transaction_pb2.RejectAddRoleAdmin() reject_payload.ParseFromString(payload.content) txn_signer_owners_address = addresser.make_role_owners_address( reject_payload.role_id, header.signer_public_key) state_entries = validate_role_admin_or_owner( header=header, confirm=reject_payload, txn_signer_rel_address=txn_signer_owners_address, state=state) handle_reject(state_entries, header, reject=reject_payload, state=state)
def apply_confirm(header, payload, state): confirm_payload = role_transaction_pb2.ConfirmAddRoleAdmin() confirm_payload.ParseFromString(payload.content) role_members_address = addresser.make_role_members_address( role_id=confirm_payload.role_id, user_id=confirm_payload.user_id) txn_signer_owners_address = addresser.make_role_owners_address( role_id=confirm_payload.role_id, user_id=header.signer_public_key) state_entries = validate_role_admin_or_owner( header=header, confirm=confirm_payload, txn_signer_rel_address=txn_signer_owners_address, state=state) handle_confirm_add(state_entries=state_entries, header=header, confirm=confirm_payload, role_rel_address=role_members_address, state=state)
def reject_add_role_members(txn_key, batch_key, proposal_id, role_id, user_id, reason): reject_payload = role_transaction_pb2.RejectAddRoleMember( proposal_id=proposal_id, role_id=role_id, user_id=user_id, reason=reason) inputs = [ addresser.make_proposal_address(role_id, user_id), addresser.make_role_owners_address(role_id, txn_key.public_key) ] outputs = [addresser.make_proposal_address(role_id, user_id)] rbac_payload = rbac_payload_pb2.RBACPayload( content=reject_payload.SerializeToString(), message_type=rbac_payload_pb2.RBACPayload.REJECT_ADD_ROLE_MEMBERS) return make_header_and_batch(rbac_payload, inputs, outputs, txn_key, batch_key)
def propose_remove_role_owners(txn_key, batch_key, proposal_id, role_id, user_id, reason, metadata): propose = role_transaction_pb2.ProposeRemoveRoleOwner( proposal_id=proposal_id, role_id=role_id, user_id=user_id, reason=reason, metadata=metadata) inputs = [ addresser.make_role_owners_address(role_id, user_id), addresser.make_role_attributes_address(role_id=role_id), addresser.make_user_address(user_id=user_id), addresser.make_proposal_address(role_id, user_id) ] outputs = [addresser.make_proposal_address(role_id, user_id)] rbac_payload = rbac_payload_pb2.RBACPayload( content=propose.SerializeToString(), message_type=rbac_payload_pb2.RBACPayload.PROPOSE_REMOVE_ROLE_OWNERS) return make_header_and_batch(rbac_payload, inputs, outputs, txn_key, batch_key)
def validate_role_task_proposal(header, propose, state): """Applies state validation rules for ADDRoleTaskProposal. - The Role exists. - The Task exists. - The Transaction was signed by a Role Owner. - There is no open Proposal for the same change. - The task is not already part of the Role. Args: header (TransactionHeader): The propobuf transaction header. propose (ProposeAddRoleTask): The protobuf transaction. state (Context): A connection to the validator to ask about state. Returns: (list of StateEntry) """ role_address = addresser.make_role_attributes_address(propose.role_id) task_address = addresser.make_task_attributes_address(propose.task_id) proposal_address = addresser.make_proposal_address( propose.role_id, propose.task_id) txn_signer_role_owner_address = addresser.make_role_owners_address( role_id=propose.role_id, user_id=header.signer_pubkey) role_tasks_address = addresser.make_role_tasks_address(propose.role_id, propose.task_id) state_entries = get_state( state=state, addresses=[role_address, task_address, proposal_address, role_tasks_address, txn_signer_role_owner_address]) validate_identifier_is_role( state_entries=state_entries, address=role_address, identifier=propose.role_id) validate_identifier_is_task( state_entries=state_entries, identifier=propose.task_id, address=task_address) try: role_task_entry = get_state_entry(state_entries, role_tasks_address) role_task_container = return_role_rel_container(role_task_entry) if is_in_role_rel_container(role_task_container, role_id=propose.role_id, identifier=propose.task_id): raise InvalidTransaction("Role {} already contains task {}".format( propose.role_id, propose.task_id)) except KeyError: # The Task is not in the RoleTask state pass try: role_owner_entry = get_state_entry( state_entries, txn_signer_role_owner_address) role_owner_container = return_role_rel_container(role_owner_entry) if not is_in_role_rel_container(role_owner_container, role_id=propose.role_id, identifier=header.signer_pubkey): raise InvalidTransaction( "Txn signer {} is not a role owner".format( header.signer_pubkey)) except KeyError: raise InvalidTransaction( "Txn signer {} is not a role owner.".format(header.signer_pubkey)) if not no_open_proposal( state_entries=state_entries, object_id=propose.role_id, related_id=propose.task_id, proposal_address=proposal_address, proposal_type=proposal_state_pb2.Proposal.ADD_ROLE_TASKS): raise InvalidTransaction( "There is already an open proposal to add task {} to " "role {}".format(propose.task_id, propose.role_id)) return state_entries
def test_role_addresses(self): """Tests the Role address creation functions as well as the address_is function. Notes: 1. Create an address of a particular type: - Role Attributes - Role Members - Role Owners - Role Admins - Role Tasks 2. Assert that address_is returns the correct address type. """ role_address = addresser.make_role_attributes_address( role_id=uuid4().hex) self.assertEqual(len(role_address), 70, "The address is a well-formed address.") self.assertEqual( addresser.address_is(role_address), AddressSpace.ROLES_ATTRIBUTES, "The Role Attributes address created must " "be found to be a Role Attributes address.") role_members_address = addresser.make_role_members_address( role_id=uuid4().hex, user_id=uuid4().hex) self.assertEqual(len(role_members_address), 70, "The address is a well-formed address.") self.assertEqual( addresser.address_is(role_members_address), AddressSpace.ROLES_MEMBERS, "The Role Members address created must be " "found to be a Role Members address.") role_owners_address = addresser.make_role_owners_address( role_id=uuid4().hex, user_id=uuid4().hex) self.assertEqual(len(role_owners_address), 70, "The address is a well-formed address.") self.assertEqual( addresser.address_is(role_owners_address), AddressSpace.ROLES_OWNERS, "The Role Owners address created must be found to be " "a Role Members address.") role_admins_address = addresser.make_role_admins_address( role_id=uuid4().hex, user_id=uuid4().hex) self.assertEqual(len(role_admins_address), 70, "The address is a well-formed address.") self.assertEqual( addresser.address_is(role_admins_address), AddressSpace.ROLES_ADMINS, "The Role Admins address created must be " "found to be a Role Admins address.") role_tasks_address = addresser.make_role_tasks_address( role_id=uuid4().hex, task_id=uuid4().hex) self.assertEqual(len(role_tasks_address), 70, "The address is a well-formed address.") self.assertEqual( addresser.address_is(role_tasks_address), AddressSpace.ROLES_TASKS, "The Role Tasks address created must be " "found to be a Role Tasks address.")