def request_refresh_token(self): # TODO turned off for now this function return # Check needed vars on OpenIDAuthentication class check_needed_class_vars(self, ['auth_code']) # Check needed vars on RESO class check_needed_class_vars(self.reso, ['client_id', 'client_secret', 'access_token']) auth_string = '{}:{}'.format(self.reso.client_id, self.reso.client_secret) headers = { 'Authorization': 'Basic {}'.format( b64encode(auth_string.encode('utf-8')).decode('utf-8')) } url_parameters = { 'client_id': self.reso.client_id, 'grant_type': 'refresh_token', 'refresh_token': self.reso.access_token, } refresh_token_response = self.context.post(self.reso.api_token_url, headers=headers, data=url_parameters) self.reso.logger.info('Got response {}'.format(refresh_token_response)) if refresh_token_response.status_code != 200: raise ValueError( refresh_token_response.json().get('error_description')) return refresh_token_response.json().get('refresh_token')
def test_validate_missing_variable(self): reso = RESO() variable = 'client_id' with self.assertRaises(MissingVariables) as context: check_needed_class_vars(reso, [variable]) self.assertEqual( 'Missing {} on {}'.format(variable, reso.__class__.__name__), str(context.exception))
def get_login_url(self): """ Forms login url for web integrations. :return: formed url """ # Check needed vars on OpenIDAuthentication class check_needed_class_vars(self, ['scope', 'redirect_uri']) # Check needed vars on RESO class check_needed_class_vars(self.reso, ['client_id', 'api_auth_url']) url_parameters = { 'client_id': self.reso.client_id, 'scope': self.scope, 'response_type': self.response_type, 'redirect_uri': self.redirect_uri, } return self.reso.api_auth_url + '?' + parse.urlencode(url_parameters)
def request_post(self, request_url, request_accept_type, post_data): """ Executes POST request on specified request_url :param request_url: path where to execute POST request :param request_accept_type: request accept type header value :param post_data: data which should be sent via POST request :return: returns response object on successful HTTP 200 request """ if not isinstance(post_data, dict): raise ParsingError('\'post_data\' must be of instance dict') # Check needed vars on RESO class check_needed_class_vars(self.reso, ['api_request_url', 'access_token']) self.reso.logger.debug('Forming request url') formed_request_url = self._return_formed_url(request_url) request_accept_type = self._form_request_accept_type( request_accept_type) headers = { 'Accept': request_accept_type, 'Authorization': 'Bearer ' + self.reso.access_token } self.reso.logger.debug( 'Sending POST request to {}'.format(formed_request_url)) response = requests.post(formed_request_url, headers=headers, data=post_data, verify=self.reso.verify_ssl) self.reso.logger.info('Got POST {} response {}'.format( formed_request_url, response)) if not response or response.status_code != 200: if response.status_code == 406: raise RequestError( "API returned HTTP code 406 - Not Acceptable. " "Please, setup a valid request accept type, current - {}". format(request_accept_type)) else: raise RequestError( "Could not retrieve API response. " "Response: {}".format( response.json() if response.json() else response)) return response
def request(self, request_url, request_accept_type): """ Executes GET request on specified request_url :param request_url: path where to execute GET request :param request_accept_type: request accept type header value :return: returns response object on successful HTTP 200 request """ # Check needed vars on RESO class check_needed_class_vars(self.reso, ['api_request_url', 'access_token']) self.reso.logger.debug('Forming request url') formed_request_url = self._return_formed_url(request_url) request_accept_type = self._form_request_accept_type( request_accept_type) headers = { 'Accept': request_accept_type, 'Authorization': 'Bearer ' + self.reso.access_token } self.reso.logger.debug( 'Sending GET request to {}'.format(formed_request_url)) response = requests.get(formed_request_url, headers=headers, verify=self.reso.verify_ssl) self.reso.logger.info('Got GET {} response {}'.format( formed_request_url, response)) if not response or response.status_code != 200: if response.status_code == 406: raise RequestError( "API returned HTTP code 406 - Not Acceptable. " "Please, setup a valid request accept type, current - {}". format(request_accept_type)) else: try: msg = response.json() except json.decoder.JSONDecodeError: msg = response raise RequestError("Could not retrieve API response. " "Response: {}".format(msg)) return response
def request_access_token(self): """ Requests access token from the server provided as 'api_token_url' parameter :return: access_token """ # Check needed vars on OpenIDAuthentication class check_needed_class_vars(self, ['auth_code', 'redirect_uri']) # Check needed vars on RESO class check_needed_class_vars( self.reso, ['client_id', 'client_secret', 'api_token_url']) auth_string = '{}:{}'.format(self.reso.client_id, self.reso.client_secret) headers = { 'Authorization': 'Basic {}'.format( b64encode(auth_string.encode('utf-8')).decode('utf-8')) } url_parameters = { 'client_id': self.reso.client_id, 'grant_type': self.grant_type, 'code': self.auth_code, 'redirect_uri': self.redirect_uri, } self.reso.logger.info( 'Retrieving access token: {} with headers {} and parameters {}'. format(self.reso.api_token_url, headers, url_parameters)) access_token_response = self.context.post(self.reso.api_token_url, headers=headers, data=url_parameters) self.reso.logger.info( 'Got access_token response {}'.format(access_token_response)) if access_token_response.status_code != 200: raise ValueError( access_token_response.json().get('error_description') or access_token_response.json()) return access_token_response.json().get('access_token')
def test_validate_no_missing_variable(self): reso = RESO(client_id='some-client-id') variable = 'client_id' check_needed_class_vars(reso, [variable])
def authorize(self, username, password): """ This function is dedicated for retrieving needed authorization code from OpenID server. :param username: username of a user :param password: password of a user :return: authorization_code """ if not username or not password: raise ValueError('Username or password was not supplied') self.reso.logger.info( "Getting auth code for client {} (username {}) with scope {} and redirect_uri {}" .format(self.reso.client_id, username, self.scope, self.redirect_uri)) # Check needed vars on OpenIDAuthentication class check_needed_class_vars(self, ['scope', 'redirect_uri']) # Check needed vars on RESO class check_needed_class_vars(self.reso, ['client_id', 'api_auth_url']) url_parameters = { 'client_id': self.reso.client_id, 'scope': self.scope, 'response_type': self.response_type, 'redirect_uri': self.redirect_uri, } # all cookies received will be stored in the session object to allow redirects form server self.reso.logger.info( 'Getting response from {} with parameters {}'.format( self.reso.api_auth_url, url_parameters)) response = self.context.get(self.reso.api_auth_url, params=url_parameters) self.reso.logger.debug('Parsing html response for form inputs') bs = BeautifulSoup(response.content, 'html.parser') raw_form = bs.find('form') if not raw_form: raise ParsingError( 'Could not receive authorization form. Check credentials') form_inputs = { raw_input.attrs.get('name'): raw_input.attrs.get('value') for raw_input in raw_form.find_all('input') } return_response = { 'url': raw_form.attrs.get('action'), 'method': raw_form.attrs.get('method'), 'inputs': form_inputs } self.reso.logger.debug('Forming login url for posting credentials') url = self._form_authentication_url(bs, return_response) headers = {"Content-Type": "application/x-www-form-urlencoded"} self._fill_authentication_data(return_response, username, password) auth_code_response = self.context.post(url, data=return_response['inputs'], headers=headers, verify=self.reso.verify_ssl) self.reso.logger.info('Getting auth code from the latest redirect') parsed_parameters = parse.parse_qs( parse.urlparse(auth_code_response.url, allow_fragments=False).query) if parsed_parameters.keys(): auth_code = parsed_parameters[list(parsed_parameters.keys())[0]] self.reso.logger.info('Parsed auth code from url parameters') return auth_code if not isinstance(auth_code, list) else auth_code[0] self.reso.logger.error( 'Could not find any parameter in url parameters.') raise ParsingError('Could not find any parameter in the redirect uri')