Skip to content

Commit

Permalink
Added support for profiles - see README
Browse files Browse the repository at this point in the history
  • Loading branch information
mahtin committed Jan 15, 2020
1 parent 12836e3 commit d3ffa85
Show file tree
Hide file tree
Showing 5 changed files with 128 additions and 16 deletions.
22 changes: 13 additions & 9 deletions CloudFlare/cloudflare.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ class CloudFlare(object):
class _v4base(object):
""" Cloudflare v4 API"""

def __init__(self, email, token, certtoken, base_url, debug, raw, use_sessions):
def __init__(self, email, token, certtoken, base_url, debug, raw, use_sessions, profile):
""" Cloudflare v4 API"""

self.email = email
Expand All @@ -28,6 +28,7 @@ def __init__(self, email, token, certtoken, base_url, debug, raw, use_sessions):
self.base_url = base_url
self.raw = raw
self.use_sessions = use_sessions
self.profile = profile
self.session = None
self.user_agent = user_agent()

Expand Down Expand Up @@ -821,13 +822,16 @@ def api_list(self, m=None, s=''):
w = w + self.api_list(a, s + '/' + n)
return w

def __init__(self, email=None, token=None, certtoken=None, debug=False, raw=False, use_sessions=True):
def __init__(self, email=None, token=None, certtoken=None, debug=False, raw=False, use_sessions=True, profile=None):
""" Cloudflare v4 API"""

base_url = BASE_URL

# class creation values override configuration values
[conf_email, conf_token, conf_certtoken, extras] = read_configs()
try:
[conf_email, conf_token, conf_certtoken, extras] = read_configs(profile)
except:
raise CloudFlareAPIError(0, 'profile/configuration read error')

if email is None:
email = conf_email
Expand All @@ -842,7 +846,7 @@ def __init__(self, email=None, token=None, certtoken=None, debug=False, raw=Fals
token = None
if certtoken == '':
certtoken = None
self._base = self._v4base(email, token, certtoken, base_url, debug, raw, use_sessions)
self._base = self._v4base(email, token, certtoken, base_url, debug, raw, use_sessions, profile)

# add the API calls
api_v4(self)
Expand Down Expand Up @@ -871,21 +875,21 @@ def __str__(self):
if self._base.email is None:
return '["%s"]' % ('REDACTED')
else:
return '["%s","%s"]' % (self._base.email, 'REDACTED')
return '["%s","%s","%s"]' % (self._base.profile, self._base.email, 'REDACTED')

def __repr__(self):
""" Cloudflare v4 API"""

if self._base.email is None:
return '%s,%s(%s,"%s","%s",%s,"%s")' % (
return '%s,%s(%s,"%s","%s","%s",%s,"%s")' % (
self.__module__, type(self).__name__,
'REDACTED', 'REDACTED',
self._base.profile, 'REDACTED', 'REDACTED',
self._base.base_url, self._base.raw, self._base.user_agent
)
else:
return '%s,%s(%s,"%s","%s","%s",%s,"%s")' % (
return '%s,%s(%s,"%s","%s","%s","%s",%s,"%s")' % (
self.__module__, type(self).__name__,
self._base.email, 'REDACTED', 'REDACTED',
self._base.profile, self._base.email, 'REDACTED', 'REDACTED',
self._base.base_url, self._base.raw, self._base.user_agent
)

22 changes: 17 additions & 5 deletions CloudFlare/read_configs.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
except ImportError:
import configparser as ConfigParser # py3

def read_configs():
def read_configs(profile=None):
""" reading the config file for Cloudflare API"""

# envioronment variables override config files
Expand All @@ -24,27 +24,39 @@ def read_configs():
os.path.expanduser('~/.cloudflare/cloudflare.cfg')
])

if profile is None:
profile = 'CloudFlare'

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]

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('CloudFlare', 'email'))
email = re.sub(r"\s+", '', config.get(profile, 'email'))
except (ConfigParser.NoOptionError, ConfigParser.NoSectionError):
email = None

if token is None:
try:
token = re.sub(r"\s+", '', config.get('CloudFlare', 'token'))
token = re.sub(r"\s+", '', config.get(profile, 'token'))
except (ConfigParser.NoOptionError, ConfigParser.NoSectionError):
token = None

if certtoken is None:
try:
certtoken = re.sub(r"\s+", '', config.get('CloudFlare', 'certtoken'))
certtoken = re.sub(r"\s+", '', config.get(profile, 'certtoken'))
except (ConfigParser.NoOptionError, ConfigParser.NoSectionError):
certtoken = None

if extras is None:
try:
extras = re.sub(r"\s+", ' ', config.get('CloudFlare', 'extras'))
extras = re.sub(r"\s+", ' ', config.get(profile, 'extras'))
except (ConfigParser.NoOptionError, ConfigParser.NoSectionError):
extras = None

Expand Down
43 changes: 43 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -194,6 +194,9 @@ import CloudFlare

# An authenticated call using an API Key and CA-Origin info
cf = CloudFlare.CloudFlare(email='user@example.com', token='00000000000000000000000000000000', certtoken='v1.0-...')

