Skip to content
This repository has been archived by the owner on Feb 2, 2018. It is now read-only.

Commit

Permalink
config: Now JSON and Etcd based by default.
Browse files Browse the repository at this point in the history
  • Loading branch information
ashcrow committed Jan 12, 2016
1 parent 09ba609 commit c3f0c80
Show file tree
Hide file tree
Showing 15 changed files with 375 additions and 96 deletions.
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@
__pycache__/
*.py[cod]

# Generated doc
doc/_build

# C extensions
*.so

Expand Down
44 changes: 1 addition & 43 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,46 +7,4 @@ A REST based cluster host manager.

Development: Getting Up And Running
===================================
To test out the current development code you will need the following installed:

- Python2.7
- virtualenv
- etcd2 (running)

Set up virtualenv
-----------------
```
$ virtualenv /where/you/want/it/to/live
...
(virtualenv)$ . /where/you/want/it/to/live/bin/activate
(virtualenv)$ pip install -r requirements.txt
...
```

(Optional): Run Unittests
-------------------------
From the repo root...

```
(virtualenv)$ pip install -r test-requirements.txt
...
(virtualenv)$ nosetests -v --with-coverage --cover-package commissaire --cover-min-percentage 80 test/
```

Adding a Host Manually
----------------------
Verify that etcd is running then execute...


```
(virtualenv)$ etcdctl set /commissaire/hosts/10.0.0.1 '{"address": "10.0.0.1","status": "available","os": "atomic","cpus": 2,"memory": 11989228,"space": 487652,"last_check": "2015-12-17T15:48:18.710454"}'
```

Running the service
-------------------
From the repo root...

```
(virtualenv)$ PYTHONPATH=`pwd`/src python src/commissaire/script.py &
...
```
See [Getting Started](http://commissaire.readthedocs.org/en/latest/gettingstarted.html#development-manual)
37 changes: 37 additions & 0 deletions conf/logger.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
{
"version": 1,
"formatters": {
"simple": {
"format": "%(asctime)s - %(name)s - %(process)d - %(thread)d - %(levelname)s - %(message)s"
}
},
"handlers": {
"console": {
"class": "logging.StreamHandler",
"formatter": "simple",
"level": "DEBUG",
"stream": "ext://sys.stdout"
}
},
"loggers": {
"authentication": {
"handlers": ["console"],
"level": "DEBUG",
"propagate": false
},
"resources": {
"handlers": ["console"],
"level": "DEBUG",
"propagate": false
},
"watcher": {
"handlers": ["console"],
"level": "DEBUG",
"propagate": false
}
},
"root": {
"handlers": ["console"],
"level": "DEBUG"
}
}
26 changes: 0 additions & 26 deletions conf/logger.yaml

This file was deleted.

5 changes: 5 additions & 0 deletions conf/users.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"a": {
"hash": "$2b$12$PtOoiwTvFBAJPMfrZn7pXubcx5MBbQiLVEDv3LAVWGeKiZZiYRGlu"
}
}
2 changes: 0 additions & 2 deletions conf/users.yaml

This file was deleted.

41 changes: 41 additions & 0 deletions doc/authentication.rst
Original file line number Diff line number Diff line change
@@ -1,6 +1,47 @@
Authentication
==============


Modifying Users
---------------

By default commissaire will look at Etcd for user/hash combinations under
the ``/commissaire/config/httpbasicauthbyuserlist`` key.

If the key does not exist the backup is to use local file authentication
using the same JSON schema.

.. code-block:: javascript
{
"username(string)": {
"hash": "bcrypthash(string)"
}...
}
Example
~~~~~~~

.. literalinclude:: ../conf/users.json
:language: json


Using Etcd
----------

To put the configuration in Etcd set the ``/commissaire/config/httpbasicauthbyuserlist`` key with
valid JSON.

.. code-block:: shell
(virtualenv)$ cat conf/users.json | etcdctl set '/commissaire/config/httpbasicauthbyuserlist'
...
Development Information
-----------------------

commissaire's authentication is handled by a simple
plugin based system. To create a new authentication plugin
subclass ``commissaire.authentication.Authenticator``
Expand Down
62 changes: 62 additions & 0 deletions doc/gettingstarted.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
Getting Started
===============

Installation
------------

Development/Manual
~~~~~~~~~~~~~~~~~~
To test out the current development code you will need the following installed:

* Python2.7
* virtualenv
* etcd2 (running)

Set up virtualenv
`````````````````

.. code-block:: shell
$ virtualenv /where/you/want/it/to/live
...
(virtualenv)$ . /where/you/want/it/to/live/bin/activate
(virtualenv)$ pip install -r requirements.txt
...
(Optional): Run Unittests
`````````````````````````
From the repo root...

.. code-block:: shell
(virtualenv)$ pip install -r test-requirements.txt
...
(virtualenv)$ nosetests -v --with-coverage --cover-package commissaire --cover-min-percentage 80 test/
Adding a Host Manually
``````````````````````
Verify that etcd is running then execute...

