示例#1
0
    def time_valid(self, request):
        """
        Is the time of the request within the allowed drift?

        :param request: Request like object
        :type request: werkzeug.wrappers.BaseRequest
        :rtype: bool
        :return: success
        """
        if request.headers.get(settings.x_mws_time, '') == '':
            raise InauthenticError(
                "Time verification failed for {}. No x-mws-time present.".
                format(request.__class__.__name__))
        if not str(request.headers.get(settings.x_mws_time, '')).isdigit():
            raise InauthenticError(
                "Time verification failed for {}. X-MWS-Time Header format incorrect."
                .format(request.__class__.__name__))
        now = datetime.datetime.now()
        # this needs a float
        signature_time = datetime.datetime.fromtimestamp(
            float(request.headers.get(settings.x_mws_time)))
        if now > signature_time + datetime.timedelta(
                seconds=self.ALLOWED_DRIFT_SECONDS):
            raise InauthenticError("Time verification failed for {}. {} "
                                   "not within {}s of {}".format(
                                       request.__class__.__name__,
                                       signature_time,
                                       self.ALLOWED_DRIFT_SECONDS,
                                       now.strftime("%Y-%m-%d %H:%M:%S")))
        return True
示例#2
0
    def token_valid(self, request):
        """
        Is the message signed correctly?

        :param request: Request object
        :type request: werkzeug.wrappers.BaseRequest
        :rtype: bool
        :return: success
        """
        if not settings.signature_info.match(
                request.headers.get(settings.x_mws_authentication)):
            raise InauthenticError(
                "Token verification failed for {}. Misformatted "
                "Signature.".format(request.__class__.__name__))
        token, app_uuid, signature, mws_time = mws_attr(request)
        if not token == settings.mws_token:
            raise InauthenticError("Token verification failed for {}. "
                                   "Expected {}; token was {}".format(
                                       request.__class__.__name__,
                                       settings.mws_token, token))
        return True
示例#3
0
    def signature_valid(self, request):
        """
        Is the signature valid?

        :param request: request object
        :type request: werkzeug.wrappers.BaseRequest
        """

        token, app_uuid, signature, mws_time = mws_attr(request)

        expected = Signature.from_request(request=request)
        try:
            token = self.secure_token_cacher.get(app_uuid=app_uuid)
            key_text = token.get('security_token').get('public_key_str')
            if "BEGIN PUBLIC KEY" in key_text:
                # Load a PKCS#1 PEM-encoded public key
                rsakey = RSAPublicKey.load_pkcs1_openssl_pem(keyfile=key_text)
            elif "BEGIN RSA PUBLIC KEY" in key_text:
                # Loads a PKCS#1.5 PEM-encoded public key
                rsakey = RSAPublicKey.load_pkcs1(keyfile=key_text,
                                                 format='PEM')
            else:
                # Unable to identify the key type
                self.secure_token_cacher.flush(app_uuid)
                raise UnableToAuthenticateError(
                    "Unable to identify Public Key type from Signature")
            padded = rsakey.public_decrypt(signature)
            signature_hash = rsakey.unpad_message(padded)
        except ValueError as exc:
            self.secure_token_cacher.flush(app_uuid)
            # importKey raises
            raise InauthenticError("Public key decryption of signature "
                                   "failed!: {}".format(exc))
        if not expected.matches(signature_hash):
            raise InauthenticError(
                "Signature verification failed for {}".format(
                    request.__class__.__name__))
        return True
示例#4
0
    def authentication_present(self, request):
        """
        Is the mauth header present (assuming request has a headers attribute) that
        can be treated like a dict

        :param request: Request object
        :type request: werkzeug.wrappers.BaseRequest
        :rtype: bool
        :return: success
        """
        if request.headers.get(settings.x_mws_authentication, '') == '':
            raise InauthenticError(
                "Authentication Failed. No mAuth signature present; X-MWS-Authentication header is blank."
            )
        return True