# An authenticated call using using a stored profile (see below)
cf = CloudFlare.CloudFlare(profile="CompanyX"))
```

If the account email and API key are not passed when you create the class, then they are retrieved from either the users exported shell environment variables or the .cloudflare.cfg or ~/.cloudflare.cfg or ~/.cloudflare/cloudflare.cfg files, in that order.
Expand All @@ -203,6 +206,7 @@ If you're using an API Token, any `cloudflare.cfg` file must either not contain
There is one call that presently doesn't need any email or token certification (the */ips* call); hence you can test without any values saved away.

### Using shell environment variables

```bash
$ export CF_API_EMAIL='user@example.com' # Do not set if using an API Token
$ export CF_API_KEY='00000000000000000000000000000000'
Expand All @@ -224,6 +228,45 @@ extras =
$
```

More than one profile can be stored within that file.
Here's an example for a work and home setup (in this example work has an API Token and home uses email/token).

```bash
$ cat ~/.cloudflare/cloudflare.cfg
[Work]
token = 00000000000000000000000000000000
[Home]
email = home@example.com
token = 00000000000000000000000000000000
$
```

To select a profile, use the `--profile profile-name` option for `cli4` command or use `profile="profile-name"` in the library call.

```bash
$ cli4 --profile Work /zones | jq '.[]|.name' | wc -l
13
$

$ cli4 --profile Home /zones | jq '.[]|.name' | wc -l
1
$
```

Here is the same in code.

```python
#!/usr/bin/env python

import CloudFlare

def main():
cf = CloudFlare.CloudFlare(profile="Work")
...
```

### About /certificates and certtoken

The *CF_API_CERTKEY* or *certtoken* values are used for the Origin-CA */certificates* API calls.
You can leave *certtoken* in the configuration with a blank value (or omit the option variable fully).

Expand Down
45 changes: 45 additions & 0 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -220,6 +220,9 @@ parameters.
# An authenticated call using an API Key and CA-Origin info
cf = CloudFlare.CloudFlare(email='user@example.com', token='00000000000000000000000000000000', certtoken='v1.0-...')
# An authenticated call using using a stored profile (see below)
cf = CloudFlare.CloudFlare(profile="CompanyX"))
If the account email and API key are not passed when you create the
class, then they are retrieved from either the users exported shell
environment variables or the .cloudflare.cfg or ~/.cloudflare.cfg or
Expand Down Expand Up @@ -261,6 +264,48 @@ Using configuration file to store email and keys
extras =
$
More than one profile can be stored within that file. Here's an example
for a work and home setup (in this example work has an API Token and
home uses email/token).

.. code:: bash
$ cat ~/.cloudflare/cloudflare.cfg
[Work]
token = 00000000000000000000000000000000
[Home]
email = home@example.com
token = 00000000000000000000000000000000
$
To select a profile, use the ``--profile profile-name`` option for
``cli4`` command or use ``profile="profile-name"`` in the library call.

.. code:: bash
$ cli4 --profile Work /zones | jq '.[]|.name' | wc -l
13
$
$ cli4 --profile Home /zones | jq '.[]|.name' | wc -l
1
$
Here is the same in code.

.. code:: python
#!/usr/bin/env python
import CloudFlare
def main():
cf = CloudFlare.CloudFlare(profile="Work")
...
About /certificates and certtoken
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

The *CF\_API\_CERTKEY* or *certtoken* values are used for the Origin-CA
*/certificates* API calls. You can leave *certtoken* in the
configuration with a blank value (or omit the option variable fully).
Expand Down
12 changes: 10 additions & 2 deletions cli4/cli4.py
Original file line number Diff line number Diff line change
Expand Up @@ -213,25 +213,28 @@ def do_it(args):
output = 'json'
raw = False
dump = False
profile = None
method = 'GET'

usage = ('usage: cli4 '
+ '[-V|--version] [-h|--help] [-v|--verbose] [-q|--quiet] '
+ '[-j|--json] [-y|--yaml] [-n|ndjson]'
+ '[-r|--raw] '
+ '[-d|--dump] '
+ '[-p|--profile profile-name] '
+ '[--get|--patch|--post|--put|--delete] '
+ '[item=value|item=@filename|@filename ...] '
+ '/command...')

try:
opts, args = getopt.getopt(args,
'VhvqjyrdGPOUD',
'Vhvqjyrdp:GPOUD',
[
'version',
'help', 'verbose', 'quiet', 'json', 'yaml', 'ndjson',
'raw',
'dump',
'profile=',
'get', 'patch', 'post', 'put', 'delete'
])
except getopt.GetoptError:
Expand All @@ -257,6 +260,8 @@ def do_it(args):
output = 'ndjson'
elif opt in ('-r', '--raw'):
raw = True
elif opt in ('-p', '--profile'):
profile = arg;
elif opt in ('-d', '--dump'):
dump = True
elif opt in ('-G', '--get'):
Expand Down Expand Up @@ -363,7 +368,10 @@ def do_it(args):
exit(usage)
command = args[0]

cf = CloudFlare.CloudFlare(debug=verbose, raw=raw)
try:
cf = CloudFlare.CloudFlare(debug=verbose, raw=raw, profile=profile)
except Exception as e:
exit(e)
results = run_command(cf, method, command, params, content, files)
write_results(results, output)

Expand Down

0 comments on commit d3ffa85

Please sign in to comment.