def auto_authentication(self, cfg, log): """ Attempts to authenticate automatically by first searching the Batch Apps configuration for an unattended session, then a cached session. :Args: - cfg (:class:`batchapps.Configuration`): An instance of the Batch Apps Configuration class, read from the file set in the addon User Preferences. - log (:class:`batchapps.log.PickleLog`): A logger object as set in BatchAppsSettings. :Returns: - ``True`` if the addon was successfully authenticated, else ``False`` """ try: log.info("Checking for unattended session...") self.props.credentials = AzureOAuth.get_unattended_session( config=cfg) log.info("Found!") return True except (AuthenticationException, InvalidConfigException) as exp: log.info("Could not get unattended session: {0}".format(exp)) try: log.info("Checking for cached session...") self.props.credentials = AzureOAuth.get_session(config=cfg) log.info("Found!") return True except (AuthenticationException, InvalidConfigException) as exp: log.info("Could not get cached session: {0}".format(exp)) return False
def auto_authentication(self, cfg, log): """ Attempts to authenticate automatically by first searching the Batch Apps configuration for an unattended session, then a cached session. :Args: - cfg (:class:`batchapps.Configuration`): An instance of the Batch Apps Configuration class, read from the file set in the addon User Preferences. - log (:class:`batchapps.log.PickleLog`): A logger object as set in BatchAppsSettings. :Returns: - ``True`` if the addon was successfully authenticated, else ``False`` """ try: log.info("Checking for unattended session...") self.props.credentials = AzureOAuth.get_unattended_session(config=cfg) log.info("Found!") return True except (AuthenticationException, InvalidConfigException) as exp: log.info("Could not get unattended session: {0}".format(exp)) try: log.info("Checking for cached session...") self.props.credentials = AzureOAuth.get_session(config=cfg) log.info("Found!") return True except (AuthenticationException, InvalidConfigException) as exp: log.info("Could not get cached session: {0}".format(exp)) return False
def test_azureoauth_get_session(self, mock_config, mock_credentials): """Test get_session""" mock_config.return_value = mock.create_autospec(Configuration) mock_config.return_value.aad_config.return_value = {'client_id':'abc'} AzureOAuth.get_session() self.assertTrue(mock_config.called) mock_credentials.assert_called_with(mock_config.return_value, 'abc') mock_config.reset_mock() mock_cfg = mock.create_autospec(Configuration) mock_cfg.aad_config.return_value = {'client_id':'abc'} AzureOAuth.get_session(config=mock_cfg) self.assertFalse(mock_config.called) mock_credentials.assert_called_with(mock_cfg, 'abc')
def open_websession(self): """ Open a web browser session to prompt the user to authenticate via their AAD credentials. This method of authentication is the 'last resort' after auto-authentication and unattended authentication have failed. :Raises: - :class:`RuntimeError` if authentication fails, which will fail the loading of the addon as all auth routes have failed. This could be due to either an :class:`batchapps.exceptions.AuthenticationException` of a :class:`batchapps.exceptions.InvalidConfigException`. """ session = bpy.context.scene.batchapps_session try: url, state = AzureOAuth.get_authorization_url(config=session.cfg) webbrowser.open(url) session.log.info("Opened web browser for authentication " "and waiting for response.") self.wait_for_request() except (AuthenticationException, InvalidConfigException) as exp: session.log.error("Unable to open Web UI auth session: " "{0}".format(exp)) raise RuntimeError("Failed to authorize addon")
def test_azureoauth_setup_session(self, mock_requests): """Test _setup_session""" AzureOAuth.state = None AzureOAuth._setup_session({}) mock_requests.OAuth2Session.assert_called_with( None, redirect_uri="http://None", state=None) session = AzureOAuth._setup_session({'redirect_uri':'1', 'client_id':'3'}) mock_requests.OAuth2Session.assert_called_with("3", redirect_uri="http://1", state=None) AzureOAuth.state = "test" session = AzureOAuth._setup_session({'redirect_uri':'1', 'client_id':'3'}) self.assertTrue(session.verify) mock_requests.OAuth2Session.assert_called_with("3", redirect_uri="http://1", state='test')
def test_azureoauth_get_authorization_token(self, mock_creds, mock_state, mock_setup, mock_config): """Test get_authorization_token""" mock_state.return_value = True mock_setup.return_value = mock.create_autospec( requests_oauthlib.OAuth2Session) mock_setup.return_value.fetch_token.return_value = {} mock_config.return_value.aad_config.return_value = {'root':'1/', 'unattended_key':'3', 'token_uri':'/auth', 'resource':'test', 'tenant':'common', 'client_id':'abc'} with self.assertRaises(InvalidConfigException): AzureOAuth.get_authorization_token("test", config="test") mock_setup.return_value.fetch_token.return_value = {} authed = AzureOAuth.get_authorization_token("test") mock_setup.assert_called_with(mock.ANY) mock_creds.assert_called_with(mock.ANY, mock.ANY, token={}) self.assertIsNotNone(authed) authed = AzureOAuth.get_authorization_token("test", state="test") mock_setup.assert_called_with(mock.ANY) mock_creds.assert_called_with(mock.ANY, 'abc', token={}) mock_state.return_value = False with self.assertRaises(AuthenticationException): authed = AzureOAuth.get_authorization_token("test") mock_state.return_value = True credentials.CA_CERT = "cacert.pem" AzureOAuth.get_authorization_token("test", state="test") mock_setup.return_value.fetch_token.assert_called_with( "https://1/common/auth", authorization_response='https://Nonetest', verify="cacert.pem") credentials.VERIFY = False AzureOAuth.get_authorization_token("test") mock_setup.return_value.fetch_token.assert_called_with( "https://1/common/auth", authorization_response='https://Nonetest', verify=False)
def test_azureoauth_get_authorization_url(self, mock_gen, mock_setup, mock_config): """Test get_authorization_url""" mock_gen.return_value = "gen_state_123" mock_setup.return_value = mock.create_autospec( requests_oauthlib.OAuth2Session) mock_config.return_value.aad_config.return_value = {'auth_uri':'1', 'resource':'2', 'root':'3', 'tenant':'4'} with self.assertRaises(AuthenticationException): AzureOAuth.get_authorization_url() mock_setup.return_value.authorization_url.return_value = ("a", "b") url, state = AzureOAuth.get_authorization_url() self.assertTrue(mock_config.called) self.assertIsNotNone(url) self.assertIsNotNone(state) mock_setup.return_value.authorization_url.assert_called_with('https://341', resource='https://2') self.assertEqual(AzureOAuth.state, "gen_state_123") mock_config.reset_mock() with self.assertRaises(InvalidConfigException): AzureOAuth.get_authorization_url(1) self.assertFalse(mock_config.called) url, state = AzureOAuth.get_authorization_url(msa=True, prompt=True, state="test") self.assertEqual(AzureOAuth.state, "test") mock_setup.return_value.authorization_url.assert_called_with('https://341', resource='https://2', prompt='login', domain_hint='live.com')
def test_azureoauth_check_state(self): """Test _check_state""" self.assertFalse(AzureOAuth._check_state("test", "abc")) self.assertFalse(AzureOAuth._check_state("test&abc", "abc")) self.assertFalse(AzureOAuth._check_state("test&state=xyx", "abc")) self.assertFalse(AzureOAuth._check_state("test&state=xyx&", "abc")) self.assertFalse(AzureOAuth._check_state("test&state=abcd&", "abc")) self.assertTrue(AzureOAuth._check_state("test&state=abc&", "abc"))
def web_authentication(self): """ Prompts user to authenticate via a web browser session, after auto-authentication and unattended authentication have failed. If web authentication is successful, the session (i.e. refresh token) will be cached to enable auto-authentication next time the addon is used. Session page will be set to the HOME page. If unsuccessful, the addon will failed to load and the ERROR page will be display. Error details will be logged to the console. """ session = bpy.context.scene.batchapps_session if self.auto_authentication(session.cfg, session.log): session.start(self.props.credentials) session.page = "HOME" session.redraw() return self.open_websession() if not self.props.code: session.log.warning("Log in timed out - please try again.") session.page = "LOGIN" elif '/?error=' in self.props.code: error = self.decode_error('/?error=') details = self.decode_error( '&error_description=').replace('+', ' ') session.log.error("Authentication failed: {0}".format(error)) session.log.error(details) session.page = "ERROR" else: session.log.info( "Received valid authentication response from web browser.") session.log.info("Now retrieving new authentication token...") self.props.credentials = AzureOAuth.get_authorization_token( self.props.code, config=session.cfg) session.start(self.props.credentials) session.log.info("Successful! Login complete.") session.redraw()
def web_authentication(self): """ Prompts user to authenticate via a web browser session, after auto-authentication and unattended authentication have failed. If web authentication is successful, the session (i.e. refresh token) will be cached to enable auto-authentication next time the addon is used. Session page will be set to the HOME page. If unsuccessful, the addon will failed to load and the ERROR page will be display. Error details will be logged to the console. """ session = bpy.context.scene.batchapps_session if self.auto_authentication(session.cfg, session.log): session.start(self.props.credentials) session.page = "HOME" session.redraw() return self.open_websession() if not self.props.code: session.log.warning("Log in timed out - please try again.") session.page = "LOGIN" elif '/?error=' in self.props.code: error = self.decode_error('/?error=') details = self.decode_error('&error_description=').replace( '+', ' ') session.log.error("Authentication failed: {0}".format(error)) session.log.error(details) session.page = "ERROR" else: session.log.info( "Received valid authentication response from web browser.") session.log.info("Now retrieving new authentication token...") self.props.credentials = AzureOAuth.get_authorization_token( self.props.code, config=session.cfg) session.start(self.props.credentials) session.log.info("Successful! Login complete.") session.redraw()
def test_azureoauth_get_principal_token(self, mock_token): """Test deprecated method get_principal_token""" AzureOAuth.get_principal_token() mock_token.assert_called_with(config=None) AzureOAuth.get_principal_token("hello") mock_token.assert_called_with(config="hello") AzureOAuth.get_principal_token(config="world") mock_token.assert_called_with(config="world")
def test_azureoauth_get_unattended_session(self, mock_creds, mock_config, mock_client, mock_req): """Test get_unattended_session""" mock_session = mock.create_autospec( requests_oauthlib.OAuth2Session) mock_req.OAuth2Session.return_value = mock_session mock_config.aad_config.return_value = {'root':'1/', 'unattended_key':'3', 'token_uri':'/auth', 'resource':'test', 'unattended_account':'abc'} with self.assertRaises(InvalidConfigException): AzureOAuth.get_unattended_session(mock_config) mock_config.aad_config.return_value['unattended_account'] = 'ClientID=abc;TenantID=common' AzureOAuth.get_unattended_session(mock_config) mock_client.assert_called_with("abc") mock_req.OAuth2Session.assert_called_with("abc", client=mock.ANY) mock_session.fetch_token.assert_called_with( "https://1/common/auth", client_id='abc', resource='https://test', client_secret='3', response_type='client_credentials', verify=True) mock_config.aad_config.return_value = {'root':'http://1/', 'unattended_key':'3', 'token_uri':'/auth', 'resource':'https://test', 'unattended_account':'ClientID=abc;TenantID=common'} AzureOAuth.get_unattended_session(mock_config) mock_client.assert_called_with("abc") mock_req.OAuth2Session.assert_called_with("abc", client=mock.ANY) mock_session.fetch_token.assert_called_with( "https://1/common/auth", client_id='abc', resource='https://test', client_secret='3', response_type='client_credentials', verify=True) credentials.CA_CERT = "cacert.pem" AzureOAuth.get_unattended_session(mock_config) mock_client.assert_called_with("abc") mock_req.OAuth2Session.assert_called_with("abc", client=mock.ANY) mock_session.fetch_token.assert_called_with( "https://1/common/auth", client_id='abc', resource='https://test', client_secret='3', response_type='client_credentials', verify="cacert.pem") credentials.VERIFY = False AzureOAuth.get_unattended_session(mock_config) mock_client.assert_called_with("abc") mock_req.OAuth2Session.assert_called_with("abc", client=mock.ANY) mock_session.fetch_token.assert_called_with( "https://1/common/auth", client_id='abc', resource='https://test', client_secret='3', response_type='client_credentials', verify=False)