Пример #1
0
    def test_decode_wrong_number_of_parts(self):
        token = self.get_token(issuer='specificCommunicationDefinitionConnectorRequest|extra')
        with mock.patch.object(LightToken, 'validate'):
            encoded = token.encode('sha256', self.SECRET)

        with self.assertRaisesMessage(ParseError, 'wrong number of parts'):
            LightToken.decode(encoded, 'sha256', self.SECRET)
 def get_token(self, issuer: str = None) -> Tuple[LightToken, str]:
     token = LightToken(id='response-token-id',
                        issuer=issuer or 'response-token-issuer',
                        created=datetime(2017, 12, 11, 14, 12, 5, 148000))
     encoded = token.encode('sha256',
                            'response-token-secret').decode('utf-8')
     return token, encoded
Пример #3
0
    def get_light_token(self,
                        parameter_name: str,
                        issuer: str,
                        hash_algorithm: str,
                        secret: str,
                        lifetime: Optional[int] = None) -> LightToken:
        """
        Retrieve and verify a light token according to token settings.

        :param parameter_name: The name of HTTP POST parameter to get the token from.
        :param issuer: Token issuer.
        :param hash_algorithm: A hashlib hash algorithm.
        :param secret: A secret shared between communication parties.
        :param lifetime: Lifetime of the token (in minutes) until its expiration.
        :return: A decoded LightToken.
        :raise ParseError: If the token is malformed and cannot be decoded.
        :raise ValidationError: If the token can be decoded but model validation fails.
        :raise SecurityError: If the token digest or issuer is invalid or the token has expired.
        """
        encoded_token = self.request.POST.get(parameter_name,
                                              '').encode('utf-8')
        LOGGER.info('[#%r] Received encoded light token: %r', self.log_id,
                    encoded_token)
        token = LightToken.decode(encoded_token, hash_algorithm, secret)
        LOGGER.info('[#%r] Decoded light token: id=%r, issuer=%r', self.log_id,
                    token.id, token.issuer)
        if token.issuer != issuer:
            raise SecurityError('Invalid token issuer.')
        if lifetime and token.created + timedelta(
                minutes=lifetime) < datetime.now():
            raise SecurityError('Token has expired.')
        return token
Пример #4
0
    def create_light_token(self, issuer: str, hash_algorithm: str,
                           secret: str) -> Tuple[LightToken, str]:
        """
        Create and encode a light token according to token settings.

        :param issuer: Token issuer.
        :param hash_algorithm: A hashlib hash algorithm.
        :param secret: A secret shared between communication parties.
        :return: A tuple of the token and its encoded form.
        """
        token = LightToken(id=create_xml_uuid(TOKEN_ID_PREFIX),
                           created=datetime.now(),
                           issuer=issuer)
        LOGGER.info('[#%r] Created light token: id=%r, issuer=%r', self.log_id,
                    token.id, token.issuer)
        encoded_token = token.encode(hash_algorithm, secret).decode('ascii')
        LOGGER.info('[#%r] Encoded light token: %r', self.log_id,
                    encoded_token)
        return token, encoded_token
    def test_post_success(self, uuid_mock: MagicMock):
        with cast(BinaryIO, (DATA_DIR / 'saml_response.xml').open('rb')) as f:
            saml_request_xml = f.read()

        response = self.client.post(self.url, {'SAMLResponse': b64encode(saml_request_xml).decode('ascii'),
                                               'RelayState': 'relay123'})

        # Context
        self.assertIn('token', response.context)
        self.assertEqual(response.context['token_parameter'], 'test_token')
        self.assertEqual(response.context['eidas_url'], 'https://test.example.net/SpecificProxyServiceResponse')
        self.assertEqual(response.context['error'], None)

        # Token
        encoded_token = response.context['token']
        token = LightToken.decode(encoded_token, 'sha256', 'response-token-secret')
        self.assertEqual(token.id, 'T0uuid4')
        self.assertEqual(token.issuer, 'response-token-issuer')
        self.assertEqual(token.created, datetime(2017, 12, 11, 14, 12, 5))

        # Storing light response
        light_response_data = LIGHT_RESPONSE_DICT.copy()
        light_response_data.update({
            'status': Status(**light_response_data['status']),
            'id': 'test-saml-response-id',  # Preserved
            'in_response_to_id': 'test-saml-request-id',  # Preserved
            'issuer': 'https://test.example.net/node-proxy-service-response',  # Replaced
        })
        light_response = LightResponse(**light_response_data)
        self.assertEqual(self.client_class_mock.mock_calls, [call(timeout=66)])
        self.assertEqual(self.client_mock.mock_calls,
                         [call.connect('test.example.net', 1234),
                          call.get_cache('test-proxy-service-response-cache'),
                          call.get_cache().put('T0uuid4', dump_xml(light_response.export_xml()).decode('utf-8'))])

        # Rendering
        self.assertContains(response, 'Redirect to eIDAS Node is in progress')
        self.assertContains(response,
                            '<form class="auto-submit" action="https://test.example.net/SpecificProxyServiceResponse"')
        self.assertContains(response, '<input type="hidden" name="test_token" value="{}"'.format(encoded_token))
        self.assertNotIn(b'An error occurred', response.content)
