def test_called_with_alt_file(self, mock_open, mock_load): cfg = ("[smc]\n" "smc_address=1.1.1.1\n" "smc_apikey=12345abcdef") mock_open.return_value = io.StringIO(u'{}'.format(cfg)) load_from_file(alt_filepath='/Users/myfile') mock_open.assert_called_once_with('/Users/myfile', 'rt', encoding='UTF-8')
def test_missing_credential_file_in_home_directory(self, mock_open): """ Raises IOError because ~/.smcrc not found and alt_filepath not specified during login. """ mock_open.side_effect = IOError() self.assertRaises(ConfigLoadError, lambda: load_from_file())
def test_missing_section(self, mock_open): """ Raises NoSectionError when missing main section [smc] """ cfg = ("[foo]\n" "smc_address=1.1.1.1") mock_open.return_value = io.StringIO(u'{}'.format(cfg)) self.assertRaises(ConfigLoadError, lambda: load_from_file())
def test_missing_field_for_section(self, mock_open): """ Raises NoOptionError when [smc] section is present but credential information is not complete """ cfg = ("[smc]\n" "smc_address=1.1.1.1") mock_open.return_value = io.StringIO(u'{}'.format(cfg)) self.assertRaises(ConfigLoadError, lambda: load_from_file())
def test_ssl_enabled_settings(self, mock_open): """ SSL specified, but verify disabled """ cfg = ("[smc]\n" "smc_address=1.1.1.1\n" "smc_apikey=12345\n" "smc_ssl=True\n") mock_open.return_value = io.StringIO(u'{}'.format(cfg)) login_dict = load_from_file() self.assertTrue(login_dict.get('verify') == False)
def test_ssl_with_no_cert_file_and_ssl_enabled(self, mock_open): """ SSL specified, verify enabled, not cert to verify against, verify will be disabled """ cfg = ("[smc]\n" "smc_address=1.1.1.1\n" "smc_apikey=12345\n" "smc_ssl=True\n" "verify_ssl=True") mock_open.return_value = io.StringIO(u'{}'.format(cfg)) login_dict = load_from_file() self.assertTrue(login_dict.get('verify') == False)
def test_bogus_entries_are_ignored(self, mock_open): cfg = ("[smc]\n" "smc_address=1.1.1.1\n" "smc_apikey=12345abcdef\n" "foo=bar\n" "bar=foo") mock_open.return_value = io.StringIO(u'{}'.format(cfg)) # This will fall down to transform_login to build the structure to call # login. This should not have unknown attributes in the dict login_dict = load_from_file() self.assertNotIn('foo', login_dict) self.assertNotIn('bar', login_dict)
def test_invalid_timeout_and_api_version(self, mock_open): """ If invalid timeout setting provided, i.e. 10a, 20b, etc, timeout is disabled """ cfg = ("[smc]\n" "smc_address=1.1.1.1\n" "smc_apikey=12345\n" "timeout=60a\n" "api_version=abc") mock_open.return_value = io.StringIO(u'{}'.format(cfg)) login_dict = load_from_file() self.assertIsNone(login_dict.get('timeout')) self.assertIsNone(login_dict.get('api_version'))
def login(self, **kwargs): """ Login to SMC API and retrieve a valid session. Session will be re-used when multiple queries are required. An example login and logout session:: from smc.qpi.session import session session.login(url='http://1.1.1.1:8082', api_key='SomeSMCG3ener@t3dPwd') .....do stuff..... session.logout() :param url: ip of SMC management server :param api_key: API key created for api client in SMC :param api_version (optional): specify api version Logout should be called to remove the session immediately from the SMC server. #TODO: Implement SSL tracking """ if kwargs: for key, value in kwargs.items(): setattr(self, key, value) else: try: self.login(**load_from_file()) except ConfigLoadError: raise self.cache = SessionCache() self.cache.get_api_entry(self.url, self.api_version) self.api_version = self.cache.api_version s = requests.session() #no session yet r = s.post(self.cache.get_entry_href('login'), json={'authenticationkey': self.api_key}, headers={'content-type': 'application/json'} ) if r.status_code == 200: self.session = s #session creation was successful self.cookies = self.session.cookies.items() logger.debug("Login succeeded and session retrieved: %s", \ self.cookies) self.connection = SMCAPIConnection(self) else: raise SMCConnectionError("Login failed, HTTP status code: %s" \ % r.status_code)
def test_valid_timeout_and_apiversion(self, mock_open): """ Test the timeout field as valid value """ cfg = ("[smc]\n" "smc_address=1.1.1.1\n" "smc_apikey=12345\n" "timeout=60\n" "api_version=6.1\n" "domain=mydomain") mock_open.return_value = io.StringIO(u'{}'.format(cfg)) login_dict = load_from_file() self.assertEqual(login_dict.get('timeout'), 60) self.assertEqual(login_dict.get('api_version'), 6.1) self.assertEqual(login_dict.get('domain'), 'mydomain')
def test_ssl_enabled_with_cert_verify(self, mock_open): """ SSL specified, verification enabled and cert file provided """ cfg = ("[smc]\n" "smc_address=1.1.1.1\n" "smc_apikey=12345\n" "smc_ssl=True\n" "verify_ssl=True\n" "ssl_cert_file=/usr/local/cert.pem") mock_open.return_value = io.StringIO(u'{}'.format(cfg)) login_dict = load_from_file() self.assertEqual(login_dict.get('verify'), '/usr/local/cert.pem') # Transformed out of the final configuration self.assertNotIn('smc_ssl', login_dict) self.assertNotIn('verify_ssl', login_dict)
def login(self, url=None, api_key=None, login=None, pwd=None, api_version=None, timeout=None, verify=True, alt_filepath=None, domain=None, **kwargs): """ Login to SMC API and retrieve a valid session. Session will be re-used when multiple queries are required. An example login and logout session:: from smc import session session.login(url='http://1.1.1.1:8082', api_key='SomeSMCG3ener@t3dPwd') .....do stuff..... session.logout() :param str url: ip of SMC management server :param str api_key: API key created for api client in SMC :param str login: Administrator user in SMC that has privilege to SMC API. :param str pwd: Password for user login. :param api_version (optional): specify api version :param int timeout: (optional): specify a timeout for initial connect; (default 10) :param str|boolean verify: verify SSL connections using cert (default: verify=True) You can pass verify the path to a CA_BUNDLE file or directory with certificates of trusted CAs :param str alt_filepath: If using .smcrc, alternate file+path :param str domain: domain to log in to. If domains are not configured, this field will be ignored and api client logged in to 'Shared Domain'. :raises ConfigLoadError: loading cfg from ~.smcrc fails For SSL connections, you can disable validation of the SMC SSL certificate by setting verify=False, however this is not a recommended practice. If you want to use the SSL certificate generated and used by the SMC API server for validation, set verify='path_to_my_dot_pem'. It is also recommended that your certificate has subjectAltName defined per RFC 2818 If SSL warnings are thrown in debug output, see: https://urllib3.readthedocs.io/en/latest/advanced-usage.html#ssl-warnings Logout should be called to remove the session immediately from the SMC server. .. note:: As of SMC 6.4 it is possible to give a standard Administrative user access to the SMC API. It is still possible to use an API Client by providing the api_key in the login call. """ if not url or (not api_key and not (login and pwd)): # First try load from file try: cfg = load_from_file(alt_filepath) if alt_filepath\ is not None else load_from_file() logger.debug('Read config data from file: %s', cfg) except ConfigLoadError: # Last ditch effort, try to load from environment cfg = load_from_environ() logger.debug('Read config data from environ: %s', cfg) url = cfg.get('url') api_key = cfg.get('api_key') api_version = cfg.get('api_version') verify = cfg.get('verify') timeout = cfg.get('timeout') domain = cfg.get('domain') kwargs = cfg.get('kwargs') if timeout: self._timeout = timeout self._api_version = get_api_version(url, api_version, timeout, verify) base = get_api_base(url, self.api_version, verify=verify) self._resource.add(get_entry_points(base, timeout, verify)) s = requests.session() # empty session json = {'domain': domain} if api_key: json.update(authenticationkey=api_key) if kwargs: json.update(**kwargs) self._extra_args.update(**kwargs) params = dict(login=login, pwd=pwd) if login and pwd else None req = dict( url=self.entry_points.get('login') if api_key else \ '{}/{}/lms_login'.format(url, self._api_version), json=json, params=params, headers={'content-type': 'application/json'}, verify=verify) r = s.post(**req) logger.info('Using SMC API version: %s', self.api_version) if r.status_code == 200: self._session = s # session creation was successful self._session.verify = verify # make verify setting persistent self._url = url self.credential.set_credentials(api_key, login, pwd) if domain: self._domain = domain self._sessions[self.domain] = self.session if self.connection is None: self._connection = smc.api.web.SMCAPIConnection(self) logger.debug( 'Login succeeded and session retrieved: %s, domain: %s', self.session_id, self.domain) # Reload entry points self.entry_points.clear() self._resource.add(reload_entry_points(self)) else: raise SMCConnectionError( 'Login failed, HTTP status code: %s and reason: %s' % (r.status_code, r.reason)) if not self._MODS_LOADED: logger.debug('Registering class mappings.') # Load the modules to register needed classes for pkg in ('smc.policy', 'smc.elements', 'smc.routing', 'smc.vpn', 'smc.administration', 'smc.core', 'smc.administration.user_auth'): import_submodules(pkg, recursive=False) self._MODS_LOADED = True
def login(self, url=None, api_key=None, api_version=None, timeout=None, verify=True, alt_filepath=None, **kwargs): """ Login to SMC API and retrieve a valid session. Session will be re-used when multiple queries are required. An example login and logout session:: from smc import session session.login(url='http://1.1.1.1:8082', api_key='SomeSMCG3ener@t3dPwd') .....do stuff..... session.logout() :param str url: ip of SMC management server :param str api_key: API key created for api client in SMC :param api_version (optional): specify api version :param int timeout: (optional): specify a timeout for initial connect; (default 10) :param str|boolean verify: verify SSL connections using cert (default: verify=True) :param str alt_filepath: If using .smcrc, alternate file+path For SSL connections, you can disable validation of the SMC SSL certificate by setting verify=False, however this is not a recommended practice. If you want to use the SSL certificate generated and used by the SMC API server for validation, set verify='path_to_my_dot_pem'. It is also recommended that your certificate has subjectAltName defined per RFC 2818 If SSL warnings are thrown in debug output, see: https://urllib3.readthedocs.io/en/latest/advanced-usage.html#ssl-warnings Logout should be called to remove the session immediately from the SMC server. """ if url and api_key: self._url = url self._api_key = api_key if timeout: self._timeout = timeout else: try: cfg = load_from_file(alt_filepath) if alt_filepath\ is not None else load_from_file() logger.debug("Config read has data: %s", cfg) self._url = cfg.get('url') self._api_key = cfg.get('api_key') api_version = cfg.get('api_version') verify = cfg.get('verify') timeout = cfg.get('timeout') if timeout: self._timeout = timeout except ConfigLoadError: raise self.cache.get_api_entry(self.url, api_version, timeout=self.timeout, verify=verify) s = requests.session() #no session yet r = s.post(self.cache.get_entry_href('login'), json={'authenticationkey': self.api_key}, headers={'content-type': 'application/json'}, verify=verify) if r.status_code == 200: self._session = s #session creation was successful self._session.verify = verify #make verify setting persistent logger.debug("Login succeeded and session retrieved: %s", \ self.session_id) self._connection = smc.api.web.SMCAPIConnection(self) else: raise SMCConnectionError("Login failed, HTTP status code: %s" \ % r.status_code)
def login(self, url=None, api_key=None, login=None, pwd=None, api_version=None, timeout=None, verify=True, alt_filepath=None, domain=None, **kwargs): """ Login to SMC API and retrieve a valid session. Session will be re-used when multiple queries are required. An example login and logout session:: from smc import session session.login(url='http://1.1.1.1:8082', api_key='SomeSMCG3ener@t3dPwd') .....do stuff..... session.logout() :param str url: ip of SMC management server :param str api_key: API key created for api client in SMC :param str login: Administrator user in SMC that has privilege to SMC API. :param str pwd: Password for user login. :param api_version (optional): specify api version :param int timeout: (optional): specify a timeout for initial connect; (default 10) :param str|boolean verify: verify SSL connections using cert (default: verify=True) You can pass verify the path to a CA_BUNDLE file or directory with certificates of trusted CAs :param str alt_filepath: If using .smcrc, alternate file+path :param str domain: domain to log in to. If domains are not configured, this field will be ignored and api client logged in to 'Shared Domain'. :param bool retry_on_busy: pass as kwarg with boolean if you want to add retries if the SMC returns HTTP 503 error during operation. You can also optionally customize this behavior and call :meth:`.set_retry_on_busy` :raises ConfigLoadError: loading cfg from ~.smcrc fails For SSL connections, you can disable validation of the SMC SSL certificate by setting verify=False, however this is not a recommended practice. If you want to use the SSL certificate generated and used by the SMC API server for validation, set verify='path_to_my_dot_pem'. It is also recommended that your certificate has subjectAltName defined per RFC 2818 If SSL warnings are thrown in debug output, see: https://urllib3.readthedocs.io/en/latest/advanced-usage.html#ssl-warnings Logout should be called to remove the session immediately from the SMC server. .. note:: As of SMC 6.4 it is possible to give a standard Administrative user access to the SMC API. It is still possible to use an API Client by providing the api_key in the login call. """ if not url or (not api_key and not (login and pwd)): # First try load from file try: cfg = load_from_file(alt_filepath) if alt_filepath\ is not None else load_from_file() logger.debug('Read config data from file: %s', cfg) except ConfigLoadError: # Last ditch effort, try to load from environment cfg = load_from_environ() logger.debug('Read config data from environ: %s', cfg) url = cfg.get('url') api_key = cfg.get('api_key') api_version = cfg.get('api_version') verify = cfg.get('verify') timeout = cfg.get('timeout') domain = cfg.get('domain') kwargs = cfg.get('kwargs', {}) self._timeout = timeout or self._timeout self._domain = domain or self._domain self._url = url # Determine and set the API version we will use. self._api_version = get_api_version(url, api_version, timeout, verify) # Set the auth provider which will determine what type of login this is self.credential = Credential(api_key, login, pwd) # Retries configured generically retry_on_busy = kwargs.pop('retry_on_busy', False) request = self._build_auth_request(verify=verify, **kwargs) # This will raise if session login fails... self._session = self._get_session(request) self.session.verify = verify logger.debug('Login succeeded and session retrieved: %s, domain: %s', self.session_id, self.domain) if retry_on_busy: self.set_retry_on_busy() self._sessions[self.domain] = self.session if self.connection is None: self._connection = smc.api.web.SMCAPIConnection(self) # Load entry points load_entry_points(self) if not self._MODS_LOADED: logger.debug('Registering class mappings.') # Load the modules to register needed classes for pkg in ('smc.policy', 'smc.elements', 'smc.routing', 'smc.vpn', 'smc.administration', 'smc.core', 'smc.administration.user_auth'): import_submodules(pkg, recursive=False) self._MODS_LOADED = True