Integrate Deckhand with keystone auth

This PS integrates Deckhand with keystone auth so
that Deckhand can check whether a keystone token is
authenticated (by way of keystonemiddleware)
before proceeding with any requests.

The architecture for this PS is borrowed from [0]
which successfully integrates keystone authentication
with the falcon web application framework. However,
additional Deckhand-specific changes were made for
tests to pass.

The following changes have been made:

  - add paste deploy configuration file which adds
    keystonemiddleware integration to Deckhand; this
    makes it trivial for keystonemiddleware to determine
    whether a token in the X-Auth-Token header is authenticated
  - use paste.deploy to create a web app
  - update unit tests for testing controllers
  - update functional test script to ignore keystone authentication
    because functional tests don't currently support keystone
    integration

[0] https://github.com/stannum-l/nautilus

Change-Id: I6eeeb4a4d9ab1f1cc8fb338e5cc21136ab4d5684
This commit is contained in:
Felipe Monteiro 2017-10-11 19:50:11 -04:00
parent d2d2312af9
commit 90226c2ae1
26 changed files with 402 additions and 101 deletions

View File

@ -16,7 +16,7 @@ from deckhand.control import api
def start_deckhand():
return api.start_api()
return api.init_application()
# Callable to be used by uwsgi.

View File

@ -33,15 +33,35 @@ barbican_opts = [
]
context_opts = [
cfg.BoolOpt('allow_anonymous_access', default=False,
help="""
Allow limited access to unauthenticated users.
Assign a boolean to determine API access for unathenticated
users. When set to False, the API cannot be accessed by
unauthenticated users. When set to True, unauthenticated users can
access the API with read-only privileges. This however only applies
when using ContextMiddleware.
Possible values:
* True
* False
"""),
]
def register_opts(conf):
conf.register_group(barbican_group)
conf.register_opts(barbican_opts, group=barbican_group)
conf.register_opts(context_opts)
ks_loading.register_auth_conf_options(conf, group=barbican_group.name)
ks_loading.register_session_conf_options(conf, group=barbican_group.name)
def list_opts():
opts = {barbican_group: barbican_opts +
opts = {None: context_opts,
barbican_group: barbican_opts +
ks_loading.get_session_conf_options() +
ks_loading.get_auth_common_conf_options() +
ks_loading.get_auth_plugin_conf_options(

View File

@ -43,3 +43,12 @@ class RequestContext(context.RequestContext):
@classmethod
def from_dict(cls, values):
return cls(**values)
def get_context():
"""A helper method to get a blank context (useful for tests)."""
return RequestContext(user_id=None,
project_id=None,
roles=[],
is_admin=False,
overwrite=False)

View File

@ -12,27 +12,22 @@
# See the License for the specific language governing permissions and
# limitations under the License.
import logging as py_logging
import os
import falcon
from oslo_config import cfg
from oslo_log import log as logging
from oslo_policy import policy
from paste import deploy
from deckhand.control import base
from deckhand.control import buckets
from deckhand.control import revision_diffing
from deckhand.control import revision_documents
from deckhand.control import revision_tags
from deckhand.control import revisions
from deckhand.control import rollback
from deckhand.control import versions
from deckhand.db.sqlalchemy import api as db_api
CONF = cfg.CONF
logging.register_options(CONF)
# TODO(fmontei): Include deckhand-paste.ini later.
CONFIG_FILES = ['deckhand.conf']
logging.register_options(CONF)
LOG = logging.getLogger(__name__)
CONFIG_FILES = ['deckhand.conf', 'deckhand-paste.ini']
def _get_config_files(env=None):
@ -42,46 +37,38 @@ def _get_config_files(env=None):
return [os.path.join(dirname, config_file) for config_file in CONFIG_FILES]
def start_api():
def setup_logging(conf):
# Add additional dependent libraries that have unhelp bug levels
extra_log_level_defaults = []
logging.set_defaults(default_log_levels=logging.get_default_log_levels() +
extra_log_level_defaults)
logging.setup(conf, 'deckhand')
py_logging.captureWarnings(True)
def init_application():
"""Main entry point for initializing the Deckhand API service.
Create routes for the v1.0 API and sets up logging.
"""
config_files = _get_config_files()
CONF([], project='deckhand', default_config_files=config_files)
logging.setup(CONF, "deckhand")
paste_file = config_files[-1]
LOG = logging.getLogger(__name__)
LOG.info('Initiated Deckhand logging.')
CONF([], project='deckhand', default_config_files=config_files)
setup_logging(CONF)
policy.Enforcer(CONF)
LOG.debug('Starting WSGI application using %s configuration file.',
paste_file)
db_api.drop_db()
db_api.setup_db()
control_api = falcon.API(request_type=base.DeckhandRequest)
v1_0_routes = [
('bucket/{bucket_name}/documents', buckets.BucketsResource()),
('revisions', revisions.RevisionsResource()),
('revisions/{revision_id}', revisions.RevisionsResource()),
('revisions/{revision_id}/diff/{comparison_revision_id}',
revision_diffing.RevisionDiffingResource()),
('revisions/{revision_id}/documents',
revision_documents.RevisionDocumentsResource()),
('revisions/{revision_id}/rendered-documents',
revision_documents.RenderedDocumentsResource()),
('revisions/{revision_id}/tags', revision_tags.RevisionTagsResource()),
('revisions/{revision_id}/tags/{tag}',
revision_tags.RevisionTagsResource()),
('rollback/{revision_id}', rollback.RollbackResource())
]
for path, res in v1_0_routes:
control_api.add_route(os.path.join('/api/v1.0', path), res)
control_api.add_route('/versions', versions.VersionsResource())
return control_api
app = deploy.loadapp('config:%s' % paste_file, name='deckhand_api')
return app
if __name__ == '__main__':
start_api()
init_application()

View File

@ -12,8 +12,6 @@
# See the License for the specific language governing permissions and
# limitations under the License.
import yaml
import falcon
from deckhand import context
@ -35,25 +33,9 @@ class BaseResource(object):
resp.headers['Allow'] = ','.join(allowed_methods)
resp.status = falcon.HTTP_200
def to_yaml_body(self, dict_body):
"""Converts JSON body into YAML response body.
:param dict_body: response body to be converted to YAML.
:returns: YAML encoding of `dict_body`.
"""
if isinstance(dict_body, dict):
return yaml.safe_dump(dict_body)
elif isinstance(dict_body, list):
return yaml.safe_dump_all(dict_body)
raise TypeError('Unrecognized dict_body type when converting response '
'body to YAML format.')
class DeckhandRequest(falcon.Request):
def __init__(self, env, options=None):
super(DeckhandRequest, self).__init__(env, options)
self.context = context.RequestContext.from_environ(self.env)
context_type = context.RequestContext
@property
def project_id(self):

View File

@ -66,8 +66,7 @@ class BucketsResource(api_base.BaseResource):
bucket_name, list(documents_to_create))
if created_documents:
resp.body = self.to_yaml_body(
self.view_builder.list(created_documents))
resp.body = self.view_builder.list(created_documents)
resp.status = falcon.HTTP_200
resp.append_header('Content-Type', 'application/x-yaml')

View File

@ -0,0 +1,134 @@
# Copyright 2017 AT&T Intellectual Property. All other 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.
import yaml
import falcon
from oslo_config import cfg
from oslo_log import log as logging
from oslo_serialization import jsonutils as json
import deckhand.context
from deckhand import errors
CONF = cfg.CONF
LOG = logging.getLogger(__name__)
class ContextMiddleware(object):
def process_request(self, req, resp):
"""Convert authentication information into a request context.
Generate a ``deckhand.context.RequestContext`` object from the
available authentication headers and store in the ``context`` attribute
of the ``req`` object.
:param req: ``falcon`` request object that will be given the context
object.
:raises: falcon.HTTPUnauthorized: when value of the
'X-Identity-Status' header is not 'Confirmed' and anonymous access
is disallowed.
"""
if req.headers.get('X-IDENTITY-STATUS') == 'Confirmed':
req.context = deckhand.context.RequestContext.from_environ(req.env)
elif CONF.allow_anonymous_access:
req.context = deckhand.context.get_context()
else:
raise falcon.HTTPUnauthorized()
class HookableMiddlewareMixin(object):
"""Provides methods to extract before and after hooks from WSGI Middleware
Prior to falcon 0.2.0b1, it's necessary to provide falcon with middleware
as "hook" functions that are either invoked before (to process requests)
or after (to process responses) the API endpoint code runs.
This mixin allows the process_request and process_response methods from a
typical WSGI middleware object to be extracted for use as these hooks, with
the appropriate method signatures.
"""
def as_before_hook(self):
"""Extract process_request method as "before" hook
:return: before hook function
"""
# Need to wrap this up in a closure because the parameter counts
# differ
def before_hook(req, resp, params=None):
return self.process_request(req, resp)
try:
return before_hook
except AttributeError as ex:
# No such method, we presume.
message_template = ("Failed to get before hook from middleware "
"{0} - {1}")
message = message_template.format(self.__name__, ex.message)
LOG.error(message)
raise errors.DeckhandException(message)
def as_after_hook(self):
"""Extract process_response method as "after" hook
:return: after hook function
"""
# Need to wrap this up in a closure because the parameter counts
# differ
def after_hook(req, resp, resource=None):
return self.process_response(req, resp, resource)
try:
return after_hook
except AttributeError as ex:
# No such method, we presume.
message_template = ("Failed to get after hook from middleware "
"{0} - {1}")
message = message_template.format(self.__name__, ex.message)
LOG.error(message)
raise errors.DeckhandException(message)
class YAMLTranslator(HookableMiddlewareMixin, object):
"""Middleware for converting all responses (error and success) to YAML.
``falcon`` error exceptions use JSON formatting and headers by default.
This middleware will intercept all responses and guarantee they are YAML
format.
.. note::
This does not include the 401 Unauthorized that is raised by
``keystonemiddleware`` which is executed in the pipeline before
``falcon`` middleware.
"""
def process_response(self, req, resp, resource):
resp.set_header('Content-Type', 'application/x-yaml')
for attr in ('body', 'data'):
if not hasattr(resp, attr):
continue
resp_attr = getattr(resp, attr)
try:
resp_attr = json.loads(resp_attr)
except (TypeError, ValueError):
pass
if isinstance(resp_attr, dict):
setattr(resp, attr, yaml.safe_dump(resp_attr))
elif isinstance(resp_attr, (list, tuple)):
setattr(resp, attr, yaml.safe_dump_all(resp_attr))

View File

@ -38,4 +38,4 @@ class RevisionDiffingResource(api_base.BaseResource):
resp.status = falcon.HTTP_200
resp.append_header('Content-Type', 'application/x-yaml')
resp.body = self.to_yaml_body(resp_body)
resp.body = resp_body

View File

@ -62,7 +62,7 @@ class RevisionDocumentsResource(api_base.BaseResource):
resp.status = falcon.HTTP_200
resp.append_header('Content-Type', 'application/x-yaml')
resp.body = self.to_yaml_body(self.view_builder.list(documents))
resp.body = self.view_builder.list(documents)
class RenderedDocumentsResource(api_base.BaseResource):
@ -109,5 +109,4 @@ class RenderedDocumentsResource(api_base.BaseResource):
resp.status = falcon.HTTP_200
resp.append_header('Content-Type', 'application/x-yaml')
resp.body = self.to_yaml_body(
self.view_builder.list(rendered_documents))
resp.body = self.view_builder.list(rendered_documents)

View File

@ -52,7 +52,7 @@ class RevisionTagsResource(api_base.BaseResource):
resp_body = revision_tag_view.ViewBuilder().show(resp_tag)
resp.status = falcon.HTTP_201
resp.append_header('Content-Type', 'application/x-yaml')
resp.body = self.to_yaml_body(resp_body)
resp.body = resp_body
def on_get(self, req, resp, revision_id, tag=None):
"""Show tag details or list all tags for a revision."""
@ -73,7 +73,7 @@ class RevisionTagsResource(api_base.BaseResource):
resp_body = revision_tag_view.ViewBuilder().show(resp_tag)
resp.status = falcon.HTTP_200
resp.append_header('Content-Type', 'application/x-yaml')
resp.body = self.to_yaml_body(resp_body)
resp.body = resp_body
@policy.authorize('deckhand:list_tags')
def _list_all_tags(self, req, resp, revision_id):
@ -86,7 +86,7 @@ class RevisionTagsResource(api_base.BaseResource):
resp_body = revision_tag_view.ViewBuilder().list(resp_tags)
resp.status = falcon.HTTP_200
resp.append_header('Content-Type', 'application/x-yaml')
resp.body = self.to_yaml_body(resp_body)
resp.body = resp_body
def on_delete(self, req, resp, revision_id, tag=None):
"""Deletes a single tag or deletes all tags for a revision."""

View File

@ -54,7 +54,7 @@ class RevisionsResource(api_base.BaseResource):
revision_resp = self.view_builder.show(revision)
resp.status = falcon.HTTP_200
resp.append_header('Content-Type', 'application/x-yaml')
resp.body = self.to_yaml_body(revision_resp)
resp.body = revision_resp
@policy.authorize('deckhand:list_revisions')
@common.sanitize_params(['tag'])
@ -64,7 +64,7 @@ class RevisionsResource(api_base.BaseResource):
resp.status = falcon.HTTP_200
resp.append_header('Content-Type', 'application/x-yaml')
resp.body = self.to_yaml_body(revisions_resp)
resp.body = revisions_resp
@policy.authorize('deckhand:delete_revisions')
def on_delete(self, req, resp):

View File

@ -48,4 +48,4 @@ class RollbackResource(api_base.BaseResource):
revision_resp = self.view_builder.show(rollback_revision)
resp.status = falcon.HTTP_201
resp.append_header('Content-Type', 'application/x-yaml')
resp.body = self.to_yaml_body(revision_resp)
resp.body = revision_resp

68
deckhand/service.py Normal file
View File

@ -0,0 +1,68 @@
# 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
import falcon
from oslo_config import cfg
from oslo_log import log
from deckhand.control import base
from deckhand.control import buckets
from deckhand.control import middleware
from deckhand.control import revision_diffing
from deckhand.control import revision_documents
from deckhand.control import revision_tags
from deckhand.control import revisions
from deckhand.control import rollback
from deckhand.control import versions
CONF = cfg.CONF
LOG = log.getLogger(__name__)
def configure_app(app, version=''):
v1_0_routes = [
('bucket/{bucket_name}/documents', buckets.BucketsResource()),
('revisions', revisions.RevisionsResource()),
('revisions/{revision_id}', revisions.RevisionsResource()),
('revisions/{revision_id}/diff/{comparison_revision_id}',
revision_diffing.RevisionDiffingResource()),
('revisions/{revision_id}/documents',
revision_documents.RevisionDocumentsResource()),
('revisions/{revision_id}/rendered-documents',
revision_documents.RenderedDocumentsResource()),
('revisions/{revision_id}/tags', revision_tags.RevisionTagsResource()),
('revisions/{revision_id}/tags/{tag}',
revision_tags.RevisionTagsResource()),
('rollback/{revision_id}', rollback.RollbackResource())
]
for path, res in v1_0_routes:
app.add_route(os.path.join('/api/%s' % version, path), res)
app.add_route('/versions', versions.VersionsResource())
return app
def deckhand_app_factory(global_config, **local_config):
# The order of the middleware is important because the `process_response`
# method for `YAMLTranslator` should execute after that of any other
# middleware to convert the response to YAML format.
middleware_list = [middleware.YAMLTranslator(),
middleware.ContextMiddleware()]
app = falcon.API(request_type=base.DeckhandRequest,
middleware=middleware_list)
return configure_app(app, version='v1.0')

View File

@ -28,7 +28,3 @@ tests:
PUT: /api/v1.0/bucket/b/documents
status: 409
data: <@resources/sample-doc.yaml
# Deckhand exceptions return the following content-type header by
# default. TODO(fmontei): Override that later.
response_headers:
content-type: 'application/json; charset=UTF-8'

View File

@ -69,7 +69,3 @@ tests:
desc: Verify that the revision was deleted
GET: /api/v1.0/revisions/$HISTORY['initialize'].$RESPONSE['$.[0].status.revision']
status: 404
# Deckhand exceptions return the following content-type header by
# default. TODO(fmontei): Override that later.
response_headers:
content-type: 'application/json; charset=UTF-8'

View File

@ -12,6 +12,8 @@
# See the License for the specific language governing permissions and
# limitations under the License.
from __future__ import absolute_import
import os
import fixtures

View File

@ -14,9 +14,9 @@
from falcon import testing as falcon_testing
from deckhand.control import api
from deckhand import service
from deckhand.tests.unit import base as test_base
from deckhand.tests.unit import policy_fixture
from deckhand.tests.unit import fixtures
class BaseControllerTest(test_base.DeckhandWithDBTestCase,
@ -25,5 +25,9 @@ class BaseControllerTest(test_base.DeckhandWithDBTestCase,
def setUp(self):
super(BaseControllerTest, self).setUp()
self.app = falcon_testing.TestClient(api.start_api())
self.policy = self.useFixture(policy_fixture.RealPolicyFixture())
self.app = falcon_testing.TestClient(
service.deckhand_app_factory(None))
self.policy = self.useFixture(fixtures.RealPolicyFixture())
# NOTE: allow_anonymous_access allows these unit tests to get around
# Keystone authentication.
self.useFixture(fixtures.ConfPatcher(allow_anonymous_access=True))

View File

@ -16,7 +16,6 @@ import inspect
import mock
from deckhand.control import api
from deckhand.control import base
from deckhand.control import buckets
from deckhand.control import revision_diffing
from deckhand.control import revision_documents
@ -45,19 +44,17 @@ class TestApi(test_base.DeckhandTestCase):
if inspect.isclass(obj)]
return class_names
@mock.patch.object(api, 'policy', autospec=True)
@mock.patch.object(api, 'db_api', autospec=True)
@mock.patch.object(api, 'logging', autospec=True)
@mock.patch.object(api, 'CONF', autospec=True)
@mock.patch.object(api, 'falcon', autospec=True)
def test_start_api(self, mock_falcon, mock_config, mock_logging,
mock_db_api):
@mock.patch('deckhand.service.falcon', autospec=True)
def test_init_application(self, mock_falcon, mock_config, mock_logging,
mock_db_api, _):
mock_falcon_api = mock_falcon.API.return_value
result = api.start_api()
self.assertEqual(mock_falcon_api, result)
api.init_application()
mock_falcon.API.assert_called_once_with(
request_type=base.DeckhandRequest)
mock_falcon_api.add_route.assert_has_calls([
mock.call('/api/v1.0/bucket/{bucket_name}/documents',
self.buckets_resource()),

View File

@ -12,6 +12,9 @@
# License for the specific language governing permissions and limitations
# under the License.
"""Fixtures for Deckhand tests."""
from __future__ import absolute_import
import os
import yaml
@ -24,10 +27,37 @@ from deckhand import policies
import deckhand.policy
from deckhand.tests.unit import fake_policy
CONF = cfg.CONF
class ConfPatcher(fixtures.Fixture):
"""Fixture to patch and restore global CONF.
This also resets overrides for everything that is patched during
it's teardown.
"""
def __init__(self, **kwargs):
"""Constructor
:params group: if specified all config options apply to that group.
:params **kwargs: the rest of the kwargs are processed as a
set of key/value pairs to be set as configuration override.
"""
super(ConfPatcher, self).__init__()
self.group = kwargs.pop('group', None)
self.args = kwargs
def setUp(self):
super(ConfPatcher, self).setUp()
for k, v in self.args.items():
self.addCleanup(CONF.clear_override, k, self.group)
CONF.set_override(k, v, self.group)
class RealPolicyFixture(fixtures.Fixture):
"""Load the live policy for tests.

View File

@ -17,7 +17,7 @@ from oslo_policy import policy as common_policy
from deckhand.control import base as api_base
import deckhand.policy
from deckhand.tests.unit import base as test_base
from deckhand.tests.unit import policy_fixture
from deckhand.tests.unit import fixtures
class PolicyBaseTestCase(test_base.DeckhandTestCase):
@ -32,7 +32,7 @@ class PolicyBaseTestCase(test_base.DeckhandTestCase):
"deckhand:list_cleartext_documents": [['rule:admin_api']]
}
self.policy = self.useFixture(policy_fixture.RealPolicyFixture())
self.policy = self.useFixture(fixtures.RealPolicyFixture())
self._set_rules()
def _set_rules(self):

View File

@ -0,0 +1,35 @@
# Copyright 2017 AT&T Intellectual Property. All other 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.
# PasteDeploy Configuration File
# Used to configure uWSGI middleware pipeline
[filter:authtoken]
paste.filter_factory = keystonemiddleware.auth_token:filter_factory
[filter:debug]
use = egg:oslo.middleware#debug
[filter:cors]
paste.filter_factory = oslo_middleware.cors:filter_factory
oslo_config_project = deckhand
[filter:request_id]
paste.filter_factory = oslo_middleware:RequestId.factory
[app:api]
paste.app_factory = deckhand.service:deckhand_app_factory
[pipeline:deckhand_api]
pipeline = authtoken api

View File

@ -1,5 +1,24 @@
[DEFAULT]
#
# From deckhand.conf
#
#
# Allow limited access to unauthenticated users.
#
# Assign a boolean to determine API access for unathenticated
# users. When set to False, the API cannot be accessed by
# unauthenticated users. When set to True, unauthenticated users can
# access the API with read-only privileges. This however only applies
# when using ContextMiddleware.
#
# Possible values:
# * True
# * False
# (boolean value)
#allow_anonymous_access = false
#
# From oslo.log
#

View File

@ -60,7 +60,8 @@
# GET /api/v1.0/revisions
#"deckhand:list_revisions": "rule:admin_api"
# Delete all revisions.
# Delete all revisions. Warning: this is equivalent to purging the
# database.
# DELETE /api/v1.0/revisions
#"deckhand:delete_revisions": "rule:admin_api"

View File

@ -10,6 +10,7 @@ pbr!=2.1.0,>=2.0.0 # Apache-2.0
PasteDeploy>=1.5.0 # MIT
Paste # MIT
Routes>=2.3.1 # MIT
keystoneauth1>=3.2.0 # Apache-2.0
six>=1.9.0 # MIT
oslo.concurrency>=3.8.0 # Apache-2.0

View File

@ -3,8 +3,8 @@ name = deckhand
summary = Secrets management persistence tool.
description-file = README.rst
author = deckhand team
home-page = http://deckhand-helm.readthedocs.io/en/latest/
author = Deckhand team
home-page = http://deckhand.readthedocs.io/en/latest/
classifier =
Intended Audience :: Information Technology
Intended Audience :: System Administrators

View File

@ -47,6 +47,9 @@ function gen_config {
cp etc/deckhand/logging.conf.sample $CONF_DIR/logging.conf
# NOTE: allow_anonymous_access allows these functional tests to get around
# Keystone authentication, but the context that is provided has zero privileges
# so we must also override the policy file for authorization to pass.
cat <<EOCONF > $CONF_DIR/deckhand.conf
[DEFAULT]
debug = true
@ -54,6 +57,7 @@ log_config_append = $CONF_DIR/logging.conf
log_file = deckhand.log
log_dir = .
use_stderr = true
allow_anonymous_access = true
[oslo_policy]
policy_file = policy.yaml
@ -64,6 +68,15 @@ policy_file = policy.yaml
connection = $DATABASE_URL
[keystone_authtoken]
# Populate keystone_authtoken with values like the following should Keystone
# integration be needed here.
# project_domain_name = Default
# project_name = admin
# user_domain_name = Default
# password = devstack
# username = admin
# auth_url = http://127.0.0.1/identity
# auth_type = password
EOCONF
echo $CONF_DIR/deckhand.conf 1>&2
@ -73,6 +86,14 @@ EOCONF
rm -f deckhand.log
}
function gen_paste {
log_section Creating paste config without [filter:authtoken]
# NOTE(fmontei): Since this script does not currently support Keystone
# integration, we remove ``filter:authtoken`` from the ``deckhand_api``
# pipeline to avoid any kind of auth issues.
sed 's/authtoken api/api/' etc/deckhand/deckhand-paste.ini &> $CONF_DIR/deckhand-paste.ini
}
function gen_policy {
log_section Creating policy file with liberal permissions
@ -92,6 +113,7 @@ function gen_policy {
}
gen_config
gen_paste
gen_policy
uwsgi \