Пример #6
0
 def test_decode_wrong_digest(self):
     encoded = (b'c3BlY2lmaWNDb21tdW5pY2F0aW9uRGVmaW5pdGlvbkNvbm5lY3RvclJlcXVlc3R8ODUyYTY0YzAtOGFjMS0'
                b'0NDVmLWIwZTEtOTkyYWRhNDkzMDMzfDIwMTctMTItMTEgMTQ6MTI6MDUgMTQ4fDdNOHArdVA4Q0tYdU1pMk'
                b'lxU2RhMXRnNDUyV2xSdmNPU3d1MGRjaXNTWWs9')
     with self.assertRaisesMessage(SecurityError, 'invalid digest'):
         LightToken.decode(encoded, 'sha256', self.SECRET)
Пример #7
0
 def test_decode_wrong_secret(self):
     with self.assertRaisesMessage(SecurityError, 'invalid digest'):
         LightToken.decode(self.ENCODED_TOKEN, 'sha256', 'Dycky Most!')
Пример #8
0
 def test_decode_max_size_exceeded(self):
     with self.assertRaisesMessage(ParseError, 'Maximal token size exceeded.'):
         LightToken.decode(self.ENCODED_TOKEN * 100, 'sha256', self.SECRET)
Пример #9
0
    def test_decode_validation_error(self):
        with mock.patch.object(LightToken, 'validate'):
            encoded = self.get_token(issuer='').encode('sha256', self.SECRET)

        with self.assertRaisesMessage(ValidationError, 'Must be str, not NoneType'):
            LightToken.decode(encoded, 'sha256', self.SECRET)
Пример #10
0
 def test_decode_ok(self):
     self.assertEqual(LightToken.decode(self.ENCODED_TOKEN, 'sha256', self.SECRET), self.get_token())
Пример #11
0
 def get_token(self, **kwargs) -> LightToken:
     data = self.VALID_DATA.copy()
     data.update(**kwargs)
     return LightToken(**data)
    def test_post_success(self, uuid_mock: MagicMock):
        self.maxDiff = None
        saml_request_xml, saml_request_encoded = self.load_saml_request(
            signed=True)
        light_request = LightRequest(**LIGHT_REQUEST_DICT)
        light_request.issuer = 'https://example.net/EidasNode/ConnectorMetadata'
        self.cache_mock.get_and_remove.return_value = dump_xml(
            light_request.export_xml()).decode('utf-8')

        response = self.client.post(
            self.url, {
                'SAMLRequest': saml_request_encoded,
                'RelayState': 'relay123',
                'country_param': 'ca'
            })

        # Context
        self.assertIn('token', response.context)
        self.assertEqual(response.context['token_parameter'],
                         'test_request_token')
        self.assertEqual(response.context['eidas_url'],
                         'http://test.example.net/SpecificConnectorRequest')
        self.assertEqual(response.context['error'], None)

        # Token
        encoded_token = response.context['token']
        token = LightToken.decode(encoded_token, 'sha256',
                                  'request-token-secret')
        self.assertEqual(token.id, 'T0uuid4')
        self.assertEqual(token.issuer, 'request-token-issuer')
        self.assertEqual(token.created, datetime(2017, 12, 11, 14, 12, 5))

        # Storing light request
        light_request_data = LIGHT_REQUEST_DICT.copy()
        light_request_data.update({
            'id': 'test-saml-request-id',
            'issuer': 'test-connector-request-issuer',
        })
        light_request = LightRequest(**light_request_data)
        light_request.requested_attributes = light_request.requested_attributes.copy(
        )
        del light_request.requested_attributes[
            'http://eidas.europa.eu/attributes/naturalperson/AdditionalAttribute']
        del light_request.requested_attributes[
            'http://eidas.europa.eu/attributes/legalperson/LegalAdditionalAttribute']
        self.assertEqual(self.client_class_mock.mock_calls, [call(timeout=66)])
        self.assertEqual(self.client_mock.mock_calls, [
            call.connect('test.example.net', 1234),
            call.get_cache('test-connector-request-cache'),
            call.get_cache().put(
                'T0uuid4',
                dump_xml(light_request.export_xml()).decode('utf-8'))
        ])

        # Rendering
        self.assertContains(response, 'Redirect to eIDAS Node is in progress')
        self.assertContains(response, 'eidas_node/connector/formautosubmit.js')
        self.assertContains(
            response, '<form class="auto-submit" '
            'action="http://test.example.net/SpecificConnectorRequest"')
        self.assertContains(
            response,
            '<input type="hidden" name="test_request_token" value="{}"'.format(
                encoded_token))
        self.assertNotIn(b'An error occurred', response.content)