def handle(self, connection_id, message_content): # check to see if there is a public key public_key = self._network.connection_id_to_public_key(connection_id) if public_key is None: LOGGER.debug( "No public key found, %s is not permitted. " "Close connection.", connection_id) violation = AuthorizationViolation( violation=RoleType.Value("NETWORK")) self._gossip.unregister_peer(connection_id) return HandlerResult( HandlerStatus.RETURN_AND_CLOSE, message_out=violation, message_type=validator_pb2.Message.AUTHORIZATION_VIOLATION) # check public key against network/default role permitted = self._permission_verifier.check_network_role(public_key) if not permitted: LOGGER.debug("Public key not permitted, %s is not permitted", connection_id) self._gossip.unregister_peer(connection_id) violation = AuthorizationViolation( violation=RoleType.Value("NETWORK")) return HandlerResult( HandlerStatus.RETURN_AND_CLOSE, message_out=violation, message_type=validator_pb2.Message.AUTHORIZATION_VIOLATION) # if allowed pass message return HandlerResult(HandlerStatus.PASS)
def handle(self, connection_id, message_content): """ If the connection wants to take on a role that requires a challenge to be signed, it will request the challenge by sending an AuthorizationChallengeRequest to the validator it wishes to connect to. The validator will send back a random payload that must be signed. If the connection has not sent a ConnectionRequest or the connection has already recieved an AuthorizationChallengeResponse, an AuthorizationViolation will be returned and the connection will be closed. """ if self._network.get_connection_status(connection_id) != \ ConnectionStatus.CONNECTION_REQUEST: LOGGER.debug( "Connection's previous message was not a" " ConnectionRequest, Remove connection to %s", connection_id) violation = AuthorizationViolation( violation=RoleType.Value("NETWORK")) return HandlerResult( HandlerStatus.RETURN_AND_CLOSE, message_out=violation, message_type=validator_pb2.Message.AUTHORIZATION_VIOLATION) random_payload = os.urandom(PAYLOAD_LENGTH) self._challenge_payload_cache[connection_id] = random_payload auth_challenge_response = AuthorizationChallengeResponse( payload=random_payload) self._network.update_connection_status( connection_id, ConnectionStatus.AUTH_CHALLENGE_REQUEST) return HandlerResult(HandlerStatus.RETURN, message_out=auth_challenge_response, message_type=validator_pb2.Message. AUTHORIZATION_CHALLENGE_RESPONSE)
def test_authorization_trust_request_bad_last_message(self): """ Test the AuthorizationTrustRequestHandler returns an AuthorizationViolation and closes the connection if the last message was not a ConnectionRequest. """ auth_trust_request = AuthorizationTrustRequest( roles=[RoleType.Value("NETWORK")], public_key="public_key") roles = {"network": AuthorizationType.TRUST} network = MockNetwork( roles, connection_status={"connection_id": "other"}) permission_verifer = MockPermissionVerifier() gossip = MockGossip() handler = AuthorizationTrustRequestHandler( network, permission_verifer, gossip) handler_status = handler.handle( "connection_id", auth_trust_request.SerializeToString()) self.assertEqual(handler_status.status, HandlerStatus.RETURN_AND_CLOSE) self.assertEqual( handler_status.message_type, validator_pb2.Message.AUTHORIZATION_VIOLATION)
def do_authorization_challenge_submit(): """ Test the AuthorizationChallengeSubmitHandler returns an AuthorizationChallengeResult. """ context = create_context('secp256k1') private_key = context.new_random_private_key() crypto_factory = CryptoFactory(context) signer = crypto_factory.new_signer(private_key) payload = os.urandom(10) signature = signer.sign(payload) auth_challenge_submit = AuthorizationChallengeSubmit( public_key=signer.get_public_key().as_hex(), signature=signature, roles=[RoleType.Value("NETWORK")]) roles = {"network": AuthorizationType.TRUST} network = MockNetwork(roles, connection_status={ "connection_id": ConnectionStatus.AUTH_CHALLENGE_REQUEST }) permission_verifer = MockPermissionVerifier() gossip = MockGossip() handler = AuthorizationChallengeSubmitHandler(network, permission_verifer, gossip, {"connection_id": payload}) handler_status = handler.handle("connection_id", auth_challenge_submit.SerializeToString()) return handler_status
def _inbound_connection_request_callback(self, request, result, connection=None): connection_response = ConnectionResponse() connection_response.ParseFromString(result.content) if connection_response.status == connection_response.ERROR: LOGGER.debug( "Received an error response to the NETWORK_CONNECT " "we sent. Removing connection: %s", connection.connection_id) self.remove_connection(connection.connection_id) # Send correct Authorization Request for network role auth_type = {"trust": [], "challenge": []} for role_entry in connection_response.roles: if role_entry.auth_type == connection_response.TRUST: auth_type["trust"].append(role_entry.role) elif role_entry.auth_type == connection_response.CHALLENGE: auth_type["challenge"].append(role_entry.role) if auth_type["trust"]: auth_trust_request = AuthorizationTrustRequest( roles=[RoleType.Value("NETWORK")], public_key=self._public_key) self.send(validator_pb2.Message.AUTHORIZATION_TRUST_REQUEST, auth_trust_request.SerializeToString(), connection) if auth_type["challenge"]: auth_challenge_request = AuthorizationChallengeRequest() self.send(validator_pb2.Message.AUTHORIZATION_CHALLENGE_REQUEST, auth_challenge_request.SerializeToString(), connection, callback=partial( self._inbound_challenge_authorization_callback, connection=connection))
def _network_violation_result(): violation = AuthorizationViolation( violation=RoleType.Value("NETWORK")) return HandlerResult( HandlerStatus.RETURN_AND_CLOSE, message_out=violation, message_type=validator_pb2.Message.AUTHORIZATION_VIOLATION)
def test_authorization_trust_request(self): """ Test the AuthorizationTrustRequestHandler returns an AuthorizationTrustResponse if the AuthorizationTrustRequest should be approved. """ auth_trust_request = AuthorizationTrustRequest( roles=[RoleType.Value("NETWORK")], public_key="public_key") roles = {"network": AuthorizationType.TRUST} network = MockNetwork(roles, connection_status={ "connection_id": ConnectionStatus.CONNECTION_REQUEST }) permission_verifer = MockPermissionVerifier() gossip = MockGossip() handler = AuthorizationTrustRequestHandler(network, permission_verifer, gossip) handler_status = handler.handle("connection_id", auth_trust_request.SerializeToString()) self.assertEqual(handler_status.status, HandlerStatus.RETURN) self.assertEqual(handler_status.message_type, validator_pb2.Message.AUTHORIZATION_TRUST_RESPONSE)
def test_authorization_trust_request_not_permitted(self): """ Test the AuthorizationTrustRequestHandler returns an AuthorizationViolation and closes the connection if the permission verifier does not permit the connections public key. """ auth_trust_request = AuthorizationTrustRequest( roles=[RoleType.Value("NETWORK")], public_key="public_key") roles = {"network": AuthorizationType.TRUST} network = MockNetwork(roles, connection_status={ "connection_id": ConnectionStatus.CONNECTION_REQUEST }) # say that connection is not permitted permission_verifer = MockPermissionVerifier(allow=False) gossip = MockGossip() handler = AuthorizationTrustRequestHandler(network, permission_verifer, gossip) handler_status = handler.handle("connection_id", auth_trust_request.SerializeToString()) self.assertEqual(handler_status.status, HandlerStatus.RETURN_AND_CLOSE) self.assertEqual(handler_status.message_type, validator_pb2.Message.AUTHORIZATION_VIOLATION)
def test_authorization_challenge_submit(self): """ Test the AuthorizationChallengeSubmitHandler returns an AuthorizationViolation and closes the connection if the permission verifier does not permit the public_key. """ private_key = signing.generate_private_key() public_key = signing.generate_public_key(private_key) payload = os.urandom(10) signature = signing.sign(payload, private_key) auth_challenge_submit = AuthorizationChallengeSubmit( public_key=public_key, payload=payload, signature=signature, roles=[RoleType.Value("NETWORK")]) roles = {"network": AuthorizationType.TRUST} network = MockNetwork(roles, connection_status={ "connection_id": ConnectionStatus.AUTH_CHALLENGE_REQUEST }) permission_verifer = MockPermissionVerifier(allow=False) gossip = MockGossip() handler = AuthorizationChallengeSubmitHandler(network, permission_verifer, gossip) handler_status = handler.handle( "connection_id", auth_challenge_submit.SerializeToString()) self.assertEqual(handler_status.status, HandlerStatus.RETURN_AND_CLOSE) self.assertEqual(handler_status.message_type, validator_pb2.Message.AUTHORIZATION_VIOLATION)
def test_authorization_challenge_submit_bad_last_message(self): """ Test the AuthorizationChallengeSubmitHandler returns an AuthorizationViolation and closes the connection if the last message was not AuthorizaitonChallengeRequest. """ context = create_context('secp256k1') private_key = context.new_random_private_key() crypto_factory = CryptoFactory(context) signer = crypto_factory.new_signer(private_key) payload = os.urandom(10) signature = signer.sign(payload) auth_challenge_submit = AuthorizationChallengeSubmit( public_key=signer.get_public_key().as_hex(), signature=signature, roles=[RoleType.Value("NETWORK")]) roles = {"network": AuthorizationType.TRUST} network = MockNetwork(roles, connection_status={"connection_id": "other"}) permission_verifer = MockPermissionVerifier() gossip = MockGossip() handler = AuthorizationChallengeSubmitHandler( network, permission_verifer, gossip, {"connection_id": payload}) handler_status = handler.handle( "connection_id", auth_challenge_submit.SerializeToString()) self.assertEqual(handler_status.status, HandlerStatus.RETURN_AND_CLOSE) self.assertEqual(handler_status.message_type, validator_pb2.Message.AUTHORIZATION_VIOLATION)
def handle(self, connection_id, message_content): message = GossipMessage() message.ParseFromString(message_content) if message.content_type == "BLOCK": public_key = \ self._network.connection_id_to_public_key(connection_id) block = Block() block.ParseFromString(message.content) header = BlockHeader() header.ParseFromString(block.header) if header.signer_public_key == public_key: permitted = \ self._permission_verifier.check_network_consensus_role( public_key) if not permitted: LOGGER.debug( "Public key is not permitted to publish block, " "remove connection: %s", connection_id) self._gossip.unregister_peer(connection_id) violation = AuthorizationViolation( violation=RoleType.Value("NETWORK")) return HandlerResult(HandlerStatus.RETURN_AND_CLOSE, message_out=violation, message_type=validator_pb2.Message. AUTHORIZATION_VIOLATION) # if allowed pass message return HandlerResult(HandlerStatus.PASS)
def _connect_callback(self, request, result, connection=None, success_callback=None, failure_callback=None): connection_response = ConnectionResponse() connection_response.ParseFromString(result.content) if connection_response.status == connection_response.ERROR: LOGGER.debug( "Received an error response to the NETWORK_CONNECT " "we sent. Removing connection: %s", connection.connection_id) self.remove_connection(connection.connection_id) if failure_callback: failure_callback(connection_id=connection.connection_id) elif connection_response.status == connection_response.OK: LOGGER.debug("Connection to %s was acknowledged", connection.connection_id) if self._authorize: auth_trust_request = AuthorizationTrustRequest( roles=[RoleType.Value("NETWORK")], public_key=self._pub_key) connection.send( validator_pb2.Message.AUTHORIZATION_TRUST_REQUEST, auth_trust_request.SerializeToString(), callback=partial(self._authorization_callback, connection=connection, success_callback=success_callback, failure_callback=failure_callback)) else: if success_callback: success_callback(connection_id=connection.connection_id)
def _inbound_connection_request_callback(self, request, result, connection=None): auth_trust_request = AuthorizationTrustRequest( roles=[RoleType.Value("NETWORK")], public_key=self._pub_key) self.send( validator_pb2.Message.AUTHORIZATION_TRUST_REQUEST, auth_trust_request.SerializeToString(), connection, )
def handle(self, connection_id, message_content): request = PingRequest() request.ParseFromString(message_content) ack = PingResponse() if self._network.get_connection_status(connection_id) == \ ConnectionStatus.CONNECTED: if connection_id in self._last_message: del self._last_message[connection_id] return HandlerResult( HandlerStatus.RETURN, message_out=ack, message_type=validator_pb2.Message.PING_RESPONSE) if connection_id in self._last_message: ping_frequency = time.time() - self._last_message[connection_id] if ping_frequency < self._allowed_frequency: LOGGER.warning("Too many Pings (%s) in %s seconds before " "authorization is complete: %s", ping_frequency, self._allowed_frequency, connection_id) violation = AuthorizationViolation( violation=RoleType.Value("NETWORK")) return HandlerResult( HandlerStatus.RETURN_AND_CLOSE, message_out=violation, message_type=validator_pb2.Message.AUTHORIZATION_VIOLATION) self._last_message[connection_id] = time.time() return HandlerResult( HandlerStatus.RETURN, message_out=ack, message_type=validator_pb2.Message.PING_RESPONSE)
def handle(self, connection_id, message_content): request = PingRequest() request.ParseFromString(message_content) #LOGGER.debug("PingHandler PingRequest %s(%s)",connection_id[:8],self._network.connection_id_to_endpoint(connection_id)) ack = PingResponse() if self._network.get_connection_status( connection_id) == ConnectionStatus.CONNECTED: if connection_id in self._last_message: del self._last_message[connection_id] """ Here we can ask SYNC with this peer -in case we have no sync yet """ #LOGGER.debug("PingRequest from CONNECTED %s(%s)",connection_id[:8],self._network.connection_id_to_endpoint(connection_id)) return HandlerResult( HandlerStatus.RETURN, message_out=ack, message_type=validator_pb2.Message.PING_RESPONSE) if connection_id in self._last_message: if time.time( ) - self._last_message[connection_id] < self._allowed_frequency: LOGGER.debug( "Too many Pings in %s seconds before authorization is complete: %s", self._allowed_frequency, connection_id) violation = AuthorizationViolation( violation=RoleType.Value("NETWORK")) return HandlerResult( HandlerStatus.RETURN_AND_CLOSE, message_out=violation, message_type=validator_pb2.Message.AUTHORIZATION_VIOLATION) self._last_message[connection_id] = time.time() return HandlerResult(HandlerStatus.RETURN, message_out=ack, message_type=validator_pb2.Message.PING_RESPONSE)
def _inbound_challenge_authorization_callback(self, request, result, connection=None): if result.message_type != \ validator_pb2.Message.AUTHORIZATION_CHALLENGE_RESPONSE: LOGGER.debug("Unable to complete Challenge Authorization.") return auth_challenge_response = AuthorizationChallengeResponse() auth_challenge_response.ParseFromString(result.content) payload = auth_challenge_response.payload signature = signing.sign(payload, self._priv_key) auth_challenge_submit = AuthorizationChallengeSubmit( public_key=self._public_key, payload=payload, signature=signature, roles=[RoleType.Value("NETWORK")]) self.send(validator_pb2.Message.AUTHORIZATION_CHALLENGE_SUBMIT, auth_challenge_submit.SerializeToString(), connection)
def handle(self, connection_id, message_content): request = PingRequest() request.ParseFromString(message_content) ack = NetworkAcknowledgement() ack.status = ack.OK if self._network.get_connection_status(connection_id) == \ ConnectionStatus.CONNECTED: if connection_id in self._last_message: del self._last_message[connection_id] return HandlerResult( HandlerStatus.RETURN, message_out=ack, message_type=validator_pb2.Message.NETWORK_ACK) if connection_id in self._last_message: if time.time() - self._last_message[connection_id] < \ self._allowed_frequency: LOGGER.debug( "Too many Pings in %s seconds before" "authorization is complete: %s", self._allowed_frequency, connection_id) violation = AuthorizationViolation( violation=RoleType.Value("NETWORK")) return HandlerResult( HandlerStatus.RETURN_AND_CLOSE, message_out=violation, message_type=validator_pb2.Message.AUTHORIZATION_VIOLATION) self._last_message[connection_id] = time.time() return HandlerResult(HandlerStatus.RETURN, message_out=ack, message_type=validator_pb2.Message.NETWORK_ACK)
def do_authorization_trust_request(): """ Test the AuthorizationTrustRequestHandler returns an AuthorizationTrustResponse if the AuthorizationTrustRequest should be approved. """ auth_trust_request = AuthorizationTrustRequest( roles=[RoleType.Value("NETWORK")], public_key="public_key") roles = {"network": AuthorizationType.TRUST} network = MockNetwork(roles, connection_status={ "connection_id": ConnectionStatus.CONNECTION_REQUEST }) permission_verifer = MockPermissionVerifier() gossip = MockGossip() handler = AuthorizationTrustRequestHandler(network, permission_verifer, gossip) handler_status = handler.handle("connection_id", auth_trust_request.SerializeToString()) return handler_status
def handle(self, connection_id, message_content): """ The simplest authorization type will be Trust. If Trust authorization is enabled, the validator will trust the connection and approve any roles requested that are available on that endpoint. If the requester wishes to gain access to every role it has permission to access, it can request access to the role ALL, and the validator will respond with all available roles. If the permission verifier deems the connection to not have access to a role, the connection has not sent a ConnectionRequest or a the connection has already recieved a AuthorizationTrustResponse, an AuthorizatinViolation will be returned and the connection will be closed. """ if self._network.get_connection_status(connection_id) != \ ConnectionStatus.CONNECTION_REQUEST: LOGGER.debug( "Connection's previous message was not a" " ConnectionRequest, Remove connection to %s", connection_id) violation = AuthorizationViolation( violation=RoleType.Value("NETWORK")) return HandlerResult( HandlerStatus.RETURN_AND_CLOSE, message_out=violation, message_type=validator_pb2.Message.AUTHORIZATION_VIOLATION) request = AuthorizationTrustRequest() request.ParseFromString(message_content) # Check that the connection's public key is allowed by the network role roles = self._network.roles for role in request.roles: if role == RoleType.Value("NETWORK") or role == \ RoleType.Value("ALL"): permitted = False if "network" in roles: permitted = self._permission_verifier.check_network_role( request.public_key) if not permitted: violation = AuthorizationViolation( violation=RoleType.Value("NETWORK")) return HandlerResult(HandlerStatus.RETURN_AND_CLOSE, message_out=violation, message_type=validator_pb2.Message. AUTHORIZATION_VIOLATION) self._network.update_connection_public_key(connection_id, request.public_key) if RoleType.Value("NETWORK") in request.roles: # Need to send ConnectionRequest to authorize ourself with the # connection if they initialized the connection try: is_outbound_connection = self._network.is_outbound_connection( connection_id) except KeyError: # Connection has gone away, drop message return HandlerResult(HandlerStatus.DROP) if not is_outbound_connection: self._network.send_connect_request(connection_id) else: # If this is an outbound connection, authorization is complete # for both connections and peering can begin. self._gossip.connect_success(connection_id) auth_trust_response = AuthorizationTrustResponse( roles=[RoleType.Value("NETWORK")]) LOGGER.debug("Connection: %s is approved", connection_id) self._network.update_connection_status(connection_id, ConnectionStatus.CONNECTED) return HandlerResult( HandlerStatus.RETURN, message_out=auth_trust_response, message_type=validator_pb2.Message.AUTHORIZATION_TRUST_RESPONSE)
def handle(self, connection_id, message_content): """ When the validator receives an AuthorizationChallengeSubmit message, it will verify the public key against the signature. If the public key is verified, the requested roles will be checked against the stored roles to see if the public key is included in the policy. If the node’s response is accepted, the node’s public key will be stored and the requester may start sending messages for the approved roles. If the requester wanted a role that is either not available on the endpoint, the requester does not have access to one of the roles requested, or the previous message was not an AuthorizationChallengeRequest, the challenge will be rejected and the connection will be closed. """ if self._network.get_connection_status(connection_id) != \ ConnectionStatus.AUTH_CHALLENGE_REQUEST: LOGGER.debug( "Connection's previous message was not a" " AuthorizationChallengeRequest, Remove connection to" "%s", connection_id) return AuthorizationChallengeSubmitHandler \ ._network_violation_result() auth_challenge_submit = AuthorizationChallengeSubmit() auth_challenge_submit.ParseFromString(message_content) try: payload = self._challenge_payload_cache[connection_id] except KeyError: LOGGER.debug( "Connection's challenge payload expired before a" "response was received. %s", connection_id) return AuthorizationChallengeSubmitHandler \ ._network_violation_result() context = create_context('secp256k1') try: public_key = Secp256k1PublicKey.from_hex( auth_challenge_submit.public_key) except ParseError: LOGGER.warning( 'Authorization Challenge Request cannot be ' 'verified. Invalid public key %s', auth_challenge_submit.public_key) return AuthorizationChallengeSubmitHandler \ ._network_violation_result() if not context.verify(auth_challenge_submit.signature, payload, public_key): LOGGER.warning( "Signature was not able to be verifed. Remove " "connection to %s", connection_id) return AuthorizationChallengeSubmitHandler \ ._network_violation_result() roles = self._network.roles for role in auth_challenge_submit.roles: if role == RoleType.Value("NETWORK") or role == \ RoleType.Value("ALL"): permitted = False if "network" in roles: permitted = self._permission_verifier.check_network_role( auth_challenge_submit.public_key) if not permitted: return AuthorizationChallengeSubmitHandler \ ._network_violation_result() self._network.update_connection_public_key( connection_id, auth_challenge_submit.public_key) if RoleType.Value("NETWORK") in auth_challenge_submit.roles: # Need to send ConnectionRequest to authorize ourself with the # connection if they initialized the connection try: is_outbound_connection = self._network.is_outbound_connection( connection_id) except KeyError: # Connection has gone away, drop message return HandlerResult(HandlerStatus.DROP) if not is_outbound_connection: self._network.send_connect_request(connection_id) else: # If this is an outbound connection, authorization is complete # for both connections and peering/topology build out can # begin. self._gossip.connect_success(connection_id) auth_challenge_result = AuthorizationChallengeResult( roles=[RoleType.Value("NETWORK")]) LOGGER.debug("Connection: %s is approved", connection_id) self._network.update_connection_status(connection_id, ConnectionStatus.CONNECTED) return HandlerResult( HandlerStatus.RETURN, message_out=auth_challenge_result, message_type=validator_pb2.Message.AUTHORIZATION_CHALLENGE_RESULT)
def handle(self, connection_id, message_content): """ A connection must use one of the supported authorization types to prove their identity. If a requester deviates from the procedure in any way, the requester will be rejected and the connection will be closed. The same is true if the requester sends multiple ConnectionRequests or multiple of any authorization-type message. The validator receiving a new connection will receive a ConnectionRequest. The validator will respond with a ConnectionResponse message. The ConnectionResponse message will contain a list of RoleEntry messages and an AuthorizationType. Role entries are the accepted type of connections that are supported on the endpoint that the ConnectionRequest was sent to. AuthorizationType describes the procedure required to gain access to that role. If the validator is not accepting connections or does not support the listed authorization type, return an ConnectionResponse.ERROR and close the connection. """ message = ConnectionRequest() message.ParseFromString(message_content) LOGGER.debug("got connect message from %s. sending ack", connection_id) # Need to use join here to get the string "0.0.0.0". Otherwise, # bandit thinks we are binding to all interfaces and returns a # Medium security risk. interfaces = ["*", ".".join(["0", "0", "0", "0"])] interfaces += netifaces.interfaces() for interface in interfaces: if interface in message.endpoint: LOGGER.debug("Endpoint cannot include '%s': %s", interface, message.endpoint) connection_response = ConnectionResponse( status=ConnectionResponse.ERROR) return HandlerResult(HandlerStatus.RETURN_AND_CLOSE, message_out=connection_response, message_type=validator_pb2.Message. AUTHORIZATION_CONNECTION_RESPONSE) LOGGER.debug("Endpoint of connecting node is %s", message.endpoint) self._network.update_connection_endpoint(connection_id, message.endpoint) # Get what AuthorizationType the network role requires roles = self._network.roles auth_type = roles.get("network") if auth_type == AuthorizationType.TRUST: role_type = ConnectionResponse.RoleEntry( role=RoleType.Value("NETWORK"), auth_type=ConnectionResponse.TRUST) connection_response = ConnectionResponse(roles=[role_type]) elif auth_type == AuthorizationType.CHALLENGE: role_type = ConnectionResponse.RoleEntry( role=RoleType.Value("NETWORK"), auth_type=ConnectionResponse.CHALLENGE) connection_response = ConnectionResponse(roles=[role_type]) else: LOGGER.warning( "Network role is set to an unsupported" "Authorization Type: %s", auth_type) connection_response = ConnectionResponse( status=ConnectionResponse.ERROR) return HandlerResult(HandlerStatus.RETURN_AND_CLOSE, message_out=connection_response, message_type=validator_pb2.Message. AUTHORIZATION_CONNECTION_RESPONSE) try: is_outbound_connection = self._network.is_outbound_connection( connection_id) except KeyError: # Connection has gone away, drop message return HandlerResult(HandlerStatus.DROP) if not is_outbound_connection: if self._network.allow_inbound_connection(): LOGGER.debug("Allowing incoming connection: %s", connection_id) connection_response.status = connection_response.OK else: connection_response.status = connection_response.ERROR return HandlerResult(HandlerStatus.RETURN_AND_CLOSE, message_out=connection_response, message_type=validator_pb2.Message. AUTHORIZATION_CONNECTION_RESPONSE) if self._network.get_connection_status(connection_id) is not None: LOGGER.debug( "Connection has already sent ConnectionRequest:" " %s, Remove connection.", connection_id) connection_response.status = connection_response.ERROR return HandlerResult(HandlerStatus.RETURN_AND_CLOSE, message_out=connection_response, message_type=validator_pb2.Message. AUTHORIZATION_CONNECTION_RESPONSE) self._network.update_connection_status( connection_id, ConnectionStatus.CONNECTION_REQUEST) return HandlerResult(HandlerStatus.RETURN, message_out=connection_response, message_type=validator_pb2.Message. AUTHORIZATION_CONNECTION_RESPONSE)
def handle(self, connection_id, message_content): """ When the validator receives an AuthorizationChallengeSubmit message, it will verify the public key against the signature. If the public key is verified, the requested roles will be checked against the stored roles to see if the public key is included in the policy. If the node’s response is accepted, the node’s public key will be stored and the requester may start sending messages for the approved roles. If the requester wanted a role that is either not available on the endpoint, the requester does not have access to one of the roles requested, or the previous message was not an AuthorizationChallengeRequest, the challenge will be rejected and the connection will be closed. """ if self._network.get_connection_status(connection_id) != \ ConnectionStatus.AUTH_CHALLENGE_REQUEST: LOGGER.debug( "Connection's previous message was not a" " AuthorizationChallengeRequest, Remove connection to" "%s", connection_id) violation = AuthorizationViolation( violation=RoleType.Value("NETWORK")) return HandlerResult( HandlerStatus.RETURN_AND_CLOSE, message_out=violation, message_type=validator_pb2.Message.AUTHORIZATION_VIOLATION) auth_challenge_submit = AuthorizationChallengeSubmit() auth_challenge_submit.ParseFromString(message_content) if not signing.verify(auth_challenge_submit.payload, auth_challenge_submit.signature, auth_challenge_submit.public_key): LOGGER.warning( "Signature was not able to be verifed. Remove " "connection to %s", connection_id) violation = AuthorizationViolation( violation=RoleType.Value("NETWORK")) return HandlerResult( HandlerStatus.RETURN_AND_CLOSE, message_out=violation, message_type=validator_pb2.Message.AUTHORIZATION_VIOLATION) roles = self._network.roles for role in auth_challenge_submit.roles: if role == RoleType.Value("NETWORK") or role == \ RoleType.Value("ALL"): permitted = False if "network" in roles: permitted = self._permission_verifier.check_network_role( auth_challenge_submit.public_key) if not permitted: violation = AuthorizationViolation( violation=RoleType.Value("NETWORK")) return HandlerResult(HandlerStatus.RETURN_AND_CLOSE, message_out=violation, message_type=validator_pb2.Message. AUTHORIZATION_VIOLATION) self._network.update_connection_public_key( connection_id, auth_challenge_submit.public_key) if RoleType.Value("NETWORK") in auth_challenge_submit.roles: # Need to send ConnectionRequest to authorize ourself with the # connection if they initialized the connection if not self._network.is_outbound_connection(connection_id): self._network.send_connect_request(connection_id) else: # If this is an outbound connection, authorization is complete # for both connections and peering/topology build out can # begin. self._gossip.connect_success(connection_id) auth_challenge_result = AuthorizationChallengeResult( roles=[RoleType.Value("NETWORK")]) LOGGER.debug("Connection: %s is approved", connection_id) self._network.update_connection_status(connection_id, ConnectionStatus.CONNECTED) return HandlerResult( HandlerStatus.RETURN, message_out=auth_challenge_result, message_type=validator_pb2.Message.AUTHORIZATION_CHALLENGE_RESULT)