%PDF- %PDF-
| Direktori : /lib/python3/dist-packages/CloudFlare/ |
| Current File : //lib/python3/dist-packages/CloudFlare/cloudflare.py |
""" Cloudflare v4 API"""
from __future__ import absolute_import
import json
import requests
from .network import CFnetwork
from .logging_helper import CFlogger
from .utils import user_agent, build_curl
from .read_configs import read_configs
from .api_v4 import api_v4
from .api_extras import api_extras
from .api_decode_from_web import api_decode_from_web
from .exceptions import CloudFlareError, CloudFlareAPIError, CloudFlareInternalError
BASE_URL = 'https://api.cloudflare.com/client/v4'
class CloudFlare(object):
""" Cloudflare v4 API"""
class _v4base(object):
""" Cloudflare v4 API"""
def __init__(self, config):
""" Cloudflare v4 API"""
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
if 'base_url' in config:
self.base_url = config['base_url']
else:
# We must have a base_url value
self.base_url = BASE_URL
self.raw = config['raw']
self.use_sessions = config['use_sessions']
self.profile = config['profile']
self.network = CFnetwork(use_sessions=self.use_sessions)
self.user_agent = user_agent()
if 'debug' in config and config['debug']:
self.logger = CFlogger(config['debug']).getLogger()
else:
self.logger = None
def call_with_no_auth(self, method, parts,
identifier1=None, identifier2=None, identifier3=None,
params=None, data=None, files=None):
""" Cloudflare v4 API"""
headers = {}
self._AddHeaders(headers)
return self._call(method, headers, parts,
identifier1, identifier2, identifier3,
params, data, files)
def _AddHeaders(self, headers):
""" Add default headers """
headers['User-Agent'] = self.user_agent
headers['Content-Type'] = 'application/json'
def _AddAuthHeaders(self, headers, method):
""" Add authentication headers """
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 token is None:
raise CloudFlareAPIError(0, 'no token defined')
if email is None:
headers['Authorization'] = 'Bearer %s' % (token)
else:
headers['X-Auth-Email'] = email
headers['X-Auth-Key'] = token
def _AddCerttokenHeaders(self, headers, method):
""" Add authentication headers """
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'] = certtoken
def call_with_auth(self, method, parts,
identifier1=None, identifier2=None, identifier3=None,
params=None, data=None, files=None):
""" Cloudflare v4 API"""
headers = {}
self._AddHeaders(headers)
self._AddAuthHeaders(headers, method)
if isinstance(data, str):
# passing javascript vs JSON
headers['Content-Type'] = 'application/javascript'
if files:
# overwrite Content-Type as we are uploading data
headers['Content-Type'] = 'multipart/form-data'
# however something isn't right and this works ... look at again later!
del headers['Content-Type']
return self._call(method, headers, parts,
identifier1, identifier2, identifier3,
params, data, files)
def call_with_auth_unwrapped(self, method, parts,
identifier1=None, identifier2=None, identifier3=None,
params=None, data=None, files=None):
""" Cloudflare v4 API"""
headers = {}
self._AddHeaders(headers)
self._AddAuthHeaders(headers, method)
if isinstance(data, str):
# passing javascript vs JSON
headers['Content-Type'] = 'application/javascript'
if files:
# overwrite Content-Type as we are uploading data
headers['Content-Type'] = 'multipart/form-data'
# however something isn't right and this works ... look at again later!
del headers['Content-Type']
return self._call_unwrapped(method, headers, parts,
identifier1, identifier2, identifier3,
params, data, files)
def call_with_certauth(self, method, parts,
identifier1=None, identifier2=None, identifier3=None,
params=None, data=None, files=None):
""" Cloudflare v4 API"""
headers = {}
self._AddHeaders(headers)
self._AddCerttokenHeaders(headers, method)
return self._call(method, headers, parts,
identifier1, identifier2, identifier3,
params, data, files)
def _call_network(self, method, headers, parts,
identifier1=None, identifier2=None, identifier3=None,
params=None, data=None, files=None):
""" Cloudflare v4 API"""
if (method is None) or (parts[0] is None):
# should never happen
raise CloudFlareInternalError(0, 'You must specify a method and endpoint')
if parts[1] is not None or (data is not None and method == 'GET'):
if identifier1 is None:
raise CloudFlareAPIError(0, 'You must specify identifier1')
if identifier2 is None:
url = (self.base_url + '/'
+ parts[0] + '/'
+ identifier1 + '/'
+ parts[1])
else:
url = (self.base_url + '/'
+ parts[0] + '/'
+ identifier1 + '/'
+ parts[1] + '/'
+ identifier2)
else:
if identifier1 is None:
url = (self.base_url + '/'
+ parts[0])
else:
url = (self.base_url + '/'
+ parts[0] + '/'
+ identifier1)
if parts[2]:
url += '/' + parts[2]
if identifier3:
url += '/' + identifier3
if self.logger:
msg = build_curl(method, url, headers, params, data, files)
self.logger.debug('Call: emulated curl command ...\n' + msg)
try:
response = self.network(method, url, headers, params, data, files)
except Exception as e:
if self.logger:
self.logger.debug('Call: exception! "%s"' % (e))
raise CloudFlareAPIError(0, 'connection failed.')
# Create response_{type|code|data}
try:
response_type = response.headers['Content-Type']
if ';' in response_type:
# remove the ;paramaters part (like charset=, etc.)
response_type = response_type[0:response_type.rfind(';')]
response_type = response_type.strip().lower()
except:
# API should always response; but if it doesn't; here's the default
response_type = 'application/octet-stream'
response_code = response.status_code
response_data = response.content
if not isinstance(response_data, (str, bytes, bytearray)):
response_data = response_data.decode("utf-8")
if self.logger:
self.logger.debug('Response: %d, %s, %s',
response_code, response_type, response_data)
if response_code >= 500 and response_code <= 599:
# 500 Internal Server Error
# 501 Not Implemented
# 502 Bad Gateway
# 503 Service Unavailable
# 504 Gateway Timeout
# 505 HTTP Version Not Supported
# 506 Variant Also Negotiates
# 507 Insufficient Storage
# 508 Loop Detected
# 509 Unassigned
# 510 Not Extended
# 511 Network Authentication Required
# the libary doesn't deal with these errors, just pass upwards!
# there's no value to add and the returned data is questionable or not useful
response.raise_for_status()
# should not be reached
raise CloudFlareInternalError(0, 'internal error in status code processing')
#if response_code >= 400 and response_code <= 499:
# # 400 Bad Request
# # 401 Unauthorized
# # 403 Forbidden
# # 405 Method Not Allowed
# # 415 Unsupported Media Type
# # 429 Too many requests
#
# # don't deal with these errors, just pass upwards!
# response.raise_for_status()
#
#if response_code >= 300 and response_code <= 399:
# # 304 Not Modified
#
# # don't deal with these errors, just pass upwards!
# response.raise_for_status()
#
# should be a 200 response at this point
return [response_type, response_code, response_data]
def _raw(self, method, headers, parts,
identifier1=None, identifier2=None, identifier3=None,
params=None, data=None, files=None):
""" Cloudflare v4 API"""
[response_type, response_code, response_data] = self._call_network(method,
headers, parts,
identifier1,
identifier2,
identifier3,
params, data, files)
if response_type == 'application/json':
# API says it's JSON; so it better be parsable as JSON
# NDJSON is returned by Enterprise Log Share i.e. /zones/:id/logs/received
if hasattr(response_data, 'decode'):
response_data = response_data.decode('utf-8')
try:
response_data = json.loads(response_data)
if not isinstance(response_data, (dict)):
response_data = {'success': True,
'result': response_data}
except ValueError:
if response_data == '':
# This should really be 'null' but it isn't. Even then, it's wrong!
if response_code == requests.codes.ok:
# 200 ok
response_data = {'success': True,
'result': None}
else:
# 3xx & 4xx errors
response_data = {'success': False,
'code': response_code,
'result': None}
else:
# Lets see if it's NDJSON data
# NDJSON is a series of JSON elements with newlines between each element
try:
r = []
for l in response_data.splitlines():
r.append(json.loads(l))
response_data = r
except:
# While this should not happen; it's always possible
if self.logger:
self.logger.debug('Response data not JSON: %r', response_data)
raise CloudFlareAPIError(0, 'JSON parse failed - report to Cloudflare.')
if response_code == requests.codes.ok:
# 200 ok - so nothing needs to be done
pass
else:
# 3xx & 4xx errors - we should report that somehow - but not quite yet
# response_data['code'] = response_code
pass
elif response_type == 'application/octet-stream' and isinstance(response_data, (int, float)):
# It's binary data
if response_code == requests.codes.ok:
# 200 ok
response_data = {'success': True,
'result': response_data}
else:
# 3xx & 4xx errors
response_data = {'success': False,
'code': response_code,
'result': response_data}
elif response_type == 'application/octet-stream' and isinstance(response_data, (bytes, bytearray)):
# API says it's text; but maybe it's actually JSON? - should be fixed in API
if hasattr(response_data, 'decode'):
response_data = response_data.decode('utf-8')
try:
response_data = json.loads(response_data)
if not isinstance(response_data, (dict)) or 'success' not in response_data:
if response_code == requests.codes.ok:
# 200 ok
response_data = {'success': True,
'result': response_data}
else:
# 3xx & 4xx errors
response_data = {'success': False,
'code': response_code,
'result': response_data}
except ValueError:
# So it wasn't JSON - moving on as if it's text!
# A single value is returned (vs an array or object)
if response_code == requests.codes.ok:
# 200 ok
response_data = {'success': True, 'result': response_data}
else:
# 3xx & 4xx errors
response_data = {'success': False,
'code': response_code,
'result': response_data}
elif response_type == 'text/plain' or response_type == 'application/octet-stream':
# API says it's text; but maybe it's actually JSON? - should be fixed in API
if hasattr(response_data, 'decode'):
response_data = response_data.decode('utf-8')
try:
response_data = json.loads(response_data)
if not isinstance(response_data, (dict)):
response_data = {'success': True,
'result': response_data}
except ValueError:
# So it wasn't JSON - moving on as if it's text!
# A single value is returned (vs an array or object)
if response_code == requests.codes.ok:
# 200 ok
response_data = {'success': True, 'result': response_data}
else:
# 3xx & 4xx errors
response_data = {'success': False,
'code': response_code,
'result': response_data}
elif response_type == 'text/javascript' or response_type == 'application/javascript':
# used by Cloudflare workers
if response_code == requests.codes.ok:
# 200 ok
response_data = {'success': True,
'result': str(response_data)}
else:
# 3xx & 4xx errors
response_data = {'success': False,
'code': response_code,
'result': str(response_data)}
elif response_type == 'text/html':
# used by media for preview
if response_code == requests.codes.ok:
# 200 ok
response_data = {'success': True,
'result': str(response_data)}
else:
# 3xx & 4xx errors
response_data = {'success': False,
'code': response_code,
'result': str(response_data)}
else:
# Assuming nothing - but continuing anyway
# A single value is returned (vs an array or object)
if response_code == requests.codes.ok:
# 200 ok
response_data = {'success': True,
'result': str(response_data)}
else:
# 3xx & 4xx errors
response_data = {'success': False,
'code': response_code,
'result': str(response_data)}
# it would be nice to return the error code and content type values; but not quite yet
return response_data
def _call(self, method, headers, parts,
identifier1=None, identifier2=None, identifier3=None,
params=None, data=None, files=None):
""" Cloudflare v4 API"""
response_data = self._raw(method, headers, parts,
identifier1, identifier2, identifier3,
params, data, files)
# Sanatize the returned results - just in case API is messed up
if 'success' not in response_data:
if 'errors' in response_data:
if response_data['errors'] == None:
# Only happens on /graphql call
if self.logger:
self.logger.debug('Response: assuming success = "True"')
response_data['success'] = True
else:
if self.logger:
self.logger.debug('Response: assuming success = "False"')
# The following only happens on /graphql call
try:
message = response_data['errors'][0]['message']
except:
message = ''
try:
location = str(response_data['errors'][0]['location'])
except:
location = ''
try:
path = '>'.join(response_data['errors'][0]['path'])
except:
path = ''
response_data['errors'] = [{'code': 99999, 'message': message + ' - ' + location + ' - ' + path}]
response_data['success'] = False
else:
if 'result' not in response_data:
# Only happens on /certificates call
# should be fixed in /certificates API
if self.logger:
self.logger.debug('Response: assuming success = "False"')
r = response_data
response_data['errors'] = []
response_data['errors'].append(r)
response_data['success'] = False
else:
if self.logger:
self.logger.debug('Response: assuming success = "True"')
response_data['success'] = True
if response_data['success'] is False:
if 'errors' in response_data:
errors = response_data['errors'][0]
else:
errors = {}
if 'code' in errors:
code = errors['code']
else:
code = 99998
if 'message' in errors:
message = errors['message']
elif 'error' in errors:
message = errors['error']
else:
message = ''
##if 'messages' in response_data:
## errors['error_chain'] = response_data['messages']
if 'error_chain' in errors:
error_chain = errors['error_chain']
for error in error_chain:
if self.logger:
self.logger.debug('Response: error %d %s - chain',
error['code'],
error['message'])
if self.logger:
self.logger.debug('Response: error %d %s', code, message)
raise CloudFlareAPIError(code, message, error_chain)
else:
if self.logger:
self.logger.debug('Response: error %d %s', code, message)
raise CloudFlareAPIError(code, message)
if self.raw:
result = {}
# theres always a result value - unless it's a graphql query
try:
result['result'] = response_data['result']
except:
result['result'] = response_data
# theres may not be a result_info on every call
if 'result_info' in response_data:
result['result_info'] = response_data['result_info']
# no need to return success, errors, or messages as they return via an exception
else:
# theres always a result value - unless it's a graphql query
try:
result = response_data['result']
except:
result = response_data
if self.logger:
self.logger.debug('Response: %s', result)
return result
def _call_unwrapped(self, method, headers, parts,
identifier1=None, identifier2=None, identifier3=None,
params=None, data=None, files=None):
""" Cloudflare v4 API"""
response_data = self._raw(method, headers, parts,
identifier1, identifier2, identifier3,
params, data, files)
if self.logger:
self.logger.debug('Response: %s', response_data)
result = response_data
return result
def _api_from_web(self):
""" Cloudflare v4 API"""
# base url isn't enough; we need less
url = '/'.join(self.base_url.split('/')[0:3])
try:
if self.logger:
self.logger.debug('Call: doit!')
response = self.network('GET', url)
if self.logger:
self.logger.debug('Call: done!')
except Exception as e:
if self.logger:
self.logger.debug('Call: exception! "%s"' % (e))
raise CloudFlareAPIError(0, 'connection failed.')
return response.text
class _AddUnused(object):
""" Cloudflare v4 API"""
def __init__(self, base, p1, p2=None, p3=None):
""" Cloudflare v4 API"""
self._base = base
self._parts_unused = [p1, p2, p3]
def __call__(self, identifier1=None, identifier2=None, identifier3=None, params=None, data=None):
""" Cloudflare v4 API"""
# This is the same as a get()
return self.get(identifier1, identifier2, identifier3, params, data)
def __str__(self):
""" Cloudflare v4 API"""
return '[%s]' % ('/' + '/:id/'.join(filter(None, self._parts_unused)))
def get(self, identifier1=None, identifier2=None, identifier3=None, params=None, data=None):
""" Cloudflare v4 API"""
raise CloudFlareAPIError(0, 'get() call not available for this endpoint')
def patch(self, identifier1=None, identifier2=None, identifier3=None, params=None, data=None):
""" Cloudflare v4 API"""
raise CloudFlareAPIError(0, 'patch() call not available for this endpoint')
def post(self, identifier1=None, identifier2=None, identifier3=None, params=None, data=None, files=None):
""" Cloudflare v4 API"""
raise CloudFlareAPIError(0, 'post() call not available for this endpoint')
def put(self, identifier1=None, identifier2=None, identifier3=None, params=None, data=None):
""" Cloudflare v4 API"""
raise CloudFlareAPIError(0, 'put() call not available for this endpoint')
def delete(self, identifier1=None, identifier2=None, identifier3=None, params=None, data=None):
""" Cloudflare v4 API"""
raise CloudFlareAPIError(0, 'delete() call not available for this endpoint')
class _AddNoAuth(object):
""" Cloudflare v4 API"""
def __init__(self, base, p1, p2=None, p3=None):
""" Cloudflare v4 API"""
self._base = base
self._parts = [p1, p2, p3]
def __call__(self, identifier1=None, identifier2=None, identifier3=None, params=None, data=None):
""" Cloudflare v4 API"""
# This is the same as a get()
return self.get(identifier1, identifier2, identifier3, params, data)
def __str__(self):
""" Cloudflare v4 API"""
return '[%s]' % ('/' + '/:id/'.join(filter(None, self._parts)))
def get(self, identifier1=None, identifier2=None, identifier3=None, params=None, data=None):
""" Cloudflare v4 API"""
return self._base.call_with_no_auth('GET', self._parts,
identifier1, identifier2, identifier3,
params, data)
def patch(self, identifier1=None, identifier2=None, identifier3=None, params=None, data=None):
""" Cloudflare v4 API"""
raise CloudFlareAPIError(0, 'patch() call not available for this endpoint')
def post(self, identifier1=None, identifier2=None, identifier3=None, params=None, data=None, files=None):
""" Cloudflare v4 API"""
raise CloudFlareAPIError(0, 'post() call not available for this endpoint')
def put(self, identifier1=None, identifier2=None, identifier3=None, params=None, data=None):
""" Cloudflare v4 API"""
raise CloudFlareAPIError(0, 'put() call not available for this endpoint')
def delete(self, identifier1=None, identifier2=None, identifier3=None, params=None, data=None):
""" Cloudflare v4 API"""
raise CloudFlareAPIError(0, 'delete() call not available for this endpoint')
class _AddWithAuth(object):
""" Cloudflare v4 API"""
def __init__(self, base, p1, p2=None, p3=None):
""" Cloudflare v4 API"""
self._base = base
self._parts = [p1, p2, p3]
def __call__(self, identifier1=None, identifier2=None, identifier3=None, params=None, data=None):
""" Cloudflare v4 API"""
# This is the same as a get()
return self.get(identifier1, identifier2, identifier3, params, data)
def __str__(self):
""" Cloudflare v4 API"""
return '[%s]' % ('/' + '/:id/'.join(filter(None, self._parts)))
def get(self, identifier1=None, identifier2=None, identifier3=None, params=None, data=None):
""" Cloudflare v4 API"""
return self._base.call_with_auth('GET', self._parts,
identifier1, identifier2, identifier3,
params, data)
def patch(self, identifier1=None, identifier2=None, identifier3=None, params=None, data=None):
""" Cloudflare v4 API"""
return self._base.call_with_auth('PATCH', self._parts,
identifier1, identifier2, identifier3,
params, data)
def post(self, identifier1=None, identifier2=None, identifier3=None, params=None, data=None, files=None):
""" Cloudflare v4 API"""
return self._base.call_with_auth('POST', self._parts,
identifier1, identifier2, identifier3,
params, data, files)
def put(self, identifier1=None, identifier2=None, identifier3=None, params=None, data=None):
""" Cloudflare v4 API"""
return self._base.call_with_auth('PUT', self._parts,
identifier1, identifier2, identifier3,
params, data)
def delete(self, identifier1=None, identifier2=None, identifier3=None, params=None, data=None):
""" Cloudflare v4 API"""
return self._base.call_with_auth('DELETE', self._parts,
identifier1, identifier2, identifier3,
params, data)
class _AddWithAuthUnwrapped(object):
""" Cloudflare v4 API"""
def __init__(self, base, p1, p2=None, p3=None):
""" Cloudflare v4 API"""
self._base = base
self._parts = [p1, p2, p3]
def __call__(self, identifier1=None, identifier2=None, identifier3=None, params=None, data=None):
""" Cloudflare v4 API"""
# This is the same as a get()
return self.get(identifier1, identifier2, identifier3, params, data)
def __str__(self):
""" Cloudflare v4 API"""
return '[%s]' % ('/' + '/:id/'.join(filter(None, self._parts)))
def get(self, identifier1=None, identifier2=None, identifier3=None, params=None, data=None):
""" Cloudflare v4 API"""
return self._base.call_with_auth_unwrapped('GET', self._parts,
identifier1, identifier2, identifier3,
params, data)
def patch(self, identifier1=None, identifier2=None, identifier3=None, params=None, data=None):
""" Cloudflare v4 API"""
return self._base.call_with_auth_unwrapped('PATCH', self._parts,
identifier1, identifier2, identifier3,
params, data)
def post(self, identifier1=None, identifier2=None, identifier3=None, params=None, data=None, files=None):
""" Cloudflare v4 API"""
return self._base.call_with_auth_unwrapped('POST', self._parts,
identifier1, identifier2, identifier3,
params, data, files)
def put(self, identifier1=None, identifier2=None, identifier3=None, params=None, data=None):
""" Cloudflare v4 API"""
return self._base.call_with_auth_unwrapped('PUT', self._parts,
identifier1, identifier2, identifier3,
params, data)
def delete(self, identifier1=None, identifier2=None, identifier3=None, params=None, data=None):
""" Cloudflare v4 API"""
return self._base.call_with_auth_unwrapped('DELETE', self._parts,
identifier1, identifier2, identifier3,
params, data)
class _AddWithCertAuth(object):
""" Cloudflare v4 API"""
def __init__(self, base, p1, p2=None, p3=None):
""" Cloudflare v4 API"""
self._base = base
self._parts = [p1, p2, p3]
def __call__(self, identifier1=None, identifier2=None, identifier3=None, params=None, data=None):
""" Cloudflare v4 API"""
# This is the same as a get()
return self.get(identifier1, identifier2, identifier3, params, data)
def __str__(self):
""" Cloudflare v4 API"""
return '[%s]' % ('/' + '/:id/'.join(filter(None, self._parts)))
def get(self, identifier1=None, identifier2=None, identifier3=None, params=None, data=None):
""" Cloudflare v4 API"""
return self._base.call_with_certauth('GET', self._parts,
identifier1, identifier2, identifier3,
params, data)
def patch(self, identifier1=None, identifier2=None, identifier3=None, params=None, data=None):
""" Cloudflare v4 API"""
return self._base.call_with_certauth('PATCH', self._parts,
identifier1, identifier2, identifier3,
params, data)
def post(self, identifier1=None, identifier2=None, identifier3=None, params=None, data=None, files=None):
""" Cloudflare v4 API"""
return self._base.call_with_certauth('POST', self._parts,
identifier1, identifier2, identifier3,
params, data, files)
def put(self, identifier1=None, identifier2=None, identifier3=None, params=None, data=None):
""" Cloudflare v4 API"""
return self._base.call_with_certauth('PUT', self._parts,
identifier1, identifier2, identifier3,
params, data)
def delete(self, identifier1=None, identifier2=None, identifier3=None, params=None, data=None):
""" Cloudflare v4 API"""
return self._base.call_with_certauth('DELETE', self._parts,
identifier1, identifier2, identifier3,
params, data)
def add(self, t, p1, p2=None, p3=None):
"""add api call to class"""
a = []
if p1:
a += p1.split('/')
if p2:
a += p2.split('/')
if p3:
a += p3.split('/')
branch = self
for element in a[0:-1]:
try:
if '-' in element:
branch = getattr(element, element.replace('-','_'))
else:
branch = getattr(branch, element)
except:
# should never happen
raise CloudFlareAPIError(0, 'api load name failed')
name = a[-1]
try:
if '-' in name:
f = getattr(element, name.replace('-','_'))
else:
f = getattr(branch, name)
# already exists - don't let it overwrite
raise CloudFlareAPIError(0, 'api duplicate name found: %s/**%s**' % ('/'.join(a[0:-1]), name))
except AttributeError:
# this is the required behavior - i.e. it's a new node to create
pass
if t == 'VOID':
f = self._AddUnused(self._base, p1, p2, p3)
elif t == 'OPEN':
f = self._AddNoAuth(self._base, p1, p2, p3)
elif t == 'AUTH':
f = self._AddWithAuth(self._base, p1, p2, p3)
elif t == 'CERT':
f = self._AddWithCertAuth(self._base, p1, p2, p3)
elif t == 'AUTH_UNWRAPPED':
f = self._AddWithAuthUnwrapped(self._base, p1, p2, p3)
else:
# should never happen
raise CloudFlareAPIError(0, 'api load type mismatch')
if '-' in name:
# dashes (vs underscores) cause issues in Python and other languages
setattr(branch, name.replace('-','_'), f)
else:
setattr(branch, name, f)
def api_list(self, m=None, s=''):
"""recursive walk of the api tree returning a list of api calls"""
if m is None:
m = self
w = []
for n in sorted(dir(m)):
if n[0] == '_':
# internal
continue
if n in ['delete', 'get', 'patch', 'post', 'put']:
# gone too far
continue
a = getattr(m, n)
d = dir(a)
if '_base' in d:
# it's a known api call - lets show the result and continue down the tree
if 'delete' in d or 'get' in d or 'patch' in d or 'post' in d or 'put' in d:
# only show the result if a call exists for this part
if '_parts' in d:
# handle underscores by returning the actual API call vs the method name
w.append(str(a)[1:-1])
## w.append(str(a)[1:-1].replace('/:id/','/'))
w = w + self.api_list(a, s + '/' + n)
return w
def api_from_web(self):
""" Cloudflare v4 API"""
return api_decode_from_web(self._base._api_from_web())
def __init__(self, email=None, token=None, certtoken=None, debug=False, raw=False, use_sessions=True, profile=None, base_url=None):
""" Cloudflare v4 API"""
try:
config = read_configs(profile)
except Exception as e:
raise CloudFlareAPIError(0, str(e))
# 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' in config and config['extras']:
api_extras(self, config['extras'])
def __call__(self):
""" Cloudflare v4 API"""
raise TypeError('object is not callable')
def __enter__(self):
""" Cloudflare v4 API"""
return self
def __exit__(self, t, v, tb):
""" Cloudflare v4 API"""
if t is None:
return True
# pretend we didn't deal with raised error - which is true
return False
def __str__(self):
""" Cloudflare v4 API"""
if self._base.email is None:
s = '["%s","%s"]' % (self._base.profile, 'REDACTED')
else:
s = '["%s","%s","%s"]' % (self._base.profile, self._base.email, 'REDACTED')
return s
def __repr__(self):
""" Cloudflare v4 API"""
if self._base.email is None:
s = '%s,%s("%s","%s","%s","%s",%s,"%s")' % (
self.__module__, type(self).__name__,
self._base.profile, 'REDACTED', 'REDACTED',
self._base.base_url, self._base.raw, self._base.user_agent
)
else:
s = '%s,%s("%s","%s","%s","%s","%s",%s,"%s")' % (
self.__module__, type(self).__name__,
self._base.profile, self._base.email, 'REDACTED', 'REDACTED',
self._base.base_url, self._base.raw, self._base.user_agent
)
return s