diff --git a/CloudFlare/api_extras.py b/CloudFlare/api_extras.py index 36ea2ae..fced7b4 100644 --- a/CloudFlare/api_extras.py +++ b/CloudFlare/api_extras.py @@ -37,7 +37,7 @@ def api_extras(self, extras=None): api_call_part1 = '/'.join(element_path) api_call_part2 = '/'.join(parts[1]) setattr(m, parts[1][0], - self._add_with_auth(self._base, api_call_part1, api_call_part2)) + self._AddWithAuth(self._base, api_call_part1, api_call_part2)) current = m continue except: @@ -48,9 +48,9 @@ def api_extras(self, extras=None): api_call_part1 = '/'.join(element_path) api_call_part2 = '/'.join(parts[1]) setattr(current, element, - self._add_with_auth(self._base, api_call_part1, api_call_part2)) + self._AddWithAuth(self._base, api_call_part1, api_call_part2)) else: api_call_part1 = '/'.join(element_path) setattr(current, element, - self._add_with_auth(self._base, api_call_part1)) + self._AddWithAuth(self._base, api_call_part1)) current = getattr(current, element) diff --git a/CloudFlare/cloudflare.py b/CloudFlare/cloudflare.py index 0d52ad3..30312f2 100644 --- a/CloudFlare/cloudflare.py +++ b/CloudFlare/cloudflare.py @@ -19,21 +19,32 @@ class CloudFlare(object): class _v4base(object): """ Cloudflare v4 API""" - def __init__(self, email, token, certtoken, base_url, debug, raw, use_sessions, profile): + def __init__(self, config): """ Cloudflare v4 API""" - self.email = email - self.token = token - self.certtoken = certtoken - self.base_url = base_url - self.raw = raw - self.use_sessions = use_sessions - self.profile = profile + self.config = config + if 'email' in config: + self.email = config['email'] + else: + self.email = None + if 'token' in config: + self.token = config['token'] + else: + self.token = None + if 'certtoken' in config: + self.certtoken = config['certtoken'] + else: + self.certtoken = None + + self.base_url = config['base_url'] + self.raw = config['raw'] + self.use_sessions = config['use_sessions'] + self.profile = config['profile'] self.session = None self.user_agent = user_agent() - if debug: - self.logger = CFlogger(debug).getLogger() + if 'debug' in config and config['debug']: + self.logger = CFlogger(config['debug']).getLogger() else: self.logger = None @@ -43,33 +54,53 @@ def call_with_no_auth(self, method, parts, """ Cloudflare v4 API""" headers = {} - self._add_headers(headers) + self._AddHeaders(headers) return self._call(method, headers, parts, identifier1, identifier2, identifier3, params, data, files) - def _add_headers(self, headers): + def _AddHeaders(self, headers): """ Add default headers """ headers['User-Agent'] = self.user_agent headers['Content-Type'] = 'application/json' - def _add_auth_headers(self, headers): + def _AddAuthHeaders(self, headers, method): """ Add authentication headers """ - if self.email is None and self.token is None: + + v = 'email' + '.' + method.lower() + if v in self.config: + email = self.config[v] # use specific value for this method + else: + email = self.email # use generic value for all methods + + v = 'token' + '.' + method.lower() + if v in self.config: + token = self.config[v] # use specific value for this method + else: + token = self.token # use generic value for all methods + + if email is None and token is None: raise CloudFlareAPIError(0, 'no email and no token defined') - if self.token is None: + if token is None: raise CloudFlareAPIError(0, 'no token defined') - if self.email is None: - headers['Authorization'] = 'Bearer %s' % (self.token) + if email is None: + headers['Authorization'] = 'Bearer %s' % (token) else: - headers['X-Auth-Email'] = self.email - headers['X-Auth-Key'] = self.token + headers['X-Auth-Email'] = email + headers['X-Auth-Key'] = token - def _add_certtoken_headers(self, headers): + def _AddCerttokenHeaders(self, headers, method): """ Add authentication headers """ - if self.certtoken is None: + + v = 'certtoken' + '.' + method.lower() + if v in self.config: + certtoken = self.config[v] # use specific value for this method + else: + certtoken = self.certtoken # use generic value for all methods + + if certtoken is None: raise CloudFlareAPIError(0, 'no cert token defined') - headers['X-Auth-User-Service-Key'] = self.certtoken + headers['X-Auth-User-Service-Key'] = certtoken def call_with_auth(self, method, parts, identifier1=None, identifier2=None, identifier3=None, @@ -77,8 +108,8 @@ def call_with_auth(self, method, parts, """ Cloudflare v4 API""" headers = {} - self._add_headers(headers) - self._add_auth_headers(headers) + self._AddHeaders(headers) + self._AddAuthHeaders(headers, method) if isinstance(data, str): # passing javascript vs JSON headers['Content-Type'] = 'application/javascript' @@ -97,8 +128,8 @@ def call_with_auth_unwrapped(self, method, parts, """ Cloudflare v4 API""" headers = {} - self._add_headers(headers) - self._add_auth_headers(headers) + self._AddHeaders(headers) + self._AddAuthHeaders(headers, method) if isinstance(data, str): # passing javascript vs JSON headers['Content-Type'] = 'application/javascript' @@ -117,8 +148,8 @@ def call_with_certauth(self, method, parts, """ Cloudflare v4 API""" headers = {} - self._add_headers(headers) - self._add_certtoken_headers(headers) + self._AddHeaders(headers) + self._AddCerttokenHeaders(headers, method) return self._call(method, headers, parts, identifier1, identifier2, identifier3, params, data, files) @@ -506,7 +537,7 @@ def _call_unwrapped(self, method, headers, parts, result = response_data return result - class _add_unused(object): + class _AddUnused(object): """ Cloudflare v4 API""" def __init__(self, base, p1, p2=None, p3=None): @@ -551,7 +582,7 @@ def delete(self, identifier1=None, identifier2=None, identifier3=None, params=No raise CloudFlareAPIError(0, 'delete() call not available for this endpoint') - class _add_noauth(object): + class _AddNoAuth(object): """ Cloudflare v4 API""" def __init__(self, base, p1, p2=None, p3=None): @@ -598,7 +629,7 @@ def delete(self, identifier1=None, identifier2=None, identifier3=None, params=No raise CloudFlareAPIError(0, 'delete() call not available for this endpoint') - class _add_with_auth(object): + class _AddWithAuth(object): """ Cloudflare v4 API""" def __init__(self, base, p1, p2=None, p3=None): @@ -653,7 +684,7 @@ def delete(self, identifier1=None, identifier2=None, identifier3=None, params=No identifier1, identifier2, identifier3, params, data) - class _add_with_auth_unwrapped(object): + class _AddWithAuthUnwrapped(object): """ Cloudflare v4 API""" def __init__(self, base, p1, p2=None, p3=None): @@ -708,7 +739,7 @@ def delete(self, identifier1=None, identifier2=None, identifier3=None, params=No identifier1, identifier2, identifier3, params, data) - class _add_with_cert_auth(object): + class _AddWithCertAuth(object): """ Cloudflare v4 API""" def __init__(self, base, p1, p2=None, p3=None): @@ -784,15 +815,15 @@ def add(self, t, p1, p2=None, p3=None): name = a[-1] if t == 'VOID': - f = self._add_unused(self._base, p1, p2, p3) + f = self._AddUnused(self._base, p1, p2, p3) elif t == 'OPEN': - f = self._add_noauth(self._base, p1, p2, p3) + f = self._AddNoAuth(self._base, p1, p2, p3) elif t == 'AUTH': - f = self._add_with_auth(self._base, p1, p2, p3) + f = self._AddWithAuth(self._base, p1, p2, p3) elif t == 'CERT': - f = self._add_with_cert_auth(self._base, p1, p2, p3) + f = self._AddWithCertAuth(self._base, p1, p2, p3) elif t == 'AUTH_UNWRAPPED': - f = self._add_with_auth_unwrapped(self._base, p1, p2, p3) + f = self._AddWithAuthUnwrapped(self._base, p1, p2, p3) else: # should never happen raise CloudFlareAPIError(0, 'api load type mismatch') @@ -827,31 +858,41 @@ def __init__(self, email=None, token=None, certtoken=None, debug=False, raw=Fals base_url = BASE_URL - # class creation values override configuration values try: - [conf_email, conf_token, conf_certtoken, extras, profile] = read_configs(profile) + config = read_configs(profile) except: raise CloudFlareAPIError(0, 'profile/configuration read error') - if email is None: - email = conf_email - if token is None: - token = conf_token - if certtoken is None: - certtoken = conf_certtoken - - if email == '': - email = None - if token == '': - token = None - if certtoken == '': - certtoken = None - self._base = self._v4base(email, token, certtoken, base_url, debug, raw, use_sessions, profile) + # class creation values override all configuration values + if email is not None: + config['email'] = email + if token is not None: + config['token'] = token + if certtoken is not None: + config['certtoken'] = certtoken + if base_url is not None: + config['base_url'] = base_url + if debug is not None: + config['debug'] = debug + if raw is not None: + config['raw'] = raw + if use_sessions is not None: + config['use_sessions'] = use_sessions + if profile is not None: + config['profile'] = profile + + # we do not need to handle item.call values - they pass straight thru + + for x in config: + if config[x] == '': + config[x] = None + + self._base = self._v4base(config) # add the API calls api_v4(self) - if extras: - api_extras(self, extras) + if 'extras' in config and config['extras']: + api_extras(self, config['extras']) def __call__(self): """ Cloudflare v4 API""" diff --git a/CloudFlare/read_configs.py b/CloudFlare/read_configs.py index cb6d36d..95243b2 100644 --- a/CloudFlare/read_configs.py +++ b/CloudFlare/read_configs.py @@ -3,64 +3,77 @@ import os import re try: - import ConfigParser # py2 + import configparser # py3 except ImportError: - import configparser as ConfigParser # py3 + import ConfigParser as configparser # py2 def read_configs(profile=None): """ reading the config file for Cloudflare API""" - # envioronment variables override config files - email = os.getenv('CF_API_EMAIL') - token = os.getenv('CF_API_KEY') - certtoken = os.getenv('CF_API_CERTKEY') - extras = os.getenv('CF_API_EXTRAS') - - # grab values from config files - config = ConfigParser.RawConfigParser() - config.read([ - '.cloudflare.cfg', - os.path.expanduser('~/.cloudflare.cfg'), - os.path.expanduser('~/.cloudflare/cloudflare.cfg') - ]) + # We return all these values + config = {'email': None, 'token': None, 'certtoken': None, 'extras': None, 'profile': None} + # envioronment variables override config files - so setup first + config['email'] = os.getenv('CF_API_EMAIL') + config['token'] = os.getenv('CF_API_KEY') + config['certtoken'] = os.getenv('CF_API_CERTKEY') + config['extras'] = os.getenv('CF_API_EXTRAS') if profile is None: profile = 'CloudFlare' + config['profile'] = profile - if len(config.sections()) == 0: - ## no config file found - so env values (even if empty) should be returned - ## this isn't an error - return [email, token, certtoken, extras, profile] - - if profile not in config.sections(): - ## section is missing - this is an error - raise - - if email is None: - try: - email = re.sub(r"\s+", '', config.get(profile, 'email')) - except (ConfigParser.NoOptionError, ConfigParser.NoSectionError): - email = None + # grab values from config files + cp = configparser.ConfigParser() + try: + cp.read([ + '.cloudflare.cfg', + os.path.expanduser('~/.cloudflare.cfg'), + os.path.expanduser('~/.cloudflare/cloudflare.cfg') + ]) + except Exception as e: + raise Exception("%s: configuration file error" % (profile)) - if token is None: + if len(cp.sections()) > 0: + # we have a configuration file - lets use it try: - token = re.sub(r"\s+", '', config.get(profile, 'token')) - except (ConfigParser.NoOptionError, ConfigParser.NoSectionError): - token = None + # grab the section - as we will use it for all values + section = cp[profile] + except Exception as e: + # however section name is missing - this is an error + raise Exception("%s: configuration section missing" % (profile)) - if certtoken is None: - try: - certtoken = re.sub(r"\s+", '', config.get(profile, 'certtoken')) - except (ConfigParser.NoOptionError, ConfigParser.NoSectionError): - certtoken = None + for option in ['email', 'token', 'certtoken', 'extras']: + if option not in config or config[option] is None: + try: + config[option] = re.sub(r"\s+", '', section.get(option)) + if config[option] == '': + config.pop(option) + except (configparser.NoOptionError, configparser.NoSectionError): + pass + except Exception as e: + pass + # do we have an override for specific calls? (i.e. token.post or email.get etc) + for method in ['get', 'patch', 'post', 'put', 'delete']: + option_for_method = option + '.' + method + try: + config[option_for_method] = re.sub(r"\s+", '', section.get(option_for_method)) + if config[option_for_method] == '': + config.pop(option_for_method) + except (configparser.NoOptionError, configparser.NoSectionError) as e: + pass + except Exception as e: + pass - if extras is None: - try: - extras = re.sub(r"\s+", ' ', config.get(profile, 'extras')) - except (ConfigParser.NoOptionError, ConfigParser.NoSectionError): - extras = None + # do any final cleanup - only needed for extras (which are multiline) + if 'extras' in config and config['extras'] is not None: + config['extras'] = config['extras'].split(' ') - if extras: - extras = extras.split(' ') + # remove blank entries + for x in sorted(config.keys()): + if config[x] is None or config[x] == '': + try: + config.pop(x) + except: + pass - return [email, token, certtoken, extras, profile] + return config