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
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
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
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
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)
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)
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)
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)