Ensure SY gets redacted rendered documents

- Introduced query parameter 'cleartext-secrets' yields
  cleartext secrets in encrypted documents if true, else
  the values are redacted.
- Introduced CLI flag '--cleartext-secrets' yields cleartext
  secrets in encrypted documents.

Change-Id: I8a950c8bded3550f4aab8e6dc662ea330c2fb73f
This commit is contained in:
anthony.bellino 2018-10-19 21:36:25 +00:00
parent 1c08239287
commit 407cdc76d0
13 changed files with 59 additions and 18 deletions

View File

@ -370,6 +370,7 @@ conf:
workflow_orchestrator:get_configdocs: rule:admin_read_access workflow_orchestrator:get_configdocs: rule:admin_read_access
workflow_orchestrator:commit_configdocs: rule:admin_create workflow_orchestrator:commit_configdocs: rule:admin_create
workflow_orchestrator:get_renderedconfigdocs: rule:admin_read_access workflow_orchestrator:get_renderedconfigdocs: rule:admin_read_access
workflow_orchestrator:get_renderedconfigdocs_cleartext: rule:admin_read_access
workflow_orchestrator:list_workflows: rule:admin_read_access workflow_orchestrator:list_workflows: rule:admin_read_access
workflow_orchestrator:get_workflow: rule:admin_read_access workflow_orchestrator:get_workflow: rule:admin_read_access
workflow_orchestrator:get_notedetails: rule:admin_read_access workflow_orchestrator:get_notedetails: rule:admin_read_access

View File

