Merge of previous project work into this project

This commit is contained in:
John Wood 2013-04-01 18:26:03 -05:00
parent 81d7598ca4
commit 8f783f473b
84 changed files with 1866 additions and 10258 deletions

3
.gitignore vendored
View File

@ -40,3 +40,6 @@ nosetests.xml
# Sqlite databases
*.sqlite3
*.db
# Misc. generated files
versiononly.txt

2
Authors Normal file
View File

@ -0,0 +1,2 @@
John Wood <john.wood@rackspace.com>

190
HACKING.rst Normal file
View 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.

View File

@ -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.
@ -199,4 +198,4 @@
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.
limitations under the License.

11
MANIFEST.in Normal file
View 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

View File

@ -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
View 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.

1
babel.cfg Normal file
View File

@ -0,0 +1 @@
[python: **.py]

View File

@ -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)

View 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.
"""
Cloudkeep's Barbican module root
"""

56
barbican/api/__init__.py Normal file
View 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
View 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
View 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
View 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
"""

View 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
"""

View 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
View 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
View 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"

View 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
View 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
View 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
View 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
View 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()

View File

@ -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.

View File

@ -0,0 +1 @@
__author__ = 'john.wood'

View File

@ -0,0 +1 @@
__author__ = 'john.wood'

View 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()

View File

@ -0,0 +1 @@
__author__ = 'john.wood'

View File

@ -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
View 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('.'))

View File

@ -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)

View File

@ -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.
"""

View File

@ -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
View File

@ -0,0 +1 @@
/var/lib/barbican/temp

94
debian/barbican-api.init vendored Normal file
View 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
View 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
View File

@ -0,0 +1,7 @@
/var/log/barbican/barbican-api.log {
daily
missingok
compress
delaycompress
notifempty
}

15
debian/barbican-api.postinst vendored Normal file
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View File

@ -0,0 +1 @@
7

96
debian/control vendored Normal file
View 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
View 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
View 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
View File

@ -0,0 +1 @@
usr/lib/python*/dist-packages/*

43
debian/rules vendored Normal file
View 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
View File

@ -0,0 +1 @@
3.0 (quilt)

1
debian/watch_tbd vendored Normal file
View File

@ -0,0 +1 @@
TBD: Eventually, we might need a true 'watch' file to barbican track of launchpad tarballs? See glance project for example.

View 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

View File

@ -0,0 +1 @@
# Default Cloudkeep Barbican Config File

9
etc/barbican/config.py Normal file
View File

@ -0,0 +1,9 @@
config = {
'sqlalchemy' : {
'url' : 'sqlite:////tmp/barbican.db',
'echo' : True,
'echo_pool' : False,
'pool_recycle' : 3600,
'encoding' : 'utf-8'
}
}

View File

@ -1,3 +0,0 @@
{
"uuid": "3e77a5e3-8778-44aa-b19e-36e2b688b815"
}

View File

@ -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'."
}

View File

@ -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
View File

@ -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
View 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

View File

@ -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
View 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

View File

@ -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/

View File

@ -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
View File

@ -0,0 +1 @@
3.0 (quilt)

6307
static/css/bootstrap.css vendored

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

View File

@ -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

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

View File

@ -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);

File diff suppressed because one or more lines are too long

View File

@ -1,4 +0,0 @@
{% extends 'layout.html' %}
{% block body %}
{% endblock %}

View File

@ -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>

View File

@ -1,96 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Sign in &middot; 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
View 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)

View File

@ -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
View File

@ -0,0 +1,4 @@
#!/bin/bash
TOOLS=`dirname $0`
VENV=$TOOLS/../.venv
source $VENV/bin/activate && $@

View File

@ -2,6 +2,7 @@
envlist = py27
[testenv]
setenv = VIRTUAL_ENV={envdir}
deps = -r{toxinidir}/tools/pip-requires
-r{toxinidir}/tools/test-requires

17
uwsgi.ini Normal file
View File

@ -0,0 +1,17 @@
[uwsgi]
socket = 172.16.83.131:8080
protocol = http
processes = 1
master = true
vaccum = true
no-default-app = true
memory-report = true
env = CONFIG_FILE=/etc/barbican/barbican.cfg
pythonpath = ./
module = barbican.api.app:application