Merge of previous project work into this project
This commit is contained in:
parent
81d7598ca4
commit
8f783f473b
3
.gitignore
vendored
3
.gitignore
vendored
@ -40,3 +40,6 @@ nosetests.xml
|
||||
# Sqlite databases
|
||||
*.sqlite3
|
||||
*.db
|
||||
|
||||
# Misc. generated files
|
||||
versiononly.txt
|
||||
|
190
HACKING.rst
Normal file
190
HACKING.rst
Normal file
@ -0,0 +1,190 @@
|
||||
Barbican Style Commandments
|
||||
=======================
|
||||
|
||||
|
||||
TBD: Translate the content below to the Barbican project.
|
||||
...Adding cosmetic change to trigger build process...
|
||||
|
||||
- Step 1: Read http://www.python.org/dev/peps/pep-0008/
|
||||
- Step 2: Read http://www.python.org/dev/peps/pep-0008/ again
|
||||
- Step 3: Read on
|
||||
|
||||
|
||||
General
|
||||
-------
|
||||
- Put two newlines between top-level code (funcs, classes, etc)
|
||||
- Put one newline between methods in classes and anywhere else
|
||||
- Do not write "except:", use "except Exception:" at the very least
|
||||
- Include your name with TODOs as in "#TODO(termie)"
|
||||
- Do not name anything the same name as a built-in or reserved word
|
||||
|
||||
|
||||
Imports
|
||||
-------
|
||||
- Do not make relative imports
|
||||
- Order your imports by the full module path
|
||||
- Organize your imports according to the following template
|
||||
|
||||
Example::
|
||||
|
||||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
{{stdlib imports in human alphabetical order}}
|
||||
\n
|
||||
{{third-party lib imports in human alphabetical order}}
|
||||
\n
|
||||
{{glance imports in human alphabetical order}}
|
||||
\n
|
||||
\n
|
||||
{{begin your code}}
|
||||
|
||||
|
||||
Human Alphabetical Order Examples
|
||||
---------------------------------
|
||||
Example::
|
||||
|
||||
import httplib
|
||||
import logging
|
||||
import random
|
||||
import StringIO
|
||||
import time
|
||||
import unittest
|
||||
|
||||
import eventlet
|
||||
import webob.exc
|
||||
|
||||
import glance.api.middleware
|
||||
from glance.api import images
|
||||
from glance.auth import users
|
||||
import glance.common
|
||||
from glance.endpoint import cloud
|
||||
from glance import test
|
||||
|
||||
|
||||
Docstrings
|
||||
----------
|
||||
|
||||
Docstrings are required for all functions and methods.
|
||||
|
||||
Docstrings should ONLY use triple-double-quotes (``"""``)
|
||||
|
||||
Single-line docstrings should NEVER have extraneous whitespace
|
||||
between enclosing triple-double-quotes.
|
||||
|
||||
**INCORRECT** ::
|
||||
|
||||
""" There is some whitespace between the enclosing quotes :( """
|
||||
|
||||
**CORRECT** ::
|
||||
|
||||
"""There is no whitespace between the enclosing quotes :)"""
|
||||
|
||||
Docstrings that span more than one line should look like this:
|
||||
|
||||
Example::
|
||||
|
||||
"""
|
||||
Start the docstring on the line following the opening triple-double-quote
|
||||
|
||||
If you are going to describe parameters and return values, use Sphinx, the
|
||||
appropriate syntax is as follows.
|
||||
|
||||
:param foo: the foo parameter
|
||||
:param bar: the bar parameter
|
||||
:returns: return_type -- description of the return value
|
||||
:returns: description of the return value
|
||||
:raises: AttributeError, KeyError
|
||||
"""
|
||||
|
||||
**DO NOT** leave an extra newline before the closing triple-double-quote.
|
||||
|
||||
|
||||
Dictionaries/Lists
|
||||
------------------
|
||||
If a dictionary (dict) or list object is longer than 80 characters, its items
|
||||
should be split with newlines. Embedded iterables should have their items
|
||||
indented. Additionally, the last item in the dictionary should have a trailing
|
||||
comma. This increases readability and simplifies future diffs.
|
||||
|
||||
Example::
|
||||
|
||||
my_dictionary = {
|
||||
"image": {
|
||||
"name": "Just a Snapshot",
|
||||
"size": 2749573,
|
||||
"properties": {
|
||||
"user_id": 12,
|
||||
"arch": "x86_64",
|
||||
},
|
||||
"things": [
|
||||
"thing_one",
|
||||
"thing_two",
|
||||
],
|
||||
"status": "ACTIVE",
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
Calling Methods
|
||||
---------------
|
||||
Calls to methods 80 characters or longer should format each argument with
|
||||
newlines. This is not a requirement, but a guideline::
|
||||
|
||||
unnecessarily_long_function_name('string one',
|
||||
'string two',
|
||||
kwarg1=constants.ACTIVE,
|
||||
kwarg2=['a', 'b', 'c'])
|
||||
|
||||
|
||||
Rather than constructing parameters inline, it is better to break things up::
|
||||
|
||||
list_of_strings = [
|
||||
'what_a_long_string',
|
||||
'not as long',
|
||||
]
|
||||
|
||||
dict_of_numbers = {
|
||||
'one': 1,
|
||||
'two': 2,
|
||||
'twenty four': 24,
|
||||
}
|
||||
|
||||
object_one.call_a_method('string three',
|
||||
'string four',
|
||||
kwarg1=list_of_strings,
|
||||
kwarg2=dict_of_numbers)
|
||||
|
||||
|
||||
Internationalization (i18n) Strings
|
||||
-----------------------------------
|
||||
In order to support multiple languages, we have a mechanism to support
|
||||
automatic translations of exception and log strings.
|
||||
|
||||
Example::
|
||||
|
||||
msg = _("An error occurred")
|
||||
raise HTTPBadRequest(explanation=msg)
|
||||
|
||||
If you have a variable to place within the string, first internationalize the
|
||||
template string then do the replacement.
|
||||
|
||||
Example::
|
||||
|
||||
msg = _("Missing parameter: %s") % ("flavor",)
|
||||
LOG.error(msg)
|
||||
|
||||
If you have multiple variables to place in the string, use keyword parameters.
|
||||
This helps our translators reorder parameters when needed.
|
||||
|
||||
Example::
|
||||
|
||||
msg = _("The server with id %(s_id)s has no key %(m_key)s")
|
||||
LOG.error(msg % {"s_id": "1234", "m_key": "imageId"})
|
||||
|
||||
|
||||
Creating Unit Tests
|
||||
-------------------
|
||||
For every new feature, unit tests should be created that both test and
|
||||
(implicitly) document the usage of said feature. If submitting a patch for a
|
||||
bug that had no unit test, a new passing unit test should be added. If a
|
||||
submitted bug fix does have a unit test, be sure to add a new one that fails
|
||||
without the patch and passes with the patch.
|
3
LICENSE
3
LICENSE
@ -1,4 +1,3 @@
|
||||
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
@ -187,7 +186,7 @@
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
Copyright 2013, Rackspace (http://www.rackspace.com)
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
|
11
MANIFEST.in
Normal file
11
MANIFEST.in
Normal file
@ -0,0 +1,11 @@
|
||||
include ChangeLog
|
||||
include README.rst
|
||||
include MANIFEST.in pylintrc
|
||||
include Authors
|
||||
include HACKING.rst
|
||||
include LICENSE
|
||||
include setup.cfg
|
||||
include babel.cfg tox.ini
|
||||
include barbican/tests/api/resources_test.py
|
||||
graft tools
|
||||
graft etc
|
43
README.md
43
README.md
@ -1,4 +1,41 @@
|
||||
barbican
|
||||
========
|
||||
# Barbican
|
||||
Barbican is a ReST API designed for the secure storage, provisioning and management of secrets. It is aimed at being useful for all environments, including large ephemeral Clouds. The project is supported by [Rackspace Hosting](http://www.rackspace.com).
|
||||
|
||||
Barbican is part of a set of applications that make up the CloudKeep ecosystem. The other systems are:
|
||||
|
||||
* [Postern](https://github.com/cloudkeep/postern) - Go based agent that provides access to secrets from the Barbican API.
|
||||
* [Palisade](https://github.com/cloudkeep/palisade) - AngularJS based web ui for the Barbican API.
|
||||
* [Keep](https://github.com/cloudkeep/keep) - A python-based command line client for the Barbican API.
|
||||
|
||||
Additional documentation can be found on the [Github Wiki](https://github.com/cloudkeep/barbican/wiki). For questions, comments or concerns, hop on the [mailing list](https://groups.google.com/forum/#!forum/cloudkeep) and let us know what you think.
|
||||
|
||||
## Getting Started
|
||||
|
||||
* Installation
|
||||
* Contributing
|
||||
* API Documentation
|
||||
* Technology
|
||||
* FAQ
|
||||
|
||||
## Why Should You Use Barbican?
|
||||
The current state of key management is atrocious. While Windows does have some decent options through the use of the Data Protection API (DPAPI) and Active Directory, Linux lacks a cohesive story around how to manage keys for applicaiton use.
|
||||
|
||||
Barbican was designed to solve this problem. The system was motivated by internal Rackspace needs, requirements from [OpenStack](http://www.openstack.org/) and a realization that the current state of the art could use some help.
|
||||
|
||||
Barbican will handle many types of secrets, including:
|
||||
|
||||
* **Symmetric Keys** - Used to perform reversible encryption of data at rest, typically using the AES algorithm set. This type of key is required to enable features like [encrypted Swift containers and Cinder volumes](http://www.openstack.org/software/openstack-storage/), [encrypted Cloud Backups](http://www.rackspace.com/cloud/backup/), etc.
|
||||
* **Asymmetric Keys** - Asymmetric key pairs (sometimes referred to as [public / private keys](http://en.wikipedia.org/wiki/Public-key_cryptography)) are used in many scenarios where communication between untrusted parties is desired. The most common case is with SSL/TLS certificates, but also is used in solutions like SSH keys, S/MIME (mail) encryption and digital signatures.
|
||||
* **Raw Secrets** - Barbican stores secrets as a base64 encoded block of data (encrypted, naturally). Clients can use the API to store any secrets in any format they desire. The [Postern](https://github.com/cloudkeep/postern) agent is capable of presenting these secrets in various formats to ease integration.
|
||||
|
||||
For the symmetric and asymmetric key types, Barbican supports full lifecycle management including provisioning, expiration, reporting, etc. A plugin system allows for multiple certificate authority support (including public and private CAs).
|
||||
|
||||
## Design Goals
|
||||
|
||||
1. Provide a central secret-store capable of distributing secret / keying material to all types of deployments including ephemeral Cloud instances.
|
||||
2. Support reasonable compliance regimes through reporting and auditability.
|
||||
3. Application adoption costs should be minimal or non-existent.
|
||||
4. Build a community and ecosystem by being open-source and extensible.
|
||||
5. Improve security through sane defaults and centralized management of [policies for all secrets](https://github.com/cloudkeep/barbican/wiki/Policies).
|
||||
6. Out of band communication mechanism to notify and protect sensitive assets.
|
||||
|
||||
A server providing secure, audited access to encryption materials.
|
||||
|
48
README.rst
Normal file
48
README.rst
Normal file
@ -0,0 +1,48 @@
|
||||
====
|
||||
Barbican
|
||||
====
|
||||
|
||||
TBD: Make this Barbican specific....
|
||||
|
||||
Glance is a project that defines services for discovering, registering,
|
||||
retrieving and storing virtual machine images. The discovery and registration
|
||||
responsibilities are handled by the `glance-registry` component while the
|
||||
retrieval and storage responsiblities are handled by the `glance-api`
|
||||
component.
|
||||
|
||||
|
||||
Quick Start
|
||||
-----------
|
||||
|
||||
If you'd like to run trunk, you can clone the git repo:
|
||||
|
||||
git clone git@github.com:openstack/glance.git
|
||||
|
||||
|
||||
Install Glance by running::
|
||||
|
||||
python setup.py build
|
||||
sudo python setup.py install
|
||||
|
||||
|
||||
By default, `glance-registry` will use a SQLite database. If you'd like to use
|
||||
MySQL, or make other adjustments, you can modify the glance.cnf file (see
|
||||
documentation for more details).
|
||||
|
||||
|
||||
Now that Glance is installed, you can start the service. The easiest way to
|
||||
do that is by using the `glance-control` utility which runs both the
|
||||
`glance-api` and `glance-registry` services::
|
||||
|
||||
glance-control all start
|
||||
|
||||
|
||||
Once both services are running, you can now use the `glance` tool to
|
||||
register new images in Glance.
|
||||
|
||||
glance add name="My Image" < /path/to/my/image
|
||||
|
||||
|
||||
With an image registered, you can now configure your IAAS provider to use
|
||||
Glance as its image service and begin spinning up instances from your
|
||||
newly registered images.
|
90
barbican.py
90
barbican.py
@ -1,90 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
Barbican
|
||||
~~~~~~~~
|
||||
|
||||
A proof of concept implementation of a key management server for
|
||||
use with the postern agent (https://github.com/cloudkeep/postern).
|
||||
|
||||
DO NOT USE THIS IN PRODUCTION. IT IS NOT SECURE IN ANY WAY.
|
||||
YOU HAVE BEEN WARNED.
|
||||
|
||||
:copyright: (c) 2013 by Jarret Raim
|
||||
:license: Apache 2.0, see LICENSE for details
|
||||
"""
|
||||
import os
|
||||
from flask import Flask, render_template, redirect, flash, request
|
||||
from flask.ext.admin import Admin
|
||||
from flask.ext.admin.contrib.sqlamodel import ModelView
|
||||
from flask.ext import login, wtf
|
||||
from flask.ext.login import login_user
|
||||
from barbican_api import api
|
||||
from database import db_session, init_db
|
||||
from models import User, Tenant, Key, Policy, Event
|
||||
|
||||
|
||||
app = Flask(__name__)
|
||||
app.secret_key = '79f9823f1f0---DEVELOPMENT---c46cebdd1c8f3d0742e02'
|
||||
app.register_blueprint(api)
|
||||
|
||||
admin = Admin(app, name="Barbican Admin")
|
||||
admin.add_view(ModelView(User, db_session))
|
||||
admin.add_view(ModelView(Tenant, db_session))
|
||||
admin.add_view(ModelView(Key, db_session))
|
||||
admin.add_view(ModelView(Policy, db_session))
|
||||
admin.add_view(ModelView(Event, db_session))
|
||||
|
||||
login_manager = login.LoginManager()
|
||||
login_manager.init_app(app)
|
||||
login_manager.login_view = 'login'
|
||||
|
||||
|
||||
@app.route("/")
|
||||
@login.login_required
|
||||
def hello():
|
||||
return render_template("index.html")
|
||||
|
||||
|
||||
#
|
||||
# Login forms
|
||||
#
|
||||
class LoginForm(wtf.Form):
|
||||
login = wtf.TextField(validators=[wtf.required()])
|
||||
password = wtf.PasswordField(validators=[wtf.required()])
|
||||
|
||||
def validate_login(self, field):
|
||||
user = self.get_user()
|
||||
if user is None or user.password != self.password.data:
|
||||
raise wtf.ValidationError('Invalid username or credentials.')
|
||||
|
||||
def get_user(self):
|
||||
return User.query.filter_by(name=self.login.data).first()
|
||||
|
||||
|
||||
@app.route("/login", methods=["GET", "POST"])
|
||||
def login():
|
||||
form = LoginForm(request.form)
|
||||
if form.validate_on_submit():
|
||||
user = form.get_user()
|
||||
login_user(user)
|
||||
flash('Logged in successfully.')
|
||||
return redirect('/admin/')
|
||||
|
||||
return render_template("login.html", form=form)
|
||||
|
||||
|
||||
@login_manager.user_loader
|
||||
def load_user(user_id):
|
||||
return User.query.get(user_id)
|
||||
|
||||
|
||||
@app.teardown_request
|
||||
def shutdown_session(exception=None):
|
||||
db_session.remove()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
if not os.path.exists('/tmp/barbican.db'):
|
||||
app.logger.info('No database detected at /tmp/barbican.db. Creating one and the admin user.')
|
||||
init_db()
|
||||
app.run(debug=True)
|
@ -0,0 +1,18 @@
|
||||
# Copyright 2010-2011 OpenStack LLC.
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
"""
|
||||
Cloudkeep's Barbican module root
|
||||
"""
|
56
barbican/api/__init__.py
Normal file
56
barbican/api/__init__.py
Normal file
@ -0,0 +1,56 @@
|
||||
# Copyright 2010-2011 OpenStack LLC.
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
"""
|
||||
API handler for Cloudkeep's Barbican
|
||||
"""
|
||||
|
||||
|
||||
import json
|
||||
import falcon
|
||||
|
||||
|
||||
class ApiResource(object):
|
||||
"""
|
||||
Base class for API resources
|
||||
"""
|
||||
pass
|
||||
|
||||
|
||||
def abort(status=falcon.HTTP_500, message=None):
|
||||
"""
|
||||
Helper function for aborting an API request process. Useful for error
|
||||
reporting and expcetion handling.
|
||||
"""
|
||||
raise falcon.HTTPError(status, message)
|
||||
|
||||
|
||||
def load_body(req, required=[]):
|
||||
"""
|
||||
Helper function for loading an HTTP request body from JSON into a
|
||||
Python dictionary
|
||||
"""
|
||||
try:
|
||||
raw_json = req.stream.read()
|
||||
|
||||
except Exception:
|
||||
abort(falcon.HTTP_500, 'Read Error')
|
||||
|
||||
try:
|
||||
parsed_body = json.loads(raw_json, 'utf-8')
|
||||
except ValueError as ve:
|
||||
abort(falcon.HTTP_400, 'Malformed JSON')
|
||||
|
||||
return parsed_body
|
63
barbican/api/app.py
Normal file
63
barbican/api/app.py
Normal file
@ -0,0 +1,63 @@
|
||||
import falcon
|
||||
|
||||
from barbican.api.resources import *
|
||||
|
||||
from config import config
|
||||
from sqlalchemy import create_engine, MetaData
|
||||
from sqlalchemy.orm import scoped_session, sessionmaker
|
||||
from barbican.model.tenant import Base
|
||||
|
||||
"""
|
||||
Locally scoped db session
|
||||
"""
|
||||
_Session = scoped_session(sessionmaker())
|
||||
_engine = None
|
||||
|
||||
|
||||
def db_session():
|
||||
return _Session
|
||||
|
||||
|
||||
def _engine_from_config(configuration):
|
||||
configuration = dict(configuration)
|
||||
url = configuration.pop('url')
|
||||
|
||||
return create_engine(url, **configuration)
|
||||
|
||||
|
||||
def init_tenant_model():
|
||||
_engine = _engine_from_config(config['sqlalchemy'])
|
||||
from barbican.model.tenant import Tenant, Secret
|
||||
Base.metadata.create_all(_engine)
|
||||
_Session.bind = _engine
|
||||
|
||||
|
||||
# Initialize the data model
|
||||
init_tenant_model()
|
||||
|
||||
# test the database out
|
||||
#from barbican.model.tenant import Tenant, Secret
|
||||
#jw_user = Tenant("jwoody")
|
||||
#_Session.add(jw_user)
|
||||
|
||||
# select all and print out all the results sorted by id
|
||||
#for instance in _Session.query(Tenant).order_by(Tenant.id):
|
||||
# print instance.username
|
||||
|
||||
# Resources
|
||||
versions = VersionResource()
|
||||
tenants = TenantsResource(db_session())
|
||||
tenant = TenantResource(db_session())
|
||||
secrets = SecretsResource(db_session())
|
||||
secret = SecretResource(db_session())
|
||||
|
||||
# Routing
|
||||
application = api = falcon.API()
|
||||
|
||||
api.add_route('/', versions)
|
||||
api.add_route('/v1', tenants)
|
||||
api.add_route('/v1/{tenant_id}', tenant)
|
||||
api.add_route('/v1/{tenant_id}/secrets', secrets)
|
||||
api.add_route('/v1/{tenant_id}/secrets/{secret_id}', secret)
|
||||
|
||||
|
184
barbican/api/resources.py
Normal file
184
barbican/api/resources.py
Normal file
@ -0,0 +1,184 @@
|
||||
import json
|
||||
import falcon
|
||||
import logging
|
||||
|
||||
from barbican.version import __version__
|
||||
from barbican.api import ApiResource, load_body, abort
|
||||
from barbican.model.util import find_tenant
|
||||
from barbican.model.util import find_secret
|
||||
from barbican.model.tenant import Tenant, Secret
|
||||
# from barbican.model.secret import Secret
|
||||
|
||||
|
||||
def _tenant_not_found():
|
||||
abort(falcon.HTTP_404, 'Unable to locate tenant.')
|
||||
|
||||
def _tenant_already_exists():
|
||||
abort(falcon.HTTP_400, 'Tenant already exists.')
|
||||
|
||||
def _secret_not_found():
|
||||
abort(falcon.HTTP_400, 'Unable to locate secret profile.')
|
||||
|
||||
def format_tenant(tenant):
|
||||
if not isinstance(tenant, dict):
|
||||
tenant = tenant.__dict__
|
||||
|
||||
return {'id': tenant['id'],
|
||||
'tenant_id': tenant['tenant_id']}
|
||||
|
||||
|
||||
class VersionResource(ApiResource):
|
||||
|
||||
def on_get(self, req, resp):
|
||||
resp.status = falcon.HTTP_200
|
||||
resp.body = json.dumps({'v1': 'current',
|
||||
'build': __version__})
|
||||
|
||||
|
||||
class TenantsResource(ApiResource):
|
||||
|
||||
def __init__(self, db_session):
|
||||
self.db = db_session
|
||||
|
||||
def on_post(self, req, resp):
|
||||
body = load_body(req)
|
||||
username = body['username']
|
||||
logging.debug('Username is {0}'.format(username))
|
||||
|
||||
tenant = find_tenant(self.db, username=username)
|
||||
|
||||
if tenant:
|
||||
abort(falcon.HTTP_400, 'Tenant with username {0} '
|
||||
'already exists'.format(username))
|
||||
|
||||
new_tenant = Tenant(username)
|
||||
self.db.add(new_tenant)
|
||||
self.db.commit()
|
||||
|
||||
resp.status = falcon.HTTP_201
|
||||
resp.set_header('Location', '/v1/{0}'.format(new_tenant.id))
|
||||
|
||||
|
||||
class TenantResource(ApiResource):
|
||||
|
||||
def __init__(self, db_session):
|
||||
self.db = db_session
|
||||
|
||||
def on_get(self, req, resp, tenant_id):
|
||||
tenant = find_tenant(self.db, id=tenant_id,
|
||||
when_not_found=_tenant_not_found)
|
||||
|
||||
resp.status = falcon.HTTP_200
|
||||
resp.body = json.dumps(tenant.format())
|
||||
|
||||
def on_delete(self, req, resp, tenant_id):
|
||||
tenant = find_tenant(self.db, id=tenant_id,
|
||||
when_not_found=_tenant_not_found)
|
||||
|
||||
self.db.delete(tenant)
|
||||
self.db.commit()
|
||||
|
||||
resp.status = falcon.HTTP_200
|
||||
|
||||
|
||||
class SecretsResource(ApiResource):
|
||||
|
||||
def __init__(self, db_session):
|
||||
self.db = db_session
|
||||
|
||||
def on_get(self, req, resp, tenant_id):
|
||||
tenant = find_tenant(self.db, id=tenant_id,
|
||||
when_not_found=_tenant_not_found)
|
||||
|
||||
resp.status = falcon.HTTP_200
|
||||
|
||||
#jsonify a list of formatted secrets
|
||||
resp.body = json.dumps([s.format() for s in tenant.secrets])
|
||||
|
||||
def on_post(self, req, resp, tenant_id):
|
||||
tenant = find_tenant(self.db, id=tenant_id,
|
||||
when_not_found=_tenant_not_found)
|
||||
|
||||
body = load_body(req)
|
||||
secret_name = body['name']
|
||||
|
||||
# Check if the tenant already has a secret with this name
|
||||
for secret in tenant.secrets:
|
||||
if secret.name == secret_name:
|
||||
abort(falcon.HTTP_400, 'Secret with name {0} already exists.'
|
||||
.format(secret.name, secret.id))
|
||||
|
||||
# Create the new secret
|
||||
new_secret = Secret(tenant.id, secret_name)
|
||||
tenant.secrets.append(new_secret)
|
||||
|
||||
self.db.add(new_secret)
|
||||
self.db.commit()
|
||||
|
||||
resp.status = falcon.HTTP_201
|
||||
resp.set_header('Location',
|
||||
'/v1/{0}/secrets/{1}'
|
||||
.format(tenant_id, new_secret.id))
|
||||
|
||||
|
||||
class SecretResource(ApiResource):
|
||||
|
||||
def __init__(self, db_session):
|
||||
self.db = db_session
|
||||
|
||||
def on_get(self, req, resp, tenant_id, secret_id):
|
||||
#verify the tenant exists
|
||||
tenant = find_tenant(self.db, tenant_id=tenant_id,
|
||||
when_not_found=_tenant_not_found)
|
||||
|
||||
#verify the secret exists
|
||||
secret = find_secret(self.db, id=secret_id,
|
||||
when_not_found=_secret_not_found)
|
||||
|
||||
#verify the secret belongs to the tenant
|
||||
if not secret in tenant.secrets:
|
||||
_secret_not_found()
|
||||
|
||||
resp.status = falcon.HTTP_200
|
||||
resp.body = json.dumps(secret.format())
|
||||
|
||||
def on_put(self, req, resp, tenant_id, secret_id):
|
||||
#verify the tenant exists
|
||||
tenant = find_tenant(self.db, tenant_id=tenant_id,
|
||||
when_not_found=_tenant_not_found)
|
||||
|
||||
#verify the secret exists
|
||||
secret = find_secret(self.db, id=secret_id,
|
||||
when_not_found=_secret_not_found)
|
||||
|
||||
#verify the secret belongs to the tenant
|
||||
if not secret in tenant.secrets:
|
||||
_secret_not_found()
|
||||
|
||||
#load the message
|
||||
body = load_body(req)
|
||||
|
||||
#if attributes are present in message, update the secret
|
||||
if 'name' in body.keys():
|
||||
secret.name = body['name']
|
||||
|
||||
self.db.commit()
|
||||
resp.status = falcon.HTTP_200
|
||||
|
||||
def on_delete(self, req, resp, tenant_id, secret_id):
|
||||
#verify the tenant exists
|
||||
tenant = find_tenant(self.db, tenant_id=tenant_id,
|
||||
when_not_found=_tenant_not_found)
|
||||
|
||||
#verify the secret exists
|
||||
secret = find_secret(self.db, id=secret_id,
|
||||
when_not_found=_secret_not_found)
|
||||
|
||||
#verify the secret belongs to the tenant
|
||||
if not secret in tenant.secrets:
|
||||
_secret_not_found()
|
||||
|
||||
self.db.delete(secret)
|
||||
self.db.commit()
|
||||
|
||||
resp.status = falcon.HTTP_200
|
18
barbican/data/__init__.py
Normal file
18
barbican/data/__init__.py
Normal file
@ -0,0 +1,18 @@
|
||||
# Copyright 2010-2011 OpenStack LLC.
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
"""
|
||||
Data operations for Cloudkeep's Barbican
|
||||
"""
|
18
barbican/data/adapters/__init__.py
Normal file
18
barbican/data/adapters/__init__.py
Normal file
@ -0,0 +1,18 @@
|
||||
# Copyright 2010-2011 OpenStack LLC.
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
"""
|
||||
Database-specific implementations
|
||||
"""
|
77
barbican/data/adapters/handler.py
Normal file
77
barbican/data/adapters/handler.py
Normal file
@ -0,0 +1,77 @@
|
||||
#from oslo.config import cfg
|
||||
#from barbican.config import get_config
|
||||
|
||||
# Handler configuration options
|
||||
#datasource_group = cfg.OptGroup(name='datasource', title='Datasource Configuration Options')
|
||||
#get_config().register_group(datasource_group)
|
||||
|
||||
#HANDLER_OPTIONS = [
|
||||
# cfg.StrOpt('handler_name',
|
||||
# default='memory',
|
||||
# help="""Sets the name of the handler to load for
|
||||
# datasource interactions. e.g. mongodb
|
||||
# """
|
||||
# ),
|
||||
# cfg.BoolOpt('verbose',
|
||||
# default=False,
|
||||
# help="""Sets whether or not the datasource handlers
|
||||
# should be verbose in their logging output.
|
||||
# """
|
||||
# )
|
||||
#]
|
||||
|
||||
#get_config().register_opts(HANDLER_OPTIONS, group=datasource_group)
|
||||
|
||||
|
||||
# Handler registration
|
||||
#_DATASOURCE_HANDLERS = DatasourceHandlerManager()
|
||||
|
||||
STATUS_NEW = 'NEW'
|
||||
STATUS_CONNECTED = 'CONNTECTED'
|
||||
STATUS_CLOSED = 'CLOSED'
|
||||
|
||||
|
||||
def datasource_handler(conf):
|
||||
handler_def = _DATASOURCE_HANDLERS[conf.handler_name]
|
||||
return handler_def(conf)
|
||||
|
||||
|
||||
def register_handler(handler_name, handler_def):
|
||||
_DATASOURCE_HANDLERS.register(handler_name, handler_def)
|
||||
|
||||
|
||||
class DatasourceHandlerManager():
|
||||
|
||||
def __init__(self):
|
||||
self.registered_handlers = dict()
|
||||
|
||||
def register(self, handler_name, handler_def):
|
||||
self.registered_handlers[handler_name] = handler_def
|
||||
|
||||
def get(self, handler_name):
|
||||
return self.registered_handlers[handler_name]
|
||||
|
||||
_DATASOURCE_HANDLERS = DatasourceHandlerManager()
|
||||
|
||||
class DatasourceHandler():
|
||||
|
||||
status = STATUS_NEW
|
||||
|
||||
def status(self):
|
||||
return self.status
|
||||
|
||||
def connect(self):
|
||||
raise NotImplementedError
|
||||
|
||||
def close(self):
|
||||
raise NotImplementedError
|
||||
|
||||
def get(self, object_name, object_id):
|
||||
raise NotImplementedError
|
||||
|
||||
def put(self, object_name, update_object):
|
||||
raise NotImplementedError
|
||||
|
||||
def delete(self, object_name, object_id):
|
||||
raise NotImplementedError
|
||||
|
15
barbican/data/tenant.py
Normal file
15
barbican/data/tenant.py
Normal file
@ -0,0 +1,15 @@
|
||||
from barbican.data.adapters.handler import datasource_handler
|
||||
|
||||
handler = datasource_handler
|
||||
|
||||
def get_tenant(tenant_id):
|
||||
pass
|
||||
|
||||
def save_tenant(tenant_object):
|
||||
pass
|
||||
|
||||
def create_tenant(tenant_id):
|
||||
pass
|
||||
|
||||
def delete_tenant(tenant_id):
|
||||
pass
|
18
barbican/locale/keep.pot
Normal file
18
barbican/locale/keep.pot
Normal file
@ -0,0 +1,18 @@
|
||||
# Translations template for barbican.
|
||||
# Copyright (C) 2013 ORGANIZATION
|
||||
# This file is distributed under the same license as
|
||||
# the barbican project. FIRST AUTHOR <EMAIL@ADDRESS>, 2012.
|
||||
#
|
||||
#, fuzzy
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: barbican 2013.1\n"
|
||||
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
|
||||
"POT-Creation-Date: 2013-03-20 17:19-0800\n"
|
||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=utf-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Generated-By: Babel 0.9.6\n"
|
18
barbican/model/__init__.py
Normal file
18
barbican/model/__init__.py
Normal file
@ -0,0 +1,18 @@
|
||||
# Copyright 2010-2011 OpenStack LLC.
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
"""
|
||||
Model objects for Cloudkeep's Barbican
|
||||
"""
|
35
barbican/model/base.py
Normal file
35
barbican/model/base.py
Normal file
@ -0,0 +1,35 @@
|
||||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
|
||||
# Copyright 2010 United States Government as represented by the
|
||||
# Administrator of the National Aeronautics and Space Administration.
|
||||
# Copyright 2011 Justin Santa Barbara
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
"""Base class for all model objects."""
|
||||
|
||||
from sqlalchemy import Column
|
||||
from sqlalchemy import Integer, ForeignKey
|
||||
from sqlalchemy.ext.declarative import declarative_base, declared_attr
|
||||
|
||||
|
||||
class Persisted(object):
|
||||
|
||||
id = Column(Integer, primary_key=True)
|
||||
|
||||
@declared_attr
|
||||
def __tablename__(self):
|
||||
return self.__name__.lower()
|
||||
|
||||
Base = declarative_base(cls=Persisted)
|
43
barbican/model/secret.py
Normal file
43
barbican/model/secret.py
Normal file
@ -0,0 +1,43 @@
|
||||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
|
||||
# Copyright 2010 United States Government as represented by the
|
||||
# Administrator of the National Aeronautics and Space Administration.
|
||||
# Copyright 2011 Justin Santa Barbara
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
"""Represents a secret to store in Cloudkeep's Barbican."""
|
||||
|
||||
#import barbican.model.Tenant
|
||||
from base import Base
|
||||
from sqlalchemy import Table, Column, String
|
||||
from sqlalchemy import Integer, ForeignKey, Boolean
|
||||
from sqlalchemy.orm import relationship, backref
|
||||
from sqlalchemy.ext.declarative import declarative_base, declared_attr
|
||||
|
||||
#
|
||||
# class Secret(Base):
|
||||
# """
|
||||
# A secret is any information that needs to be stored and protected within
|
||||
# Cloudkeep's Barbican.
|
||||
# """
|
||||
#
|
||||
# secret_id = Column(String)
|
||||
# name = Column(String)
|
||||
# tenant_id = Column(Integer, ForeignKey('tenant.id'))
|
||||
# tenant = relationship(Tenant, primaryjoin=tenant_id == Tenant.id)
|
||||
#
|
||||
# def __init__(self, secret_id):
|
||||
# self.secret_id = secret_id
|
||||
|
86
barbican/model/tenant.py
Normal file
86
barbican/model/tenant.py
Normal file
@ -0,0 +1,86 @@
|
||||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
|
||||
# Copyright 2010 United States Government as represented by the
|
||||
# Administrator of the National Aeronautics and Space Administration.
|
||||
# Copyright 2011 Justin Santa Barbara
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
"""Generic Node base class for all workers that run on hosts."""
|
||||
|
||||
import logging
|
||||
from base import Base
|
||||
from sqlalchemy import Table, Column, String
|
||||
from sqlalchemy import Integer, ForeignKey, Boolean
|
||||
from sqlalchemy.orm import relationship, relation, backref
|
||||
from sqlalchemy.ext.declarative import declarative_base, declared_attr
|
||||
|
||||
|
||||
class Tenant(Base):
|
||||
"""
|
||||
Tenants are users that wish to store secret information within Cloudkeep's Barbican.
|
||||
"""
|
||||
|
||||
logging.debug('In Tenant table setup')
|
||||
|
||||
__tablename__ = "tenants"
|
||||
|
||||
id = Column(Integer, primary_key=True)
|
||||
username = Column(String)
|
||||
# secrets = relationship('Secret', backref='tenant', lazy='dynamic')
|
||||
# secrets = relationship('Secret', secondary=_secrets)
|
||||
# secrets = relationship("Secret",
|
||||
# order_by="desc(Secret.name)",
|
||||
# primaryjoin="Secret.tenant_id==Tenant.id")
|
||||
|
||||
|
||||
def __init__(self, username):
|
||||
self.username = username
|
||||
|
||||
def __init__(self, username, secrets=[]):
|
||||
self.username = username
|
||||
self.secrets = secrets
|
||||
|
||||
def format(self):
|
||||
return {'id': self.id,
|
||||
'username': self.username}
|
||||
|
||||
|
||||
class Secret(Base):
|
||||
"""
|
||||
A secret is any information that needs to be stored and protected within Cloudkeep's Barbican.
|
||||
"""
|
||||
|
||||
__tablename__ = "secrets"
|
||||
|
||||
id = Column(Integer, primary_key=True)
|
||||
name = Column(String)
|
||||
tenant_id = Column(Integer, ForeignKey('tenants.id'))
|
||||
tenant = relationship("Tenant", backref=backref('secrets', order_by=id))
|
||||
# tenant = relationship(Tenant, primaryjoin=tenant_id == Tenant.id)
|
||||
|
||||
# creates a bidirectional relationship
|
||||
# from Secret to Tenant it's Many-to-One
|
||||
# from Tenant to Secret it's One-to-Many
|
||||
# tenant = relation(Tenant, backref=backref('secret', order_by=id))
|
||||
|
||||
def __init__(self, tenant_id, name):
|
||||
self.tenant_id = tenant_id
|
||||
self.name = name
|
||||
|
||||
def format(self):
|
||||
return {'id': self.id,
|
||||
'name': self.username,
|
||||
'tenant_id': self.tenant_id}
|
||||
|
33
barbican/model/util.py
Normal file
33
barbican/model/util.py
Normal file
@ -0,0 +1,33 @@
|
||||
from sqlalchemy.orm.exc import NoResultFound, MultipleResultsFound
|
||||
|
||||
from barbican.model.tenant import Tenant, Secret
|
||||
|
||||
|
||||
def _empty_condition():
|
||||
pass
|
||||
|
||||
|
||||
def find_tenant(db_session, id=None, username=None,
|
||||
when_not_found=_empty_condition,
|
||||
when_multiple_found=_empty_condition):
|
||||
try:
|
||||
if id:
|
||||
return db_session.query(Tenant).filter_by(id=id).one()
|
||||
elif username:
|
||||
return db_session.query(Tenant).filter_by(username=username).one()
|
||||
except NoResultFound:
|
||||
when_not_found()
|
||||
except MultipleResultsFound:
|
||||
when_multiple_found()
|
||||
|
||||
return None
|
||||
|
||||
|
||||
def find_secret(db_session, id, when_not_found=_empty_condition,
|
||||
when_multiple_found=_empty_condition):
|
||||
try:
|
||||
return db_session.query(Secret).filter_by(id=id).one()
|
||||
except NoResultFound:
|
||||
when_not_found()
|
||||
except MultipleResultsFound:
|
||||
when_multiple_found()
|
@ -1,23 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
Sample Python file, here to test out code coverage in Jenkins.
|
||||
~~~~~~~~~~~~
|
||||
|
||||
DO NOT USE THIS IN PRODUCTION. IT IS NOT SECURE IN ANY WAY.
|
||||
YOU HAVE BEEN WARNED.
|
||||
|
||||
:copyright: (c) 2013 by Jarret Raim
|
||||
:license: Apache 2.0, see LICENSE for details
|
||||
"""
|
||||
def a_sample_method_here():
|
||||
foo = "bar"
|
||||
i = 1
|
||||
if ("bar" == foo):
|
||||
print "saw bar"
|
||||
i += 2
|
||||
else:
|
||||
print "not bar"
|
||||
i += 4
|
||||
print "total",i
|
||||
|
||||
a_sample_method_here() # Do something coverage can chew on.
|
@ -0,0 +1 @@
|
||||
__author__ = 'john.wood'
|
1
barbican/tests/api/__init__.py
Normal file
1
barbican/tests/api/__init__.py
Normal file
@ -0,0 +1 @@
|
||||
__author__ = 'john.wood'
|
67
barbican/tests/api/resources_test.py
Normal file
67
barbican/tests/api/resources_test.py
Normal file
@ -0,0 +1,67 @@
|
||||
from datetime import datetime
|
||||
from barbican.api.resources import *
|
||||
from barbican.model.tenant import Tenant
|
||||
|
||||
from mock import MagicMock
|
||||
|
||||
import falcon
|
||||
import unittest
|
||||
|
||||
|
||||
def suite():
|
||||
suite = unittest.TestSuite()
|
||||
suite.addTest(WhenTestingVersionResource())
|
||||
|
||||
return suite
|
||||
|
||||
|
||||
class WhenTestingVersionResource(unittest.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
self.req = MagicMock()
|
||||
self.resp = MagicMock()
|
||||
self.resource = VersionResource()
|
||||
|
||||
def test_should_return_200_on_get(self):
|
||||
self.resource.on_get(self.req, self.resp)
|
||||
self.assertEqual(falcon.HTTP_200, self.resp.status)
|
||||
|
||||
def test_should_return_version_json(self):
|
||||
self.resource.on_get(self.req, self.resp)
|
||||
|
||||
parsed_body = json.loads(self.resp.body)
|
||||
|
||||
self.assertTrue('v1' in parsed_body)
|
||||
self.assertEqual('current', parsed_body['v1'])
|
||||
|
||||
|
||||
class WhenCreatingTenantsUsingTenantsResource(unittest.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
db_filter = MagicMock()
|
||||
db_filter.one.return_value = Tenant('tenant_id')
|
||||
|
||||
db_query = MagicMock()
|
||||
db_query.filter_by.return_value = db_filter
|
||||
|
||||
self.db_session = MagicMock()
|
||||
self.db_session.query.return_value = db_query
|
||||
|
||||
self.stream = MagicMock()
|
||||
self.stream.read.return_value = u'{ "username" : "1234" }'
|
||||
|
||||
self.req = MagicMock()
|
||||
self.req.stream = self.stream
|
||||
|
||||
self.resp = MagicMock()
|
||||
self.resource = TenantsResource(self.db_session)
|
||||
|
||||
def test_should_throw_exception_for_tenants_that_exist(self):
|
||||
with self.assertRaises(falcon.HTTPError):
|
||||
self.resource.on_post(self.req, self.resp)
|
||||
|
||||
self.db_session.query.assert_called_once_with(Tenant)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
1
barbican/tests/api/tenant/__init__.py
Normal file
1
barbican/tests/api/tenant/__init__.py
Normal file
@ -0,0 +1 @@
|
||||
__author__ = 'john.wood'
|
@ -1,38 +0,0 @@
|
||||
#import os
|
||||
import unittest
|
||||
|
||||
#from oslo.config import cfg
|
||||
#from meniscus.config import init_config, get_config
|
||||
|
||||
|
||||
# Configuration test configuration options
|
||||
#test_group = cfg.OptGroup(name='test', title='Configuration Test')
|
||||
|
||||
#CFG_TEST_OPTIONS = [
|
||||
# cfg.BoolOpt('should_pass',
|
||||
# default=False,
|
||||
# help="""Example option to make sure configuration
|
||||
# loading passes test.
|
||||
# """
|
||||
# )
|
||||
#]
|
||||
|
||||
|
||||
def suite():
|
||||
suite = unittest.TestSuite()
|
||||
suite.addTest(WhenConfiguring())
|
||||
|
||||
return suite
|
||||
|
||||
|
||||
class WhenConfiguring(unittest.TestCase):
|
||||
|
||||
def test_loading(self):
|
||||
self.assertTrue(True)
|
||||
# init_config(['--config-file', '../etc/meniscus/meniscus.conf'])
|
||||
|
||||
# conf = get_config()
|
||||
# conf.register_group(test_group)
|
||||
# conf.register_opts(CFG_TEST_OPTIONS, group=test_group)
|
||||
|
||||
# self.assertTrue(conf.test.should_pass)
|
21
barbican/version.py
Normal file
21
barbican/version.py
Normal file
@ -0,0 +1,21 @@
|
||||
# Copyright 2010-2011 OpenStack LLC.
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
"""
|
||||
Cloudkeep's Barbican version
|
||||
"""
|
||||
|
||||
__version__ = '0.1.74dev'
|
||||
__version_info__ = tuple(__version__.split('.'))
|
106
barbican_api.py
106
barbican_api.py
@ -1,106 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
Barbican API
|
||||
~~~~~~~~~~~~
|
||||
|
||||
The API for Barbican.
|
||||
|
||||
DO NOT USE THIS IN PRODUCTION. IT IS NOT SECURE IN ANY WAY.
|
||||
YOU HAVE BEEN WARNED.
|
||||
|
||||
:copyright: (c) 2013 by Jarret Raim
|
||||
:license: Apache 2.0, see LICENSE for details
|
||||
"""
|
||||
import uuid
|
||||
import datetime
|
||||
from dateutil.parser import parse
|
||||
from flask import Blueprint, request, jsonify, Response, json
|
||||
from models import Event, Tenant, Key, Agent, Policy
|
||||
from database import db_session
|
||||
|
||||
api = Blueprint('api', __name__, url_prefix="/api")
|
||||
|
||||
|
||||
@api.route('/')
|
||||
def root():
|
||||
return jsonify(hello='World')
|
||||
|
||||
|
||||
@api.route('/<int:tenant_id>/policies/', methods=['GET', 'POST'])
|
||||
def policies(tenant_id):
|
||||
if request.method == 'POST':
|
||||
for policy in request.json['policies']:
|
||||
keys = []
|
||||
for k in policy['keys']:
|
||||
key = Key(uuid=k['uuid'], filename=k['filename'], mime_type=k['mime_type'],
|
||||
expiration=parse(k['expiration']), secret=k['secret'], owner=k['owner'],
|
||||
group=k['group'], cacheable=k['cacheable'])
|
||||
keys.append(key)
|
||||
|
||||
policy = Policy(uuid=policy['uuid'], name=policy['name'], tenant_id=tenant_id,
|
||||
directory_name=policy['directory_name'],
|
||||
max_key_accesses=policy['max_key_accesses'],
|
||||
time_available_after_reboot=policy['time_available_after_reboot'])
|
||||
policy.keys.extend(keys)
|
||||
db_session.add(policy)
|
||||
db_session.commit()
|
||||
|
||||
return Response(status=200)
|
||||
else:
|
||||
policy = Policy.query.filter_by(tenant_id=tenant_id).first()
|
||||
|
||||
if policy is None:
|
||||
return Response('No policies defined for tenant', status=404)
|
||||
|
||||
return jsonify(policy.as_dict())
|
||||
|
||||
|
||||
@api.route('/<int:tenant_id>/agents/', methods=['GET', 'POST'])
|
||||
def agents(tenant_id):
|
||||
if request.method == 'POST':
|
||||
tenant = Tenant.query.get(tenant_id)
|
||||
agent = Agent(tenant=tenant, uuid=request.json['uuid'])
|
||||
db_session.add(agent)
|
||||
db_session.commit()
|
||||
return jsonify(agent.as_dict())
|
||||
else:
|
||||
agents = Agent.query.filter_by(tenant_id=tenant_id)
|
||||
agents_dicts = map(Agent.as_dict, agents.all())
|
||||
return Response(json.dumps(agents_dicts, cls=DateTimeJsonEncoder), mimetype='application/json')
|
||||
|
||||
|
||||
|
||||
@api.route('/<int:tenant_id>/logs/', methods=['GET', 'POST'])
|
||||
def logs(tenant_id):
|
||||
if request.method == 'POST':
|
||||
agent_id = uuid.UUID(request.json['agent_id'])
|
||||
received_on = parse(request.json['received_on'])
|
||||
key_id = uuid.UUID(request.json['key_id'])
|
||||
|
||||
if request.json['severity'] in ['DEBUG', 'INFO', 'WARN', 'FATAL']:
|
||||
severity = request.json['severity']
|
||||
else:
|
||||
severity = 'UNKNOWN'
|
||||
|
||||
# Load the key and tenant
|
||||
tenant = Tenant.query.get(tenant_id)
|
||||
key = Key.query.filter_by(uuid=str(key_id)).first()
|
||||
|
||||
ev = Event(tenant_id=tenant_id, agent_id=str(agent_id), received_on=received_on,
|
||||
severity=severity, message=request.json['message'], tenant=tenant, key=key)
|
||||
db_session.add(ev)
|
||||
db_session.commit()
|
||||
|
||||
return Response(json.dumps(ev.as_dict(), cls=DateTimeJsonEncoder), mimetype='application/json')
|
||||
else:
|
||||
events = Event.query.filter_by(tenant_id=tenant_id).order_by(Event.received_on)
|
||||
events_dicts = map(Event.as_dict, events.all())
|
||||
return Response(json.dumps(events_dicts, cls=DateTimeJsonEncoder), mimetype='application/json')
|
||||
|
||||
|
||||
class DateTimeJsonEncoder(json.JSONEncoder):
|
||||
def default(self, obj):
|
||||
if isinstance(obj, datetime.datetime):
|
||||
return obj.isoformat()
|
||||
else:
|
||||
return super(DateTimeJsonEncoder, self).default(obj)
|
@ -1,12 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
Barbican Tests
|
||||
~~~~~~~~~~~~~~
|
||||
|
||||
Tests the Barbican application.
|
||||
|
||||
:copyright: (c) 2013 by Jarret Raim
|
||||
:license: Apache 2.0, see LICENSE for more details.
|
||||
"""
|
||||
|
||||
|
34
database.py
34
database.py
@ -1,34 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
Barbican Models
|
||||
~~~~~~~~~~~~~~~
|
||||
|
||||
The models for the Barbican application.
|
||||
|
||||
DO NOT USE THIS IN PRODUCTION. IT IS NOT SECURE IN ANY WAY.
|
||||
YOU HAVE BEEN WARNED.
|
||||
|
||||
:copyright: (c) 2013 by Jarret Raim
|
||||
:license: Apache 2.0, see LICENSE for details
|
||||
"""
|
||||
from sqlalchemy import create_engine
|
||||
from sqlalchemy.orm import scoped_session, sessionmaker
|
||||
from sqlalchemy.ext.declarative import declarative_base
|
||||
|
||||
engine = create_engine('sqlite:////tmp/barbican.db', convert_unicode=True)
|
||||
db_session = scoped_session(sessionmaker(autocommit=False,
|
||||
autoflush=False,
|
||||
bind=engine))
|
||||
Base = declarative_base()
|
||||
Base.query = db_session.query_property()
|
||||
|
||||
|
||||
def init_db():
|
||||
# import all modules here that might define models so that
|
||||
# they will be registered properly on the metadata. Otherwise
|
||||
# you will have to import them first before calling init_db()
|
||||
import models
|
||||
Base.metadata.create_all(bind=engine)
|
||||
db_session.add(models.User('admin', 'admin@localhost', 'Passw0rd'))
|
||||
db_session.add(models.Tenant(id=123))
|
||||
db_session.commit()
|
1
debian/barbican-api.dirs
vendored
Normal file
1
debian/barbican-api.dirs
vendored
Normal file
@ -0,0 +1 @@
|
||||
/var/lib/barbican/temp
|
94
debian/barbican-api.init
vendored
Normal file
94
debian/barbican-api.init
vendored
Normal file
@ -0,0 +1,94 @@
|
||||
#!/bin/sh
|
||||
### BEGIN INIT INFO
|
||||
# Provides: barbican-api
|
||||
# Required-Start: $network $local_fs $remote_fs $syslog
|
||||
# Required-Stop: $remote_fs
|
||||
# Default-Start: 2 3 4 5
|
||||
# Default-Stop: 0 1 6
|
||||
# Short-Description: Barbican API server
|
||||
# Description: Frontend Barbican API server
|
||||
### END INIT INFO
|
||||
|
||||
# Author: John Wood <john.wood@rackspace.com>
|
||||
|
||||
# PATH should only include /usr/* if it runs after the mountnfs.sh script
|
||||
PATH=/sbin:/usr/sbin:/bin:/usr/bin
|
||||
DESC="Key Management Service API"
|
||||
NAME=barbican-api
|
||||
DAEMON=/usr/bin/barbican-api
|
||||
PIDFILE=/var/run/$NAME.pid
|
||||
SCRIPTNAME=/etc/init.d/$NAME
|
||||
|
||||
# Exit if the package is not installed
|
||||
[ -x $DAEMON ] || exit 0
|
||||
|
||||
# Read configuration variable file if it is present
|
||||
[ -r /etc/default/$NAME ] && . /etc/default/$NAME
|
||||
|
||||
. /lib/lsb/init-functions
|
||||
|
||||
do_start()
|
||||
{
|
||||
start-stop-daemon --start --background --quiet --chuid barbican:barbican --make-pidfile --pidfile $PIDFILE --startas $DAEMON --test > /dev/null \
|
||||
|| return 1
|
||||
start-stop-daemon --start --background --quiet --chuid barbican:barbican --make-pidfile --pidfile $PIDFILE --startas $DAEMON \
|
||||
|| return 2
|
||||
}
|
||||
|
||||
do_stop()
|
||||
{
|
||||
start-stop-daemon --stop --quiet --retry=TERM/30/KILL/5 --pidfile $PIDFILE
|
||||
RETVAL="$?"
|
||||
rm -f $PIDFILE
|
||||
|
||||
# TBD: HACK ALERT! Need to figure out why uwsgi spawns a separate process!
|
||||
killall -s SIGKILL uwsgi
|
||||
|
||||
return "$RETVAL"
|
||||
}
|
||||
|
||||
case "$1" in
|
||||
start)
|
||||
log_daemon_msg "Starting $DESC" "$NAME"
|
||||
do_start
|
||||
case "$?" in
|
||||
0|1) log_end_msg 0 ;;
|
||||
2) log_end_msg 1 ;;
|
||||
esac
|
||||
;;
|
||||
stop)
|
||||
log_daemon_msg "Stopping $DESC" "$NAME"
|
||||
do_stop
|
||||
case "$?" in
|
||||
0|1) log_end_msg 0 ;;
|
||||
2) log_end_msg 1 ;;
|
||||
esac
|
||||
;;
|
||||
status)
|
||||
status_of_proc "$DAEMON" "$NAME" && exit 0 || exit $?
|
||||
;;
|
||||
restart|force-reload)
|
||||
log_daemon_msg "Restarting $DESC" "$NAME"
|
||||
do_stop
|
||||
case "$?" in
|
||||
0|1)
|
||||
do_start
|
||||
case "$?" in
|
||||
0) log_end_msg 0 ;;
|
||||
1) log_end_msg 1 ;; # Old process is still running
|
||||
*) log_end_msg 1 ;; # Failed to start
|
||||
esac
|
||||
;;
|
||||
*)
|
||||
# Failed to stop
|
||||
log_end_msg 1
|
||||
;;
|
||||
esac
|
||||
;;
|
||||
*)
|
||||
echo "Usage: $SCRIPTNAME {start|stop|status|restart|force-reload}" >&2
|
||||
exit 3
|
||||
;;
|
||||
esac
|
||||
|
||||
:
|
3
debian/barbican-api.install
vendored
Normal file
3
debian/barbican-api.install
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
etc/barbican/barbican-api.ini etc/barbican
|
||||
etc/barbican/config.py etc/barbican
|
||||
usr/bin/barbican-api
|
7
debian/barbican-api.logrotate
vendored
Normal file
7
debian/barbican-api.logrotate
vendored
Normal file
@ -0,0 +1,7 @@
|
||||
/var/log/barbican/barbican-api.log {
|
||||
daily
|
||||
missingok
|
||||
compress
|
||||
delaycompress
|
||||
notifempty
|
||||
}
|
15
debian/barbican-api.postinst
vendored
Normal file
15
debian/barbican-api.postinst
vendored
Normal file
@ -0,0 +1,15 @@
|
||||
#!/bin/sh
|
||||
|
||||
set -e
|
||||
|
||||
if [ "$1" = "configure" ]
|
||||
then
|
||||
|
||||
# TBD: If we need to ask the user for config info such as 'use lightweight/standalone mode?',
|
||||
# then look at Glance's glance-api.postinst
|
||||
# . /usr/share/debconf/confmodule
|
||||
|
||||
chown barbican:barbican /var/lib/barbican -R
|
||||
fi
|
||||
|
||||
#DEBHELPER#
|
14
debian/barbican-api.prerm
vendored
Normal file
14
debian/barbican-api.prerm
vendored
Normal file
@ -0,0 +1,14 @@
|
||||
#!/bin/sh
|
||||
|
||||
set -e
|
||||
|
||||
case $1 in
|
||||
remove|purge)
|
||||
if [ -x /etc/init.d/barbican-api ]; then
|
||||
invoke-rc.d barbican-api stop || true
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
|
||||
#DEBHELPER#
|
||||
|
4
debian/barbican-common.dirs
vendored
Normal file
4
debian/barbican-common.dirs
vendored
Normal file
@ -0,0 +1,4 @@
|
||||
/etc/barbican/
|
||||
/var/lib/barbican/
|
||||
/var/lib/barbican/temp/
|
||||
/var/log/barbican/
|
27
debian/barbican-common.postinst
vendored
Normal file
27
debian/barbican-common.postinst
vendored
Normal file
@ -0,0 +1,27 @@
|
||||
#!/bin/sh
|
||||
|
||||
set -e
|
||||
|
||||
if [ "$1" = "configure" ]
|
||||
then
|
||||
. /usr/share/debconf/confmodule
|
||||
if ! getent group barbican > /dev/null 2>&1
|
||||
then
|
||||
addgroup --quiet --system barbican >/dev/null
|
||||
fi
|
||||
if ! getent passwd barbican > /dev/null 2>&1
|
||||
then
|
||||
adduser --quiet --system --home /var/lib/barbican --ingroup barbican --no-create-home --shell /bin/false barbican
|
||||
fi
|
||||
|
||||
chown -R barbican:adm /var/log/barbican/
|
||||
chmod 0750 /var/log/barbican/
|
||||
chown barbican:barbican -R /var/lib/barbican/ /etc/barbican/
|
||||
chmod 0700 /etc/barbican/
|
||||
|
||||
# TBD: Replace these pip requirements with a true Debian package equivalent (via stdeb perhaps).
|
||||
pip install uwsgi
|
||||
pip install falcon
|
||||
fi
|
||||
|
||||
#DEBHELPER#
|
21
debian/barbican-common.postrm
vendored
Normal file
21
debian/barbican-common.postrm
vendored
Normal file
@ -0,0 +1,21 @@
|
||||
#!/bin/sh
|
||||
|
||||
set -e
|
||||
|
||||
case $1 in
|
||||
purge)
|
||||
echo "Purging barbican. Backup of /var/lib/barbican can be found at /var/lib/barbican.tar.bz2" >&2
|
||||
if ( getent passwd barbican) > /dev/null 2>&1; then
|
||||
userdel barbican || true
|
||||
fi
|
||||
|
||||
if ( getent group barbican) > /dev/null 2>&1; then
|
||||
groupdel barbican || true
|
||||
fi
|
||||
|
||||
[ -e /var/lib/barbican ] && rm -rf /var/lib/barbican
|
||||
[ -e /var/log/barbican ] && rm -rf /var/log/barbican
|
||||
;;
|
||||
esac
|
||||
|
||||
#DEBHELPER#
|
5
debian/changelog
vendored
Normal file
5
debian/changelog
vendored
Normal file
@ -0,0 +1,5 @@
|
||||
barbican (0.1.1dev-1) stable; urgency=high
|
||||
|
||||
* Updated version.
|
||||
|
||||
-- John Wood <john.wood@rackspace.com> Mon, 01 Apr 2013 16:27:50 -0500
|
5
debian/changelog-orig
vendored
Normal file
5
debian/changelog-orig
vendored
Normal file
@ -0,0 +1,5 @@
|
||||
barbican (0.1.42-1) stable; urgency=high
|
||||
|
||||
* Initial upload.
|
||||
|
||||
-- John Wood <john.wood@rackspace.com> Wed, 20 Mar 2013 12:01:32 +0100
|
1
debian/compat
vendored
Normal file
1
debian/compat
vendored
Normal file
@ -0,0 +1 @@
|
||||
7
|
96
debian/control
vendored
Normal file
96
debian/control
vendored
Normal file
@ -0,0 +1,96 @@
|
||||
Source: barbican
|
||||
Section: net
|
||||
Priority: extra
|
||||
Maintainer: PKG OpenStack <openstack-devel@lists.alioth.debian.org>
|
||||
Uploaders: John Wood <john.wood@rackspace.com>
|
||||
Build-Depends: debhelper (>= 7.0.50),
|
||||
po-debconf,
|
||||
python-all (>= 2.6.6-3~),
|
||||
sqlite3,
|
||||
curl,
|
||||
pep8,
|
||||
python-boto,
|
||||
python-crypto,
|
||||
python-eventlet,
|
||||
python-httplib2 (>= 0.6.0),
|
||||
python-iso8601,
|
||||
python-kombu,
|
||||
python-novaclient,
|
||||
python-migrate,
|
||||
python-mox,
|
||||
python-nose,
|
||||
python-paste,
|
||||
python-pastedeploy,
|
||||
python-routes,
|
||||
python-setuptools,
|
||||
python-sphinx,
|
||||
python-sqlalchemy (>= 0.7.4),
|
||||
python-pysqlite2,
|
||||
python-swift,
|
||||
python-webob,
|
||||
python-xattr,
|
||||
procps
|
||||
Standards-Version: 3.9.3
|
||||
X-Python-Version: >= 2.6
|
||||
Homepage: http://launchpad.net/barbican
|
||||
Vcs-Browser: https://github.com/jfwood/barbican
|
||||
Vcs-Git: git://github.com/jfwood/barbican.git
|
||||
|
||||
Package: python-barbican
|
||||
Architecture: amd64
|
||||
Section: python
|
||||
Depends: ${python:Depends}, ${misc:Depends},
|
||||
python-boto,
|
||||
python-crypto,
|
||||
python-eventlet,
|
||||
python-httplib2 (>= 0.6.0),
|
||||
python-iso8601,
|
||||
python-kombu,
|
||||
python-migrate,
|
||||
python-pastedeploy,
|
||||
python-routes,
|
||||
python-sqlalchemy (>= 0.7.4),
|
||||
python-pysqlite2,
|
||||
python-swift,
|
||||
python-webob,
|
||||
python-xattr
|
||||
Description: OpenStack Key Management Service - Python client library
|
||||
The Barbican project provides services for storing and retrieving sensitive
|
||||
client information such as encryption keys.
|
||||
.
|
||||
This package contains the Python client libraries.
|
||||
|
||||
Package: barbican-common
|
||||
Architecture: amd64
|
||||
Section: python
|
||||
Depends: ${python:Depends}, ${misc:Depends},
|
||||
python-barbican (= ${binary:Version}),
|
||||
adduser,
|
||||
debconf,
|
||||
Description: OpenStack Key Management Service - common files
|
||||
The Barbican project provides services for storing and retrieving sensitive
|
||||
client information such as encryption keys.
|
||||
.
|
||||
This package contains common files for Barbican.
|
||||
|
||||
Package: barbican-api
|
||||
Architecture: amd64
|
||||
Section: python
|
||||
Depends: ${python:Depends}, ${misc:Depends},
|
||||
barbican-common (= ${binary:Version})
|
||||
Description: OpenStack Key Management Service - API Server
|
||||
The Barbican project provides services for storing and retrieving sensitive
|
||||
client information such as encryption keys.
|
||||
.
|
||||
This package contains the Barbican API Server.
|
||||
|
||||
Package: barbican
|
||||
Architecture: amd64
|
||||
Section: python
|
||||
Depends: barbican-api (= ${binary:Version}),
|
||||
${misc:Depends}
|
||||
Description: OpenStack Key Management Service - metapackage
|
||||
The Barbican project provides services for storing and retrieving sensitive
|
||||
client information such as encryption keys.
|
||||
.
|
||||
This is a dependency package to install all of the Cloudkeep suite.
|
23
debian/copyright
vendored
Normal file
23
debian/copyright
vendored
Normal file
@ -0,0 +1,23 @@
|
||||
Format: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
|
||||
Upstream-Name: barbican
|
||||
Source: https://code.launchpad.net/barbican
|
||||
|
||||
Files: *
|
||||
Copyright: 2010 United States Government as represented by the Administrator of the National Aeronautics and Space Administration.
|
||||
2010-2011 OpenStack LLC
|
||||
Others (See individual files for more details)
|
||||
License: Apache-2
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
.
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
.
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
.
|
||||
On Debian-based systems the full text of the Apache version 2.0 license
|
||||
can be found in `/usr/share/common-licenses/Apache-2.0'.
|
8
debian/gbp.conf
vendored
Normal file
8
debian/gbp.conf
vendored
Normal file
@ -0,0 +1,8 @@
|
||||
[DEFAULT]
|
||||
upstream-branch = master
|
||||
debian-branch = master
|
||||
upstream-tag = %(version)
|
||||
compression = xz
|
||||
|
||||
[git-buildpackage]
|
||||
export-dir = ../build-area/
|
1
debian/python-barbican.install
vendored
Normal file
1
debian/python-barbican.install
vendored
Normal file
@ -0,0 +1 @@
|
||||
usr/lib/python*/dist-packages/*
|
43
debian/rules
vendored
Normal file
43
debian/rules
vendored
Normal file
@ -0,0 +1,43 @@
|
||||
#!/usr/bin/make -f
|
||||
|
||||
# Verbose mode
|
||||
#export DH_VERBOSE=1
|
||||
|
||||
%:
|
||||
dh $@ --with python2
|
||||
|
||||
#ifeq (,$(findstring nocheck, $(DEB_BUILD_OPTIONS)))
|
||||
#override_dh_auto_test:
|
||||
# bash run_tests.sh -N || true
|
||||
#endif
|
||||
|
||||
# TBD: Use this when we are ready for sphinx docs
|
||||
#override_dh_auto_build:
|
||||
# dh_auto_build
|
||||
#ifeq (,$(findstring nodocs, $(DEB_BUILD_OPTIONS)))
|
||||
# mkdir -p doc/build
|
||||
# python setup.py build_sphinx
|
||||
#else
|
||||
# mkdir -p doc/build/html
|
||||
#endif
|
||||
|
||||
override_dh_auto_clean:
|
||||
dh_auto_clean
|
||||
# TDB: OpenStack: debconf-updatepo
|
||||
# TBD: OpenStack: rm -rf doc/build
|
||||
rm -rf build barbican.egg-info
|
||||
# TBD: OpenStack: po/barbican.pot barbican/vcsversion.py
|
||||
rm -rf barbican.sqlite
|
||||
|
||||
# TBD: Revisit this for OpenStack usage...probably can't do the 'dev' for snapshots approach anymore!
|
||||
#get-vcs-source:
|
||||
# git remote add upstream git://github.com/jfwood/barbican.git || true
|
||||
# git fetch upstream
|
||||
## cd barbican
|
||||
## VER=$(python -c 'import barbican.version; print barbican.version.__version__')
|
||||
# if ! [ -f ../glance_2012.1.orig.tar.xz ] ; then git archive --prefix=glance-2012.1/ 2012.1 | xz >../glance_2012.1.orig.tar.xz ; fi
|
||||
# if ! git checkout master ; then \
|
||||
# echo "No upstream branch: checking out" ; \
|
||||
# git checkout -b master upstream/master ; \
|
||||
# fi
|
||||
# git checkout debian/unstable
|
1
debian/source/format
vendored
Normal file
1
debian/source/format
vendored
Normal file
@ -0,0 +1 @@
|
||||
3.0 (quilt)
|
1
debian/watch_tbd
vendored
Normal file
1
debian/watch_tbd
vendored
Normal file
@ -0,0 +1 @@
|
||||
TBD: Eventually, we might need a true 'watch' file to barbican track of launchpad tarballs? See glance project for example.
|
11
etc/barbican/barbican-api.ini
Normal file
11
etc/barbican/barbican-api.ini
Normal file
@ -0,0 +1,11 @@
|
||||
[uwsgi]
|
||||
socket = :8080
|
||||
protocol = http
|
||||
processes = 1
|
||||
master = true
|
||||
vaccum = true
|
||||
no-default-app = true
|
||||
memory-report = true
|
||||
|
||||
pythonpath = /etc/barbican
|
||||
module = barbican.api.app:application
|
1
etc/barbican/barbican.conf
Normal file
1
etc/barbican/barbican.conf
Normal file
@ -0,0 +1 @@
|
||||
# Default Cloudkeep Barbican Config File
|
9
etc/barbican/config.py
Normal file
9
etc/barbican/config.py
Normal file
@ -0,0 +1,9 @@
|
||||
config = {
|
||||
'sqlalchemy' : {
|
||||
'url' : 'sqlite:////tmp/barbican.db',
|
||||
'echo' : True,
|
||||
'echo_pool' : False,
|
||||
'pool_recycle' : 3600,
|
||||
'encoding' : 'utf-8'
|
||||
}
|
||||
}
|
@ -1,3 +0,0 @@
|
||||
{
|
||||
"uuid": "3e77a5e3-8778-44aa-b19e-36e2b688b815"
|
||||
}
|
@ -1,7 +0,0 @@
|
||||
{
|
||||
"agent_id": "869b81e0-6181-4afc-bf5b-3f785f2d503f",
|
||||
"received_on": "2013-02-26T19:53:14+18:00",
|
||||
"severity": "INFO",
|
||||
"key_id": "331d341c-b171-47f9-9a7b-7a15e8d85893",
|
||||
"message": "Key accessed by user 'apache'."
|
||||
}
|
@ -1,23 +0,0 @@
|
||||
{
|
||||
"policies": [
|
||||
{
|
||||
"uuid": "01fb57ff-058c-4d68-85e9-d81844dd0089",
|
||||
"name": "Available after reboot",
|
||||
"directory_name": "my-app-key",
|
||||
"max_key_accesses": 1,
|
||||
"time_available_after_reboot": 10,
|
||||
"keys": [
|
||||
{
|
||||
"uuid": "e2b633c7-fda5-4be8-b42c-9a2c9280284d",
|
||||
"filename": "configuration_key",
|
||||
"mime_type": "application/aes-256-cbc",
|
||||
"expiration": "2014-02-28T19:14:44.180394",
|
||||
"secret": "b7990b786ee9659b43e6b1cd6136de07d9c5aa06513afe5d091c04bde981b280",
|
||||
"owner": "myapp",
|
||||
"group": "myapp",
|
||||
"cacheable": false
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
216
models.py
216
models.py
@ -1,216 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
Barbican Models
|
||||
~~~~~~~~~~~~~~~
|
||||
|
||||
The models for Barbican.
|
||||
|
||||
:copyright: (c) 2013 by Jarret Raim
|
||||
:license: Apache 2.0, see LICENSE for details
|
||||
"""
|
||||
from uuid import uuid4
|
||||
from sqlalchemy import Column, Integer, String, DateTime, Text, Boolean
|
||||
from sqlalchemy.orm import relationship, backref
|
||||
from sqlalchemy.schema import ForeignKey
|
||||
from database import Base
|
||||
|
||||
|
||||
class User(Base):
|
||||
__tablename__ = 'users'
|
||||
id = Column(Integer, primary_key=True)
|
||||
name = Column(String(50), unique=True)
|
||||
email = Column(String(120), unique=True)
|
||||
password = Column(String(50))
|
||||
|
||||
def is_authenticated(self):
|
||||
return True
|
||||
|
||||
def is_active(self):
|
||||
return True
|
||||
|
||||
def is_anonymous(self):
|
||||
return False
|
||||
|
||||
def get_id(self):
|
||||
return self.id
|
||||
|
||||
def __init__(self, name=None, email=None, password=None):
|
||||
self.name = name
|
||||
self.email = email
|
||||
self.password = password
|
||||
|
||||
def __repr__(self):
|
||||
return '<User %r>' % self.name
|
||||
|
||||
|
||||
class Tenant(Base):
|
||||
__tablename__ = 'tenants'
|
||||
id = Column(Integer, primary_key=True)
|
||||
uuid = Column(String(36), unique=True)
|
||||
|
||||
def __init__(self, uuid=None, id=None):
|
||||
self.id = id
|
||||
if uuid is None:
|
||||
self.uuid = str(uuid4())
|
||||
else:
|
||||
self.uuid = uuid
|
||||
|
||||
def __repr__(self):
|
||||
return '<Tenant %s>' % self.uuid
|
||||
|
||||
|
||||
class Key(Base):
|
||||
__tablename__ = 'keys'
|
||||
id = Column(Integer, primary_key=True)
|
||||
uuid = Column(String(36), unique=True)
|
||||
filename = Column(String(128))
|
||||
mime_type = Column(String(128))
|
||||
expiration = Column(DateTime)
|
||||
secret = Column(Text)
|
||||
owner = Column(String(33))
|
||||
group = Column(String(33))
|
||||
cacheable = Column(Boolean)
|
||||
|
||||
policy_id = Column(Integer, ForeignKey('policies.id'))
|
||||
policy = relationship("Policy", backref=backref('keys'))
|
||||
|
||||
def __init__(self, uuid=None, filename=None, mime_type=None, expiration=None, secret=None,
|
||||
owner=None, group=None, cacheable=None, policy_id=None):
|
||||
if uuid is None:
|
||||
self.uuid = str(uuid4())
|
||||
else:
|
||||
self.uuid = uuid
|
||||
|
||||
self.filename = filename
|
||||
self.mime_type = mime_type
|
||||
self.expiration = expiration
|
||||
self.secret = secret
|
||||
self.owner = owner
|
||||
self.group = group
|
||||
self.cacheable = cacheable
|
||||
self.policy_id = policy_id
|
||||
|
||||
def __repr__(self):
|
||||
return '<Key %s>' % self.uuid
|
||||
|
||||
def as_dict(self):
|
||||
json = {
|
||||
'uuid': self.uuid,
|
||||
'filename': self.filename,
|
||||
'mime_type': self.mime_type,
|
||||
'expiration': self.expiration.isoformat(),
|
||||
'secret': self.secret,
|
||||
'owner': self.owner,
|
||||
'group': self.group,
|
||||
'cachecable': self.cacheable
|
||||
}
|
||||
return json
|
||||
|
||||
|
||||
class Agent(Base):
|
||||
__tablename__ = 'agents'
|
||||
id = Column(Integer, primary_key=True)
|
||||
uuid = Column(String(36), unique=True)
|
||||
|
||||
tenant_id = Column(Integer, ForeignKey('tenants.id'))
|
||||
tenant = relationship("Tenant", backref=backref('agents', order_by=id))
|
||||
|
||||
def __init__(self, tenant=None, uuid=None):
|
||||
self.tenant = tenant
|
||||
if uuid is None:
|
||||
self.uuid = str(uuid4())
|
||||
else:
|
||||
self.uuid = uuid
|
||||
|
||||
def __repr__(self):
|
||||
return '<Agent %s>' % self.uuid
|
||||
|
||||
def as_dict(self):
|
||||
agent = {
|
||||
'tenant_id': self.tenant_id,
|
||||
'uuid': self.uuid
|
||||
}
|
||||
return agent
|
||||
|
||||
|
||||
class Policy(Base):
|
||||
__tablename__ = 'policies'
|
||||
id = Column(Integer, primary_key=True)
|
||||
uuid = Column(String(36), unique=True)
|
||||
name = Column(String(100))
|
||||
directory_name = Column(String(254))
|
||||
max_key_accesses = Column(Integer)
|
||||
time_available_after_reboot = Column(Integer)
|
||||
|
||||
tenant_id = Column(Integer, ForeignKey('tenants.id'))
|
||||
tenant = relationship("Tenant", backref=backref('policies', order_by=id))
|
||||
|
||||
def __init__(self, uuid=None, name=None, directory_name=None, max_key_accesses=None,
|
||||
time_available_after_reboot=None, tenant_id=None):
|
||||
if uuid is None:
|
||||
self.uuid = str(uuid4())
|
||||
else:
|
||||
self.uuid = uuid
|
||||
|
||||
self.name = name
|
||||
self.directory_name = directory_name
|
||||
self.max_key_accesses = max_key_accesses
|
||||
self.time_available_after_reboot = time_available_after_reboot
|
||||
self.tenant_id = tenant_id
|
||||
|
||||
def __repr__(self):
|
||||
return '<Policy %s >' % self.uuid
|
||||
|
||||
def as_dict(self):
|
||||
keys = map(Key.as_dict, self.keys)
|
||||
|
||||
json = {
|
||||
'uuid': self.uuid,
|
||||
'name': self.name,
|
||||
'directory_name': self.directory_name,
|
||||
'max_key_accesses': self.max_key_accesses,
|
||||
'time_available_after_reboot': self.time_available_after_reboot,
|
||||
'tenant_id': self.tenant_id,
|
||||
'keys': keys
|
||||
}
|
||||
return json
|
||||
|
||||
|
||||
class Event(Base):
|
||||
__tablename__ = 'events'
|
||||
id = Column(Integer, primary_key=True)
|
||||
agent_id = Column(String(36))
|
||||
received_on = Column(DateTime())
|
||||
severity = Column(String(10))
|
||||
message = Column(Text())
|
||||
|
||||
tenant_id = Column(Integer, ForeignKey('tenants.id'))
|
||||
tenant = relationship("Tenant", backref=backref('events', order_by=id))
|
||||
|
||||
key_id = Column(Integer, ForeignKey('keys.id'))
|
||||
key = relationship("Key", backref=backref('events', order_by=id))
|
||||
|
||||
def __init__(self, tenant_id=None, agent_id=None, received_on=None, severity=None,
|
||||
message=None, tenant=None, key=None):
|
||||
self.tenant_id = tenant_id
|
||||
self.agent_id = agent_id
|
||||
self.received_on = received_on
|
||||
self.severity = severity
|
||||
self.message = message
|
||||
self.key = key
|
||||
self.tenant = tenant
|
||||
|
||||
def __repr__(self):
|
||||
return '<Event %s [%s] - %s >' % (self.received_on, self.severity, self.message[:25])
|
||||
|
||||
def as_dict(self):
|
||||
json = {
|
||||
'id': self.id,
|
||||
'agent_id': self.agent_id,
|
||||
'received_on': self.received_on.isoformat(),
|
||||
'severity': self.severity,
|
||||
'tenant_id': self.tenant_id,
|
||||
'key_id': self.key_id,
|
||||
'message': self.message
|
||||
}
|
||||
return json
|
27
pylintrc
Normal file
27
pylintrc
Normal file
@ -0,0 +1,27 @@
|
||||
[Messages Control]
|
||||
# W0511: TODOs in code comments are fine.
|
||||
# W0142: *args and **kwargs are fine.
|
||||
# W0622: Redefining id is fine.
|
||||
disable-msg=W0511,W0142,W0622
|
||||
|
||||
[Basic]
|
||||
# Variable names can be 1 to 31 characters long, with lowercase and underscores
|
||||
variable-rgx=[a-z_][a-z0-9_]{0,30}$
|
||||
|
||||
# Argument names can be 2 to 31 characters long, with lowercase and underscores
|
||||
argument-rgx=[a-z_][a-z0-9_]{1,30}$
|
||||
|
||||
# Method names should be at least 3 characters long
|
||||
# and be lowecased with underscores
|
||||
method-rgx=[a-z_][a-z0-9_]{2,50}$
|
||||
|
||||
# Module names matching nova-* are ok (files in bin/)
|
||||
module-rgx=(([a-z_][a-z0-9_]*)|([A-Z][a-zA-Z0-9]+)|(nova-[a-z0-9_-]+))$
|
||||
|
||||
# Don't require docstrings on tests.
|
||||
no-docstring-rgx=((__.*__)|([tT]est.*)|setUp|tearDown)$
|
||||
|
||||
[Design]
|
||||
max-public-methods=100
|
||||
min-public-methods=0
|
||||
max-args=6
|
@ -1,14 +0,0 @@
|
||||
Flask==0.9
|
||||
Flask-Admin==1.0.4
|
||||
Flask-Login==0.1.3
|
||||
Flask-SQLAlchemy==0.16
|
||||
Flask-WTF==0.8.2
|
||||
Jinja2==2.6
|
||||
SQLAlchemy==0.8.0b2
|
||||
WTForms==1.0.3
|
||||
Werkzeug==0.8.3
|
||||
argparse==1.2.1
|
||||
ipython==0.13.1
|
||||
python-dateutil==2.1
|
||||
six==1.2.0
|
||||
wsgiref==0.1.2
|
96
run_tests.sh
Executable file
96
run_tests.sh
Executable file
@ -0,0 +1,96 @@
|
||||
#!/bin/bash
|
||||
|
||||
function usage {
|
||||
echo "Usage: $0 [OPTION]..."
|
||||
echo "Run Glance's test suite(s)"
|
||||
echo ""
|
||||
echo " -V, --virtual-env Always use virtualenv. Install automatically if not present"
|
||||
echo " -N, --no-virtual-env Don't use virtualenv. Run tests in local environment"
|
||||
echo " -f, --force Force a clean re-build of the virtual environment. Useful when dependencies have been added."
|
||||
echo " --unittests-only Run unit tests only, exclude functional tests."
|
||||
echo " -p, --pep8 Just run pep8"
|
||||
echo " -h, --help Print this usage message"
|
||||
echo ""
|
||||
echo "Note: with no options specified, the script will try to run the tests in a virtual environment,"
|
||||
echo " If no virtualenv is found, the script will ask if you would like to create one. If you "
|
||||
echo " prefer to run tests NOT in a virtual environment, simply pass the -N option."
|
||||
exit
|
||||
}
|
||||
|
||||
function process_option {
|
||||
case "$1" in
|
||||
-h|--help) usage;;
|
||||
-V|--virtual-env) let always_venv=1; let never_venv=0;;
|
||||
-N|--no-virtual-env) let always_venv=0; let never_venv=1;;
|
||||
-p|--pep8) let just_pep8=1;;
|
||||
-f|--force) let force=1;;
|
||||
--unittests-only) noseargs="$noseargs --exclude-dir=glance/tests/functional";;
|
||||
*) noseargs="$noseargs $1"
|
||||
esac
|
||||
}
|
||||
|
||||
venv=.venv
|
||||
with_venv=tools/with_venv.sh
|
||||
always_venv=0
|
||||
never_venv=0
|
||||
force=0
|
||||
noseargs=
|
||||
wrapper=""
|
||||
just_pep8=0
|
||||
|
||||
for arg in "$@"; do
|
||||
process_option $arg
|
||||
done
|
||||
|
||||
function run_tests {
|
||||
# Just run the test suites in current environment
|
||||
${wrapper} rm -f tests.sqlite
|
||||
${wrapper} $NOSETESTS 2> run_tests.err.log
|
||||
}
|
||||
|
||||
function run_pep8 {
|
||||
echo "Running pep8 ..."
|
||||
PEP8_OPTIONS="--exclude=$PEP8_EXCLUDE --repeat"
|
||||
PEP8_INCLUDE="bin/* glance tools setup.py run_tests.py"
|
||||
${wrapper} pep8 $PEP8_OPTIONS $PEP8_INCLUDE
|
||||
}
|
||||
|
||||
|
||||
NOSETESTS="python run_tests.py $noseargs"
|
||||
|
||||
if [ $never_venv -eq 0 ]
|
||||
then
|
||||
# Remove the virtual environment if --force used
|
||||
if [ $force -eq 1 ]; then
|
||||
echo "Cleaning virtualenv..."
|
||||
rm -rf ${venv}
|
||||
fi
|
||||
if [ -e ${venv} ]; then
|
||||
wrapper="${with_venv}"
|
||||
else
|
||||
if [ $always_venv -eq 1 ]; then
|
||||
# Automatically install the virtualenv
|
||||
python tools/install_venv.py
|
||||
wrapper="${with_venv}"
|
||||
else
|
||||
echo -e "No virtual environment found...create one? (Y/n) \c"
|
||||
read use_ve
|
||||
if [ "x$use_ve" = "xY" -o "x$use_ve" = "x" -o "x$use_ve" = "xy" ]; then
|
||||
# Install the virtualenv and run the test suite in it
|
||||
python tools/install_venv.py
|
||||
wrapper=${with_venv}
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ $just_pep8 -eq 1 ]; then
|
||||
run_pep8
|
||||
exit
|
||||
fi
|
||||
|
||||
run_tests || exit
|
||||
|
||||
if [ -z "$noseargs" ]; then
|
||||
run_pep8
|
||||
fi
|
41
setup.cfg
41
setup.cfg
@ -1,3 +1,28 @@
|
||||
[build_sphinx]
|
||||
all_files = 1
|
||||
build-dir = doc/build
|
||||
source-dir = doc/source
|
||||
|
||||
[egg_info]
|
||||
tag_build =
|
||||
tag_date = 0
|
||||
tag_svn_revision = 0
|
||||
|
||||
# TBD: Use this for OpenStack
|
||||
#[compile_catalog]
|
||||
#directory = barbican/locale
|
||||
#domain = barbican
|
||||
#
|
||||
#[update_catalog]
|
||||
#domain = barbican
|
||||
#output_dir = barbican/locale
|
||||
#input_file = barbican/locale/barbican.pot
|
||||
#
|
||||
#[extract_messages]
|
||||
#keywords = _ gettext ngettext l_ lazy_gettext
|
||||
#mapping_file = babel.cfg
|
||||
#output_file = barbican/locale/barbican.pot
|
||||
|
||||
[nosetests]
|
||||
where=barbican
|
||||
nocapture=1
|
||||
@ -8,3 +33,19 @@ traverse-namespace=1
|
||||
#with-coverage=1
|
||||
cover-package=barbican
|
||||
cover-inclusive=1
|
||||
|
||||
# TBD: OpenStack stuff...
|
||||
# NOTE(jkoelker) To run the test suite under nose install the following
|
||||
# coverage http://pypi.python.org/pypi/coverage
|
||||
# tissue http://pypi.python.org/pypi/tissue (pep8 checker)
|
||||
# openstack-nose https://github.com/jkoelker/openstack-nose
|
||||
#verbosity=2
|
||||
#detailed-errors=1
|
||||
#with-openstack=1
|
||||
#openstack-red=0.05
|
||||
#openstack-yellow=0.025
|
||||
#openstack-show-elapsed=1
|
||||
#openstack-color=1
|
||||
|
||||
#[install]
|
||||
#install-lib=/usr/local/bin/vbarbican/lib/python2.7/site-packages/
|
||||
|
77
setup.py
77
setup.py
@ -1,26 +1,73 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#!/usr/bin/python
|
||||
# Copyright (c) 2010 OpenStack, LLC.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
# implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
import os
|
||||
try:
|
||||
from setuptools import setup, find_packages
|
||||
from setuptools.command.sdist import sdist
|
||||
except ImportError:
|
||||
from ez_setup import use_setuptools
|
||||
use_setuptools()
|
||||
from setuptools import setup, find_packages
|
||||
from setuptools.command.sdist import sdist
|
||||
|
||||
# Determine version of this application.
|
||||
# TBD: Revisit version flows and processing once integrating with OpenStack, see glance setup.py
|
||||
PKG = "barbican"
|
||||
VERSIONFILE = os.path.join(PKG, "version.py")
|
||||
version = "unknown"
|
||||
try:
|
||||
version_file = open(VERSIONFILE, "r")
|
||||
for line in version_file:
|
||||
if '__version__' in line:
|
||||
version = line.split("'")[1]
|
||||
break
|
||||
except EnvironmentError:
|
||||
pass # Okay, there is no version file.
|
||||
|
||||
class local_sdist(sdist):
|
||||
"""Customized sdist hook - builds the ChangeLog file from VC first"""
|
||||
|
||||
def run(self):
|
||||
sdist.run(self)
|
||||
|
||||
cmdclass = {'sdist': local_sdist}
|
||||
|
||||
# TDB: Revisit sphinx documentation needs once move to OpenStack...see glance setup.py
|
||||
|
||||
setup(
|
||||
name = 'barbican',
|
||||
version = '0.1',
|
||||
description = '',
|
||||
author = 'Project Barbican',
|
||||
author_email = '',
|
||||
install_requires = [
|
||||
"falcon",
|
||||
"mock",
|
||||
"wsgiref",
|
||||
"uWSGI",
|
||||
"pymongo",
|
||||
version = version,
|
||||
description = 'The Barbican project provides a service for storing '
|
||||
'sensitive client information such as encryption keys',
|
||||
license='Apache License (2.0)',
|
||||
author = 'OpenStack',
|
||||
author_email = 'john.wood@rackspace.com',
|
||||
url='http://barbican.openstack.org/',
|
||||
packages = find_packages(exclude=['bin']),
|
||||
test_suite = 'nose.collector',
|
||||
cmdclass=cmdclass,
|
||||
include_package_data=True,
|
||||
classifiers=[
|
||||
'Development Status :: 4 - Beta',
|
||||
'License :: OSI Approved :: Apache Software License',
|
||||
'Operating System :: POSIX :: Linux',
|
||||
'Programming Language :: Python :: 2.7',
|
||||
'Environment :: No Input/Output (Daemon)',
|
||||
],
|
||||
test_suite = 'barbican.tests',
|
||||
zip_safe = False,
|
||||
include_package_data = True,
|
||||
packages = find_packages(exclude=['ez_setup'])
|
||||
scripts=['bin/barbican-api'],
|
||||
py_modules=[]
|
||||
)
|
||||
|
1
source/format
Normal file
1
source/format
Normal file
@ -0,0 +1 @@
|
||||
3.0 (quilt)
|
6307
static/css/bootstrap.css
vendored
6307
static/css/bootstrap.css
vendored
File diff suppressed because it is too large
Load Diff
873
static/css/bootstrap.min.css
vendored
873
static/css/bootstrap.min.css
vendored
File diff suppressed because one or more lines are too long
@ -1,24 +0,0 @@
|
||||
/* Tweak navbar brand link to be super sleek
|
||||
-------------------------------------------------- */
|
||||
|
||||
body > .navbar {
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
/* Change the docs' brand */
|
||||
body > .navbar .brand {
|
||||
padding-right: 0;
|
||||
padding-left: 0;
|
||||
margin-left: 20px;
|
||||
float: right;
|
||||
font-weight: bold;
|
||||
color: #000;
|
||||
text-shadow: 0 1px 0 rgba(255,255,255,.1), 0 0 30px rgba(255,255,255,.125);
|
||||
-webkit-transition: all .2s linear;
|
||||
-moz-transition: all .2s linear;
|
||||
transition: all .2s linear;
|
||||
}
|
||||
body > .navbar .brand:hover {
|
||||
text-decoration: none;
|
||||
text-shadow: 0 1px 0 rgba(255,255,255,.1), 0 0 30px rgba(255,255,255,.4);
|
||||
}
|
Binary file not shown.
Before Width: | Height: | Size: 1.4 KiB |
Binary file not shown.
Before Width: | Height: | Size: 8.6 KiB |
Binary file not shown.
Before Width: | Height: | Size: 12 KiB |
2279
static/js/bootstrap.js
vendored
2279
static/js/bootstrap.js
vendored
File diff suppressed because it is too large
Load Diff
7
static/js/bootstrap.min.js
vendored
7
static/js/bootstrap.min.js
vendored
File diff suppressed because one or more lines are too long
8
static/js/html5shiv.js
vendored
8
static/js/html5shiv.js
vendored
@ -1,8 +0,0 @@
|
||||
/*
|
||||
HTML5 Shiv v3.6.2pre | @afarkas @jdalton @jon_neal @rem | MIT/GPL2 Licensed
|
||||
*/
|
||||
(function(l,f){function m(){var a=e.elements;return"string"==typeof a?a.split(" "):a}function i(a){var b=n[a[o]];b||(b={},h++,a[o]=h,n[h]=b);return b}function p(a,b,c){b||(b=f);if(g)return b.createElement(a);c||(c=i(b));b=c.cache[a]?c.cache[a].cloneNode():r.test(a)?(c.cache[a]=c.createElem(a)).cloneNode():c.createElem(a);return b.canHaveChildren&&!s.test(a)?c.frag.appendChild(b):b}function t(a,b){if(!b.cache)b.cache={},b.createElem=a.createElement,b.createFrag=a.createDocumentFragment,b.frag=b.createFrag();
|
||||
a.createElement=function(c){return!e.shivMethods?b.createElem(c):p(c,a,b)};a.createDocumentFragment=Function("h,f","return function(){var n=f.cloneNode(),c=n.createElement;h.shivMethods&&("+m().join().replace(/\w+/g,function(a){b.createElem(a);b.frag.createElement(a);return'c("'+a+'")'})+");return n}")(e,b.frag)}function q(a){a||(a=f);var b=i(a);if(e.shivCSS&&!j&&!b.hasCSS){var c,d=a;c=d.createElement("p");d=d.getElementsByTagName("head")[0]||d.documentElement;c.innerHTML="x<style>article,aside,figcaption,figure,footer,header,hgroup,main,nav,section{display:block}mark{background:#FF0;color:#000}</style>";
|
||||
c=d.insertBefore(c.lastChild,d.firstChild);b.hasCSS=!!c}g||t(a,b);return a}var k=l.html5||{},s=/^<|^(?:button|map|select|textarea|object|iframe|option|optgroup)$/i,r=/^(?:a|b|code|div|fieldset|h1|h2|h3|h4|h5|h6|i|label|li|ol|p|q|span|strong|style|table|tbody|td|th|tr|ul)$/i,j,o="_html5shiv",h=0,n={},g;(function(){try{var a=f.createElement("a");a.innerHTML="<xyz></xyz>";j="hidden"in a;var b;if(!(b=1==a.childNodes.length)){f.createElement("a");var c=f.createDocumentFragment();b="undefined"==typeof c.cloneNode||
|
||||
"undefined"==typeof c.createDocumentFragment||"undefined"==typeof c.createElement}g=b}catch(d){g=j=!0}})();var e={elements:k.elements||"abbr article aside audio bdi canvas data datalist details figcaption figure footer header hgroup main mark meter nav output progress section summary time video",version:"3.6.2pre",shivCSS:!1!==k.shivCSS,supportsUnknownElements:g,shivMethods:!1!==k.shivMethods,type:"default",shivDocument:q,createElement:p,createDocumentFragment:function(a,b){a||(a=f);if(g)return a.createDocumentFragment();
|
||||
for(var b=b||i(a),c=b.frag.cloneNode(),d=0,e=m(),h=e.length;d<h;d++)c.createElement(e[d]);return c}};l.html5=e;q(f)})(this,document);
|
5
static/js/jquery-1.9.1.min.js
vendored
5
static/js/jquery-1.9.1.min.js
vendored
File diff suppressed because one or more lines are too long
@ -1,4 +0,0 @@
|
||||
{% extends 'layout.html' %}
|
||||
|
||||
{% block body %}
|
||||
{% endblock %}
|
@ -1,66 +0,0 @@
|
||||
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>{% block title %}Welcome{% endblock %} | Barbican</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta name="description" content="">
|
||||
<meta name="author" content="Jarret Raim">
|
||||
|
||||
<!-- Le styles -->
|
||||
<link href="/static/css/bootstrap.css" rel="stylesheet">
|
||||
<link href="/static/css/style.css" rel="stylesheet">
|
||||
<style>
|
||||
body {
|
||||
padding-top: 60px; /* 60px to make the container go all the way to the bottom of the topbar */
|
||||
}
|
||||
</style>
|
||||
|
||||
<!-- HTML5 shim, for IE6-8 support of HTML5 elements -->
|
||||
<!--[if lt IE 9]>
|
||||
<script src="/static/js/html5shiv.js"></script>
|
||||
<![endif]-->
|
||||
|
||||
<!-- Fav and touch icons -->
|
||||
<link rel="apple-touch-icon-precomposed" sizes="144x144" href="/static/ico/apple-touch-icon-144-precomposed.png">
|
||||
<link rel="apple-touch-icon-precomposed" sizes="114x114" href="/static/ico/apple-touch-icon-114-precomposed.png">
|
||||
<link rel="apple-touch-icon-precomposed" sizes="72x72" href="/static/ico/apple-touch-icon-72-precomposed.png">
|
||||
<link rel="apple-touch-icon-precomposed" href="/static/ico/apple-touch-icon-57-precomposed.png">
|
||||
<link rel="shortcut icon" href="/static/ico/favicon.ico">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<div class="navbar navbar-inverse navbar-fixed-top">
|
||||
<div class="navbar-inner">
|
||||
<div class="container">
|
||||
<button type="button" class="btn btn-navbar" data-toggle="collapse" data-target=".nav-collapse">
|
||||
<span class="icon-bar"></span>
|
||||
<span class="icon-bar"></span>
|
||||
<span class="icon-bar"></span>
|
||||
</button>
|
||||
<a class="brand" href="#">Barbican</a>
|
||||
<div class="nav-collapse collapse">
|
||||
<ul class="nav">
|
||||
<li><a href="/">Home</a></li>
|
||||
<li><a href="/users">Users</a></li>
|
||||
<li><a href="/tenants">Tenants</a></li>
|
||||
</ul>
|
||||
</div><!--/.nav-collapse -->
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="container">
|
||||
|
||||
{% block body %}{% endblock %}
|
||||
|
||||
</div> <!-- /container -->
|
||||
|
||||
<!-- Le javascript
|
||||
================================================== -->
|
||||
<!-- Placed at the end of the document so the pages load faster -->
|
||||
<script src="/static/js/jquery-1.9.1.min.js"></script>
|
||||
</body>
|
||||
</html>
|
@ -1,96 +0,0 @@
|
||||
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Sign in · Twitter Bootstrap</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta name="description" content="">
|
||||
<meta name="author" content="">
|
||||
|
||||
<!-- Le styles -->
|
||||
<link href="/static/css/bootstrap.css" rel="stylesheet">
|
||||
<style type="text/css">
|
||||
body {
|
||||
padding-top: 40px;
|
||||
padding-bottom: 40px;
|
||||
background-color: #f5f5f5;
|
||||
}
|
||||
|
||||
.form-signin {
|
||||
max-width: 300px;
|
||||
padding: 19px 29px 29px;
|
||||
margin: 0 auto 20px;
|
||||
background-color: #fff;
|
||||
border: 1px solid #e5e5e5;
|
||||
-webkit-border-radius: 5px;
|
||||
-moz-border-radius: 5px;
|
||||
border-radius: 5px;
|
||||
-webkit-box-shadow: 0 1px 2px rgba(0,0,0,.05);
|
||||
-moz-box-shadow: 0 1px 2px rgba(0,0,0,.05);
|
||||
box-shadow: 0 1px 2px rgba(0,0,0,.05);
|
||||
}
|
||||
.form-signin .form-signin-heading,
|
||||
.form-signin .checkbox {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
.form-signin input[type="text"],
|
||||
.form-signin input[type="password"] {
|
||||
font-size: 16px;
|
||||
height: auto;
|
||||
margin-bottom: 15px;
|
||||
padding: 7px 9px;
|
||||
}
|
||||
|
||||
</style>
|
||||
<link href="/static/css/bootstrap-responsive.css" rel="stylesheet">
|
||||
|
||||
<!-- HTML5 shim, for IE6-8 support of HTML5 elements -->
|
||||
<!--[if lt IE 9]>
|
||||
<script src="/static/js/html5shiv.js"></script>
|
||||
<![endif]-->
|
||||
|
||||
<!-- Fav and touch icons -->
|
||||
<link rel="apple-touch-icon-precomposed" sizes="144x144" href="/static/ico/apple-touch-icon-144-precomposed.png">
|
||||
<link rel="apple-touch-icon-precomposed" sizes="114x114" href="/static/ico/apple-touch-icon-114-precomposed.png">
|
||||
<link rel="apple-touch-icon-precomposed" sizes="72x72" href="/static/ico/apple-touch-icon-72-precomposed.png">
|
||||
<link rel="apple-touch-icon-precomposed" href="/static/ico/apple-touch-icon-57-precomposed.png">
|
||||
<link rel="shortcut icon" href="/static/ico/favicon.png">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<div class="container">
|
||||
|
||||
<form method="post" class="form-signin">
|
||||
{{ form.hidden_tag() }}
|
||||
<h2 class="form-signin-heading">Please sign in</h2>
|
||||
{% if form.errors %}
|
||||
<div class="alert alert-error alert-block">
|
||||
<h4>Errors</h4>
|
||||
<ul>
|
||||
{% for field_name, field_errors in form.errors|dictsort if field_errors %}
|
||||
{% for error in field_errors %}
|
||||
<li>{{ form[field_name].name }}: {{ error }}</li>
|
||||
{% endfor %}
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{% for f in form if f.type != 'CSRFTokenField' %}
|
||||
{{ f.label }}{{ f }}
|
||||
|
||||
{% endfor %}
|
||||
|
||||
<button class="btn btn-large btn-primary" type="submit">Sign in</button>
|
||||
</form>
|
||||
|
||||
</div> <!-- /container -->
|
||||
|
||||
<!-- Le javascript
|
||||
================================================== -->
|
||||
<!-- Placed at the end of the document so the pages load faster -->
|
||||
<script src="/static/js/jquery-1.9.1.min.js"></script>
|
||||
</body>
|
||||
</html>
|
153
tools/install_venv.py
Normal file
153
tools/install_venv.py
Normal file
@ -0,0 +1,153 @@
|
||||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
|
||||
# Copyright 2010 United States Government as represented by the
|
||||
# Administrator of the National Aeronautics and Space Administration.
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# Copyright 2010 OpenStack LLC.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
"""
|
||||
Installation script for Barbican's development virtualenv
|
||||
"""
|
||||
|
||||
import os
|
||||
import subprocess
|
||||
import sys
|
||||
|
||||
|
||||
ROOT = os.path.dirname(os.path.dirname(os.path.realpath(__file__)))
|
||||
VENV = os.path.join(ROOT, '.venv')
|
||||
PIP_REQUIRES = os.path.join(ROOT, 'tools', 'pip-requires')
|
||||
TEST_REQUIRES = os.path.join(ROOT, 'tools', 'test-requires')
|
||||
|
||||
|
||||
def die(message, *args):
|
||||
print >> sys.stderr, message % args
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
def run_command(cmd, redirect_output=True, check_exit_code=True):
|
||||
"""
|
||||
Runs a command in an out-of-process shell, returning the
|
||||
output of that command. Working directory is ROOT.
|
||||
"""
|
||||
if redirect_output:
|
||||
stdout = subprocess.PIPE
|
||||
else:
|
||||
stdout = None
|
||||
|
||||
proc = subprocess.Popen(cmd, cwd=ROOT, stdout=stdout)
|
||||
output = proc.communicate()[0]
|
||||
if check_exit_code and proc.returncode != 0:
|
||||
die('Command "%s" failed.\n%s', ' '.join(cmd), output)
|
||||
return output
|
||||
|
||||
|
||||
HAS_EASY_INSTALL = bool(run_command(['which', 'easy_install'],
|
||||
check_exit_code=False).strip())
|
||||
HAS_VIRTUALENV = bool(run_command(['which', 'virtualenv'],
|
||||
check_exit_code=False).strip())
|
||||
|
||||
|
||||
def check_dependencies():
|
||||
"""Make sure virtualenv is in the path."""
|
||||
|
||||
if not HAS_VIRTUALENV:
|
||||
print 'not found.'
|
||||
# Try installing it via easy_install...
|
||||
if HAS_EASY_INSTALL:
|
||||
print 'Installing virtualenv via easy_install...',
|
||||
if not run_command(['which', 'easy_install']):
|
||||
die('ERROR: virtualenv not found.\n\n'
|
||||
'Barbican development requires virtualenv, please install'
|
||||
' it using your favorite package management tool')
|
||||
print 'done.'
|
||||
print 'done.'
|
||||
|
||||
|
||||
def create_virtualenv(venv=VENV):
|
||||
"""
|
||||
Creates the virtual environment and installs PIP only into the
|
||||
virtual environment
|
||||
"""
|
||||
print 'Creating venv...',
|
||||
run_command(['virtualenv', '-q', '--no-site-packages', VENV])
|
||||
print 'done.'
|
||||
print 'Installing pip in virtualenv...',
|
||||
if not run_command(['tools/with_venv.sh', 'easy_install',
|
||||
'pip>1.0']).strip():
|
||||
die("Failed to install pip.")
|
||||
print 'done.'
|
||||
|
||||
|
||||
def pip_install(*args):
|
||||
run_command(['tools/with_venv.sh',
|
||||
'pip', 'install', '--upgrade'] + list(args),
|
||||
redirect_output=False)
|
||||
|
||||
|
||||
def install_dependencies(venv=VENV):
|
||||
print 'Installing dependencies with pip (this can take a while)...'
|
||||
|
||||
pip_install('pip')
|
||||
|
||||
pip_install('-r', PIP_REQUIRES)
|
||||
pip_install('-r', TEST_REQUIRES)
|
||||
|
||||
# Tell the virtual env how to "import barbican"
|
||||
py_ver = _detect_python_version(venv)
|
||||
pthfile = os.path.join(venv, "lib", py_ver, "site-packages", "barbican.pth")
|
||||
f = open(pthfile, 'w')
|
||||
f.write("%s\n" % ROOT)
|
||||
|
||||
|
||||
def _detect_python_version(venv):
|
||||
lib_dir = os.path.join(venv, "lib")
|
||||
for pathname in os.listdir(lib_dir):
|
||||
if pathname.startswith('python'):
|
||||
return pathname
|
||||
raise Exception('Unable to detect Python version')
|
||||
|
||||
|
||||
def print_help():
|
||||
help = """
|
||||
Barbican development environment setup is complete.
|
||||
|
||||
Barbican development uses virtualenv to track and manage Python dependencies
|
||||
while in development and testing.
|
||||
|
||||
To activate the Barbican virtualenv for the extent of your current shell session
|
||||
you can run:
|
||||
|
||||
$ source .venv/bin/activate
|
||||
|
||||
Or, if you prefer, you can run commands in the virtualenv on a case by case
|
||||
basis by running:
|
||||
|
||||
$ tools/with_venv.sh <your command>
|
||||
|
||||
Also, make test will automatically use the virtualenv.
|
||||
"""
|
||||
print help
|
||||
|
||||
|
||||
def main(argv):
|
||||
check_dependencies()
|
||||
create_virtualenv()
|
||||
install_dependencies()
|
||||
print_help()
|
||||
|
||||
if __name__ == '__main__':
|
||||
main(sys.argv)
|
@ -1,7 +1,8 @@
|
||||
falcon
|
||||
falcon>=0.1.1
|
||||
uWSGI>=1.4.5
|
||||
wsgiref>=0.1.2
|
||||
pymongo>=2.4.2
|
||||
pysqlite>=2.6.3
|
||||
|
||||
|
||||
# Right now this is the location for oslo-config (its not in PyPi yet)
|
||||
http://tarballs.openstack.org/oslo-config/oslo-config-2013.1b3.tar.gz#egg=oslo-config
|
||||
|
4
tools/with_venv.sh
Normal file
4
tools/with_venv.sh
Normal file
@ -0,0 +1,4 @@
|
||||
#!/bin/bash
|
||||
TOOLS=`dirname $0`
|
||||
VENV=$TOOLS/../.venv
|
||||
source $VENV/bin/activate && $@
|
1
tox.ini
1
tox.ini
@ -2,6 +2,7 @@
|
||||
envlist = py27
|
||||
|
||||
[testenv]
|
||||
setenv = VIRTUAL_ENV={envdir}
|
||||
deps = -r{toxinidir}/tools/pip-requires
|
||||
-r{toxinidir}/tools/test-requires
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user