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 _validate_state_and_return_user(header, user_proposal, state): """Validate that 1. There is no other open proposal for the manager change 2. The user is a User 3. the manager is a User 4. The manager is the signer of the transaction. Args: header (TransactionHeader): The transaction header. user_proposal (ProposeUpdateUserManager): The transaction that makes the proposal to update the user's manager. state (Context): The way to set and get values from state. """ prop_state_entries = _validate_unique_proposal( header, user_proposal, state) user_address = addresser.make_user_address( user_id=user_proposal.user_id) user_state_entries = get_state( state, [user_address]) validate_identifier_is_user( state_entries=user_state_entries, identifier=user_proposal.user_id, address=user_address) manager_address = addresser.make_user_address( user_id=user_proposal.new_manager_id) manager_state_entries = get_state( state, [manager_address]) validate_identifier_is_user( state_entries=manager_state_entries, identifier=user_proposal.new_manager_id, address=manager_address) user_state_entry = get_state_entry( user_state_entries, user_address) user_container = return_user_container(user_state_entry) _validate_manager_is_signer(header, user_container, user_proposal.user_id) return prop_state_entries
def validate_manager_state(header, create_user, state): manager_entries = get_state( state, [addresser.make_user_address(create_user.manager_id)]) if not manager_entries: raise InvalidTransaction("User id {} listed as manager is not " "in state.".format(create_user.manager_id)) state_entry = get_state_entry( manager_entries, addresser.make_user_address(user_id=create_user.manager_id)) manager_container = return_user_container(state_entry) if not is_in_user_container(manager_container, create_user.manager_id): raise InvalidTransaction( "user id {} listed as manager is not within the User container " "in state".format(create_user.manager_id))
def test_deterministic_user_address(self): """Tests that a specific user_id generates the expected user address, and thus is probably deterministic. """ ident = '966ab67317234df489adb4bc1f517b88' expected_address = '9f444847e7570f3f6f7d2c1635f6de\ eabc1f4d78d9d42b64b70e1819f244138c1e38d6' address = addresser.make_user_address(ident) 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.USER, "The User address created must be found to be a User address.")
def propose_add_task_admins(txn_key, batch_key, proposal_id, task_id, user_id, reason, metadata): propose_payload = task_transaction_pb2.ProposeAddTaskAdmin( proposal_id=proposal_id, task_id=task_id, user_id=user_id, reason=reason, metadata=metadata) inputs = [addresser.make_user_address(user_id), addresser.make_task_admins_address( task_id=task_id, user_id=user_id), addresser.make_proposal_address(task_id, user_id), addresser.make_task_attributes_address(task_id)] outputs = [addresser.make_proposal_address(task_id, user_id)] rbac_payload = rbac_payload_pb2.RBACPayload( content=propose_payload.SerializeToString(), message_type=rbac_payload_pb2.RBACPayload.PROPOSE_ADD_TASK_ADMINS) return make_header_and_batch( rbac_payload, inputs, outputs, txn_key, batch_key)
def propose_remove_task_owners(txn_key, batch_key, proposal_id, task_id, user_id, reason, metadata): propose = task_transaction_pb2.ProposeRemoveTaskOwner( proposal_id=proposal_id, task_id=task_id, user_id=user_id, reason=reason, metadata=metadata) inputs = [addresser.make_user_address(user_id), addresser.make_task_owners_address( task_id=task_id, user_id=user_id), addresser.make_proposal_address(task_id, user_id), addresser.make_task_attributes_address(task_id)] outputs = [addresser.make_proposal_address(task_id, user_id)] rbac_payload = rbac_payload_pb2.RBACPayload( content=propose.SerializeToString(), message_type=rbac_payload_pb2.RBACPayload.PROPOSE_REMOVE_TASK_OWNERS) return make_header_and_batch( rbac_payload, inputs, outputs, txn_key, batch_key)
def validate_task_rel_proposal(header, propose, rel_address, state): """Validates that the User exists, the Task exists, and the User is not in the Task's relationship specified by rel_address. Args: header (TransactionHeader): The transaction header. propose (ProposeAddTask_____): The Task relationship proposal. rel_address (str): The Task relationship address produced by the Task and the User. state (sawtooth_sdk.Context): The way to communicate to the validator the state gets and sets. Returns: (dict of addresses) """ user_address = addresser.make_user_address(propose.user_id) task_address = addresser.make_task_attributes_address(propose.task_id) proposal_address = addresser.make_proposal_address( object_id=propose.task_id, related_id=propose.user_id) state_entries = get_state(state, [user_address, task_address, proposal_address, rel_address]) validate_identifier_is_user(state_entries, identifier=propose.user_id, address=user_address) user_entry = get_state_entry(state_entries, user_address) user = get_user_from_container( return_user_container(user_entry), propose.user_id) if header.signer_public_key not in [user.user_id, user.manager_id]: raise InvalidTransaction( "Txn signer {} is not the user or the user's " "manager {}".format(header.signer_public_key, [user.user_id, user.manager_id])) validate_identifier_is_task(state_entries, identifier=propose.task_id, address=task_address) try: task_admins_entry = get_state_entry(state_entries, rel_address) task_rel_container = return_task_rel_container(task_admins_entry) if is_in_task_rel_container( task_rel_container, propose.task_id, propose.user_id): raise InvalidTransaction( "User {} is already in the Role {} " "relationship".format(propose.user_id, propose.task_id)) except KeyError: # The task rel container doesn't exist so no task relationship exists pass return state_entries
def validate_list_of_user_are_users(state_return, admins): for address, user_id in [(addresser.make_user_address(a), a) for a in admins]: validate_identifier_is_user( state_entries=state_return, identifier=user_id, address=address)
def handle_confirm_state_set(container, proposal, closer, reason, address, user_id, new_manager_id, state): proposal.status = proposal_state_pb2.Proposal.CONFIRMED proposal.closer = closer proposal.close_reason = reason set_state(state, { address: container.SerializeToString() }) user_address = addresser.make_user_address(user_id) state_entries = get_state(state, [user_address]) state_entry = get_state_entry( state_entries=state_entries, address=user_address) user_container = return_user_container(state_entry) user = get_user_from_container(user_container, user_id) user.manager_id = new_manager_id set_state(state, { user_address: user_container.SerializeToString() })
def create_user(txn_key, batch_key, name, user_name, user_id, metadata, manager_id=None): """Create a BatchList with a CreateUser RBAC transaction. Args: txn_key (Key): The transaction signer's public/private key pair. batch_key (Key): The batch signer's public/private key pair. user_name (str): The user name of the User. user_id (str): The User's public key. metadata (str): Client supplied metadata. manager_id (str): The optional id of the manager of this User. Returns: tuple The CreateUser BatchList as the zeroth element and the batch header_signature as the first element. """ create_user_payload = user_transaction_pb2.CreateUser( name=name, user_name=user_name, user_id=user_id, metadata=metadata) inputs = [addresser.make_user_address(user_id=user_id)] outputs = [addresser.make_user_address(user_id=user_id)] if manager_id: create_user_payload.manager_id = manager_id inputs.append(addresser.make_user_address(user_id=manager_id)) outputs.append(addresser.make_user_address(user_id=manager_id)) rbac_payload = rbac_payload_pb2.RBACPayload( content=create_user_payload.SerializeToString(), message_type=rbac_payload_pb2.RBACPayload.CREATE_USER) return make_header_and_batch( rbac_payload, inputs, outputs, txn_key, batch_key)
def create_task(txn_key, batch_key, task_id, task_name, admins, owners, metadata): create_payload = task_transaction_pb2.CreateTask( task_id=task_id, name=task_name) create_payload.admins.extend(admins) inputs = [addresser.make_task_attributes_address(task_id=task_id), addresser.make_sysadmin_members_address(txn_key.public_key)] inputs.extend([addresser.make_user_address(user_id=u) for u in admins]) inputs.extend([addresser.make_task_admins_address(task_id, u) for u in admins]) outputs = [addresser.make_task_attributes_address(task_id=task_id)] outputs.extend([addresser.make_task_admins_address(task_id, u) for u in admins]) if owners: create_payload.owners.extend(owners) inputs.extend([addresser.make_user_address(user_id=u) for u in owners]) inputs.extend([addresser.make_task_owners_address(task_id, u) for u in owners]) outputs.extend([addresser.make_task_owners_address(task_id, u) for u in owners]) rbac_payload = rbac_payload_pb2.RBACPayload( content=create_payload.SerializeToString(), message_type=rbac_payload_pb2.RBACPayload.CREATE_TASK) return make_header_and_batch( rbac_payload, inputs, outputs, txn_key, batch_key)
def validate_task_rel_del_proposal(header, propose, rel_address, state): """Validates that the User exists, the Task exists, and the User is in the Tasks's relationship specified by the rel_address. Args: header (TransactionHeader): The transaction header. propose (ProposeRemoveTask____): The Task Remove relationship proposal rel_address (str): The task relationship address. state (Context:: The way to communicate to the validator State gets and sets. Returns: (list of StateEntry) """ user_address = addresser.make_user_address(propose.user_id) task_address = addresser.make_task_attributes_address(propose.task_id) proposal_address = addresser.make_proposal_address( object_id=propose.task_id, related_id=propose.user_id) state_entries = get_state( state, [user_address, task_address, proposal_address, rel_address]) validate_identifier_is_user(state_entries, identifier=propose.user_id, address=user_address) user_entry = get_state_entry(state_entries, user_address) user = get_user_from_container(return_user_container(user_entry), propose.user_id) if header.signer_pubkey not in [user.user_id, user.manager_id]: raise InvalidTransaction( "Txn signer {} is not the user {} or the user's manager {}".format( header.signer_pubkey, user.user_id, user.manager_id)) validate_identifier_is_task(state_entries, identifier=propose.task_id, address=task_address) try: task_rel_entry = get_state_entry(state_entries, rel_address) task_rel_container = return_task_rel_container(task_rel_entry) if not is_in_task_rel_container(task_rel_container, propose.task_id, propose.user_id): raise InvalidTransaction("User {} isn't in the Task {} " "relationship".format( propose.user_id, propose.task_id)) except KeyError: raise InvalidTransaction( "User {} isn't in the Task {} relationship, " "since there isn't a container at the address".format( propose.user_id, propose.task_id)) return state_entries
def propose_manager(txn_key, batch_key, proposal_id, user_id, new_manager_id, reason, metadata): """Create a BatchList with a ProposeUpdateUserManager transaction in it. Args: txn_key (Key): The transaction signer public/private key pair batch_key (Key): The batch signer public/private key pair proposal_id (str): The id of the proposal supplied by the rest api. user_id (str): The User id of the user whose manager will be updated. new_manager_id (str): The new manager's id. reason (str): The reason for this update. metadata (str): Client supplied metadata. Returns: tuple BatchList, batch header_signature tuple """ propose_update_payload = user_transaction_pb2.ProposeUpdateUserManager( proposal_id=proposal_id, user_id=user_id, new_manager_id=new_manager_id, reason=reason, metadata=metadata) inputs = [ addresser.make_user_address(user_id=user_id), addresser.make_user_address(user_id=new_manager_id), addresser.make_proposal_address(object_id=user_id, related_id=new_manager_id) ] outputs = [ addresser.make_proposal_address(object_id=user_id, related_id=new_manager_id) ] rbac_payload = rbac_payload_pb2.RBACPayload( content=propose_update_payload.SerializeToString(), message_type=rbac_payload_pb2.RBACPayload.PROPOSE_UPDATE_USER_MANAGER) return make_header_and_batch(rbac_payload, inputs, outputs, txn_key, batch_key)
def _validate_create_role_state(create_role, state): state_return = get_state( state, [addresser.make_role_attributes_address(create_role.role_id)]) if _role_already_exists(state_return, create_role.role_id): raise InvalidTransaction("Role id {} is already in state".format( create_role.role_id)) users = list(create_role.admins) + list(create_role.owners) user_state_return = get_state( state, [addresser.make_user_address(u) for u in users]) validate_list_of_user_are_users(user_state_return, users)
def apply_create_task(header, payload, state): create_payload = task_transaction_pb2.CreateTask() create_payload.ParseFromString(payload.content) addresses = [ addresser.make_task_attributes_address(create_payload.task_id) ] if not create_payload.admins: raise InvalidTransaction("New tasks must have administrators.") if not create_payload.owners: raise InvalidTransaction("New tasks must have owners.") if create_payload.admins: addresses.extend( [addresser.make_user_address(u) for u in create_payload.admins]) addresses.extend([ addresser.make_task_admins_address(task_id=create_payload.task_id, user_id=u) for u in create_payload.admins ]) if create_payload.owners: addresses.extend( [addresser.make_user_address(u) for u in create_payload.owners]) addresses.extend([ addresser.make_task_owners_address(create_payload.task_id, user_id=u) for u in create_payload.owners ]) state_entries = get_state(state, addresses) validate_create_task_state(state_entries=state_entries, payload=create_payload) handle_create_task(state_entries, create_payload, state)
def handle_user_state_set(header, create_user, state, manager_id=None): user_container = user_state_pb2.UserContainer() user = user_state_pb2.User(user_id=create_user.user_id, name=create_user.name, metadata=create_user.metadata) if manager_id: user.manager_id = manager_id user_container.users.extend([user]) set_state(state, [ StateEntry(address=addresser.make_user_address(create_user.user_id), data=user_container.SerializeToString()) ])
def confirm_manager(txn_key, batch_key, proposal_id, user_id, manager_id, reason): """Create a BatchList with a ConfirmUpdateUserManager transaction in it. Args: txn_key (Key): The transaction signer public/private key pair. batch_key (Key): The batch signer public/private key pair. proposal_id (str): The identifier of the proposal. reason (str): The client supplied reason for the confirmation. Returns: tuple BatchList, batch header_signature tuple """ confirm_update_payload = user_transaction_pb2.ConfirmUpdateUserManager( proposal_id=proposal_id, user_id=user_id, manager_id=manager_id, reason=reason) inputs = [ addresser.make_proposal_address(user_id, manager_id), addresser.make_user_address(user_id) ] outputs = [ addresser.make_proposal_address(user_id, manager_id), addresser.make_user_address(user_id) ] rbac_payload = rbac_payload_pb2.RBACPayload( content=confirm_update_payload.SerializeToString(), message_type=rbac_payload_pb2.RBACPayload.CONFIRM_UPDATE_USER_MANAGER) return make_header_and_batch(rbac_payload, inputs, outputs, txn_key, batch_key)
def validate_user_state(header, create_user, state): user_entries = get_state( state, [addresser.make_user_address(create_user.user_id)]) if user_entries: # this is necessary for state collisions. try: user_container = return_user_container(user_entries[0]) _index_of_user_in_container(user_container, create_user.user_id) raise InvalidTransaction( "User with user_id {} already exists.".format( create_user.user_id)) except KeyError: # The user does not exist yet in state and so the transaction # is valid. pass
def test_users_address(self): """Tests the Users address creation function as well as the address_is function. Notes: 1. Create a Users address. 2. Assert that address_is returns the correct address type. """ users_address = addresser.make_user_address(user_id=uuid4().hex) self.assertEqual(len(users_address), 70, "The address is a well-formed address.") self.assertEqual( addresser.address_is(users_address), AddressSpace.USER, "The User address created must be found " "to be a User address.")
def propose_add_role_admins(txn_key, batch_key, proposal_id, role_id, user_id, reason, metadata): """Create a BatchList with a ProposeAddRoleAdmins transaction in it. Args: txn_key (Key): The txn signer key pair. batch_key (Key): The batch signer key pair. role_id (str): The role's id. user_id (str): The user that is being proposed to be an admin. reason (str): The client supplied reason for the proposal. metadata (str): The client supplied metadata. Returns: tuple BatchList, batch header_signature tuple """ propose_add_payload = role_transaction_pb2.ProposeAddRoleAdmin( proposal_id=proposal_id, role_id=role_id, user_id=user_id, reason=reason, metadata=metadata) inputs = [ addresser.make_user_address(user_id=user_id), addresser.make_proposal_address(role_id, user_id), addresser.make_role_admins_address(role_id, user_id), addresser.make_role_attributes_address(role_id=role_id) ] outputs = [addresser.make_proposal_address(role_id, user_id)] rbac_payload = rbac_payload_pb2.RBACPayload( content=propose_add_payload.SerializeToString(), message_type=rbac_payload_pb2.RBACPayload.PROPOSE_ADD_ROLE_ADMINS) return make_header_and_batch(rbac_payload, inputs, outputs, txn_key, batch_key)
def test_generated_user_address(self): """Tests the Users address creation function as well as the address_is function. """ ident = uuid4().hex address = addresser.make_user_address(ident) 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.USER, "The address created must be found to be a User address.")
def propose_remove_role_admins(txn_key, batch_key, proposal_id, role_id, user_id, reason, metadata): propose = role_transaction_pb2.ProposeRemoveRoleAdmin( proposal_id=proposal_id, role_id=role_id, user_id=user_id, reason=reason, metadata=metadata) inputs = [ addresser.make_user_address(user_id=user_id), addresser.make_proposal_address(role_id, user_id), addresser.make_role_admins_address(role_id, user_id), addresser.make_role_attributes_address(role_id=role_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_ADMINS) return make_header_and_batch(rbac_payload, inputs, outputs, txn_key, batch_key)
def propose_add_role_members(txn_key, batch_key, proposal_id, role_id, user_id, reason, metadata): propose_payload = role_transaction_pb2.ProposeAddRoleMember( proposal_id=proposal_id, role_id=role_id, user_id=user_id, reason=reason) inputs = [ addresser.make_role_members_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_payload.SerializeToString(), message_type=rbac_payload_pb2.RBACPayload.PROPOSE_ADD_ROLE_MEMBERS) return make_header_and_batch(rbac_payload, inputs, outputs, txn_key, batch_key)