示例#5
0
 def test_authenticate_error_conditions_inauthentic(self, authenticate):
     """ We get a False back if we raise a InauthenticError """
     authenticate.side_effect = InauthenticError("")
     request = mock.Mock(headers={
         settings.x_mws_time:
         self.mws_time,
         settings.x_mws_authentication:
         "MWS %s:somethingelse" % self.app_uuid
     },
                         path="/mauth/v2/mauth.json?open=1",
                         method="GET",
                         data="")
     authentic, status, message = self.authenticator.is_authentic(request)
     self.assertFalse(authentic)
     self.assertEqual(401, status)
     self.assertEqual("", message)
示例#6
0
 def test_is_authentic_some_signature_invalid(self, token_valid, time_valid,
                                              authentication_present,
                                              signature_valid):
     """LocalAuthenticator: We get a False back if token invalid"""
     request = mock.Mock(headers={
         settings.x_mws_time:
         self.mws_time,
         settings.x_mws_authentication:
         "MWS %s:somethingelse" % self.app_uuid
     },
                         path="/mauth/v2/mauth.json?open=1",
                         method="GET",
                         data="")
     token_valid.return_value = True
     time_valid.return_value = True
     authentication_present.return_value = True
     signature_valid.side_effect = InauthenticError()
     authentic, status, message = self.authenticator.is_authentic(request)
     self.assertFalse(authentic)
示例#7
0
    def signature_valid(self, request):
        """
        Is the signature valid?

        :param request: Request instance
        :type request: werkzeug.wrappers.BaseRequest
        """
        token, app_uuid, signature, mws_time = mws_attr(request)
        url = urljoin(self._mauth_base_url, "/mauth/{mauth_api_version}/" \
                                            "authentication_tickets.json".format(
            mauth_api_version=self._mauth_api_version))
        authentication_ticket = dict(
            verb=request.method,
            app_uuid=app_uuid,
            client_signature=signature,
            request_url=request.path,
            request_time=mws_time,
            b64encoded_body=b64encode(
                request.data.encode('utf-8')).decode('utf-8'))
        response = requests.post(
            url,
            data=json.dumps(dict(authentication_ticket=authentication_ticket)),
            auth=self._mauth_auth)
        if response.status_code in (412, 404):
            # the mAuth service responds with 412 when the given request is not authentically signed.
            # older versions of the mAuth service respond with 404 when the given app_uuid
            # does not exist, which is also considered to not be authentically signed. newer
            # versions of the service respond 412 in all cases, so the 404 check may be removed
            # when the old version of the mAuth service is out of service.
            raise InauthenticError(
                "The mAuth service responded with {status}: {body}".format(
                    status=response.status_code, body=response.content))
        elif 200 <= response.status_code <= 299:
            return True
        else:
            # e.g. 500 error
            # NOTE: this raises the underlying UnableToAuthenticateError
            self.log_mauth_service_response_error(request=request,
                                                  response=response)
示例#8
0
    def _remote_get(self, app_uuid):
        # type: (str) -> None
        """
        Attempt to retrieve a credential set from the remote store

        :param app_uuid: APP_UUID to retrieve
        """
        if not uuid_pattern.match(app_uuid):
            raise UnableToAuthenticateError("APP UUID format is not conformant")
        url = urljoin(self.mauth_base_url, "/mauth/{mauth_api_version}/security_tokens" \
                                           "/{app_uuid}.json".format(mauth_api_version=self.mauth_api_version,
                                                                     app_uuid=app_uuid))
        response = requests.get(url, auth=self.auth)
        if response.status_code == 404:
            raise InauthenticError("mAuth service responded with 404 looking up public "
                                   "key for {app_uuid}".format(app_uuid=app_uuid))
        elif response.status_code == 200:
            self._cache[app_uuid] = response.json()
        else:
            raise UnableToAuthenticateError("The mAuth service responded "
                                            "with {status}: {body}".format(status=response.status_code,
                                                                           body=response.content),
                                            response)