@ -259,8 +259,11 @@ Returns the full set of configdocs in their rendered form.
Query Parameters Query Parameters
'''''''''''''''' ''''''''''''''''
version=committed | last_site_action | successful_site_action | **buffer** - version=committed | last_site_action | successful_site_action | **buffer**
Return the documents for the version specified - buffer by default. Return the documents for the version specified - buffer by default.
- cleartext-secrets=true/**false**
If true then returns cleartext secrets in encrypted documents, otherwise
those values are redacted.
Responses Responses
''''''''' '''''''''

View File

@ -726,6 +726,7 @@ applying Deckhand layering and substitution.
shipyard get renderedconfigdocs shipyard get renderedconfigdocs
[--committed | --last-site-action | --successful-site-action | --buffer] [--committed | --last-site-action | --successful-site-action | --buffer]
[--cleartext-secrets]
Example: Example:
shipyard get renderedconfigdocs shipyard get renderedconfigdocs
@ -743,6 +744,10 @@ applying Deckhand layering and substitution.
Retrieve the documents that have been loaded into Shipyard since the Retrieve the documents that have been loaded into Shipyard since the
prior commit. (default) prior commit. (default)
\--cleartext-secrets
Returns secrets as cleartext for encrypted documents if the user has the appropriate
permissions in the target environment.
Sample Sample
^^^^^^ ^^^^^^

View File

@ -98,7 +98,7 @@ class ConfigDocsResource(BaseResource):
Returns a collection of documents Returns a collection of documents
""" """
version = (req.params.get('version') or 'buffer') version = (req.params.get('version') or 'buffer')
cleartext_secrets = req.get_param_as_bool('cleartext-secrets') cleartext_secrets = req.get_param_as_bool('cleartext-secrets') or False
self._validate_version_parameter(version) self._validate_version_parameter(version)
helper = ConfigdocsHelper(req.context) helper = ConfigdocsHelper(req.context)
# Not reformatting to JSON or YAML since just passing through # Not reformatting to JSON or YAML since just passing through

View File

@ -43,11 +43,19 @@ class RenderedConfigDocsResource(BaseResource):
Returns the whole set of rendered documents Returns the whole set of rendered documents
""" """
version = (req.params.get('version') or 'buffer') version = (req.params.get('version') or 'buffer')
cleartext_secrets = req.get_param_as_bool('cleartext-secrets') or False
self._validate_version_parameter(version) self._validate_version_parameter(version)
helper = ConfigdocsHelper(req.context) helper = ConfigdocsHelper(req.context)
# Check access to cleartext_secrets
if cleartext_secrets:
policy.check_auth(req.context,
policy.GET_RENDEREDCONFIGDOCS_CLRTXT)
resp.body = self.get_rendered_configdocs( resp.body = self.get_rendered_configdocs(
helper=helper, helper=helper,
version=version version=version,
cleartext_secrets=cleartext_secrets
) )
resp.append_header('Content-Type', 'application/x-yaml') resp.append_header('Content-Type', 'application/x-yaml')
resp.status = falcon.HTTP_200 resp.status = falcon.HTTP_200
@ -64,8 +72,9 @@ class RenderedConfigDocsResource(BaseResource):
retry=False, retry=False,
) )
def get_rendered_configdocs(self, helper, version='buffer'): def get_rendered_configdocs(self, helper, version='buffer',
cleartext_secrets=False):
""" """
Get and return the rendered configdocs from the helper/Deckhand Get and return the rendered configdocs from the helper/Deckhand
""" """
return helper.get_rendered_configdocs(version) return helper.get_rendered_configdocs(version, cleartext_secrets)

View File

@ -375,7 +375,7 @@ class ConfigdocsHelper(object):
status=falcon.HTTP_404, status=falcon.HTTP_404,
retry=False) retry=False)
def get_rendered_configdocs(self, version=BUFFER): def get_rendered_configdocs(self, version=BUFFER, cleartext_secrets=False):
""" """
Returns the rendered configuration documents for the specified Returns the rendered configuration documents for the specified
revision (by name BUFFER, COMMITTED, LAST_SITE_ACTION, revision (by name BUFFER, COMMITTED, LAST_SITE_ACTION,
@ -397,6 +397,7 @@ class ConfigdocsHelper(object):
try: try:
return self.deckhand.get_rendered_docs_from_revision( return self.deckhand.get_rendered_docs_from_revision(
cleartext_secrets=cleartext_secrets,
revision_id=revision_id) revision_id=revision_id)
except DeckhandError as de: except DeckhandError as de:
raise ApiError( raise ApiError(

View File

@ -232,7 +232,8 @@ class DeckhandClient(object):
response.text))}) response.text))})
return errors return errors
def get_rendered_docs_from_revision(self, revision_id, bucket_id=None): def get_rendered_docs_from_revision(self, revision_id, bucket_id=None,
cleartext_secrets=False):
""" """
Returns the full set of rendered documents for a revision Returns the full set of rendered documents for a revision
""" """
@ -240,9 +241,11 @@ class DeckhandClient(object):
DeckhandPaths.RENDERED_REVISION_DOCS DeckhandPaths.RENDERED_REVISION_DOCS
).format(revision_id) ).format(revision_id)
query = None query = {}
if bucket_id is not None: if bucket_id is not None:
query = {'status.bucket': bucket_id} query = {'status.bucket': bucket_id}
if cleartext_secrets is True:
query['cleartext-secrets'] = 'true'
response = self._get_request(url, params=query) response = self._get_request(url, params=query)
self._handle_bad_response(response) self._handle_bad_response(response)
return response.text return response.text

View File

@ -38,6 +38,7 @@ CREATE_CONFIGDOCS = 'workflow_orchestrator:create_configdocs'
GET_CONFIGDOCS = 'workflow_orchestrator:get_configdocs' GET_CONFIGDOCS = 'workflow_orchestrator:get_configdocs'
COMMIT_CONFIGDOCS = 'workflow_orchestrator:commit_configdocs' COMMIT_CONFIGDOCS = 'workflow_orchestrator:commit_configdocs'
GET_RENDEREDCONFIGDOCS = 'workflow_orchestrator:get_renderedconfigdocs' GET_RENDEREDCONFIGDOCS = 'workflow_orchestrator:get_renderedconfigdocs'
GET_RENDEREDCONFIGDOCS_CLRTXT = 'workflow_orchestrator:get_renderedconfigdocs_cleartext' # noqa
LIST_WORKFLOWS = 'workflow_orchestrator:list_workflows' LIST_WORKFLOWS = 'workflow_orchestrator:list_workflows'
GET_WORKFLOW = 'workflow_orchestrator:get_workflow' GET_WORKFLOW = 'workflow_orchestrator:get_workflow'
GET_NOTEDETAILS = 'workflow_orchestrator:get_notedetails' GET_NOTEDETAILS = 'workflow_orchestrator:get_notedetails'
@ -187,6 +188,16 @@ class ShipyardPolicy(object):
'method': 'GET' 'method': 'GET'
}] }]
), ),
policy.DocumentedRuleDefault(
GET_RENDEREDCONFIGDOCS_CLRTXT,
RULE_ADMIN_REQUIRED,
('Retrieve the configuration documents with cleartext secrets '
'rendered by Deckhand into a complete design'),
[{
'path': '/api/v1.0/renderedconfigdocs',
'method': 'GET'
}]
),
policy.DocumentedRuleDefault( policy.DocumentedRuleDefault(
LIST_WORKFLOWS, LIST_WORKFLOWS,
RULE_ADMIN_REQUIRED, RULE_ADMIN_REQUIRED,

View File

@ -52,7 +52,7 @@ def test_get_rendered_configdocs():
helper = ConfigdocsHelper(CTX) helper = ConfigdocsHelper(CTX)
rcdr.get_rendered_configdocs(helper, version='buffer') rcdr.get_rendered_configdocs(helper, version='buffer')
mock_method.assert_called_once_with('buffer') mock_method.assert_called_once_with('buffer', False)
def test_get_rendered_last_site_action_configdocs(): def test_get_rendered_last_site_action_configdocs():
@ -68,7 +68,7 @@ def test_get_rendered_last_site_action_configdocs():
helper = ConfigdocsHelper(CTX) helper = ConfigdocsHelper(CTX)
rcdr.get_rendered_configdocs(helper, version='last_site_action') rcdr.get_rendered_configdocs(helper, version='last_site_action')
mock_method.assert_called_once_with('last_site_action') mock_method.assert_called_once_with('last_site_action', False)
def test_get_rendered_successful_site_action_configdocs(): def test_get_rendered_successful_site_action_configdocs():
@ -84,4 +84,4 @@ def test_get_rendered_successful_site_action_configdocs():
helper = ConfigdocsHelper(CTX) helper = ConfigdocsHelper(CTX)
rcdr.get_rendered_configdocs(helper, version='successful_site_action') rcdr.get_rendered_configdocs(helper, version='successful_site_action')
mock_method.assert_called_once_with('successful_site_action') mock_method.assert_called_once_with('successful_site_action', False)

View File

@ -96,7 +96,7 @@ class ShipyardClient(BaseClient):
url = ApiPaths.GET_CONFIGDOCS.value.format(self.get_endpoint()) url = ApiPaths.GET_CONFIGDOCS.value.format(self.get_endpoint())
return self.get_resp(url, query_params) return self.get_resp(url, query_params)
def get_rendereddocs(self, version='buffer'): def get_rendereddocs(self, version='buffer', cleartext_secrets=False):
""" """
:param str version: committed|buffer|last_site_action| :param str version: committed|buffer|last_site_action|
successful_site_action successful_site_action
@ -104,6 +104,8 @@ class ShipyardClient(BaseClient):
:rtype: Response object :rtype: Response object
""" """
query_params = {"version": version} query_params = {"version": version}
if cleartext_secrets is True:
query_params['cleartext-secrets'] = 'true'
url = ApiPaths.GET_RENDERED.value.format( url = ApiPaths.GET_RENDERED.value.format(
self.get_endpoint() self.get_endpoint()
) )

View File

@ -119,16 +119,18 @@ class GetConfigdocsStatus(CliAction):
class GetRenderedConfigdocs(CliAction): class GetRenderedConfigdocs(CliAction):
"""Action to Get Rendered Configdocs""" """Action to Get Rendered Configdocs"""
def __init__(self, ctx, version): def __init__(self, ctx, version, cleartext_secrets=False):
"""Sets parameters.""" """Sets parameters."""
super().__init__(ctx) super().__init__(ctx)
self.logger.debug("GetRenderedConfigdocs action initialized") self.logger.debug("GetRenderedConfigdocs action initialized")
self.version = version self.version = version
self.cleartext_secrets = cleartext_secrets
def invoke(self): def invoke(self):
"""Calls API Client and formats response from API Client""" """Calls API Client and formats response from API Client"""
self.logger.debug("Calling API Client get_rendereddocs.") self.logger.debug("Calling API Client get_rendereddocs.")
return self.get_api_client().get_rendereddocs(version=self.version) return self.get_api_client().get_rendereddocs(
version=self.version, cleartext_secrets=self.cleartext_secrets)
# Handle 404 with default error handler for cli. # Handle 404 with default error handler for cli.
cli_handled_err_resp_codes = [404] cli_handled_err_resp_codes = [404]

View File

@ -100,7 +100,6 @@ SHORT_DESC_CONFIGDOCS = ("Retrieve documents loaded into Shipyard, either "
'executed site action.') 'executed site action.')
@click.option( @click.option(
'--cleartext-secrets', '--cleartext-secrets',
'-t',
help='Returns cleartext secrets in documents', help='Returns cleartext secrets in documents',
is_flag=True) is_flag=True)
@click.pass_context @click.pass_context
@ -170,14 +169,19 @@ SHORT_DESC_RENDEREDCONFIGDOCS = (
flag_value='successful_site_action', flag_value='successful_site_action',
help='Holds the revision information for the most recent successfully ' help='Holds the revision information for the most recent successfully '
'executed site action.') 'executed site action.')
@click.option(
'--cleartext-secrets',
help='Returns cleartext secrets in encrypted documents',
is_flag=True)
@click.pass_context @click.pass_context
def get_renderedconfigdocs(ctx, buffer, committed, last_site_action, def get_renderedconfigdocs(ctx, buffer, committed, last_site_action,
successful_site_action): successful_site_action, cleartext_secrets):
# Get version # Get version
_version = get_version(ctx, buffer, committed, last_site_action, _version = get_version(ctx, buffer, committed, last_site_action,
successful_site_action) successful_site_action)
click.echo(GetRenderedConfigdocs(ctx, _version).invoke_and_return_resp()) click.echo(GetRenderedConfigdocs(ctx, _version,
cleartext_secrets).invoke_and_return_resp())
DESC_WORKFLOWS = """ DESC_WORKFLOWS = """

View File

@ -88,7 +88,7 @@ def test_get_renderedconfigdocs(*args):
runner = CliRunner() runner = CliRunner()
with patch.object(GetRenderedConfigdocs, '__init__') as mock_method: with patch.object(GetRenderedConfigdocs, '__init__') as mock_method:
runner.invoke(shipyard, [auth_vars, 'get', 'renderedconfigdocs']) runner.invoke(shipyard, [auth_vars, 'get', 'renderedconfigdocs'])
mock_method.assert_called_once_with(ANY, 'buffer') mock_method.assert_called_once_with(ANY, 'buffer', False)
def test_get_renderedconfigdocs_negative(*args): def test_get_renderedconfigdocs_negative(*args):