.. code-block:: shell
(virtualenv)$ etcdctl set /commissaire/hosts/10.0.0.1 '{"address": "10.0.0.1","status": "available","os": "atomic","cpus": 2,"memory": 11989228,"space": 487652,"last_check": "2015-12-17T15:48:18.710454"}'
(Optional): Put Configs in Etcd
```````````````````````````````
commissaire will default back to the local files but using Etcd is where configuration should be stored.

.. code-block:: shell
(virtualenv)$ cat conf/users.json | etcdctl set '/commissaire/config/httpbasicauthbyuserlist'
...
(virtualenv)$ cat conf/logger.json | etcdctl set '/commissaire/config/logger'
...
Running the service
```````````````````
From the repo root...

.. code-block:: shell
(virtualenv)$ PYTHONPATH=`pwd`/src python src/commissaire/script.py http://127.0.0.1:2379 &
...
2 changes: 2 additions & 0 deletions doc/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ Contents:
.. toctree::
:maxdepth: 2

gettingstarted
logging
authentication
endpoints
enums
Expand Down
23 changes: 23 additions & 0 deletions doc/logging.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
Logging
=======
Logging configuration is defined either in Etcd or via a local file. In both
cases the syntax is the same.


Example: Default Local file
---------------------------

.. literalinclude:: ../conf/logger.json
:language: json


Using Etcd
----------

To put the configuration in Etcd set the ``/commissaire/config/logger`` key with
valid JSON.

.. code-block:: shell
(virtualenv)$ cat conf/logger.json | etcdctl set '/commissaire/config/logger'
...
1 change: 0 additions & 1 deletion requirements.txt
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
gevent==1.1rc3
falcon>=0.3
bcrypt
PyYAML
python-etcd==0.4.2
77 changes: 69 additions & 8 deletions src/commissaire/authentication/httpauth.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,9 @@


import bcrypt
import etcd
import falcon
import yaml
import json

from commissaire.authentication import Authenticator
from commissaire.compat.b64 import base64
Expand Down Expand Up @@ -49,14 +50,14 @@ def _decode_basic_auth(self, req):

class HTTPBasicAuthByFile(_HTTPBasicAuth):
"""
HTTP Basic auth backed by a YAML file.
HTTP Basic auth backed by a JSON file.
"""

def __init__(self, filepath):
"""
Creates an instance of the HTTPBasicAuthByFile authenticator.
:param filepath: The file path to the YAML file backing authentication.
:param filepath: The file path to the JSON file backing authentication.
:type filepath: string
:returns: HTTPBasicAuthByFile
"""
Expand All @@ -66,15 +67,16 @@ def __init__(self, filepath):

def load(self):
"""
Loads the authentication information from the YAML file.
Loads the authentication information from the JSON file.
"""
try:
with open(self.filepath, 'r') as afile:
self._data = yaml.safe_load(afile)
except (yaml.parser.ParserError, IOError) as pe:
self._data = json.load(afile)
self.logger.info('Loaded authentication data from local file.')
except (ValueError, IOError) as ve:
self.logger.warn(
'Denying all access due to problem parsing '
'YAML file: {0}'.format(pe))
'JSON file: {0}'.format(ve))
self._data = {}

def authenticate(self, req, resp):
Expand All @@ -92,7 +94,66 @@ def authenticate(self, req, resp):
if user in self._data.keys():
if bcrypt.hashpw(
passwd.encode('utf-8'),
self._data[user].encode('utf-8')):
self._data[user]['hash'].encode('utf-8')):
return # Authentication is good

# Forbid by default
raise falcon.HTTPForbidden('Forbidden', 'Forbidden')


class HTTPBasicAuthByEtcd(_HTTPBasicAuth):
"""
HTTP Basic auth backed by Etcd JSON value.
"""

def __init__(self, ds):
"""
Creates an instance of the HTTPBasicAuthByEtcd authenticator.
:param datastore: The Etcd client to use.
:type datastore: etcd.Client
:returns: HTTPBasicAuthByEtcd
"""
self.ds = ds
self._data = {}
self.load()

def load(self):
"""
Loads the authentication information from etcd.
"""
try:
d = self.ds.get(
'/commissaire/config/httpbasicauthbyuserlist')
self._data = json.loads(d.value)
self.logger.info('Loaded authentication data from Etcd.')
# TODO: Watch endpoint and reload on changes
except etcd.EtcdKeyNotFound as eknf:
self.logger.warn(
'User configuration not found in Etcd. Raising...')
self._data = {}
raise eknf
except ValueError as ve:
self.logger.warn(
'User configuration in Etcd is not valid JSON. Raising...')
raise ve

def authenticate(self, req, resp):
"""
Implements the authentication logic.
:param req: Request instance that will be passed through.
:type req: falcon.Request
:param resp: Response instance that will be passed through.
:type resp: falcon.Response
:raises: falcon.HTTPForbidden
"""
user, passwd = self._decode_basic_auth(req)
if user is not None and passwd is not None:
if user in self._data.keys():
if bcrypt.hashpw(
passwd.encode('utf-8'),
self._data[user]['hash'].encode('utf-8')):
return # Authentication is good

# Forbid by default
Expand Down

0 comments on commit c3f0c80

Please sign in to comment.