Retire Packaging Deb project repos

This commit is part of a series to retire the Packaging Deb
project. Step 2 is to remove all content from the project
repos, replacing it with a README notification where to find
ongoing work, and how to recover the repo if needed at some
future point (as in
https://docs.openstack.org/infra/manual/drivers.html#retiring-a-project).

Change-Id: Id80e2fd6d0f5f1873bd0b11403d19e86de400530
This commit is contained in:
Tony Breeds
2017-09-12 16:01:07 -06:00
parent a8de72a1a2
commit e50ef75f7e
292 changed files with 14 additions and 38833 deletions

View File

@@ -1,7 +0,0 @@
[run]
branch = True
source = keystoneclient
omit = keystoneclient/tests/*
[report]
ignore_errors = True

26
.gitignore vendored
View File

@@ -1,26 +0,0 @@
.coverage
.testrepository
subunit.log
.venv
*,cover
cover
*.pyc
.idea
*.sw?
*.egg
*~
.tox
AUTHORS
ChangeLog
build
dist
python_keystoneclient.egg-info
keystoneclient/versioninfo
doc/source/api
# Development environment files
.project
.pydevproject
# Temporary files created during test, but not removed
examples/pki/certs/tmp*
# Files created by releasenotes build
releasenotes/build

View File

@@ -1,4 +0,0 @@
[gerrit]
host=review.openstack.org
port=29418
project=openstack/python-keystoneclient.git

View File

@@ -1,6 +0,0 @@
# Format is:
# <preferred e-mail> <other e-mail 1>
# <preferred e-mail> <other e-mail 2>
<mr.alex.meade@gmail.com> <hatboy112@yahoo.com>
<mr.alex.meade@gmail.com> <alex.meade@rackspace.com>
<morgan.fainberg@gmail.com> <m@metacloud.com>

View File

@@ -1,4 +0,0 @@
[DEFAULT]
test_command=${PYTHON:-python} -m subunit.run discover -t ./ ${OS_TEST_PATH:-./keystoneclient/tests/unit} $LISTOPT $IDOPTION
test_id_option=--load-list $IDFILE
test_list_option=--list

View File

@@ -1,18 +0,0 @@
If you would like to contribute to the development of OpenStack,
you must follow the steps documented at:
https://docs.openstack.org/infra/manual/developers.html
If you already have a good understanding of how the system works
and your OpenStack accounts are set up, you can skip to the
development workflow section of this documentation to learn how
changes to OpenStack should be submitted for review via the
Gerrit tool:
https://docs.openstack.org/infra/manual/developers.html#development-workflow
Pull requests submitted through GitHub will be ignored.
Bugs should be filed on Launchpad, not GitHub:
https://bugs.launchpad.net/python-keystoneclient

View File

@@ -1,24 +0,0 @@
Keystone Style Commandments
===========================
- Step 1: Read the OpenStack Style Commandments
https://docs.openstack.org/hacking/latest/
- Step 2: Read on
Exceptions
----------
When dealing with exceptions from underlying libraries, translate those
exceptions to an instance or subclass of ClientException.
=======
Testing
=======
python-keystoneclient uses testtools and testr for its unittest suite
and its test runner. Basic workflow around our use of tox and testr can
be found at https://wiki.openstack.org/testr. If you'd like to learn more
in depth:
https://testtools.readthedocs.org/
https://testrepository.readthedocs.org/

209
LICENSE
View File

@@ -1,209 +0,0 @@
Copyright (c) 2009 Jacob Kaplan-Moss - initial codebase (< v2.1)
Copyright (c) 2011 Rackspace - OpenStack extensions (>= v2.1)
Copyright (c) 2011 Nebula, Inc - Keystone refactor (>= v2.7)
All rights reserved.
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
--- License for python-keystoneclient versions prior to 2.1 ---
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
3. Neither the name of this project nor the names of its contributors may
be used to endorse or promote products derived from this software without
specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

14
README Normal file
View File

@@ -0,0 +1,14 @@
This project is no longer maintained.
The contents of this repository are still available in the Git
source code management system. To see the contents of this
repository before it reached its end of life, please check out the
previous commit with "git checkout HEAD^1".
For ongoing work on maintaining OpenStack packages in the Debian
distribution, please see the Debian OpenStack packaging team at
https://wiki.debian.org/OpenStack/.
For any further questions, please email
openstack-dev@lists.openstack.org or join #openstack-dev on
Freenode.

View File

@@ -1,64 +0,0 @@
========================
Team and repository tags
========================
.. image:: https://governance.openstack.org/badges/python-keystoneclient.svg
:target: https://governance.openstack.org/reference/tags/index.html
.. Change things from this point on
Python bindings to the OpenStack Identity API (Keystone)
========================================================
.. image:: https://img.shields.io/pypi/v/python-keystoneclient.svg
:target: https://pypi.python.org/pypi/python-keystoneclient/
:alt: Latest Version
.. image:: https://img.shields.io/pypi/dm/python-keystoneclient.svg
:target: https://pypi.python.org/pypi/python-keystoneclient/
:alt: Downloads
This is a client for the OpenStack Identity API, implemented by the Keystone
team; it contains a Python API (the ``keystoneclient`` module) for
OpenStack's Identity Service. For command line interface support, use
`OpenStackClient`_.
* `PyPi`_ - package installation
* `Online Documentation`_
* `Launchpad project`_ - release management
* `Blueprints`_ - feature specifications
* `Bugs`_ - issue tracking
* `Source`_
* `Specs`_
* `How to Contribute`_
.. _PyPi: https://pypi.python.org/pypi/python-keystoneclient
.. _Online Documentation: https://docs.openstack.org/python-keystoneclient/latest/
.. _Launchpad project: https://launchpad.net/python-keystoneclient
.. _Blueprints: https://blueprints.launchpad.net/python-keystoneclient
.. _Bugs: https://bugs.launchpad.net/python-keystoneclient
.. _Source: https://git.openstack.org/cgit/openstack/python-keystoneclient
.. _OpenStackClient: https://pypi.python.org/pypi/python-openstackclient
.. _How to Contribute: https://docs.openstack.org/infra/manual/developers.html
.. _Specs: https://specs.openstack.org/openstack/keystone-specs/
.. contents:: Contents:
:local:
Python API
----------
By way of a quick-start::
>>> from keystoneauth1.identity import v3
>>> from keystoneauth1 import session
>>> from keystoneclient.v3 import client
>>> auth = v3.Password(auth_url="http://example.com:5000/v3", username="admin",
... password="password", project_name="admin",
... user_domain_id="default", project_domain_id="default")
>>> sess = session.Session(auth=auth)
>>> keystone = client.Client(session=sess)
>>> keystone.projects.list()
[...]
>>> project = keystone.projects.create(name="test", description="My new Project!", domain="default", enabled=True)
>>> project.delete()

View File

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

View File

@@ -1,23 +0,0 @@
# This is a cross-platform list tracking distribution packages needed by tests;
# see https://docs.openstack.org/infra/bindep/ for additional information.
gettext
libssl-dev
dbus-devel [platform:rpm]
dbus-glib-devel [platform:rpm]
libdbus-1-dev [platform:dpkg]
libdbus-glib-1-dev [platform:dpkg]
libffi-dev [platform:dpkg]
libffi-devel [platform:rpm]
libsasl2-dev [platform:dpkg]
libxml2-dev [platform:dpkg]
libxslt1-dev [platform:dpkg]
python-all-dev [platform:dpkg]
python3-all-dev [platform:dpkg]
cyrus-sasl-devel [platform:rpm]
libxml2-devel [platform:rpm]
python-devel [platform:rpm]
python3-devel [platform:fedora]
python34-devel [platform:centos]

1
doc/.gitignore vendored
View File

@@ -1 +0,0 @@
build/

View File

@@ -1,90 +0,0 @@
# Makefile for Sphinx documentation
#
# You can set these variables from the command line.
SPHINXOPTS =
SPHINXBUILD = sphinx-build
SPHINXSOURCE = source
PAPER =
BUILDDIR = build
# Internal variables.
PAPEROPT_a4 = -D latex_paper_size=a4
PAPEROPT_letter = -D latex_paper_size=letter
ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) $(SPHINXSOURCE)
.PHONY: help clean html dirhtml pickle json htmlhelp qthelp latex changes linkcheck doctest
help:
@echo "Please use \`make <target>' where <target> is one of"
@echo " html to make standalone HTML files"
@echo " dirhtml to make HTML files named index.html in directories"
@echo " pickle to make pickle files"
@echo " json to make JSON files"
@echo " htmlhelp to make HTML files and a HTML help project"
@echo " qthelp to make HTML files and a qthelp project"
@echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter"
@echo " changes to make an overview of all changed/added/deprecated items"
@echo " linkcheck to check all external links for integrity"
@echo " doctest to run all doctests embedded in the documentation (if enabled)"
clean:
-rm -rf $(BUILDDIR)/*
html:
$(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html
@echo
@echo "Build finished. The HTML pages are in $(BUILDDIR)/html."
dirhtml:
$(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml
@echo
@echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml."
pickle:
$(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle
@echo
@echo "Build finished; now you can process the pickle files."
json:
$(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json
@echo
@echo "Build finished; now you can process the JSON files."
htmlhelp:
$(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp
@echo
@echo "Build finished; now you can run HTML Help Workshop with the" \
".hhp project file in $(BUILDDIR)/htmlhelp."
qthelp:
$(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp
@echo
@echo "Build finished; now you can run "qcollectiongenerator" with the" \
".qhcp project file in $(BUILDDIR)/qthelp, like this:"
@echo "# qcollectiongenerator $(BUILDDIR)/qthelp/python-keystoneclient.qhcp"
@echo "To view the help file:"
@echo "# assistant -collectionFile $(BUILDDIR)/qthelp/python-keystoneclient.qhc"
latex:
$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
@echo
@echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex."
@echo "Run \`make all-pdf' or \`make all-ps' in that directory to" \
"run these through (pdf)latex."
changes:
$(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes
@echo
@echo "The overview file is in $(BUILDDIR)/changes."
linkcheck:
$(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck
@echo
@echo "Link check complete; look for any errors in the above output " \
"or in $(BUILDDIR)/linkcheck/output.txt."
doctest:
$(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest
@echo "Testing of doctests in the sources finished, look at the " \
"results in $(BUILDDIR)/doctest/output.txt."

View File

@@ -1,232 +0,0 @@
# python-keystoneclient documentation build configuration file, created by
# sphinx-quickstart on Sun Dec 6 14:19:25 2009.
#
# This file is execfile()d with the current directory set to its containing
# dir.
#
# Note that not all possible configuration values are present in this
# autogenerated file.
#
# All configuration values have a default; values that are commented out
# serve to show the default.
from __future__ import unicode_literals
import os
import sys
import pbr.version
sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__),
'..', '..')))
# If extensions (or modules to document with autodoc) are in another directory,
# add these directories to sys.path here. If the directory is relative to the
# documentation root, use os.path.abspath to make it absolute, like shown here.
#sys.path.append(os.path.abspath('.'))
# -- General configuration ----------------------------------------------------
# Add any Sphinx extension module names here, as strings. They can be
# extensions
# coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
extensions = ['sphinx.ext.autodoc',
'sphinx.ext.todo',
'sphinx.ext.coverage',
'sphinx.ext.intersphinx',
'openstackdocstheme',
]
todo_include_todos = True
# Add any paths that contain templates here, relative to this directory.
# templates_path = ['_templates']
# The suffix of source filenames.
source_suffix = '.rst'
# The encoding of source files.
#source_encoding = 'utf-8'
# The master toctree document.
master_doc = 'index'
# General information about the project.
project = 'python-keystoneclient'
copyright = 'OpenStack Contributors'
# The version info for the project you're documenting, acts as replacement for
# |version| and |release|, also used in various other places throughout the
# built documents.
version_info = pbr.version.VersionInfo('python-keystoneclient')
# The short X.Y version.
version = version_info.version_string()
# The full version, including alpha/beta/rc tags.
release = version_info.release_string()
# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.
#language = None
# There are two options for replacing |today|: either, you set today to some
# non-false value, then it is used:
#today = ''
# Else, today_fmt is used as the format for a strftime call.
#today_fmt = '%B %d, %Y'
# List of documents that shouldn't be included in the build.
#unused_docs = []
# List of directories, relative to source directory, that shouldn't be searched
# for source files.
exclude_trees = []
# The reST default role (used for this markup: `text`) to use for all
# documents.
#default_role = None
# If true, '()' will be appended to :func: etc. cross-reference text.
add_function_parentheses = True
# If true, the current module name will be prepended to all description
# unit titles (such as .. function::).
add_module_names = True
# If true, sectionauthor and moduleauthor directives will be shown in the
# output. They are ignored by default.
#show_authors = False
# The name of the Pygments (syntax highlighting) style to use.
pygments_style = 'sphinx'
# A list of ignored prefixes for module index sorting.
modindex_common_prefix = ['keystoneclient.']
# Grouping the document tree for man pages.
# List of tuples 'sourcefile', 'target', 'title', 'Authors name', 'manual'
#man_pages = []
# -- Options for HTML output --------------------------------------------------
# The theme to use for HTML and HTML Help pages. Major themes that come with
# Sphinx are currently 'default' and 'sphinxdoc'.
#html_theme_path = ["."]
#html_theme = '_theme'
html_theme = 'openstackdocs'
# Theme options are theme-specific and customize the look and feel of a theme
# further. For a list of options available for each theme, see the
# documentation.
#html_theme_options = {}
# Add any paths that contain custom themes here, relative to this directory.
#html_theme_path = []
# The name for this set of Sphinx documents. If None, it defaults to
# "<project> v<release> documentation".
#html_title = None
# A shorter title for the navigation bar. Default is the same as html_title.
#html_short_title = None
# The name of an image file (relative to this directory) to place at the top
# of the sidebar.
#html_logo = None
# The name of an image file (within the static path) to use as favicon of the
# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32
# pixels large.
#html_favicon = None
# Add any paths that contain custom static files (such as style sheets) here,
# relative to this directory. They are copied after the builtin static files,
# so a file named "default.css" will overwrite the builtin "default.css".
#html_static_path = ['static']
# If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
# using the given strftime format.
html_last_updated_fmt = '%Y-%m-%d %H:%M'
# If true, SmartyPants will be used to convert quotes and dashes to
# typographically correct entities.
#html_use_smartypants = True
# Custom sidebar templates, maps document names to template names.
#html_sidebars = {}
# Additional templates that should be rendered to pages, maps page names to
# template names.
#html_additional_pages = {}
# If false, no module index is generated.
#html_use_modindex = True
# If false, no index is generated.
#html_use_index = True
# If true, the index is split into individual pages for each letter.
#html_split_index = False
# If true, links to the reST sources are added to the pages.
#html_show_sourcelink = True
# If true, an OpenSearch description file will be output, and all pages will
# contain a <link> tag referring to it. The value of this option must be the
# base URL from which the finished HTML is served.
#html_use_opensearch = ''
# If nonempty, this is the file name suffix for HTML files (e.g. ".xhtml").
#html_file_suffix = ''
# Output file base name for HTML help builder.
htmlhelp_basename = 'python-keystoneclientdoc'
# -- Options for LaTeX output -------------------------------------------------
# The paper size ('letter' or 'a4').
#latex_paper_size = 'letter'
# The font size ('10pt', '11pt' or '12pt').
#latex_font_size = '10pt'
# Grouping the document tree into LaTeX files. List of tuples
# (source start file, target name, title, author, documentclass [howto/manual])
# .
latex_documents = [
('index', 'python-keystoneclient.tex',
'python-keystoneclient Documentation',
'Nebula Inc, based on work by Rackspace and Jacob Kaplan-Moss',
'manual'),
]
# The name of an image file (relative to this directory) to place at the top of
# the title page.
#latex_logo = None
# For "manual" documents, if this is true, then toplevel headings are parts,
# not chapters.
#latex_use_parts = False
# Additional stuff for the LaTeX preamble.
#latex_preamble = ''
# Documents to append as an appendix to all manuals.
#latex_appendices = []
# If false, no module index is generated.
#latex_use_modindex = True
keystoneauth_url = 'https://docs.openstack.org/keystoneauth/latest/'
intersphinx_mapping = {
'python': ('https://docs.python.org/', None),
'osloconfig': ('https://docs.openstack.org/oslo.config/latest/', None),
'keystoneauth1': (keystoneauth_url, None),
}
# -- Options for openstackdocstheme -------------------------------------------
repository_name = 'openstack/python-keystoneclient'
bug_project = 'python-keystoneclient'
bug_tag = ''

View File

@@ -1 +0,0 @@
.. include:: ../../ChangeLog

View File

@@ -1,54 +0,0 @@
Python bindings to the OpenStack Identity API (Keystone)
========================================================
This is a client for OpenStack Identity API. There's a Python API for
:doc:`Identity API v3 <using-api-v3>` and :doc:`v2 <using-api-v2>` (the
:mod:`keystoneclient` modules).
Contents:
.. toctree::
:maxdepth: 1
using-api-v3
using-sessions
using-api-v2
api/modules
Related Identity Projects
=========================
In addition to creating the Python client library, the Keystone team also
provides `Identity Service`_, as well as `WSGI Middleware`_.
.. _`Identity Service`: https://docs.openstack.org/keystone/latest/
.. _`WSGI Middleware`: https://docs.openstack.org/keystonemiddleware/latest/
Release Notes
=============
.. toctree::
:maxdepth: 1
history
Contributing
============
Code is hosted `on GitHub`_. Submit bugs to the Keystone project on
`Launchpad`_. Submit code to the ``openstack/python-keystoneclient`` project
using `Gerrit`_.
.. _on GitHub: https://github.com/openstack/python-keystoneclient
.. _Launchpad: https://launchpad.net/python-keystoneclient
.. _Gerrit: https://docs.openstack.org/infra/manual/developers.html#development-workflow
Run tests with ``tox``.
Indices and tables
==================
* :ref:`genindex`
* :ref:`modindex`
* :ref:`search`

View File

@@ -1,125 +0,0 @@
=======================
Using the V2 client API
=======================
Introduction
============
The main concepts in the Identity v2 API are:
* tenants
* users
* roles
* services
* endpoints
The V2 client API lets you query and make changes through
managers. For example, to manipulate tenants, you interact with a
``keystoneclient.v2_0.tenants.TenantManager`` object.
You obtain access to managers via attributes of the
``keystoneclient.v2_0.client.Client`` object. For example, the ``tenants``
attribute of the ``Client`` class is a tenant manager::
>>> from keystoneclient.v2_0 import client
>>> keystone = client.Client(...)
>>> keystone.tenants.list() # List tenants
You create a valid ``keystoneclient.v2_0.client.Client`` object by passing
a :class:`~keystoneauth1.session.Session` to the constructor. Authentication
and examples of common tasks are provided below.
You can generally expect that when the client needs to propagate an exception
it will raise an instance of subclass of
``keystoneclient.exceptions.ClientException``
Authenticating
==============
There are two ways to authenticate against keystone:
* against the admin endpoint with the admin token
* against the public endpoint with a username and password
If you are an administrator, you can authenticate by connecting to the admin
endpoint and using the admin token (sometimes referred to as the service
token). The token is specified as the ``admin_token`` configuration option in
your keystone.conf config file, which is typically in /etc/keystone::
>>> from keystoneauth1.identity import v2
>>> from keystoneauth1 import session
>>> from keystoneclient.v2_0 import client
>>> token = '012345SECRET99TOKEN012345'
>>> endpoint = 'http://192.168.206.130:35357/v2.0'
>>> auth = v2.Token(auth_url=endpoint, token=token)
>>> sess = session.Session(auth=auth)
>>> keystone = client.Client(session=sess)
If you have a username and password, authentication is done against the
public endpoint. You must also specify a tenant that is associated with the
user::
>>> from keystoneauth1.identity import v2
>>> from keystoneauth1 import session
>>> from keystoneclient.v2_0 import client
>>> username='adminUser'
>>> password='secretword'
>>> tenant_name='openstackDemo'
>>> auth_url='http://192.168.206.130:5000/v2.0'
>>> auth = v2.Password(username=username, password=password,
... tenant_name=tenant_name, auth_url=auth_url)
>>> sess = session.Session(auth=auth)
>>> keystone = client.Client(session=sess)
Creating tenants
================
This example will create a tenant named *openstackDemo*::
>>> from keystoneclient.v2_0 import client
>>> keystone = client.Client(...)
>>> keystone.tenants.create(tenant_name="openstackDemo",
... description="Default Tenant", enabled=True)
<Tenant {u'id': u'9b7962da6eb04745b477ae920ad55939', u'enabled': True, u'description': u'Default Tenant', u'name': u'openstackDemo'}>
Creating users
==============
This example will create a user named *adminUser* with a password *secretword*
in the openstackDemo tenant. We first need to retrieve the tenant::
>>> from keystoneclient.v2_0 import client
>>> keystone = client.Client(...)
>>> tenants = keystone.tenants.list()
>>> my_tenant = [x for x in tenants if x.name=='openstackDemo'][0]
>>> my_user = keystone.users.create(name="adminUser",
... password="secretword",
... tenant_id=my_tenant.id)
Creating roles and adding users
===============================
This example will create an admin role and add the *my_user* user to that
role, but only for the *my_tenant* tenant:
>>> from keystoneclient.v2_0 import client
>>> keystone = client.Client(...)
>>> role = keystone.roles.create('admin')
>>> my_tenant = ...
>>> my_user = ...
>>> keystone.roles.add_user_role(my_user, role, my_tenant)
Creating services and endpoints
===============================
This example will create the service and corresponding endpoint for the
Compute service::
>>> from keystoneclient.v2_0 import client
>>> keystone = client.Client(...)
>>> service = keystone.services.create(name="nova", service_type="compute",
... description="Nova Compute Service")
>>> keystone.endpoints.create(
... region="RegionOne", service_id=service.id,
... publicurl="http://192.168.206.130:8774/v2/%(tenant_id)s",
... adminurl="http://192.168.206.130:8774/v2/%(tenant_id)s",
... internalurl="http://192.168.206.130:8774/v2/%(tenant_id)s")

View File

@@ -1,140 +0,0 @@
=======================
Using the V3 Client API
=======================
Introduction
============
The main concepts in the Identity v3 API are:
* :py:mod:`~keystoneclient.v3.credentials`
* :py:mod:`~keystoneclient.v3.domain_configs`
* :py:mod:`~keystoneclient.v3.domains`
* :py:mod:`~keystoneclient.v3.endpoints`
* :py:mod:`~keystoneclient.v3.groups`
* :py:mod:`~keystoneclient.v3.policies`
* :py:mod:`~keystoneclient.v3.projects`
* :py:mod:`~keystoneclient.v3.regions`
* :py:mod:`~keystoneclient.v3.role_assignments`
* :py:mod:`~keystoneclient.v3.roles`
* :py:mod:`~keystoneclient.v3.services`
* :py:mod:`~keystoneclient.v3.tokens`
* :py:mod:`~keystoneclient.v3.users`
The :py:mod:`keystoneclient.v3.client` API lets you query and make changes
through ``managers``. For example, to manipulate a project (formerly
called tenant), you interact with a
:py:class:`keystoneclient.v3.projects.ProjectManager` object.
You obtain access to managers through attributes of a
:py:class:`keystoneclient.v3.client.Client` object. For example, the
``projects`` attribute of a ``Client`` object is a projects manager::
>>> from keystoneclient.v3 import client
>>> keystone = client.Client(...)
>>> keystone.projects.list() # List projects
While it is possible to instantiate a
:py:class:`keystoneclient.v3.client.Client` object (as done above for
clarity), the recommended approach is to use the discovery mechanism
provided by the :py:class:`keystoneclient.client.Client` class. The
appropriate class will be instantiated depending on the API versions
available::
>>> from keystoneclient import client
>>> keystone =
... client.Client(auth_url='http://localhost:5000', ...)
>>> type(keystone)
<class 'keystoneclient.v3.client.Client'>
One can force the use of a specific version of the API, either by
using the ``version`` keyword argument::
>>> from keystoneclient import client
>>> keystone = client.Client(auth_url='http://localhost:5000',
version=(2,), ...)
>>> type(keystone)
<class 'keystoneclient.v2_0.client.Client'>
>>> keystone = client.Client(auth_url='http://localhost:5000',
version=(3,), ...)
>>> type(keystone)
<class 'keystoneclient.v3.client.Client'>
Or by specifying directly the specific API version authentication URL
as the auth_url keyword argument::
>>> from keystoneclient import client
>>> keystone =
... client.Client(auth_url='http://localhost:5000/v2.0', ...)
>>> type(keystone)
<class 'keystoneclient.v2_0.client.Client'>
>>> keystone =
... client.Client(auth_url='http://localhost:5000/v3', ...)
>>> type(keystone)
<class 'keystoneclient.v3.client.Client'>
Upon successful authentication, a :py:class:`keystoneclient.v3.client.Client`
object is returned (when using the Identity v3 API). Authentication and
examples of common tasks are provided below.
You can generally expect that when the client needs to propagate an
exception it will raise an instance of subclass of
:class:`keystoneclient.exceptions.ClientException`.
Authenticating Using Sessions
=============================
Instantiate a :py:class:`keystoneclient.v3.client.Client` using a
:py:class:`~keystoneauth1.session.Session` to provide the authentication
plugin, SSL/TLS certificates, and other data::
>>> from keystoneauth1.identity import v3
>>> from keystoneauth1 import session
>>> from keystoneclient.v3 import client
>>> auth = v3.Password(auth_url='https://my.keystone.com:5000/v3',
... user_id='myuserid',
... password='mypassword',
... project_id='myprojectid')
>>> sess = session.Session(auth=auth)
>>> keystone = client.Client(session=sess)
For more information on Sessions refer to: `Using Sessions`_.
.. _`Using Sessions`: using-sessions.html
Non-Session Authentication (deprecated)
=======================================
The *deprecated* way to authenticate is to pass the username, the user's domain
name (which will default to 'Default' if it is not specified), and a
password::
>>> from keystoneclient import client
>>> auth_url = 'http://localhost:5000'
>>> username = 'adminUser'
>>> user_domain_name = 'Default'
>>> password = 'secreetword'
>>> keystone = client.Client(auth_url=auth_url, version=(3,),
... username=username, password=password,
... user_domain_name=user_domain_name)
A :py:class:`~keystoneauth1.session.Session` should be passed to the Client
instead. Using a Session you're not limited to authentication using a username
and password but can take advantage of other more secure authentication
methods.
You may optionally specify a domain or project (along with its project
domain name), to obtain a scoped token::
>>> from keystoneclient import client
>>> auth_url = 'http://localhost:5000'
>>> username = 'adminUser'
>>> user_domain_name = 'Default'
>>> project_name = 'demo'
>>> project_domain_name = 'Default'
>>> password = 'secreetword'
>>> keystone = client.Client(auth_url=auth_url, version=(3,),
... username=username, password=password,
... user_domain_name=user_domain_name,
... project_name=project_name,
... project_domain_name=project_domain_name)

View File

@@ -1,200 +0,0 @@
==============
Using Sessions
==============
Introduction
============
The :py:class:`keystoneauth1.session.Session` class was introduced into
keystoneclient as an attempt to bring a unified interface to the various
OpenStack clients that share common authentication and request parameters
between a variety of services.
The model for using a Session and auth plugin as well as the general terms used
have been heavily inspired by the `requests <http://docs.python-requests.org>`_
library. However neither the Session class nor any of the authentication
plugins rely directly on those concepts from the requests library so you should
not expect a direct translation.
Features
--------
- Common client authentication
Authentication is handled by one of a variety of authentication plugins and
then this authentication information is shared between all the services that
use the same Session object.
- Security maintenance
Security code is maintained in a single place and reused between all
clients such that in the event of problems it can be fixed in a single
location.
- Standard discovery mechanisms
Clients are not expected to have any knowledge of an identity token or any
other form of identification credential. Service and endpoint discovery are
handled by the Session and plugins.
Sessions for Users
==================
The Session object is the contact point to your OpenStack cloud services. It
stores the authentication credentials and connection information required to
communicate with OpenStack such that it can be reused to communicate with many
services. When creating services this Session object is passed to the client
so that it may use this information.
A Session will authenticate on demand. When a request that requires
authentication passes through the Session the authentication plugin will be
asked for a valid token. If a valid token is available it will be used
otherwise the authentication plugin may attempt to contact the authentication
service and fetch a new one.
An example from keystoneclient::
>>> from keystoneauth1.identity import v3
>>> from keystoneauth1 import session
>>> from keystoneclient.v3 import client
>>> auth = v3.Password(auth_url='https://my.keystone.com:5000/v3',
... username='myuser',
... password='mypassword',
... project_id='proj',
... user_domain_id='domain')
>>> sess = session.Session(auth=auth,
... verify='/path/to/ca.cert')
>>> ks = client.Client(session=sess)
>>> users = ks.users.list()
As clients adopt this means of operating they will be created in a similar
fashion by passing the Session object to the client's constructor.
Migrating keystoneclient to use a Session
-----------------------------------------
By using a session with a keystoneclient Client we presume that you have opted
in to new behavior defined by the session. For example authentication is now
on-demand rather than on creation. To allow this change in behavior there are
a number of functions that have changed behavior or are no longer available.
For example the
:py:meth:`keystoneclient.httpclient.HTTPClient.authenticate` method used
to be able to always re-authenticate the current client and fetch a new token.
As this is now controlled by the Session and not the client this has changed,
however the function will still exist to provide compatibility with older
clients.
Likewise certain parameters such as ``user_id`` and ``auth_token`` that used to
be available on the client object post authentication will remain
uninitialized.
When converting an application to use a session object with keystoneclient you
should be aware of the possibility of changes to authentication and
authentication parameters and make sure to test your code thoroughly. It should
have no impact on the typical CRUD interaction with the client.
Sharing Authentication Plugins
------------------------------
A session can only contain one authentication plugin however there is nothing
that specifically binds the authentication plugin to that session, a new
Session can be created that reuses the existing authentication plugin::
>>> new_sess = session.Session(auth=sess.auth,
verify='/path/to/different-cas.cert')
In this case we cannot know which session object will be used when the plugin
performs the authentication call so the command must be able to succeed with
either.
Authentication plugins can also be provided on a per-request basis. This will
be beneficial in a situation where a single session is juggling multiple
authentication credentials::
>>> sess.get('https://my.keystone.com:5000/v3',
auth=my_auth_plugin)
If an auth plugin is provided via parameter then it will override any auth
plugin on the session.
Sessions for Client Developers
==============================
Sessions are intended to take away much of the hassle of dealing with
authentication data and token formats. Clients should be able to specify filter
parameters for selecting the endpoint and have the parsing of the catalog
managed for them.
Authentication
--------------
When making a request with a session object you can simply pass the keyword
parameter ``authenticated`` to indicate whether the argument should contain a
token, by default a token is included if an authentication plugin is available::
>>> # In keystone this route is unprotected by default
>>> resp = sess.get('https://my.keystone.com:5000/v3',
authenticated=False)
Service Discovery
-----------------
In OpenStack the URLs of available services are distributed to the user as a
part of the token they receive called the Service Catalog. Clients are expected
to use the URLs from the Service Catalog rather than have them provided.
In general a client does not need to know the full URL for the server that they
are communicating with, simply that it should send a request to a path
belonging to the correct service.
This is controlled by the ``endpoint_filter`` parameter to a request which
contains all the information an authentication plugin requires to determine the
correct URL to which to send a request. When using this mode only the path for
the request needs to be specified::
>>> resp = session.get('/v3/users',
endpoint_filter={'service_type': 'identity',
'interface': 'public',
'region_name': 'myregion'})
``endpoint_filter`` accepts a number of arguments with which it can determine
an endpoint url:
- ``service_type``: the type of service. For example ``identity``, ``compute``,
``volume`` or many other predefined identifiers.
- ``interface``: the network exposure the interface has. This will be one of:
- ``public``: An endpoint that is available to the wider internet or network.
- ``internal``: An endpoint that is only accessible within the private network.
- ``admin``: An endpoint to be used for administrative tasks.
- ``region_name``: the name of the region where the endpoint resides.
The endpoint filter is a simple key-value filter and can be provided with any
number of arguments. It is then up to the auth plugin to correctly use the
parameters it understands.
The session object determines the URL matching the filter and append to it the
provided path and so create a valid request. If multiple URL matches are found
then any one may be chosen.
While authentication plugins will endeavour to maintain a consistent set of
arguments for an ``endpoint_filter`` the concept of an authentication plugin is
purposefully generic and a specific mechanism may not know how to interpret
certain arguments and ignore them. For example the
:py:class:`keystoneauth1.identity.generic.token.Token` plugin (which is used
when you want to always use a specific endpoint and token combination) will
always return the same endpoint regardless of the parameters to
``endpoint_filter`` or a custom OpenStack authentication mechanism may not have
the concept of multiple ``interface`` options and choose to ignore that
parameter.
There is some expectation on the user that they understand the limitations of
the authentication system they are using.

View File

@@ -1,23 +0,0 @@
-----BEGIN CERTIFICATE-----
MIID1jCCAr6gAwIBAgIJAJOtRP2+wrM/MA0GCSqGSIb3DQEBBQUAMIGeMQowCAYD
VQQFEwE1MQswCQYDVQQGEwJVUzELMAkGA1UECBMCQ0ExEjAQBgNVBAcTCVN1bm55
dmFsZTESMBAGA1UEChMJT3BlblN0YWNrMREwDwYDVQQLEwhLZXlzdG9uZTElMCMG
CSqGSIb3DQEJARYWa2V5c3RvbmVAb3BlbnN0YWNrLm9yZzEUMBIGA1UEAxMLU2Vs
ZiBTaWduZWQwIBcNMTMwOTEzMTYyNTQyWhgPMjA3MjAzMDcxNjI1NDJaMIGeMQow
CAYDVQQFEwE1MQswCQYDVQQGEwJVUzELMAkGA1UECBMCQ0ExEjAQBgNVBAcTCVN1
bm55dmFsZTESMBAGA1UEChMJT3BlblN0YWNrMREwDwYDVQQLEwhLZXlzdG9uZTEl
MCMGCSqGSIb3DQEJARYWa2V5c3RvbmVAb3BlbnN0YWNrLm9yZzEUMBIGA1UEAxML
U2VsZiBTaWduZWQwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCl8906
EaRpibQFcCBWfxzLi5x/XpZ9iL6UX92NrSJxcDbaGws7s+GtjgDy8UOEonesRWTe
qQEZtHpC3/UHHOnsA8F6ha/pq9LioqT7RehCnZCLBJwh5Ct+lclpWs15SkjJD2LT
Dkjox0eA9nOBx+XDlWyU/GAyqx5Wsvg/Kxr0iod9/4IcJdnSdUjq4v0Cxg/zNk08
XPJX+F0bUDhgdUf7JrAmmS5LA8wphRnbIgtVsf6VN9HrbqtHAJDxh8gEfuwdhEW1
df1fBtZ+6WMIF3IRSbIsZELFB6sqcyRj7HhMoWMkdEyPb2f8mq61MzTgE6lJGIyT
RvEoFie7qtGADIofAgMBAAGjEzARMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcN
AQEFBQADggEBAJRMdEwAdN+crqI9dBLYlbBbnQ8xr9mk+REMdz9+SKhDCNdVisWU
iLEZvK/aozrsRsDi81JjS4Tz0wXo8zsPPoDnXgDYEicNPTKifbPKgHdDIGFOwBKn
y2cF6fHEn8n3KIBrDCNY6rHcYGZ7lbq/8eF0GoYQboPiuYesvVpynPmIK5/Mmire
EuuZALAe1IFqqFt+l6tiJU2JWUFjLkFARMOD14qFZm+SInl64toi08j6gdou+NMW
7GEMbVHwNTafM/TgFN5j0yP9SAnYubckLSyH6hwR+rM8dztP5769joxQfnc9O/Bn
TBD9KFpeQv6VJWLAxiIKcQCRTTDJLZZ0MQI=
-----END CERTIFICATE-----

View File

@@ -1,50 +0,0 @@
-----BEGIN CERTIFICATE-----
MIIDpjCCAo4CARAwDQYJKoZIhvcNAQEFBQAwgZ4xCjAIBgNVBAUTATUxCzAJBgNV
BAYTAlVTMQswCQYDVQQIEwJDQTESMBAGA1UEBxMJU3Vubnl2YWxlMRIwEAYDVQQK
EwlPcGVuU3RhY2sxETAPBgNVBAsTCEtleXN0b25lMSUwIwYJKoZIhvcNAQkBFhZr
ZXlzdG9uZUBvcGVuc3RhY2sub3JnMRQwEgYDVQQDEwtTZWxmIFNpZ25lZDAgFw0x
MzA5MTMxNjI1NDNaGA8yMDcyMDMwNzE2MjU0M1owgZAxCzAJBgNVBAYTAlVTMQsw
CQYDVQQIEwJDQTESMBAGA1UEBxMJU3Vubnl2YWxlMRIwEAYDVQQKEwlPcGVuU3Rh
Y2sxETAPBgNVBAsTCEtleXN0b25lMSUwIwYJKoZIhvcNAQkBFhZrZXlzdG9uZUBv
cGVuc3RhY2sub3JnMRIwEAYDVQQDEwlsb2NhbGhvc3QwggEiMA0GCSqGSIb3DQEB
AQUAA4IBDwAwggEKAoIBAQDL06AaJROwHPgJ9tcySSBepzJ81jYars2sMvLjyuvd
iIBbhWvbS/a9Tw3WgL8H6OALkHiOU/f0A6Rpv8dGDIDsxZQVjT/4SLaQUOeDM+9b
fkKHpSd9G3CsdSSZgOH08n+MyZ7slPHfUHLYWso0SJD0vAi1gmGDlSM/mmhhHTpC
DGo6Wbwqare6JNeTCGJTJYwrxtoMCh/W1ZrslPC5lFvlHD7KBBf6IU2A8Xh/dUa3
p5pmQeHPW8Em90DzIB1qH0DRXl3KANc24xYRR45pPCVkk6vFsy6P0JwwpnkszB+L
cK6CEsJhLsOYvQFsiQfSZ8m7YGhgrMLxtop4YEPirGGrAgMBAAEwDQYJKoZIhvcN
AQEFBQADggEBAAjU7YomUx/U56p1KWHvr1B7oczHF8fPHYbuk5c/N81WOJeSRy+P
5ZGZ2UPjvqqXByv+78YWMKGY1BZ/2doeWuydr0sdSxEwmIUBYxFpujuYY+0AjS/n
mMr1ZijK7TJssteKM7/MClzghUhPweDZrAg3ff1hbhK5QSy+9UPxUqLH44tfYSVC
/BzM6se0p5ToM0bwdsa8TofaBRE1L1IW/Hg4VIGOoKs0R0uLm7+Oot2me2cEuZ6h
Wls6MED8ND1Nz8EAKwndkeDu2iMM+qx/YFp6K8BQ5E5nXd2rbUZUlQMp1WbUlZ87
KvC98aT0UYIq6uo1Lx/dQvJs7faAkYd4lmE=
-----END CERTIFICATE-----
-----BEGIN PRIVATE KEY-----
MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDL06AaJROwHPgJ
9tcySSBepzJ81jYars2sMvLjyuvdiIBbhWvbS/a9Tw3WgL8H6OALkHiOU/f0A6Rp
v8dGDIDsxZQVjT/4SLaQUOeDM+9bfkKHpSd9G3CsdSSZgOH08n+MyZ7slPHfUHLY
Wso0SJD0vAi1gmGDlSM/mmhhHTpCDGo6Wbwqare6JNeTCGJTJYwrxtoMCh/W1Zrs
lPC5lFvlHD7KBBf6IU2A8Xh/dUa3p5pmQeHPW8Em90DzIB1qH0DRXl3KANc24xYR
R45pPCVkk6vFsy6P0JwwpnkszB+LcK6CEsJhLsOYvQFsiQfSZ8m7YGhgrMLxtop4
YEPirGGrAgMBAAECggEATwvbY0hNwlb5uqOIAXBqpUqiQdexU9fG26lGmSDxKBDv
9o5frcRgBDrMWwvDCgY+HT4CAvB9kJx4/qnpVjkzJp/ZNiJ5VIiehIlbv348rXbh
xkk+bz5dDATCFOXuu1fwL2FhyM5anwhMAav0DyK1VLQ3jGzr9GO6L8hqAn+bQFFu
6ngiODwfhBMl5aRoL9UOBEhccK07znrH0JGRz+3+5Cdz59Xw91Bv210LhNNDL58+
0JD0N+YztVOQd2bgwo0bQbOEijzmYq+0mjoqAnJh1/++y7PlIPs0AnPgqSnFPx9+
6FsQEVRgk5Uq3kvPLaP4nT2y6MDZSp+ujYldvJhyQQKBgQDuX2pZIJMZ4aFnkG+K
TmJ5wsLa/u9an0TmvAL9RLtBpVpQNKD8cQ+y8PUZavXDbAIt5NWqZVnTbCR79Dnd
mZKblwcHhtsyA5f89el5KcxY2BREWdHdTnJpNd7XRlUECmzvX1zGj77lA982PhII
yflRBRV3vqLkgC8vfoYgRyRElwKBgQDa5jnLdx/RahfYMOgn1HE5o4hMzLR4Y0Dd
+gELshcUbPqouoP5zOb8WOagVJIgZVOSN+/VqbilVYrqRiNTn2rnoxs+HHRdaJNN
3eXllD4J2HfC2BIj1xSpIdyh2XewAJqw9IToHNB29QUhxOtgwseHciPG6JaKH2ik
kqGKH/EKDQKBgFFAftygiOPCkCTgC9UmANUmOQsy6N2H+pF3tsEj43xt44oBVnqW
A1boYXNnjRwuvdNs9BPf9i1l6E3EItFRXrLgWQoMwryakv0ryYh+YeRKyyW9RBbe
fYs1TJ8unx4Ae79gTxxztQsVNcmkgLs0NWKTjAzEE3w14V+cDhYEie1DAoGBAJdI
V5cLrBzBstsB6eBlDR9lqrRRIUS2a8U9m+1mVlcSfiWQSdehSd4K3tDdwePLw3ch
W4qR8n+pYAlLEe0gFvUhn5lMdwt7U5qUCeehjUKmrRYm2FqWsbu2IFJnBjXIJSC4
zQXRrC0aZ0KQYpAL7XPpaVp1slyhGmPqxuO78Y0dAoGBAMHo3EIMwu9rfuGwFodr
GFsOZhfJqgo5GDNxxf89Q9WWpMDTCdX+wdBTrN/wsMbBuwIDHrUuRnk6D5CWRjSk
/ikCgHN3kOtrbL8zzqRomGAIIWKYGFEIGe1GHVGo5r//HXHdPxFXygvruQ/xbOA4
RGvmDiji8vVDq7Shho8I6KuT
-----END PRIVATE KEY-----

View File

@@ -1,22 +0,0 @@
-----BEGIN CERTIFICATE-----
MIIDpTCCAo0CAREwDQYJKoZIhvcNAQEFBQAwgZ4xCjAIBgNVBAUTATUxCzAJBgNV
BAYTAlVTMQswCQYDVQQIEwJDQTESMBAGA1UEBxMJU3Vubnl2YWxlMRIwEAYDVQQK
EwlPcGVuU3RhY2sxETAPBgNVBAsTCEtleXN0b25lMSUwIwYJKoZIhvcNAQkBFhZr
ZXlzdG9uZUBvcGVuc3RhY2sub3JnMRQwEgYDVQQDEwtTZWxmIFNpZ25lZDAgFw0x
MzA5MTMxNjI1NDNaGA8yMDcyMDMwNzE2MjU0M1owgY8xCzAJBgNVBAYTAlVTMQsw
CQYDVQQIEwJDQTESMBAGA1UEBxMJU3Vubnl2YWxlMRIwEAYDVQQKEwlPcGVuU3Rh
Y2sxETAPBgNVBAsTCEtleXN0b25lMSUwIwYJKoZIhvcNAQkBFhZrZXlzdG9uZUBv
cGVuc3RhY2sub3JnMREwDwYDVQQDEwhLZXlzdG9uZTCCASIwDQYJKoZIhvcNAQEB
BQADggEPADCCAQoCggEBAMz5WsgsuX3rZUdLwQpZXN2Ro7LQ6jEZnreBqMztVObw
BuC1WdiJsg6dVlC7PVdt+0gY1c8WFg1TKmsucxesQSyfGAPg+9T/hsRMb6y12uJx
fp3Wgqqw0U1HsXvMiaJH87MaGnt043BxzF+R9fhAcDk6Cyj5cx9J0LvZJEOzN4J4
ZRyO6j/DZZItb3lK5W9xkuoT+mTdDZOQJnXyG818uiWfjdCkLjr1ruytRcBOo4na
Y828voT/A7I95+YCgKgbjiUWhHeTaNmMEQiGy0nGYfteC+oSsHOlxZ3b12azzHPk
83Bh2ez0Ih9vcZoe9DqvlFOXfv9q8OsYc5Yo6gPTXEsCAwEAATANBgkqhkiG9w0B
AQUFAAOCAQEAmaYE98kOQWu6DV84ZcZP/OdT8eeu3vdB247nRj+6+GYItN/Gzqt4
HVvz7c+FVTolCcAQQ+z3XGswI9fIJ78Hb0p9CgnLprc3L7Xtk60Im59Xlf3tcurn
r/ZnSDcjRBXKiEDrSM0VrhAnc0GoSeb6aDWopec+1hWOWfBVAg9R8yJgU9sUgO3O
0gimGyrw8eubmNhckSQLJTunUTsrkcBjuSg63wAD9OqCiX6c2eoQr+0YBp2eV2/n
aOiJXWNLbeueMKSYiJNyyvM/dlON7/56cdwDTzKzgD34TImouM5VKipUwCX1ovLu
ITLzALzpqFFzc8ugV9pMgUKtDbZoPp9EEA==
-----END CERTIFICATE-----

View File

@@ -1,22 +0,0 @@
-----BEGIN CERTIFICATE-----
MIIDpjCCAo4CARAwDQYJKoZIhvcNAQEFBQAwgZ4xCjAIBgNVBAUTATUxCzAJBgNV
BAYTAlVTMQswCQYDVQQIEwJDQTESMBAGA1UEBxMJU3Vubnl2YWxlMRIwEAYDVQQK
EwlPcGVuU3RhY2sxETAPBgNVBAsTCEtleXN0b25lMSUwIwYJKoZIhvcNAQkBFhZr
ZXlzdG9uZUBvcGVuc3RhY2sub3JnMRQwEgYDVQQDEwtTZWxmIFNpZ25lZDAgFw0x
MzA5MTMxNjI1NDNaGA8yMDcyMDMwNzE2MjU0M1owgZAxCzAJBgNVBAYTAlVTMQsw
CQYDVQQIEwJDQTESMBAGA1UEBxMJU3Vubnl2YWxlMRIwEAYDVQQKEwlPcGVuU3Rh
Y2sxETAPBgNVBAsTCEtleXN0b25lMSUwIwYJKoZIhvcNAQkBFhZrZXlzdG9uZUBv
cGVuc3RhY2sub3JnMRIwEAYDVQQDEwlsb2NhbGhvc3QwggEiMA0GCSqGSIb3DQEB
AQUAA4IBDwAwggEKAoIBAQDL06AaJROwHPgJ9tcySSBepzJ81jYars2sMvLjyuvd
iIBbhWvbS/a9Tw3WgL8H6OALkHiOU/f0A6Rpv8dGDIDsxZQVjT/4SLaQUOeDM+9b
fkKHpSd9G3CsdSSZgOH08n+MyZ7slPHfUHLYWso0SJD0vAi1gmGDlSM/mmhhHTpC
DGo6Wbwqare6JNeTCGJTJYwrxtoMCh/W1ZrslPC5lFvlHD7KBBf6IU2A8Xh/dUa3
p5pmQeHPW8Em90DzIB1qH0DRXl3KANc24xYRR45pPCVkk6vFsy6P0JwwpnkszB+L
cK6CEsJhLsOYvQFsiQfSZ8m7YGhgrMLxtop4YEPirGGrAgMBAAEwDQYJKoZIhvcN
AQEFBQADggEBAAjU7YomUx/U56p1KWHvr1B7oczHF8fPHYbuk5c/N81WOJeSRy+P
5ZGZ2UPjvqqXByv+78YWMKGY1BZ/2doeWuydr0sdSxEwmIUBYxFpujuYY+0AjS/n
mMr1ZijK7TJssteKM7/MClzghUhPweDZrAg3ff1hbhK5QSy+9UPxUqLH44tfYSVC
/BzM6se0p5ToM0bwdsa8TofaBRE1L1IW/Hg4VIGOoKs0R0uLm7+Oot2me2cEuZ6h
Wls6MED8ND1Nz8EAKwndkeDu2iMM+qx/YFp6K8BQ5E5nXd2rbUZUlQMp1WbUlZ87
KvC98aT0UYIq6uo1Lx/dQvJs7faAkYd4lmE=
-----END CERTIFICATE-----

View File

@@ -1,88 +0,0 @@
{
"access": {
"token": {
"expires": "2038-01-18T21:14:07Z",
"issued_at": "2002-01-18T21:14:07Z",
"id": "placeholder",
"tenant": {
"id": "tenant_id1",
"enabled": true,
"description": null,
"name": "tenant_name1"
}
},
"serviceCatalog": [
{
"endpoints_links": [],
"endpoints": [
{
"adminURL": "http://127.0.0.1:8776/v1/64b6f3fbcc53435e8a60fcf89bb6617a",
"region": "regionOne",
"internalURL": "http://127.0.0.1:8776/v1/64b6f3fbcc53435e8a60fcf89bb6617a",
"publicURL": "http://127.0.0.1:8776/v1/64b6f3fbcc53435e8a60fcf89bb6617a"
}
],
"type": "volume",
"name": "volume"
},
{
"endpoints_links": [],
"endpoints": [
{
"adminURL": "http://127.0.0.1:9292/v1",
"region": "regionOne",
"internalURL": "http://127.0.0.1:9292/v1",
"publicURL": "http://127.0.0.1:9292/v1"
}
],
"type": "image",
"name": "glance"
},
{
"endpoints_links": [],
"endpoints": [
{
"adminURL": "http://127.0.0.1:8774/v1.1/64b6f3fbcc53435e8a60fcf89bb6617a",
"region": "regionOne",
"internalURL": "http://127.0.0.1:8774/v1.1/64b6f3fbcc53435e8a60fcf89bb6617a",
"publicURL": "http://127.0.0.1:8774/v1.1/64b6f3fbcc53435e8a60fcf89bb6617a"
}
],
"type": "compute",
"name": "nova"
},
{
"endpoints_links": [],
"endpoints": [
{
"adminURL": "http://127.0.0.1:35357/v2.0",
"region": "RegionOne",
"internalURL": "http://127.0.0.1:35357/v2.0",
"publicURL": "http://127.0.0.1:5000/v2.0"
}
],
"type": "identity",
"name": "keystone"
}
],
"user": {
"username": "revoked_username1",
"roles_links": [
"role1",
"role2"
],
"id": "revoked_user_id1",
"roles": [
{
"id": "f03fda8f8a3249b2a70fb1f176a7b631",
"name": "role1"
},
{
"id": "f03fda8f8a3249b2a70fb1f176a7b631",
"name": "role2"
}
],
"name": "revoked_username1"
}
}
}

View File

@@ -1,79 +0,0 @@
-----BEGIN CMS-----
MIIOTQYJKoZIhvcNAQcCoIIOPjCCDjoCAQExCTAHBgUrDgMCGjCCDFoGCSqGSIb3
DQEHAaCCDEsEggxHew0KICAgICJhY2Nlc3MiOiB7DQogICAgICAgICJ0b2tlbiI6
IHsNCiAgICAgICAgICAgICJleHBpcmVzIjogIjIwMzgtMDEtMThUMjE6MTQ6MDda
IiwNCiAgICAgICAgICAgICJpc3N1ZWRfYXQiOiAiMjAwMi0wMS0xOFQyMToxNDow
N1oiLA0KICAgICAgICAgICAgImlkIjogInBsYWNlaG9sZGVyIiwNCiAgICAgICAg
ICAgICJ0ZW5hbnQiOiB7DQogICAgICAgICAgICAgICAgImlkIjogInRlbmFudF9p
ZDEiLA0KICAgICAgICAgICAgICAgICJlbmFibGVkIjogdHJ1ZSwNCiAgICAgICAg
ICAgICAgICAiZGVzY3JpcHRpb24iOiBudWxsLA0KICAgICAgICAgICAgICAgICJu
YW1lIjogInRlbmFudF9uYW1lMSINCiAgICAgICAgICAgIH0NCiAgICAgICAgfSwN
CiAgICAgICAgInNlcnZpY2VDYXRhbG9nIjogWw0KICAgICAgICAgICAgew0KICAg
ICAgICAgICAgICAgICJlbmRwb2ludHNfbGlua3MiOiBbXSwNCiAgICAgICAgICAg
ICAgICAiZW5kcG9pbnRzIjogWw0KICAgICAgICAgICAgICAgICAgICB7DQogICAg
ICAgICAgICAgICAgICAgICAgICAiYWRtaW5VUkwiOiAiaHR0cDovLzEyNy4wLjAu
MTo4Nzc2L3YxLzY0YjZmM2ZiY2M1MzQzNWU4YTYwZmNmODliYjY2MTdhIiwNCiAg
ICAgICAgICAgICAgICAgICAgICAgICJyZWdpb24iOiAicmVnaW9uT25lIiwNCiAg
ICAgICAgICAgICAgICAgICAgICAgICJpbnRlcm5hbFVSTCI6ICJodHRwOi8vMTI3
LjAuMC4xOjg3NzYvdjEvNjRiNmYzZmJjYzUzNDM1ZThhNjBmY2Y4OWJiNjYxN2Ei
LA0KICAgICAgICAgICAgICAgICAgICAgICAgInB1YmxpY1VSTCI6ICJodHRwOi8v
MTI3LjAuMC4xOjg3NzYvdjEvNjRiNmYzZmJjYzUzNDM1ZThhNjBmY2Y4OWJiNjYx
N2EiDQogICAgICAgICAgICAgICAgICAgIH0NCiAgICAgICAgICAgICAgICBdLA0K
ICAgICAgICAgICAgICAgICJ0eXBlIjogInZvbHVtZSIsDQogICAgICAgICAgICAg
ICAgIm5hbWUiOiAidm9sdW1lIg0KICAgICAgICAgICAgfSwNCiAgICAgICAgICAg
IHsNCiAgICAgICAgICAgICAgICAiZW5kcG9pbnRzX2xpbmtzIjogW10sDQogICAg
ICAgICAgICAgICAgImVuZHBvaW50cyI6IFsNCiAgICAgICAgICAgICAgICAgICAg
ew0KICAgICAgICAgICAgICAgICAgICAgICAgImFkbWluVVJMIjogImh0dHA6Ly8x
MjcuMC4wLjE6OTI5Mi92MSIsDQogICAgICAgICAgICAgICAgICAgICAgICAicmVn
aW9uIjogInJlZ2lvbk9uZSIsDQogICAgICAgICAgICAgICAgICAgICAgICAiaW50
ZXJuYWxVUkwiOiAiaHR0cDovLzEyNy4wLjAuMTo5MjkyL3YxIiwNCiAgICAgICAg
ICAgICAgICAgICAgICAgICJwdWJsaWNVUkwiOiAiaHR0cDovLzEyNy4wLjAuMTo5
MjkyL3YxIg0KICAgICAgICAgICAgICAgICAgICB9DQogICAgICAgICAgICAgICAg
XSwNCiAgICAgICAgICAgICAgICAidHlwZSI6ICJpbWFnZSIsDQogICAgICAgICAg
ICAgICAgIm5hbWUiOiAiZ2xhbmNlIg0KICAgICAgICAgICAgfSwNCiAgICAgICAg
ICAgIHsNCiAgICAgICAgICAgICAgICAiZW5kcG9pbnRzX2xpbmtzIjogW10sDQog
ICAgICAgICAgICAgICAgImVuZHBvaW50cyI6IFsNCiAgICAgICAgICAgICAgICAg
ICAgew0KICAgICAgICAgICAgICAgICAgICAgICAgImFkbWluVVJMIjogImh0dHA6
Ly8xMjcuMC4wLjE6ODc3NC92MS4xLzY0YjZmM2ZiY2M1MzQzNWU4YTYwZmNmODli
YjY2MTdhIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICJyZWdpb24iOiAicmVn
aW9uT25lIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICJpbnRlcm5hbFVSTCI6
ICJodHRwOi8vMTI3LjAuMC4xOjg3NzQvdjEuMS82NGI2ZjNmYmNjNTM0MzVlOGE2
MGZjZjg5YmI2NjE3YSIsDQogICAgICAgICAgICAgICAgICAgICAgICAicHVibGlj
VVJMIjogImh0dHA6Ly8xMjcuMC4wLjE6ODc3NC92MS4xLzY0YjZmM2ZiY2M1MzQz
NWU4YTYwZmNmODliYjY2MTdhIg0KICAgICAgICAgICAgICAgICAgICB9DQogICAg
ICAgICAgICAgICAgXSwNCiAgICAgICAgICAgICAgICAidHlwZSI6ICJjb21wdXRl
IiwNCiAgICAgICAgICAgICAgICAibmFtZSI6ICJub3ZhIg0KICAgICAgICAgICAg
fSwNCiAgICAgICAgICAgIHsNCiAgICAgICAgICAgICAgICAiZW5kcG9pbnRzX2xp
bmtzIjogW10sDQogICAgICAgICAgICAgICAgImVuZHBvaW50cyI6IFsNCiAgICAg
ICAgICAgICAgICAgICAgew0KICAgICAgICAgICAgICAgICAgICAgICAgImFkbWlu
VVJMIjogImh0dHA6Ly8xMjcuMC4wLjE6MzUzNTcvdjIuMCIsDQogICAgICAgICAg
ICAgICAgICAgICAgICAicmVnaW9uIjogIlJlZ2lvbk9uZSIsDQogICAgICAgICAg
ICAgICAgICAgICAgICAiaW50ZXJuYWxVUkwiOiAiaHR0cDovLzEyNy4wLjAuMToz
NTM1Ny92Mi4wIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICJwdWJsaWNVUkwi
OiAiaHR0cDovLzEyNy4wLjAuMTo1MDAwL3YyLjAiDQogICAgICAgICAgICAgICAg
ICAgIH0NCiAgICAgICAgICAgICAgICBdLA0KICAgICAgICAgICAgICAgICJ0eXBl
IjogImlkZW50aXR5IiwNCiAgICAgICAgICAgICAgICAibmFtZSI6ICJrZXlzdG9u
ZSINCiAgICAgICAgICAgIH0NCiAgICAgICAgXSwNCiAgICAgICAgInVzZXIiOiB7
DQogICAgICAgICAgICAidXNlcm5hbWUiOiAicmV2b2tlZF91c2VybmFtZTEiLA0K
ICAgICAgICAgICAgInJvbGVzX2xpbmtzIjogWw0KICAgICAgICAgICAgICAgICJy
b2xlMSIsDQogICAgICAgICAgICAgICAgInJvbGUyIg0KICAgICAgICAgICAgXSwN
CiAgICAgICAgICAgICJpZCI6ICJyZXZva2VkX3VzZXJfaWQxIiwNCiAgICAgICAg
ICAgICJyb2xlcyI6IFsNCiAgICAgICAgICAgICAgICB7DQogICAgICAgICAgICAg
ICAgICAgICJpZCI6ICJmMDNmZGE4ZjhhMzI0OWIyYTcwZmIxZjE3NmE3YjYzMSIs
DQogICAgICAgICAgICAgICAgICAgICJuYW1lIjogInJvbGUxIg0KICAgICAgICAg
ICAgICAgIH0sDQogICAgICAgICAgICAgICAgew0KICAgICAgICAgICAgICAgICAg
ICAiaWQiOiAiZjAzZmRhOGY4YTMyNDliMmE3MGZiMWYxNzZhN2I2MzEiLA0KICAg
ICAgICAgICAgICAgICAgICAibmFtZSI6ICJyb2xlMiINCiAgICAgICAgICAgICAg
ICB9DQogICAgICAgICAgICBdLA0KICAgICAgICAgICAgIm5hbWUiOiAicmV2b2tl
ZF91c2VybmFtZTEiDQogICAgICAgIH0NCiAgICB9DQp9DQoxggHKMIIBxgIBATCB
pDCBnjEKMAgGA1UEBRMBNTELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAkNBMRIwEAYD
VQQHEwlTdW5ueXZhbGUxEjAQBgNVBAoTCU9wZW5TdGFjazERMA8GA1UECxMIS2V5
c3RvbmUxJTAjBgkqhkiG9w0BCQEWFmtleXN0b25lQG9wZW5zdGFjay5vcmcxFDAS
BgNVBAMTC1NlbGYgU2lnbmVkAgERMAcGBSsOAwIaMA0GCSqGSIb3DQEBAQUABIIB
AA2C5qslA4D7vzbiPJ+PzI6CWKH4fxy2nl6wFneHRlzflRGVtbk7/gwVpgHvVH8+
FvQEWeXiCvpXDcHUae0YsdB6aifDRkRctoBwWZkSIkLtdLjZTBrwoOBD2cWPTlr6
gFPp0ARCKVP87YXiKHXStvivZDQFbnBrPTZbGwsCZFXzDYtVPkDvgWOIzHP+olB0
k0wrFXdTQrr62GmkUdgmY31SBLAmPRlvbFBsdM8R62EVc9Mdk7A8Xenpib6+3hPV
7Jgj5IcC3WWtI1A/WOzuEepfW5AU3bcmsJ4UrsJdZLPYqxy/FS37s7oekBOfSR+Y
WSVmaaTY21X3kOqAQULJTDI=
-----END CMS-----

View File

@@ -1 +0,0 @@
PKIZ_eJylVkt3ozgT3etXzD6nTwBjJ17MQjyMRSzZYAxIOwMJIB524gePX_8Jk0xn0n2me77xxlCC4ureW1X69k38NNNC5A8db4ebbwAjhHaQ2k8HhrJrTKAT6wcRczxd1w1Jh47Z6h5caunuzUixbnFd10rJ0rev1hZFE2A45hLuRbBQzTRlT8-dnVGFlPEE5-tce0C1e90r_gXxQyrWjvGEyCxwX2joiHWYA8xhg3OpwVupXS-cDnuHlhiHhsiHfKXDnIVZsw_tMu7QDOmowwZWVx5sV56p-gZqwZqb0prDSZCjE9LtI9OHB-0mshacBdk1stwyHtckFkyzqHZGZJV_oYF9Aiy4BaS49svhi_tghJZYwwNTKVTKAm9vCcS9XA5bEdsqo2pxSRbzCxiC7w8ULCQ8rsomscprlAsk1lSOrHb-sm3ES4KXmh2p4hs0dLPImtdDMhBMTrmAVsTW_BjVbj8EhxgN3PM-mPq7orkh2i9dKTYO11VvdqRTmxWHF8GXCkgfK6sJbVc9lShnFVZYThUs497pSbBTqUcbVpFqbZQ55WLFSzKUD4jskinlFdyg6nbHguQYKdNNVO3ykYupxMJh3-0_ogADjP8fhSYDWrVHKvtbb5TvkCzdZp0_XrGHJrd96mq75umE9PSacPNKuJuTivassjntdz0gBpaZl2WEaxVVqLoO7Jxw2hLFzF98aVBHSOY2kVJekiV5iazysh9dGoVCHSA0ncbWbtS-mp-SQesBXiVME3yJ1yLh8u-qgX8p2xTzohv4-lACDFL8FyXAzzNr8u-RW3Rg7aGB3d8i7DNf-0DOmLLLwQBVFMaZbW9fqkUVXqhYGPwv6v9TwjHR0C-YJR-jckQH_ll7Z0B3wdtHhVhIYVw4oCKceFjCvV-uLVMB2GKc8XRKK6QQbk7oWJnvhKo3uHHl1_tgfvGU6bvEApHldwL5Cfi-jW81XmVSsoSzVffYYh4PKIR05mxtiCamzxW8VX9qdfArr_9KDfBv9vvjdu05uMkj-htbatfBOLE8P4n_t1sTXZyTQaVkWTbvKvFIkZskdP-yO_jwe1TNlSHjSh8b5l8Jx0QitiiioLx85Qx8JQ2LEiVeLLaDROxHRXZfFAGfJfmVIj9J3oBE9PZ9QH5VhTI2YCNqpRP3f7M9-D3fizlQu8dkWeRfrP8GWFj2iTW_MEHg-KLfsxC9z8Xh-vNAsUvRXN6G2ZiEYk68q_DRHMQY8_tQaY9RdR7nQ2d3kdJ-DJ7xOreTzxMMCJ8rkXIu2WIux4rffRpltxe-y1gWI8G0EVYuqJdVwly9mM7OlHI7I71oqnxRYS9WqJeIxorbr70xFr2ReeZHqd8G8VDOFTZIxSxTZTzLcI-kdYA66sWiPlDLhGVJJWwrmviPQ9YWk8nadaiWky_sdixkw8GiCCfficSC6JdQatN0-SRONlqbIg1AT9eOhq7V3HzCMLWgvDM1F2vEM1cYFuN9hnXfx63eQ1tLia_B1IMF0bCLGmBCaviOszSb0kuC6eU5ZGJ071qTQ2d8-ODpu3kjZoGXiEPHvjddDB9vifUWI7BV_Gk8ca-ilbe2B7mWFq9ZkVvzRtJ0xwwW1bl8Dokk2n3pWLdE_S1RN73GVdyChQG345ewp8ukjCya7pSyjiq_gOnwtdjStqc1bNAew--nM3E406Czg0ATZMAFn0pmu102GdE2eWhrLybzSqvOEc8n8LJlq0g9L06bbtIfD1acv21OyvLk6kb3Bp4QtCYpT6PzDLZP8n5Wwf1dc7w7blCXcsuzZEWPC3y_UFf1RZVbQ1XWj538TKM7PF89WkDG98-hu9laucfd3RVqao5fpSe-Wbjqw7qfxcfkGDMyu3cbVWXO9m55ThBaeQQnC1p6BKiBVOuHh24fsocHLV3fvzqlVlPJC-zjYfase-fr9IyuxdWfNefEhnpyj9y7N_rcsOeFaGOgxmdovBYUBqbjPhQPrSvL-LHbrifzqzQld_3GOLGNUt_Npe40zarJpFyJOb905oi60SsEslMv7oOYuA9v_Cl5aJhHZhMEX7B9-BPcjtMmMb4frf8Hm6bNOA==

View File

@@ -1,88 +0,0 @@
{
"access": {
"token": {
"expires": "2038-01-18T21:14:07Z",
"issued_at": "2002-01-18T21:14:07Z",
"id": "placeholder",
"tenant": {
"id": "tenant_id1",
"enabled": true,
"description": null,
"name": "tenant_name1"
}
},
"serviceCatalog": [
{
"endpoints_links": [],
"endpoints": [
{
"adminURL": "http://127.0.0.1:8776/v1/64b6f3fbcc53435e8a60fcf89bb6617a",
"region": "regionOne",
"internalURL": "http://127.0.0.1:8776/v1/64b6f3fbcc53435e8a60fcf89bb6617a",
"publicURL": "http://127.0.0.1:8776/v1/64b6f3fbcc53435e8a60fcf89bb6617a"
}
],
"type": "volume",
"name": "volume"
},
{
"endpoints_links": [],
"endpoints": [
{
"adminURL": "http://127.0.0.1:9292/v1",
"region": "regionOne",
"internalURL": "http://127.0.0.1:9292/v1",
"publicURL": "http://127.0.0.1:9292/v1"
}
],
"type": "image",
"name": "glance"
},
{
"endpoints_links": [],
"endpoints": [
{
"adminURL": "http://127.0.0.1:8774/v1.1/64b6f3fbcc53435e8a60fcf89bb6617a",
"region": "regionOne",
"internalURL": "http://127.0.0.1:8774/v1.1/64b6f3fbcc53435e8a60fcf89bb6617a",
"publicURL": "http://127.0.0.1:8774/v1.1/64b6f3fbcc53435e8a60fcf89bb6617a"
}
],
"type": "compute",
"name": "nova"
},
{
"endpoints_links": [],
"endpoints": [
{
"adminURL": "http://127.0.0.1:35357/v2.0",
"region": "RegionOne",
"internalURL": "http://127.0.0.1:35357/v2.0",
"publicURL": "http://127.0.0.1:5000/v2.0"
}
],
"type": "identity",
"name": "keystone"
}
],
"user": {
"username": "user_name1",
"roles_links": [
"role1",
"role2"
],
"id": "user_id1",
"roles": [
{
"id": "f03fda8f8a3249b2a70fb1f176a7b631",
"name": "role1"
},
{
"id": "f03fda8f8a3249b2a70fb1f176a7b631",
"name": "role2"
}
],
"name": "user_name1"
}
}
}

View File

@@ -1,78 +0,0 @@
-----BEGIN CMS-----
MIIONwYJKoZIhvcNAQcCoIIOKDCCDiQCAQExCTAHBgUrDgMCGjCCDEQGCSqGSIb3
DQEHAaCCDDUEggwxew0KICAgICJhY2Nlc3MiOiB7DQogICAgICAgICJ0b2tlbiI6
IHsNCiAgICAgICAgICAgICJleHBpcmVzIjogIjIwMzgtMDEtMThUMjE6MTQ6MDda
IiwNCiAgICAgICAgICAgICJpc3N1ZWRfYXQiOiAiMjAwMi0wMS0xOFQyMToxNDow
N1oiLA0KICAgICAgICAgICAgImlkIjogInBsYWNlaG9sZGVyIiwNCiAgICAgICAg
ICAgICJ0ZW5hbnQiOiB7DQogICAgICAgICAgICAgICAgImlkIjogInRlbmFudF9p
ZDEiLA0KICAgICAgICAgICAgICAgICJlbmFibGVkIjogdHJ1ZSwNCiAgICAgICAg
ICAgICAgICAiZGVzY3JpcHRpb24iOiBudWxsLA0KICAgICAgICAgICAgICAgICJu
YW1lIjogInRlbmFudF9uYW1lMSINCiAgICAgICAgICAgIH0NCiAgICAgICAgfSwN
CiAgICAgICAgInNlcnZpY2VDYXRhbG9nIjogWw0KICAgICAgICAgICAgew0KICAg
ICAgICAgICAgICAgICJlbmRwb2ludHNfbGlua3MiOiBbXSwNCiAgICAgICAgICAg
ICAgICAiZW5kcG9pbnRzIjogWw0KICAgICAgICAgICAgICAgICAgICB7DQogICAg
ICAgICAgICAgICAgICAgICAgICAiYWRtaW5VUkwiOiAiaHR0cDovLzEyNy4wLjAu
MTo4Nzc2L3YxLzY0YjZmM2ZiY2M1MzQzNWU4YTYwZmNmODliYjY2MTdhIiwNCiAg
ICAgICAgICAgICAgICAgICAgICAgICJyZWdpb24iOiAicmVnaW9uT25lIiwNCiAg
ICAgICAgICAgICAgICAgICAgICAgICJpbnRlcm5hbFVSTCI6ICJodHRwOi8vMTI3
LjAuMC4xOjg3NzYvdjEvNjRiNmYzZmJjYzUzNDM1ZThhNjBmY2Y4OWJiNjYxN2Ei
LA0KICAgICAgICAgICAgICAgICAgICAgICAgInB1YmxpY1VSTCI6ICJodHRwOi8v
MTI3LjAuMC4xOjg3NzYvdjEvNjRiNmYzZmJjYzUzNDM1ZThhNjBmY2Y4OWJiNjYx
N2EiDQogICAgICAgICAgICAgICAgICAgIH0NCiAgICAgICAgICAgICAgICBdLA0K
ICAgICAgICAgICAgICAgICJ0eXBlIjogInZvbHVtZSIsDQogICAgICAgICAgICAg
ICAgIm5hbWUiOiAidm9sdW1lIg0KICAgICAgICAgICAgfSwNCiAgICAgICAgICAg
IHsNCiAgICAgICAgICAgICAgICAiZW5kcG9pbnRzX2xpbmtzIjogW10sDQogICAg
ICAgICAgICAgICAgImVuZHBvaW50cyI6IFsNCiAgICAgICAgICAgICAgICAgICAg
ew0KICAgICAgICAgICAgICAgICAgICAgICAgImFkbWluVVJMIjogImh0dHA6Ly8x
MjcuMC4wLjE6OTI5Mi92MSIsDQogICAgICAgICAgICAgICAgICAgICAgICAicmVn
aW9uIjogInJlZ2lvbk9uZSIsDQogICAgICAgICAgICAgICAgICAgICAgICAiaW50
ZXJuYWxVUkwiOiAiaHR0cDovLzEyNy4wLjAuMTo5MjkyL3YxIiwNCiAgICAgICAg
ICAgICAgICAgICAgICAgICJwdWJsaWNVUkwiOiAiaHR0cDovLzEyNy4wLjAuMTo5
MjkyL3YxIg0KICAgICAgICAgICAgICAgICAgICB9DQogICAgICAgICAgICAgICAg
XSwNCiAgICAgICAgICAgICAgICAidHlwZSI6ICJpbWFnZSIsDQogICAgICAgICAg
ICAgICAgIm5hbWUiOiAiZ2xhbmNlIg0KICAgICAgICAgICAgfSwNCiAgICAgICAg
ICAgIHsNCiAgICAgICAgICAgICAgICAiZW5kcG9pbnRzX2xpbmtzIjogW10sDQog
ICAgICAgICAgICAgICAgImVuZHBvaW50cyI6IFsNCiAgICAgICAgICAgICAgICAg
ICAgew0KICAgICAgICAgICAgICAgICAgICAgICAgImFkbWluVVJMIjogImh0dHA6
Ly8xMjcuMC4wLjE6ODc3NC92MS4xLzY0YjZmM2ZiY2M1MzQzNWU4YTYwZmNmODli
YjY2MTdhIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICJyZWdpb24iOiAicmVn
aW9uT25lIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICJpbnRlcm5hbFVSTCI6
ICJodHRwOi8vMTI3LjAuMC4xOjg3NzQvdjEuMS82NGI2ZjNmYmNjNTM0MzVlOGE2
MGZjZjg5YmI2NjE3YSIsDQogICAgICAgICAgICAgICAgICAgICAgICAicHVibGlj
VVJMIjogImh0dHA6Ly8xMjcuMC4wLjE6ODc3NC92MS4xLzY0YjZmM2ZiY2M1MzQz
NWU4YTYwZmNmODliYjY2MTdhIg0KICAgICAgICAgICAgICAgICAgICB9DQogICAg
ICAgICAgICAgICAgXSwNCiAgICAgICAgICAgICAgICAidHlwZSI6ICJjb21wdXRl
IiwNCiAgICAgICAgICAgICAgICAibmFtZSI6ICJub3ZhIg0KICAgICAgICAgICAg
fSwNCiAgICAgICAgICAgIHsNCiAgICAgICAgICAgICAgICAiZW5kcG9pbnRzX2xp
bmtzIjogW10sDQogICAgICAgICAgICAgICAgImVuZHBvaW50cyI6IFsNCiAgICAg
ICAgICAgICAgICAgICAgew0KICAgICAgICAgICAgICAgICAgICAgICAgImFkbWlu
VVJMIjogImh0dHA6Ly8xMjcuMC4wLjE6MzUzNTcvdjIuMCIsDQogICAgICAgICAg
ICAgICAgICAgICAgICAicmVnaW9uIjogIlJlZ2lvbk9uZSIsDQogICAgICAgICAg
ICAgICAgICAgICAgICAiaW50ZXJuYWxVUkwiOiAiaHR0cDovLzEyNy4wLjAuMToz
NTM1Ny92Mi4wIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICJwdWJsaWNVUkwi
OiAiaHR0cDovLzEyNy4wLjAuMTo1MDAwL3YyLjAiDQogICAgICAgICAgICAgICAg
ICAgIH0NCiAgICAgICAgICAgICAgICBdLA0KICAgICAgICAgICAgICAgICJ0eXBl
IjogImlkZW50aXR5IiwNCiAgICAgICAgICAgICAgICAibmFtZSI6ICJrZXlzdG9u
ZSINCiAgICAgICAgICAgIH0NCiAgICAgICAgXSwNCiAgICAgICAgInVzZXIiOiB7
DQogICAgICAgICAgICAidXNlcm5hbWUiOiAidXNlcl9uYW1lMSIsDQogICAgICAg
ICAgICAicm9sZXNfbGlua3MiOiBbDQogICAgICAgICAgICAgICAgInJvbGUxIiwN
CiAgICAgICAgICAgICAgICAicm9sZTIiDQogICAgICAgICAgICBdLA0KICAgICAg
ICAgICAgImlkIjogInVzZXJfaWQxIiwNCiAgICAgICAgICAgICJyb2xlcyI6IFsN
CiAgICAgICAgICAgICAgICB7DQogICAgICAgICAgICAgICAgICAgICJpZCI6ICJm
MDNmZGE4ZjhhMzI0OWIyYTcwZmIxZjE3NmE3YjYzMSIsDQogICAgICAgICAgICAg
ICAgICAgICJuYW1lIjogInJvbGUxIg0KICAgICAgICAgICAgICAgIH0sDQogICAg
ICAgICAgICAgICAgew0KICAgICAgICAgICAgICAgICAgICAiaWQiOiAiZjAzZmRh
OGY4YTMyNDliMmE3MGZiMWYxNzZhN2I2MzEiLA0KICAgICAgICAgICAgICAgICAg
ICAibmFtZSI6ICJyb2xlMiINCiAgICAgICAgICAgICAgICB9DQogICAgICAgICAg
ICBdLA0KICAgICAgICAgICAgIm5hbWUiOiAidXNlcl9uYW1lMSINCiAgICAgICAg
fQ0KICAgIH0NCn0NCjGCAcowggHGAgEBMIGkMIGeMQowCAYDVQQFEwE1MQswCQYD
VQQGEwJVUzELMAkGA1UECBMCQ0ExEjAQBgNVBAcTCVN1bm55dmFsZTESMBAGA1UE
ChMJT3BlblN0YWNrMREwDwYDVQQLEwhLZXlzdG9uZTElMCMGCSqGSIb3DQEJARYW
a2V5c3RvbmVAb3BlbnN0YWNrLm9yZzEUMBIGA1UEAxMLU2VsZiBTaWduZWQCAREw
BwYFKw4DAhowDQYJKoZIhvcNAQEBBQAEggEAxyHPqb53KXaWJH1IE6IFp3zzm5vl
zlotcMxMepMRIxQPUDwJrP2ZJwXemQXVTpRa3Aer7hSkCRlyI++mcj/rD4h5Ygb0
q9sscjfeZB11Y436E4ZhXCdTfrtmKyBlHMqyhTBz64zroN0P+DVH7OLZDX/gqN2U
KTX99HTN+LvUa8VqQYIzsjNv80CU6pog/YOCGPixjMKE9m9xYUr9huKZUxliHtX2
AHoCfQPhI8nsnNHLzCx6u5xIM7A69ZIDPQ82hSHC58k+g0bq9uflRCixBSD7ulR7
7ZRJM8IgOgFGpNeuyKcHJsCdPpZS8p1MmDCkwTOt5Kvf7Nopz+Cc325uOA==
-----END CMS-----

View File

@@ -1 +0,0 @@
PKIZ_eJylVkmTmzgUvutXzN2VCotxm0MObMbQSG52ixtLG5DBdrttC_j1I3An6XRSk8wMVS6jJ_H0vfe97dMn9qiGaaG_NOiPi08AWpa1KbH9eEys6pYjxc21I5M9Dhp7ck1xjU4LlLVahme9hJpJNE3d56bmv5i-lYlAd421kjIhKY2yxNxzb1dYQE0uwnpTqw_WwbulQnS1yLFke6dcRHwSezu8ddm-UgNIFAprjkKf6zYrt4fBsUP6kSL-WDuaUifbiqZbu8l7a2FpVg91OHcCpXMCYx7pVgc2xOA2RBHj2nq1NPuUaONBm2bmiiRxdctMr8nve1wSS1V2cO_I2uiKY_sVJPEk4PJD1Iw3pvEdWmGOByRuKzR76E8K2JpvRlOYWU3Wrq7FSr6CUfh2YJ9sEcnbhhZmc8tqhsSU-Mzs5J1P2UfML4fkhIVIx1uvykz5MCoDsfhaM2jMr_IpO3jDKBxlOPYuaSxF4Z5OiNK1x-X68eYMRo_6OXWIcmX-mgM05IIj4s4ZMIdJ0kIhqbEAeTi4A4rDOQ4wTVrUbvSmxoTtBEVl1SMiu0mE5gYmqJrdJ3FxygTpKWvD-u4LiUu2o93dP6IAI4z_jkLlAW67E-YjP7jTdyzWHt3UyxsMLHGyU5t3G1KKaMC3ghg3RLwatXhIWpvgIRwA0iGfBFWFiNpiAc83sV0jgjskGPUu4kZ2GGUezYTmWqzRLjOba3qP0mzL2AGMUyk3wzv3rfxajFyP8FoWNPEH-YEpXP_IGviXtEmQ7PvRX1-ZACMV_4cJ8GvNKv9nzt33YBNYo3f_yGHv_ZXGfJUIYQ1GqCwxLok_3XRgWXjFbGOMf5b_7xTeFY31IjH5U9bc0YF_5t4d0V2hvxSQaQkJYRHQIoICyMEhajamIQBoJiQhpYRbS0DEEPE9M98cOp_g5m10SGP5GgjSG8UMkRn1DPkriCIbTjneVlyxVhZOv-wgyUcUjDpjsdFZEdNkAfrzX4Y6-F2s_44N8G_s_dlcWwYTPay-JWv1NgZOzsuv7P88FdHVpRhZKtYNfWOJZAJPi633LdzB13jPWlkYNTravWB-U3hXxGSrfRY3148-Ax-dBlmKoiBn5lhM9jMj4QdGwHtKfsfIL5RTULDansbod1nIQ12hLFd6tv4h7MGfxT3rAwfvVKz39YfQP4Nk2wyFKV8T5sD7h9GQbK23vji-v28o03o3KQiMSRnIWbVhDeUHBKxQsJYWfi0a43tvNdz71sfnQtSPXQu8daU-E7rmO2XN_u5MTFnY7nFQtSyQBkhcCRO7QgOrn2TVwiAXAA4KVkRh97EOTsgYzLe0_npzC3XUJqYxT0hVwcHiwCa2ehzkLBesLmHhiVoWoqxg_9xQ30w58MV7R4Lv9ky3d-yAvAtMTcmPtCzXplIaKrTMPfs9Q_dINQXrkeuuDGrw0H2lQHMngWlQOwoHw4HK3lT40NBUqLmc0RlEcdUSRaqSB1qE-KyVpIIFHTPPh6pigulwBe1AVJusQRyO0Rl6BtXppNgxaOV8ozowGqjBb_MRG49soHg4ZjOQlIveLWsjJRsVHe6KnFbuk8EIoWpNqJQOOqEQvSa1GqRxcWXDicYUGFSlePVI57pSHanuvp_YDFV1FTZ8GcrR8fnhBZ6Ka4w_z1waumEnBQICw9PlSQwpmuOXQEtDY1bOTjj9LPl8uh4cdbkwrm2OWkvkdNecc8q88Qq03ivo7FKXPokgTtSDB88PHJnrPsGz2usVbDw-n8O63D4f3dNgPKk7a_biR4EmIrWSU4srF7vhtnDD54cdmMmZX1mv-7amy94ZxKBOLw9pcsj4gX19SR_oSrDzM5JO9SyfyVJcbZ6e02A2S-OLTC4FkJf-jddP9bBYPl1m5Voqs3WnWhDXnVq-SMre4j9_3qsCryp28xwfnzDPZaQ4s5GkWyzkGNyiy9Z9sG_4Obsttd3jUhXFonIWc_9Y53m3ibLSDg2P1fIjuZ1Z3WnDTVPBLjy2p8H98gVME7OB9O_T89-mTsWe

View File

@@ -1,88 +0,0 @@
{
"access": {
"token": {
"expires": "2010-06-02T14:47:34Z",
"issued_at": "2002-01-18T21:14:07Z",
"id": "placeholder",
"tenant": {
"id": "tenant_id1",
"enabled": true,
"description": null,
"name": "tenant_name1"
}
},
"serviceCatalog": [
{
"endpoints_links": [],
"endpoints": [
{
"adminURL": "http://127.0.0.1:8776/v1/64b6f3fbcc53435e8a60fcf89bb6617a",
"region": "regionOne",
"internalURL": "http://127.0.0.1:8776/v1/64b6f3fbcc53435e8a60fcf89bb6617a",
"publicURL": "http://127.0.0.1:8776/v1/64b6f3fbcc53435e8a60fcf89bb6617a"
}
],
"type": "volume",
"name": "volume"
},
{
"endpoints_links": [],
"endpoints": [
{
"adminURL": "http://127.0.0.1:9292/v1",
"region": "regionOne",
"internalURL": "http://127.0.0.1:9292/v1",
"publicURL": "http://127.0.0.1:9292/v1"
}
],
"type": "image",
"name": "glance"
},
{
"endpoints_links": [],
"endpoints": [
{
"adminURL": "http://127.0.0.1:8774/v1.1/64b6f3fbcc53435e8a60fcf89bb6617a",
"region": "regionOne",
"internalURL": "http://127.0.0.1:8774/v1.1/64b6f3fbcc53435e8a60fcf89bb6617a",
"publicURL": "http://127.0.0.1:8774/v1.1/64b6f3fbcc53435e8a60fcf89bb6617a"
}
],
"type": "compute",
"name": "nova"
},
{
"endpoints_links": [],
"endpoints": [
{
"adminURL": "http://127.0.0.1:35357/v2.0",
"region": "RegionOne",
"internalURL": "http://127.0.0.1:35357/v2.0",
"publicURL": "http://127.0.0.1:5000/v2.0"
}
],
"type": "identity",
"name": "keystone"
}
],
"user": {
"username": "user_name1",
"roles_links": [
"role1",
"role2"
],
"id": "user_id1",
"roles": [
{
"id": "f03fda8f8a3249b2a70fb1f176a7b631",
"name": "role1"
},
{
"id": "f03fda8f8a3249b2a70fb1f176a7b631",
"name": "role2"
}
],
"name": "user_name1"
}
}
}

View File

@@ -1,76 +0,0 @@
-----BEGIN CMS-----
MIINuQYJKoZIhvcNAQcCoIINqjCCDaYCAQExCTAHBgUrDgMCGjCCC8YGCSqGSIb3
DQEHAaCCC7cEgguzew0KICAgICJhY2Nlc3MiOiB7DQogICAgICAgICJ0b2tlbiI6
IHsNCiAgICAgICAgICAgICJleHBpcmVzIjogIjIwMTAtMDYtMDJUMTQ6NDc6MzRa
IiwNCiAgICAgICAgICAgICJpc3N1ZWRfYXQiOiAiMjAwMi0wMS0xOFQyMToxNDow
N1oiLA0KICAgICAgICAgICAgImlkIjogInBsYWNlaG9sZGVyIiwNCiAgICAgICAg
ICAgICJ0ZW5hbnQiOiB7DQogICAgICAgICAgICAgICAgImlkIjogInRlbmFudF9p
ZDEiLA0KICAgICAgICAgICAgICAgICJlbmFibGVkIjogdHJ1ZSwNCiAgICAgICAg
ICAgICAgICAiZGVzY3JpcHRpb24iOiBudWxsLA0KICAgICAgICAgICAgICAgICJu
YW1lIjogInRlbmFudF9uYW1lMSINCiAgICAgICAgICAgIH0NCiAgICAgICAgfSwN
CiAgICAgICAgInNlcnZpY2VDYXRhbG9nIjogWw0KICAgICAgICAgICAgew0KICAg
ICAgICAgICAgICAgICJlbmRwb2ludHNfbGlua3MiOiBbXSwNCiAgICAgICAgICAg
ICAgICAiZW5kcG9pbnRzIjogWw0KICAgICAgICAgICAgICAgICAgICB7DQogICAg
ICAgICAgICAgICAgICAgICAgICAiYWRtaW5VUkwiOiAiaHR0cDovLzEyNy4wLjAu
MTo4Nzc2L3YxLzY0YjZmM2ZiY2M1MzQzNWU4YTYwZmNmODliYjY2MTdhIiwNCiAg
ICAgICAgICAgICAgICAgICAgICAgICJyZWdpb24iOiAicmVnaW9uT25lIiwNCiAg
ICAgICAgICAgICAgICAgICAgICAgICJpbnRlcm5hbFVSTCI6ICJodHRwOi8vMTI3
LjAuMC4xOjg3NzYvdjEvNjRiNmYzZmJjYzUzNDM1ZThhNjBmY2Y4OWJiNjYxN2Ei
LA0KICAgICAgICAgICAgICAgICAgICAgICAgInB1YmxpY1VSTCI6ICJodHRwOi8v
MTI3LjAuMC4xOjg3NzYvdjEvNjRiNmYzZmJjYzUzNDM1ZThhNjBmY2Y4OWJiNjYx
N2EiDQogICAgICAgICAgICAgICAgICAgIH0NCiAgICAgICAgICAgICAgICBdLA0K
ICAgICAgICAgICAgICAgICJ0eXBlIjogInZvbHVtZSIsDQogICAgICAgICAgICAg
ICAgIm5hbWUiOiAidm9sdW1lIg0KICAgICAgICAgICAgfSwNCiAgICAgICAgICAg
IHsNCiAgICAgICAgICAgICAgICAiZW5kcG9pbnRzX2xpbmtzIjogW10sDQogICAg
ICAgICAgICAgICAgImVuZHBvaW50cyI6IFsNCiAgICAgICAgICAgICAgICAgICAg
ew0KICAgICAgICAgICAgICAgICAgICAgICAgImFkbWluVVJMIjogImh0dHA6Ly8x
MjcuMC4wLjE6OTI5Mi92MSIsDQogICAgICAgICAgICAgICAgICAgICAgICAicmVn
aW9uIjogInJlZ2lvbk9uZSIsDQogICAgICAgICAgICAgICAgICAgICAgICAiaW50
ZXJuYWxVUkwiOiAiaHR0cDovLzEyNy4wLjAuMTo5MjkyL3YxIiwNCiAgICAgICAg
ICAgICAgICAgICAgICAgICJwdWJsaWNVUkwiOiAiaHR0cDovLzEyNy4wLjAuMTo5
MjkyL3YxIg0KICAgICAgICAgICAgICAgICAgICB9DQogICAgICAgICAgICAgICAg
XSwNCiAgICAgICAgICAgICAgICAidHlwZSI6ICJpbWFnZSIsDQogICAgICAgICAg
ICAgICAgIm5hbWUiOiAiZ2xhbmNlIg0KICAgICAgICAgICAgfSwNCiAgICAgICAg
ICAgIHsNCiAgICAgICAgICAgICAgICAiZW5kcG9pbnRzX2xpbmtzIjogW10sDQog
ICAgICAgICAgICAgICAgImVuZHBvaW50cyI6IFsNCiAgICAgICAgICAgICAgICAg
ICAgew0KICAgICAgICAgICAgICAgICAgICAgICAgImFkbWluVVJMIjogImh0dHA6
Ly8xMjcuMC4wLjE6ODc3NC92MS4xLzY0YjZmM2ZiY2M1MzQzNWU4YTYwZmNmODli
YjY2MTdhIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICJyZWdpb24iOiAicmVn
aW9uT25lIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICJpbnRlcm5hbFVSTCI6
ICJodHRwOi8vMTI3LjAuMC4xOjg3NzQvdjEuMS82NGI2ZjNmYmNjNTM0MzVlOGE2
MGZjZjg5YmI2NjE3YSIsDQogICAgICAgICAgICAgICAgICAgICAgICAicHVibGlj
VVJMIjogImh0dHA6Ly8xMjcuMC4wLjE6ODc3NC92MS4xLzY0YjZmM2ZiY2M1MzQz
NWU4YTYwZmNmODliYjY2MTdhIg0KICAgICAgICAgICAgICAgICAgICB9DQogICAg
ICAgICAgICAgICAgXSwNCiAgICAgICAgICAgICAgICAidHlwZSI6ICJjb21wdXRl
IiwNCiAgICAgICAgICAgICAgICAibmFtZSI6ICJub3ZhIg0KICAgICAgICAgICAg
fSwNCiAgICAgICAgICAgIHsNCiAgICAgICAgICAgICAgICAiZW5kcG9pbnRzX2xp
bmtzIjogW10sDQogICAgICAgICAgICAgICAgImVuZHBvaW50cyI6IFsNCiAgICAg
ICAgICAgICAgICAgICAgew0KICAgICAgICAgICAgICAgICAgICAgICAgImFkbWlu
VVJMIjogImh0dHA6Ly8xMjcuMC4wLjE6MzUzNTcvdjIuMCIsDQogICAgICAgICAg
ICAgICAgICAgICAgICAicmVnaW9uIjogIlJlZ2lvbk9uZSIsDQogICAgICAgICAg
ICAgICAgICAgICAgICAiaW50ZXJuYWxVUkwiOiAiaHR0cDovLzEyNy4wLjAuMToz
NTM1Ny92Mi4wIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICJwdWJsaWNVUkwi
OiAiaHR0cDovLzEyNy4wLjAuMTo1MDAwL3YyLjAiDQogICAgICAgICAgICAgICAg
ICAgIH0NCiAgICAgICAgICAgICAgICBdLA0KICAgICAgICAgICAgICAgICJ0eXBl
IjogImlkZW50aXR5IiwNCiAgICAgICAgICAgICAgICAibmFtZSI6ICJrZXlzdG9u
ZSINCiAgICAgICAgICAgIH0NCiAgICAgICAgXSwNCiAgICAgICAgInVzZXIiOiB7
DQogICAgICAgICAgICAidXNlcm5hbWUiOiAidXNlcl9uYW1lMSIsDQogICAgICAg
ICAgICAicm9sZXNfbGlua3MiOiBbDQogICAgICAgICAgICAgICAgInJvbGUxIiwN
CiAgICAgICAgICAgICAgICAicm9sZTIiDQogICAgICAgICAgICBdLA0KICAgICAg
ICAgICAgImlkIjogInVzZXJfaWQxIiwNCiAgICAgICAgICAgICJyb2xlcyI6IFsN
CiAgICAgICAgICAgICAgICB7DQogICAgICAgICAgICAgICAgICAgICJuYW1lIjog
InJvbGUxIg0KICAgICAgICAgICAgICAgIH0sDQogICAgICAgICAgICAgICAgew0K
ICAgICAgICAgICAgICAgICAgICAibmFtZSI6ICJyb2xlMiINCiAgICAgICAgICAg
ICAgICB9DQogICAgICAgICAgICBdLA0KICAgICAgICAgICAgIm5hbWUiOiAidXNl
cl9uYW1lMSINCiAgICAgICAgfQ0KICAgIH0NCn0NCjGCAcowggHGAgEBMIGkMIGe
MQowCAYDVQQFEwE1MQswCQYDVQQGEwJVUzELMAkGA1UECBMCQ0ExEjAQBgNVBAcT
CVN1bm55dmFsZTESMBAGA1UEChMJT3BlblN0YWNrMREwDwYDVQQLEwhLZXlzdG9u
ZTElMCMGCSqGSIb3DQEJARYWa2V5c3RvbmVAb3BlbnN0YWNrLm9yZzEUMBIGA1UE
AxMLU2VsZiBTaWduZWQCAREwBwYFKw4DAhowDQYJKoZIhvcNAQEBBQAEggEAw7K9
7FaxXE6QNbsWmTAo/mtppDB2hv2DCwxMnjaZuOlV3g7UGnF8mxHjWd2Pcj1r0oGb
0iACE9qmoZVHTPWU6WWBClAIF/bcs6Y+5S10bCu1uRVrzUCsLEbbJOLxBZG1qiEZ
opLn6pBIOY8ovxcoKKmI56JgsqVGclZM5yH9Z9E5hSZgMREJZFZcVHA3pTJeTjc2
9Mpb3RS5Q/FXf2nP09YA4Mp9+J15gFH/YuhBQiyo+LqvHtg+DdWdxcM3keAaTuxw
Z8Cd26T+cTv1iS5qXcykd8OP7V0eIF7i39wshXGm6B9XpwFEYiLTZy7398O/yeGd
izImJNpCowBA0Pyr8w==
-----END CMS-----

View File

@@ -1 +0,0 @@
PKIZ_eJylVtuSmzgQfddX7PtUKiDsGfOQBy4yhrHEcLd4MzAGZLA99pjb16_Ak2QySW2yu66iDC3RnO5zulufPvGfigyT_KVhb3z4BLBpmnZOrcdjbBZNShQn1Y7c9jho_JdqioM6zVdWah6c9RxrBtM0dZ8amvdieGYiAd1BK2XLjSxHeU6F594qKCRVKuHSLtUH8-A2WxheTXbM-doplYgYR-6Obhy-rpQAM6XFpdBiT-jspdNj_9gR_dgS8ViuNaWMN0W73VhV2pv3pmb2WEft2lcgv_pQRwKwmSPZDAtRaV5MzTrF2rjRahNjyeKoaBLDrdLbmhBH8yI5ODdkdXilkXUBcTQZhPQQVuMXt9ENWmaMG-bCBlZ77E0O-LNYjaHwsKqkXl6zpXwFo_Ftwz7eEJbWVZsZVZOUHIkxFxOjk3dey1_ieTnEJwpDnW7cIjHkw-gMRNKl5NB4XuVTcnCH0TjaaOS-bqN5GOzbCdF25QqpfmzWA-pJP2vXTLnyfM0AGVK4lmi3HqhAWVxjGJcUYhEPzkCiYEZ92sY1qW29KinjK35WmOWIyKpiWDVggqpZfRxlpwTOn5I6KG-5mAvxZoy7-0cUYITx31GoIqB1d6Ji6Pk3-o7Zym3tctFg35SmOLVZZ7NcIgNtMoYawtyS1HSIa4vRIRgA0bEY-0VBmFpTSGd2ZJWE0Y5AVO5CYWSHU-a2Cayu2YrsEqO6bm8qTTacHcA5nadGcOO-li_ZyPUIr-aiiT7YD9zh6kfWwL-kbY7Zvh_z9ZUJMFLxf5gAv_asin-W3H0PbN8cs_tHCXufr20kFjEMSjBC5YXxGnvTlw68Cq-UL4z65_X_zuHN0dgvYkM8JdUNHfhn7p0R3RV7C0gME8aMK6AmjPhYwENY2QaCABsxi1k-p7UJCUMSvVXmW0JnE9y0Dg_bSL76cP5GMUdkhD1HfgFhaOGpxutCyFbK_bpfdJilIwpOHbq3dd7ENBlib_ZLqYPfaf13bIB_E-_P4VoymOjh_S1eqc0onFSUL_z_PDXR5Ws2spStqvaNJZZAsc027je5g696T2oZjh7X2q1hfnN4c8Rty30SVdePOQMfk4Z5iRI_5eGY3PYzI8EHRsB7Sn7HyC-ctyDjvX0bkd9VoYh1peW10vPnH2QP_kz3fA4c3FO22pcfpH8G8aYaMkO-xjyBtxfDId6Yb3NxvH8_UKbn3eTAR5MzkPJuwwfKDwh4o-AjLfjaNMb73qyE96NPTGHYj1MLvE2lPoFd9Z2yan9LJm25bPfUL2oupAEzZ06ZVZCB90-2rLGfQkD9jDdR3H3sgxMyDvOtrL9-ucY6qWMDzWJWFHgw-XSOzJ76Ka8Fs4u5PEnNJcob9s8D9S2Ug5i9TyT4Hs_09Y5vkHe-oSnpsc3zlaHkSMWmsefXM3aOraZQPXScJWqRiJ1LCzRnMhiotcJgQGus7A1FDJCmYs0RUIeY4qg5CVUl9bWQiEk9n2dcdDw8D6uKAabNBbZ8Sa2Sigg0ImfsolZvJ8dr1Bbrb1T7qMIa_nY-4scjCygujfgZaJ5KbpPUoZKMjg43R-ta7uMBBVg1J1RKh9cBDC9xqfrbKLvyw4nGHaBWbenysZ3pSnFsdef9iQ2pqqPwwxdShifbKSSRDulneAkzL_1s95acEXAHC8PNXUdeP_Qrz9qeXvW6znWfvOrq4irIun8XtKKPVnfijJEHMs9DT9RWUmE9KjynDFjbJfPxS7Mx0iWWFs9RWj46L3Z3V587XM1PWwNCXoTJ2UNDv1d9vvuz9tLXV_kxDWYYgqHYd8_N6XzoH2b-xWpw0y_gJtAsxErjRb5G4RbKz_YBO2VeLXPDpyxSbVs8N2ZTBVgCiztMAyErXrdbb7N_Me8fcjVnq9dBsKTF4alod0-SuyzE3LNe81ZdFU6TCv48WWXN-hB7O_B8DmemaSyebLh7CO6kaJFerYooCIa2zek7UjVGq6ck30Okq4_3s0OV2WpyLwkwwsqXL2A6MSOifz89_w1E1sSW

View File

@@ -1,26 +0,0 @@
{
"access": {
"token": {
"expires": "2112-08-17T15:35:34Z",
"issued_at": "2002-01-18T21:14:07Z",
"id": "01e032c996ef4406b144335915a41e79"
},
"serviceCatalog": {},
"user": {
"username": "user_name1",
"roles_links": [],
"id": "c9c89e3be3ee453fbf00c7966f6d3fbd",
"roles": [
{
"id": "359da42d31c04437a32812aeb79e9c0b",
"name": "role1"
},
{
"id": "581af19726fa4af5bda745789ab2bf2b",
"name": "role2"
}
],
"name": "user_name1"
}
}
}

View File

@@ -1,29 +0,0 @@
-----BEGIN CMS-----
MIIE9gYJKoZIhvcNAQcCoIIE5zCCBOMCAQExCTAHBgUrDgMCGjCCAwMGCSqGSIb3
DQEHAaCCAvQEggLwew0KICAgICJhY2Nlc3MiOiB7DQogICAgICAgICJ0b2tlbiI6
IHsNCiAgICAgICAgICAgICJleHBpcmVzIjogIjIxMTItMDgtMTdUMTU6MzU6MzRa
IiwNCiAgICAgICAgICAgICJpc3N1ZWRfYXQiOiAiMjAwMi0wMS0xOFQyMToxNDow
N1oiLA0KICAgICAgICAgICAgImlkIjogIjAxZTAzMmM5OTZlZjQ0MDZiMTQ0MzM1
OTE1YTQxZTc5Ig0KICAgICAgICB9LA0KICAgICAgICAic2VydmljZUNhdGFsb2ci
OiB7fSwNCiAgICAgICAgInVzZXIiOiB7DQogICAgICAgICAgICAidXNlcm5hbWUi
OiAidXNlcl9uYW1lMSIsDQogICAgICAgICAgICAicm9sZXNfbGlua3MiOiBbXSwN
CiAgICAgICAgICAgICJpZCI6ICJjOWM4OWUzYmUzZWU0NTNmYmYwMGM3OTY2ZjZk
M2ZiZCIsDQogICAgICAgICAgICAicm9sZXMiOiBbDQogICAgICAgICAgICAgICAg
ew0KICAgICAgICAgICAgICAgICAgICAiaWQiOiAiMzU5ZGE0MmQzMWMwNDQzN2Ez
MjgxMmFlYjc5ZTljMGIiLA0KICAgICAgICAgICAgICAgICAgICAibmFtZSI6ICJy
b2xlMSINCiAgICAgICAgICAgICAgICB9LA0KICAgICAgICAgICAgICAgIHsNCiAg
ICAgICAgICAgICAgICAgICAgImlkIjogIjU4MWFmMTk3MjZmYTRhZjViZGE3NDU3
ODlhYjJiZjJiIiwNCiAgICAgICAgICAgICAgICAgICAgIm5hbWUiOiAicm9sZTIi
DQogICAgICAgICAgICAgICAgfQ0KICAgICAgICAgICAgXSwNCiAgICAgICAgICAg
ICJuYW1lIjogInVzZXJfbmFtZTEiDQogICAgICAgIH0NCiAgICB9DQp9DQoxggHK
MIIBxgIBATCBpDCBnjEKMAgGA1UEBRMBNTELMAkGA1UEBhMCVVMxCzAJBgNVBAgT
AkNBMRIwEAYDVQQHEwlTdW5ueXZhbGUxEjAQBgNVBAoTCU9wZW5TdGFjazERMA8G
A1UECxMIS2V5c3RvbmUxJTAjBgkqhkiG9w0BCQEWFmtleXN0b25lQG9wZW5zdGFj
ay5vcmcxFDASBgNVBAMTC1NlbGYgU2lnbmVkAgERMAcGBSsOAwIaMA0GCSqGSIb3
DQEBAQUABIIBAMmrhRIUjSd+SLUAYn+18MDB8MXiaiF+FJQbu86IFW3OpL86ksvg
CTP44Rvu1F4vvoZAQ60/tOfFVNTnBgnMv0NEfl4huiFqYrXjCphnNFQ5OYnmU6LR
bFV+dvjZXWUn0wJDroUUEjbgyy/mqUnULzQgUzyK7Ho8T0dWahQc7EFMNVjoeKfa
K7DeRe9trNNHM8anKVaeKhpWIfzbxiwIwypukce6wVGfdhaP+58jeFnGwHUIsY8V
8rzWj9UN46ko61piMAZljcktbrpqw2fDJ1H9Xl23G83rnXY7uVLQWUe7fRcUFtQt
gQvKsGkN2hqlOgMT/FxFM3HC8kcl3wmzrNA=
-----END CMS-----

View File

@@ -1 +0,0 @@
PKIZ_eJxdVNmWqjoQfecr7nuvs5pBW30MIWKiCSKT5A1QZqQdEOTrb7DPHVmLIVWVqr03Vfn1S1w6MjH7A1JnWvySKMZGa4dk23KcPxMG7AS2wlaVEILZDAIbDdAFGz3zbkZGoTnZo5kJnavp4FiTDBttQCSMfImyzIzPL5KHKqsTjRZWoS_w5fCMVL_DZZsJ33eiMYUHhzQ82sIPComWoKeF3FNHHqy1_aJuOzCj7ZnSFjsICn7M--hI6uSFvzDEwo9eOxfMdi7SfAMpklVSRdxyUOA7huSbw3dgTwOvpyMpLbdSeRDKzABqWCLxpiNzq4EFSBYxmmQ5ZDVVSlT_dWrqknssP5nre6wmbwqp02f44o_8iH9Tmr5JFwZKPdGSfhvSuFk_uIvesJNmdedHlsZm3UU_WsTHKVFTV9Mm3NB5OGZz7rJCEo-au7ZCVV5woUc4JnNW8oY19sgbUuFiQkCesemP0-ZAuxdR8CMgHb25xE3BRQTTgPbMsEemopGW2UCbdR2WiahSl9TEb2RvlM6kEXnF6lBTQV_aQcHrL2ilN6PBuqFupVGBInQPOS_9QhTRmOFpllHnYUkEUlK8kTXzXIoD7w3nzdvFRerL09_4W6T_a5QelRUNsf6aGioJoSQ6rc8iu8_4bIAlwHrGfB14LnC9AY6A_KxDF9S-S-17D-3Q8G0bo54YtoscierABIqH9IEST_O7-FKrYSD4HXCPwDt4i_p6n5h-52kH0aX3Ablg_5P47koQPerzkcmxOheieD3u_z0Xlb7O-Y0f6_Fkrjru6c8pUfKTqIs1cpHowe5R9q5koP7h8mBo8Jp9c5GQC0boP4MEmJ5V17wqzFUv64L-WgLAEROnxy40lpf0Fg28fjkQr5bP1g0-Qt-trp_4RXab1bDZlQvFPkffLGmr62wLSLTYS9b2MhKrIzeowvOwgP35VOcfVbq7nLEefJzqb1nfpgf1dh0sdWM-QmW3WJwrvzBhh9bFl0R2kU3U3eqYwodf6ddV2OfzzAwWt0fqJI62dwS7fUn0wd2q22tM5LzxcyqjjMNZt46kpJtbY5PjW218jXuv1s3ncAZhvFAeymKFu2I8xOuxSZf-vH76-_IKeLy0WKvq7Hgbv6A0dJ-seV45vZufmqwrsZ68hlxvo2ZBFrkV55blvzbp_nOZmZes3LycHQkPVlzKz9N2OXxJS00ui6s_aPNNIDjeWsvY-vuP9mOuIel96iFm_HMC_gm2VKmF

View File

@@ -1,140 +0,0 @@
{
"token": {
"catalog": [
{
"endpoints": [
{
"id": "3b5e554bcf114f2483e8a1be7a0506d1",
"interface": "admin",
"url": "http://127.0.0.1:8776/v1/64b6f3fbcc53435e8a60fcf89bb6617a",
"region": "regionOne"
},
{
"id": "54abd2dc463c4ba4a72915498f8ecad1",
"interface": "internal",
"url": "http://127.0.0.1:8776/v1/64b6f3fbcc53435e8a60fcf89bb6617a",
"region": "regionOne"
},
{
"id": "70a7efa4b1b941968357cc43ae1419ee",
"interface": "public",
"url": "http://127.0.0.1:8776/v1/64b6f3fbcc53435e8a60fcf89bb6617a",
"region": "regionOne"
}
],
"id": "5707c3fc0a294703a3c638e9cf6a6c3a",
"type": "volume",
"name": "volume"
},
{
"endpoints": [
{
"id": "92217a3b95394492859bc49fd474382f",
"interface": "admin",
"url": "http://127.0.0.1:9292/v1",
"region": "regionOne"
},
{
"id": "f20563bdf66f4efa8a1f11d99b672be1",
"interface": "internal",
"url": "http://127.0.0.1:9292/v1",
"region": "regionOne"
},
{
"id": "375f9ba459a447738fb60fe5fc26e9aa",
"interface": "public",
"url": "http://127.0.0.1:9292/v1",
"region": "regionOne"
}
],
"id": "15c21aae6b274a8da52e0a068e908aac",
"type": "image",
"name": "glance"
},
{
"endpoints": [
{
"id": "edbd9f50f66746ae9ed11dc3b1ae35da",
"interface": "admin",
"url": "http://127.0.0.1:8774/v1.1/64b6f3fbcc53435e8a60fcf89bb6617a",
"region": "regionOne"
},
{
"id": "9e03c46c80a34a159cb39f5cb0498b92",
"interface": "internal",
"url": "http://127.0.0.1:8774/v1.1/64b6f3fbcc53435e8a60fcf89bb6617a",
"region": "regionOne"
},
{
"id": "1df0b44d92634d59bd0e0d60cf7ce432",
"interface": "public",
"url": "http://127.0.0.1:8774/v1.1/64b6f3fbcc53435e8a60fcf89bb6617a",
"region": "regionOne"
}
],
"id": "2f404fdb89154c589efbc10726b029ec",
"type": "compute",
"name": "nova"
},
{
"endpoints": [
{
"id": "a4501e141a4b4e14bf282e7bffd81dc5",
"interface": "admin",
"url": "http://127.0.0.1:35357/v3",
"region": "RegionOne"
},
{
"id": "3d17e3227bfc4483b58de5eaa584e360",
"interface": "internal",
"url": "http://127.0.0.1:35357/v3",
"region": "RegionOne"
},
{
"id": "8cd4b957090f4ca5842a22e9a74099cd",
"interface": "public",
"url": "http://127.0.0.1:5000/v3",
"region": "RegionOne"
}
],
"id": "c5d926d566424e4fba4f80c37916cde5",
"type": "identity",
"name": "keystone"
}
],
"issued_at": "2002-01-18T21:14:07Z",
"expires_at": "2038-01-18T21:14:07Z",
"audit_ids": ["ZzzZ2ZZYqT8OzfUVvrjEITQ", "cCCCCCctTzO1-XUk5STybw"],
"project": {
"enabled": true,
"description": null,
"name": "tenant_name1",
"id": "tenant_id1",
"domain": {
"id": "domain_id1",
"name": "domain_name1"
}
},
"user": {
"name": "revoked_username1",
"id": "revoked_user_id1",
"domain": {
"id": "domain_id1",
"name": "domain_name1"
}
},
"roles": [
{
"id": "f03fda8f8a3249b2a70fb1f176a7b631",
"name": "role1"
},
{
"id": "f03fda8f8a3249b2a70fb1f176a7b631",
"name": "role2"
}
],
"methods": [
"password"
]
}
}

View File

@@ -1,123 +0,0 @@
-----BEGIN CMS-----
MIIWqQYJKoZIhvcNAQcCoIIWmjCCFpYCAQExCTAHBgUrDgMCGjCCFLYGCSqGSIb3
DQEHAaCCFKcEghSjew0KICAgICJ0b2tlbiI6IHsNCiAgICAgICAgImNhdGFsb2ci
OiBbDQogICAgICAgICAgICB7DQogICAgICAgICAgICAgICAgImVuZHBvaW50cyI6
IFsNCiAgICAgICAgICAgICAgICAgICAgew0KICAgICAgICAgICAgICAgICAgICAg
ICAgImlkIjogIjNiNWU1NTRiY2YxMTRmMjQ4M2U4YTFiZTdhMDUwNmQxIiwNCiAg
ICAgICAgICAgICAgICAgICAgICAgICJpbnRlcmZhY2UiOiAiYWRtaW4iLA0KICAg
ICAgICAgICAgICAgICAgICAgICAgInVybCI6ICJodHRwOi8vMTI3LjAuMC4xOjg3
NzYvdjEvNjRiNmYzZmJjYzUzNDM1ZThhNjBmY2Y4OWJiNjYxN2EiLA0KICAgICAg
ICAgICAgICAgICAgICAgICAgInJlZ2lvbiI6ICJyZWdpb25PbmUiDQogICAgICAg
ICAgICAgICAgICAgIH0sDQogICAgICAgICAgICAgICAgICAgIHsNCiAgICAgICAg
ICAgICAgICAgICAgICAgICJpZCI6ICI1NGFiZDJkYzQ2M2M0YmE0YTcyOTE1NDk4
ZjhlY2FkMSIsDQogICAgICAgICAgICAgICAgICAgICAgICAiaW50ZXJmYWNlIjog
ImludGVybmFsIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICJ1cmwiOiAiaHR0
cDovLzEyNy4wLjAuMTo4Nzc2L3YxLzY0YjZmM2ZiY2M1MzQzNWU4YTYwZmNmODli
YjY2MTdhIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICJyZWdpb24iOiAicmVn
aW9uT25lIg0KICAgICAgICAgICAgICAgICAgICB9LA0KICAgICAgICAgICAgICAg
ICAgICB7DQogICAgICAgICAgICAgICAgICAgICAgICAiaWQiOiAiNzBhN2VmYTRi
MWI5NDE5NjgzNTdjYzQzYWUxNDE5ZWUiLA0KICAgICAgICAgICAgICAgICAgICAg
ICAgImludGVyZmFjZSI6ICJwdWJsaWMiLA0KICAgICAgICAgICAgICAgICAgICAg
ICAgInVybCI6ICJodHRwOi8vMTI3LjAuMC4xOjg3NzYvdjEvNjRiNmYzZmJjYzUz
NDM1ZThhNjBmY2Y4OWJiNjYxN2EiLA0KICAgICAgICAgICAgICAgICAgICAgICAg
InJlZ2lvbiI6ICJyZWdpb25PbmUiDQogICAgICAgICAgICAgICAgICAgIH0NCiAg
ICAgICAgICAgICAgICBdLA0KICAgICAgICAgICAgICAgICJpZCI6ICI1NzA3YzNm
YzBhMjk0NzAzYTNjNjM4ZTljZjZhNmMzYSIsDQogICAgICAgICAgICAgICAgInR5
cGUiOiAidm9sdW1lIiwNCiAgICAgICAgICAgICAgICAibmFtZSI6ICJ2b2x1bWUi
DQogICAgICAgICAgICB9LA0KICAgICAgICAgICAgew0KICAgICAgICAgICAgICAg
ICJlbmRwb2ludHMiOiBbDQogICAgICAgICAgICAgICAgICAgIHsNCiAgICAgICAg
ICAgICAgICAgICAgICAgICJpZCI6ICI5MjIxN2EzYjk1Mzk0NDkyODU5YmM0OWZk
NDc0MzgyZiIsDQogICAgICAgICAgICAgICAgICAgICAgICAiaW50ZXJmYWNlIjog
ImFkbWluIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICJ1cmwiOiAiaHR0cDov
LzEyNy4wLjAuMTo5MjkyL3YxIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICJy
ZWdpb24iOiAicmVnaW9uT25lIg0KICAgICAgICAgICAgICAgICAgICB9LA0KICAg
ICAgICAgICAgICAgICAgICB7DQogICAgICAgICAgICAgICAgICAgICAgICAiaWQi
OiAiZjIwNTYzYmRmNjZmNGVmYThhMWYxMWQ5OWI2NzJiZTEiLA0KICAgICAgICAg
ICAgICAgICAgICAgICAgImludGVyZmFjZSI6ICJpbnRlcm5hbCIsDQogICAgICAg
ICAgICAgICAgICAgICAgICAidXJsIjogImh0dHA6Ly8xMjcuMC4wLjE6OTI5Mi92
MSIsDQogICAgICAgICAgICAgICAgICAgICAgICAicmVnaW9uIjogInJlZ2lvbk9u
ZSINCiAgICAgICAgICAgICAgICAgICAgfSwNCiAgICAgICAgICAgICAgICAgICAg
ew0KICAgICAgICAgICAgICAgICAgICAgICAgImlkIjogIjM3NWY5YmE0NTlhNDQ3
NzM4ZmI2MGZlNWZjMjZlOWFhIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICJp
bnRlcmZhY2UiOiAicHVibGljIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICJ1
cmwiOiAiaHR0cDovLzEyNy4wLjAuMTo5MjkyL3YxIiwNCiAgICAgICAgICAgICAg
ICAgICAgICAgICJyZWdpb24iOiAicmVnaW9uT25lIg0KICAgICAgICAgICAgICAg
ICAgICB9DQogICAgICAgICAgICAgICAgXSwNCiAgICAgICAgICAgICAgICAiaWQi
OiAiMTVjMjFhYWU2YjI3NGE4ZGE1MmUwYTA2OGU5MDhhYWMiLA0KICAgICAgICAg
ICAgICAgICJ0eXBlIjogImltYWdlIiwNCiAgICAgICAgICAgICAgICAibmFtZSI6
ICJnbGFuY2UiDQogICAgICAgICAgICB9LA0KICAgICAgICAgICAgew0KICAgICAg
ICAgICAgICAgICJlbmRwb2ludHMiOiBbDQogICAgICAgICAgICAgICAgICAgIHsN
CiAgICAgICAgICAgICAgICAgICAgICAgICJpZCI6ICJlZGJkOWY1MGY2Njc0NmFl
OWVkMTFkYzNiMWFlMzVkYSIsDQogICAgICAgICAgICAgICAgICAgICAgICAiaW50
ZXJmYWNlIjogImFkbWluIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICJ1cmwi
OiAiaHR0cDovLzEyNy4wLjAuMTo4Nzc0L3YxLjEvNjRiNmYzZmJjYzUzNDM1ZThh
NjBmY2Y4OWJiNjYxN2EiLA0KICAgICAgICAgICAgICAgICAgICAgICAgInJlZ2lv
biI6ICJyZWdpb25PbmUiDQogICAgICAgICAgICAgICAgICAgIH0sDQogICAgICAg
ICAgICAgICAgICAgIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICJpZCI6ICI5
ZTAzYzQ2YzgwYTM0YTE1OWNiMzlmNWNiMDQ5OGI5MiIsDQogICAgICAgICAgICAg
ICAgICAgICAgICAiaW50ZXJmYWNlIjogImludGVybmFsIiwNCiAgICAgICAgICAg
ICAgICAgICAgICAgICJ1cmwiOiAiaHR0cDovLzEyNy4wLjAuMTo4Nzc0L3YxLjEv
NjRiNmYzZmJjYzUzNDM1ZThhNjBmY2Y4OWJiNjYxN2EiLA0KICAgICAgICAgICAg
ICAgICAgICAgICAgInJlZ2lvbiI6ICJyZWdpb25PbmUiDQogICAgICAgICAgICAg
ICAgICAgIH0sDQogICAgICAgICAgICAgICAgICAgIHsNCiAgICAgICAgICAgICAg
ICAgICAgICAgICJpZCI6ICIxZGYwYjQ0ZDkyNjM0ZDU5YmQwZTBkNjBjZjdjZTQz
MiIsDQogICAgICAgICAgICAgICAgICAgICAgICAiaW50ZXJmYWNlIjogInB1Ymxp
YyIsDQogICAgICAgICAgICAgICAgICAgICAgICAidXJsIjogImh0dHA6Ly8xMjcu
MC4wLjE6ODc3NC92MS4xLzY0YjZmM2ZiY2M1MzQzNWU4YTYwZmNmODliYjY2MTdh
IiwNCiAgICAgICAgICAgICAgICAgICAgICAgICJyZWdpb24iOiAicmVnaW9uT25l
Ig0KICAgICAgICAgICAgICAgICAgICB9DQogICAgICAgICAgICAgICAgXSwNCiAg
ICAgICAgICAgICAgICAiaWQiOiAiMmY0MDRmZGI4OTE1NGM1ODllZmJjMTA3MjZi
MDI5ZWMiLA0KICAgICAgICAgICAgICAgICJ0eXBlIjogImNvbXB1dGUiLA0KICAg
ICAgICAgICAgICAgICJuYW1lIjogIm5vdmEiDQogICAgICAgICAgICB9LA0KICAg
ICAgICAgICAgew0KICAgICAgICAgICAgICAgICJlbmRwb2ludHMiOiBbDQogICAg
ICAgICAgICAgICAgICAgIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICJpZCI6
ICJhNDUwMWUxNDFhNGI0ZTE0YmYyODJlN2JmZmQ4MWRjNSIsDQogICAgICAgICAg
ICAgICAgICAgICAgICAiaW50ZXJmYWNlIjogImFkbWluIiwNCiAgICAgICAgICAg
ICAgICAgICAgICAgICJ1cmwiOiAiaHR0cDovLzEyNy4wLjAuMTozNTM1Ny92MyIs
DQogICAgICAgICAgICAgICAgICAgICAgICAicmVnaW9uIjogIlJlZ2lvbk9uZSIN
CiAgICAgICAgICAgICAgICAgICAgfSwNCiAgICAgICAgICAgICAgICAgICAgew0K
ICAgICAgICAgICAgICAgICAgICAgICAgImlkIjogIjNkMTdlMzIyN2JmYzQ0ODNi
NThkZTVlYWE1ODRlMzYwIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICJpbnRl
cmZhY2UiOiAiaW50ZXJuYWwiLA0KICAgICAgICAgICAgICAgICAgICAgICAgInVy
bCI6ICJodHRwOi8vMTI3LjAuMC4xOjM1MzU3L3YzIiwNCiAgICAgICAgICAgICAg
ICAgICAgICAgICJyZWdpb24iOiAiUmVnaW9uT25lIg0KICAgICAgICAgICAgICAg
ICAgICB9LA0KICAgICAgICAgICAgICAgICAgICB7DQogICAgICAgICAgICAgICAg
ICAgICAgICAiaWQiOiAiOGNkNGI5NTcwOTBmNGNhNTg0MmEyMmU5YTc0MDk5Y2Qi
LA0KICAgICAgICAgICAgICAgICAgICAgICAgImludGVyZmFjZSI6ICJwdWJsaWMi
LA0KICAgICAgICAgICAgICAgICAgICAgICAgInVybCI6ICJodHRwOi8vMTI3LjAu
MC4xOjUwMDAvdjMiLA0KICAgICAgICAgICAgICAgICAgICAgICAgInJlZ2lvbiI6
ICJSZWdpb25PbmUiDQogICAgICAgICAgICAgICAgICAgIH0NCiAgICAgICAgICAg
ICAgICBdLA0KICAgICAgICAgICAgICAgICJpZCI6ICJjNWQ5MjZkNTY2NDI0ZTRm
YmE0ZjgwYzM3OTE2Y2RlNSIsDQogICAgICAgICAgICAgICAgInR5cGUiOiAiaWRl
bnRpdHkiLA0KICAgICAgICAgICAgICAgICJuYW1lIjogImtleXN0b25lIg0KICAg
ICAgICAgICAgfQ0KICAgICAgICBdLA0KICAgICAgICAiaXNzdWVkX2F0IjogIjIw
MDItMDEtMThUMjE6MTQ6MDdaIiwNCiAgICAgICAgImV4cGlyZXNfYXQiOiAiMjAz
OC0wMS0xOFQyMToxNDowN1oiLA0KICAgICAgICAicHJvamVjdCI6IHsNCiAgICAg
ICAgICAgICJlbmFibGVkIjogdHJ1ZSwNCiAgICAgICAgICAgICJkZXNjcmlwdGlv
biI6IG51bGwsDQogICAgICAgICAgICAibmFtZSI6ICJ0ZW5hbnRfbmFtZTEiLA0K
ICAgICAgICAgICAgImlkIjogInRlbmFudF9pZDEiLA0KICAgICAgICAgICAgImRv
bWFpbiI6IHsNCiAgICAgICAgICAgICAgICAiaWQiOiAiZG9tYWluX2lkMSIsDQog
ICAgICAgICAgICAgICAgIm5hbWUiOiAiZG9tYWluX25hbWUxIg0KICAgICAgICAg
ICAgfQ0KICAgICAgICB9LA0KICAgICAgICAidXNlciI6IHsNCiAgICAgICAgICAg
ICJuYW1lIjogInJldm9rZWRfdXNlcm5hbWUxIiwNCiAgICAgICAgICAgICJpZCI6
ICJyZXZva2VkX3VzZXJfaWQxIiwNCiAgICAgICAgICAgICJkb21haW4iOiB7DQog
ICAgICAgICAgICAgICAgImlkIjogImRvbWFpbl9pZDEiLA0KICAgICAgICAgICAg
ICAgICJuYW1lIjogImRvbWFpbl9uYW1lMSINCiAgICAgICAgICAgIH0NCiAgICAg
ICAgfSwNCiAgICAgICAgInJvbGVzIjogWw0KICAgICAgICAgICAgew0KICAgICAg
ICAgICAgICAgICJpZCI6ICJmMDNmZGE4ZjhhMzI0OWIyYTcwZmIxZjE3NmE3YjYz
MSIsDQogICAgICAgICAgICAgICAgIm5hbWUiOiAicm9sZTEiDQogICAgICAgICAg
ICB9LA0KICAgICAgICAgICAgew0KICAgICAgICAgICAgICAgICJpZCI6ICJmMDNm
ZGE4ZjhhMzI0OWIyYTcwZmIxZjE3NmE3YjYzMSIsDQogICAgICAgICAgICAgICAg
Im5hbWUiOiAicm9sZTIiDQogICAgICAgICAgICB9DQogICAgICAgIF0sDQogICAg
ICAgICJtZXRob2RzIjogWw0KICAgICAgICAgICAgInBhc3N3b3JkIg0KICAgICAg
ICBdDQogICAgfQ0KfQ0KMYIByjCCAcYCAQEwgaQwgZ4xCjAIBgNVBAUTATUxCzAJ
BgNVBAYTAlVTMQswCQYDVQQIEwJDQTESMBAGA1UEBxMJU3Vubnl2YWxlMRIwEAYD
VQQKEwlPcGVuU3RhY2sxETAPBgNVBAsTCEtleXN0b25lMSUwIwYJKoZIhvcNAQkB
FhZrZXlzdG9uZUBvcGVuc3RhY2sub3JnMRQwEgYDVQQDEwtTZWxmIFNpZ25lZAIB
ETAHBgUrDgMCGjANBgkqhkiG9w0BAQEFAASCAQCy1xOK1+nQy8tL3fORdWkcp0Y5
88cNgl4sXmJOE1TOOEauMyVWE188gtxHelVDCFWr8kICALvAnPX0UbIhoEaxscey
mvcUazUMP2WWSsBMgSXBfbl6amTZp5KMgpMmAuGjP1xok3yvOecEF6Szh8yE3Q5O
sNEKsMI5UiJTDU7WWSUp1Zs7E4UvFjAepZGhIQWOCxSvEnrl3Mfw1f7HWKDBlijR
4XnPqJPiTmYLjzyDmi31GOHWZM4nZxShHfidLblPV4AyA/gsCh27/cZxYW/Q+cyL
wfQogs4g7XfNgLdDHlbvv7NCS06RhydhLeiqNUcCp4hnZZC16KDPWzJ2Ql5y
-----END CMS-----

View File

@@ -1 +0,0 @@
PKIZ_eJydWEt3ozgT3etXfPucPgPCpOPFLHgZ47FEwDws7QxODELYTvzA8OunsJ3uPDrT6a9z-uSE4FLdqntvlfLtG_wzHdej_7PIrP_hGyKeR4qGTf7ZcK845tQIcmsDzx5sy7LHgWUEzsmKjLG5ip_tFbFcYVnWNnCt2ZM78zIN2YEzNhbwcBM7q9WT-dBOlAzvZVZ6t954V2ZpoizcYZW38PNoV-buqMu15TGvg3I-a1bIW0-OmZt0ntisUm1XLtKg9Euj5MLoeB0WvssGLCIttWVJakcjLi9Jyk604wXFHkakc8qpZZRZPdrzGZxiTdoMnySZTYZTy_zu1bLqg3s1awjmFYuK2nedjohAZ2JSINqZNROjmkQ5ZtGypIKcvLKBD-hFlsbnbPJ6uOORVz4myg4OkA9jc5vXSTfHIwWdowuvId1qT2xnT6IiJsK5JVFwS-zl4hxsbUJWW8m0Ht6rrNahRJD6YTkabrl9gcLd4Z6l8tC_AAXdcusMq8qwWixS_RFq9CZDdC7Y9UNzfH548taXVCF4CQU-n7YcT1Q-6z8YyhzTdjE3lUU6PJwhZOtkl1lvcS_d5MBSXXkXVLB5WGTucP3SNcRTvcrd4TZbh69aqSt8PqlZSuWlA6Mq62Gd65G02QXWZjkOG4BwdySRp02FcSDW4OSLlUY7dlwK50hFWNKaAR_g5C7uqE1UHhUFFdA5zAZ-OikRFUAKfCkgtGbd47pUeCI5lsesGh6AH33614J6HROJpFGssJrWiESOwoWn-DaVQJATq2ONRYZKbF69ItMBatLyeiSuZOshyxxqhgBPH13N6-ZcvMU4VHJ7c5x2TkvbQXOGFm0GtMvxVGOnaccUJngNrCwZJipQOehoGgPfWcMhJR84zwT8KloWl6JdoZQXmvN0uc2wfp_V8Rk2ehEPjcKC1UHLXaJQAV_upKAuiEdUJxoFei8qntKiJ9wj8KEnWQ8D5TUvGL5yfpwAcaT4Vbs-6xb6ars-6xb6j3aB9h2Np6B71zsxUSkkqrAPwSkGiDbAgQ5CG6Xk0K7eXUBdeu5eqQwSXqaqvAjnqj4Ra6BQAR0QELz1o0BDBCIRDF9dIf2UQh8czDpavPeEHwF7TYDVvUgA_b8aeCkq-lnVClLyeg38Ca11RITXVxf4XamkqxRqQyA71llNFD_lFbVzBdyq5eWvaY1e8_qLtNaBXG1P6x4a-h1Vf9q819CIdawOawpaoG5Sg0MXqPd4kgJVUw_TblJCb99Q9XdMRZ9T9WtFRe_NgnZJDdQtaF_IKFBAxp0P06inNY8g7c7DPJIFu5IPvWbfIlULjt9iJ1EiiBgVLI0xE54GCh1w11FJHTdgPBj5bqwTu4AXyPsRt87c0aHHf60J2HzYZBjaOCb9gMn6OqH3hWJpuF-kg3Ow5XyyuzCyUJZj43ba3p2IyPsaQUudW9_ONUStISazwQer-qpTod_2Pw1LbsuaRib0n2nU5iBjULDtnMC9OgSTGR6Agbif9_8qMphUzQdo6DNsX4WG_tSFX6D5aQwLB1EQrckA3KWD_oL7SsEE0blI4Luh-FFR-v1i8R_URn_mwkFP7QOZ3WHwSTA2gADzTdCIgOaTfrRhWKIEFyvwAxCXcDR2ofoVyuC68lx0EWFdoremOaq4MEtqhxWkDj4ZVgAL2mhK4gYQHDwTUwm233prdXmeTMuxbK7UFbDGNMt5-M6JJzW1DQVWvtK3-ykVQsYrHey-ZJ3TwJbmgUiM1k8T8d6Js3qI2Y8JnRz42Dz2nLgsnfuzvaF3Y7vgrrqFFn7F2jqonYpoC4RpPxYqflWoN5BqR6GRceqnEklhMjERShKFvecNSL9c1Lzm9qrnufoyRD7Oi4szg_R36Gsc6Ckca-DE3Xu29p44-4yuBAcwM2LYi70-MxiowYBgT_XdUNI0KVgUDxB1YZwL44-c-HWm6G2qIBDbALqSj04sfz3eAII3YDhQ-tFGO0MnLsgXO6pvBy2LvLZ3YNBA44PQuPVxDYAdKZSQ9nY5rt7gZ11ypjO3Y9BE0AJUYGO_NzFQLwH7B1bWtEI8it-78TOfy27p9qm-nJh0fO5dV_3wmKWj7cuV6MeW9nNjl7BgnjGChanXvl8_JIfnZ5cF9HIoernm8Dk_LnBSzbX-tMn1xddT68M757sDuq7xxTKFOvQXj-vQ8OT29kFu2lRueZ4Eg0jb1knCcV5vR7MkDC_0pjaC6WcHmCrJeHtPZipL0p0aq6HO1vn5Wge0hWteIvloWCwv87OFVrdT0MM0cgYosT3ov6P4wtBS2EIeI9cy8k2zWo1dY-WYxHMr-P9Agk1jGcxOgmDkNDAbg11jBcxG8MB1mkkSd86UGJVrqLFjmcQKFOfkCCMwVzQxjTyyEqpmta4vQUCgxBkxjfO7yCrIJNJMmUmqgNqeSeg0dnM-aeo0xfRHSyNHEov8uPLCjXdihCxFUFU916BNdWJkfaD1JdC0Hra8c2JieueTjBOZxjjZ8dKMFunywFO4V8NhyGzY6J9mYBvFprGD15dwxzQDA-7TjtGOxMIPR9FjE37XlON2OLSOB7sjD972Dj3vk-d2KBcPs-i21Uf3T1YNbT7l2DUf13g_cx-GOztaP5Uj9n3HlcmoCOybMtQmpSKD5BQhO1wbnuf8VQxq81BFdydp7pVVfCrNm25YBtSST48zfdt8V7ARb2Ot3DaMTjl_rr2tk_ylofomW_jOatpsRxv_xr9ZVVYs75RsIBUdVHqzte-8gzmYPVZW87zOHruTtDb5Kdb8h0X2qHkomd3WT8Pd6GCnj3KCT-U8NZXn440q0yM7TXLn4K6G08aLk0qtd_e3M3pj4XEi56UbYWMNV1823R3Nm6ySHdnj1nkw_MhV8NPqefKPqdLh-AFnnXW_N-82BSmq2VgeqvvWHAyCv_9G5z-CONT--QeRfwEmaLr4

View File

@@ -1,140 +0,0 @@
{
"token": {
"methods": [
"password"
],
"roles": [
{
"id": "f03fda8f8a3249b2a70fb1f176a7b631",
"name": "role1"
},
{
"id": "f03fda8f8a3249b2a70fb1f176a7b631",
"name": "role2"
}
],
"issued_at": "2002-01-18T21:14:07Z",
"expires_at": "2038-01-18T21:14:07Z",
"audit_ids": ["VcxU2JYqT8OzfUVvrjEITQ", "qNUTIJntTzO1-XUk5STybw"],
"project": {
"id": "tenant_id1",
"domain": {
"id": "domain_id1",
"name": "domain_name1"
},
"enabled": true,
"description": null,
"name": "tenant_name1"
},
"catalog": [
{
"endpoints": [
{
"id": "3b5e554bcf114f2483e8a1be7a0506d1",
"interface": "admin",
"url": "http://127.0.0.1:8776/v1/64b6f3fbcc53435e8a60fcf89bb6617a",
"region": "regionOne"
},
{
"id": "54abd2dc463c4ba4a72915498f8ecad1",
"interface": "internal",
"url": "http://127.0.0.1:8776/v1/64b6f3fbcc53435e8a60fcf89bb6617a",
"region": "regionOne"
},
{
"id": "70a7efa4b1b941968357cc43ae1419ee",
"interface": "public",
"url": "http://127.0.0.1:8776/v1/64b6f3fbcc53435e8a60fcf89bb6617a",
"region": "regionOne"
}
],
"id": "5707c3fc0a294703a3c638e9cf6a6c3a",
"type": "volume",
"name": "volume"
},
{
"endpoints": [
{
"id": "92217a3b95394492859bc49fd474382f",
"interface": "admin",
"url": "http://127.0.0.1:9292/v1",
"region": "regionOne"
},
{
"id": "f20563bdf66f4efa8a1f11d99b672be1",
"interface": "internal",
"url": "http://127.0.0.1:9292/v1",
"region": "regionOne"
},
{
"id": "375f9ba459a447738fb60fe5fc26e9aa",
"interface": "public",
"url": "http://127.0.0.1:9292/v1",
"region": "regionOne"
}
],
"id": "15c21aae6b274a8da52e0a068e908aac",
"type": "image",
"name": "glance"
},
{
"endpoints": [
{
"id": "edbd9f50f66746ae9ed11dc3b1ae35da",
"interface": "admin",
"url": "http://127.0.0.1:8774/v1.1/64b6f3fbcc53435e8a60fcf89bb6617a",
"region": "regionOne"
},
{
"id": "9e03c46c80a34a159cb39f5cb0498b92",
"interface": "internal",
"url": "http://127.0.0.1:8774/v1.1/64b6f3fbcc53435e8a60fcf89bb6617a",
"region": "regionOne"
},
{
"id": "1df0b44d92634d59bd0e0d60cf7ce432",
"interface": "public",
"url": "http://127.0.0.1:8774/v1.1/64b6f3fbcc53435e8a60fcf89bb6617a",
"region": "regionOne"
}
],
"id": "2f404fdb89154c589efbc10726b029ec",
"type": "compute",
"name": "nova"
},
{
"endpoints": [
{
"id": "a4501e141a4b4e14bf282e7bffd81dc5",
"interface": "admin",
"url": "http://127.0.0.1:35357/v3",
"region": "RegionOne"
},
{
"id": "3d17e3227bfc4483b58de5eaa584e360",
"interface": "internal",
"url": "http://127.0.0.1:35357/v3",
"region": "RegionOne"
},
{
"id": "8cd4b957090f4ca5842a22e9a74099cd",
"interface": "public",
"url": "http://127.0.0.1:5000/v3",
"region": "RegionOne"
}
],
"id": "c5d926d566424e4fba4f80c37916cde5",
"type": "identity",
"name": "keystone"
}
],
"user": {
"domain": {
"id": "domain_id1",
"name": "domain_name1"
},
"name": "user_name1",
"id": "user_id1"
}
}
}

View File

@@ -1,123 +0,0 @@
-----BEGIN CMS-----
MIIWmgYJKoZIhvcNAQcCoIIWizCCFocCAQExCTAHBgUrDgMCGjCCFKcGCSqGSIb3
DQEHAaCCFJgEghSUew0KICAgICJ0b2tlbiI6IHsNCiAgICAgICAgIm1ldGhvZHMi
OiBbDQogICAgICAgICAgICAicGFzc3dvcmQiDQogICAgICAgIF0sDQogICAgICAg
ICJyb2xlcyI6IFsNCiAgICAgICAgICAgIHsNCiAgICAgICAgICAgICAgICAiaWQi
OiAiZjAzZmRhOGY4YTMyNDliMmE3MGZiMWYxNzZhN2I2MzEiLA0KICAgICAgICAg
ICAgICAgICJuYW1lIjogInJvbGUxIg0KICAgICAgICAgICAgfSwNCiAgICAgICAg
ICAgIHsNCiAgICAgICAgICAgICAgICAiaWQiOiAiZjAzZmRhOGY4YTMyNDliMmE3
MGZiMWYxNzZhN2I2MzEiLA0KICAgICAgICAgICAgICAgICJuYW1lIjogInJvbGUy
Ig0KICAgICAgICAgICAgfQ0KICAgICAgICBdLA0KICAgICAgICAiaXNzdWVkX2F0
IjogIjIwMDItMDEtMThUMjE6MTQ6MDdaIiwNCiAgICAgICAgImV4cGlyZXNfYXQi
OiAiMjAzOC0wMS0xOFQyMToxNDowN1oiLA0KICAgICAgICAicHJvamVjdCI6IHsN
CiAgICAgICAgICAgICJpZCI6ICJ0ZW5hbnRfaWQxIiwNCiAgICAgICAgICAgICJk
b21haW4iOiB7DQogICAgICAgICAgICAgICAgImlkIjogImRvbWFpbl9pZDEiLA0K
ICAgICAgICAgICAgICAgICJuYW1lIjogImRvbWFpbl9uYW1lMSINCiAgICAgICAg
ICAgIH0sDQogICAgICAgICAgICAiZW5hYmxlZCI6IHRydWUsDQogICAgICAgICAg
ICAiZGVzY3JpcHRpb24iOiBudWxsLA0KICAgICAgICAgICAgIm5hbWUiOiAidGVu
YW50X25hbWUxIg0KICAgICAgICB9LA0KICAgICAgICAiY2F0YWxvZyI6IFsNCiAg
ICAgICAgICAgIHsNCiAgICAgICAgICAgICAgICAiZW5kcG9pbnRzIjogWw0KICAg
ICAgICAgICAgICAgICAgICB7DQogICAgICAgICAgICAgICAgICAgICAgICAiaWQi
OiAiM2I1ZTU1NGJjZjExNGYyNDgzZThhMWJlN2EwNTA2ZDEiLA0KICAgICAgICAg
ICAgICAgICAgICAgICAgImludGVyZmFjZSI6ICJhZG1pbiIsDQogICAgICAgICAg
ICAgICAgICAgICAgICAidXJsIjogImh0dHA6Ly8xMjcuMC4wLjE6ODc3Ni92MS82
NGI2ZjNmYmNjNTM0MzVlOGE2MGZjZjg5YmI2NjE3YSIsDQogICAgICAgICAgICAg
ICAgICAgICAgICAicmVnaW9uIjogInJlZ2lvbk9uZSINCiAgICAgICAgICAgICAg
ICAgICAgfSwNCiAgICAgICAgICAgICAgICAgICAgew0KICAgICAgICAgICAgICAg
ICAgICAgICAgImlkIjogIjU0YWJkMmRjNDYzYzRiYTRhNzI5MTU0OThmOGVjYWQx
IiwNCiAgICAgICAgICAgICAgICAgICAgICAgICJpbnRlcmZhY2UiOiAiaW50ZXJu
YWwiLA0KICAgICAgICAgICAgICAgICAgICAgICAgInVybCI6ICJodHRwOi8vMTI3
LjAuMC4xOjg3NzYvdjEvNjRiNmYzZmJjYzUzNDM1ZThhNjBmY2Y4OWJiNjYxN2Ei
LA0KICAgICAgICAgICAgICAgICAgICAgICAgInJlZ2lvbiI6ICJyZWdpb25PbmUi
DQogICAgICAgICAgICAgICAgICAgIH0sDQogICAgICAgICAgICAgICAgICAgIHsN
CiAgICAgICAgICAgICAgICAgICAgICAgICJpZCI6ICI3MGE3ZWZhNGIxYjk0MTk2
ODM1N2NjNDNhZTE0MTllZSIsDQogICAgICAgICAgICAgICAgICAgICAgICAiaW50
ZXJmYWNlIjogInB1YmxpYyIsDQogICAgICAgICAgICAgICAgICAgICAgICAidXJs
IjogImh0dHA6Ly8xMjcuMC4wLjE6ODc3Ni92MS82NGI2ZjNmYmNjNTM0MzVlOGE2
MGZjZjg5YmI2NjE3YSIsDQogICAgICAgICAgICAgICAgICAgICAgICAicmVnaW9u
IjogInJlZ2lvbk9uZSINCiAgICAgICAgICAgICAgICAgICAgfQ0KICAgICAgICAg
ICAgICAgIF0sDQogICAgICAgICAgICAgICAgImlkIjogIjU3MDdjM2ZjMGEyOTQ3
MDNhM2M2MzhlOWNmNmE2YzNhIiwNCiAgICAgICAgICAgICAgICAidHlwZSI6ICJ2
b2x1bWUiLA0KICAgICAgICAgICAgICAgICJuYW1lIjogInZvbHVtZSINCiAgICAg
ICAgICAgIH0sDQogICAgICAgICAgICB7DQogICAgICAgICAgICAgICAgImVuZHBv
aW50cyI6IFsNCiAgICAgICAgICAgICAgICAgICAgew0KICAgICAgICAgICAgICAg
ICAgICAgICAgImlkIjogIjkyMjE3YTNiOTUzOTQ0OTI4NTliYzQ5ZmQ0NzQzODJm
IiwNCiAgICAgICAgICAgICAgICAgICAgICAgICJpbnRlcmZhY2UiOiAiYWRtaW4i
LA0KICAgICAgICAgICAgICAgICAgICAgICAgInVybCI6ICJodHRwOi8vMTI3LjAu
MC4xOjkyOTIvdjEiLA0KICAgICAgICAgICAgICAgICAgICAgICAgInJlZ2lvbiI6
ICJyZWdpb25PbmUiDQogICAgICAgICAgICAgICAgICAgIH0sDQogICAgICAgICAg
ICAgICAgICAgIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICJpZCI6ICJmMjA1
NjNiZGY2NmY0ZWZhOGExZjExZDk5YjY3MmJlMSIsDQogICAgICAgICAgICAgICAg
ICAgICAgICAiaW50ZXJmYWNlIjogImludGVybmFsIiwNCiAgICAgICAgICAgICAg
ICAgICAgICAgICJ1cmwiOiAiaHR0cDovLzEyNy4wLjAuMTo5MjkyL3YxIiwNCiAg
ICAgICAgICAgICAgICAgICAgICAgICJyZWdpb24iOiAicmVnaW9uT25lIg0KICAg
ICAgICAgICAgICAgICAgICB9LA0KICAgICAgICAgICAgICAgICAgICB7DQogICAg
ICAgICAgICAgICAgICAgICAgICAiaWQiOiAiMzc1ZjliYTQ1OWE0NDc3MzhmYjYw
ZmU1ZmMyNmU5YWEiLA0KICAgICAgICAgICAgICAgICAgICAgICAgImludGVyZmFj
ZSI6ICJwdWJsaWMiLA0KICAgICAgICAgICAgICAgICAgICAgICAgInVybCI6ICJo
dHRwOi8vMTI3LjAuMC4xOjkyOTIvdjEiLA0KICAgICAgICAgICAgICAgICAgICAg
ICAgInJlZ2lvbiI6ICJyZWdpb25PbmUiDQogICAgICAgICAgICAgICAgICAgIH0N
CiAgICAgICAgICAgICAgICBdLA0KICAgICAgICAgICAgICAgICJpZCI6ICIxNWMy
MWFhZTZiMjc0YThkYTUyZTBhMDY4ZTkwOGFhYyIsDQogICAgICAgICAgICAgICAg
InR5cGUiOiAiaW1hZ2UiLA0KICAgICAgICAgICAgICAgICJuYW1lIjogImdsYW5j
ZSINCiAgICAgICAgICAgIH0sDQogICAgICAgICAgICB7DQogICAgICAgICAgICAg
ICAgImVuZHBvaW50cyI6IFsNCiAgICAgICAgICAgICAgICAgICAgew0KICAgICAg
ICAgICAgICAgICAgICAgICAgImlkIjogImVkYmQ5ZjUwZjY2NzQ2YWU5ZWQxMWRj
M2IxYWUzNWRhIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICJpbnRlcmZhY2Ui
OiAiYWRtaW4iLA0KICAgICAgICAgICAgICAgICAgICAgICAgInVybCI6ICJodHRw
Oi8vMTI3LjAuMC4xOjg3NzQvdjEuMS82NGI2ZjNmYmNjNTM0MzVlOGE2MGZjZjg5
YmI2NjE3YSIsDQogICAgICAgICAgICAgICAgICAgICAgICAicmVnaW9uIjogInJl
Z2lvbk9uZSINCiAgICAgICAgICAgICAgICAgICAgfSwNCiAgICAgICAgICAgICAg
ICAgICAgew0KICAgICAgICAgICAgICAgICAgICAgICAgImlkIjogIjllMDNjNDZj
ODBhMzRhMTU5Y2IzOWY1Y2IwNDk4YjkyIiwNCiAgICAgICAgICAgICAgICAgICAg
ICAgICJpbnRlcmZhY2UiOiAiaW50ZXJuYWwiLA0KICAgICAgICAgICAgICAgICAg
ICAgICAgInVybCI6ICJodHRwOi8vMTI3LjAuMC4xOjg3NzQvdjEuMS82NGI2ZjNm
YmNjNTM0MzVlOGE2MGZjZjg5YmI2NjE3YSIsDQogICAgICAgICAgICAgICAgICAg
ICAgICAicmVnaW9uIjogInJlZ2lvbk9uZSINCiAgICAgICAgICAgICAgICAgICAg
fSwNCiAgICAgICAgICAgICAgICAgICAgew0KICAgICAgICAgICAgICAgICAgICAg
ICAgImlkIjogIjFkZjBiNDRkOTI2MzRkNTliZDBlMGQ2MGNmN2NlNDMyIiwNCiAg
ICAgICAgICAgICAgICAgICAgICAgICJpbnRlcmZhY2UiOiAicHVibGljIiwNCiAg
ICAgICAgICAgICAgICAgICAgICAgICJ1cmwiOiAiaHR0cDovLzEyNy4wLjAuMTo4
Nzc0L3YxLjEvNjRiNmYzZmJjYzUzNDM1ZThhNjBmY2Y4OWJiNjYxN2EiLA0KICAg
ICAgICAgICAgICAgICAgICAgICAgInJlZ2lvbiI6ICJyZWdpb25PbmUiDQogICAg
ICAgICAgICAgICAgICAgIH0NCiAgICAgICAgICAgICAgICBdLA0KICAgICAgICAg
ICAgICAgICJpZCI6ICIyZjQwNGZkYjg5MTU0YzU4OWVmYmMxMDcyNmIwMjllYyIs
DQogICAgICAgICAgICAgICAgInR5cGUiOiAiY29tcHV0ZSIsDQogICAgICAgICAg
ICAgICAgIm5hbWUiOiAibm92YSINCiAgICAgICAgICAgIH0sDQogICAgICAgICAg
ICB7DQogICAgICAgICAgICAgICAgImVuZHBvaW50cyI6IFsNCiAgICAgICAgICAg
ICAgICAgICAgew0KICAgICAgICAgICAgICAgICAgICAgICAgImlkIjogImE0NTAx
ZTE0MWE0YjRlMTRiZjI4MmU3YmZmZDgxZGM1IiwNCiAgICAgICAgICAgICAgICAg
ICAgICAgICJpbnRlcmZhY2UiOiAiYWRtaW4iLA0KICAgICAgICAgICAgICAgICAg
ICAgICAgInVybCI6ICJodHRwOi8vMTI3LjAuMC4xOjM1MzU3L3YzIiwNCiAgICAg
ICAgICAgICAgICAgICAgICAgICJyZWdpb24iOiAiUmVnaW9uT25lIg0KICAgICAg
ICAgICAgICAgICAgICB9LA0KICAgICAgICAgICAgICAgICAgICB7DQogICAgICAg
ICAgICAgICAgICAgICAgICAiaWQiOiAiM2QxN2UzMjI3YmZjNDQ4M2I1OGRlNWVh
YTU4NGUzNjAiLA0KICAgICAgICAgICAgICAgICAgICAgICAgImludGVyZmFjZSI6
ICJpbnRlcm5hbCIsDQogICAgICAgICAgICAgICAgICAgICAgICAidXJsIjogImh0
dHA6Ly8xMjcuMC4wLjE6MzUzNTcvdjMiLA0KICAgICAgICAgICAgICAgICAgICAg
ICAgInJlZ2lvbiI6ICJSZWdpb25PbmUiDQogICAgICAgICAgICAgICAgICAgIH0s
DQogICAgICAgICAgICAgICAgICAgIHsNCiAgICAgICAgICAgICAgICAgICAgICAg
ICJpZCI6ICI4Y2Q0Yjk1NzA5MGY0Y2E1ODQyYTIyZTlhNzQwOTljZCIsDQogICAg
ICAgICAgICAgICAgICAgICAgICAiaW50ZXJmYWNlIjogInB1YmxpYyIsDQogICAg
ICAgICAgICAgICAgICAgICAgICAidXJsIjogImh0dHA6Ly8xMjcuMC4wLjE6NTAw
MC92MyIsDQogICAgICAgICAgICAgICAgICAgICAgICAicmVnaW9uIjogIlJlZ2lv
bk9uZSINCiAgICAgICAgICAgICAgICAgICAgfQ0KICAgICAgICAgICAgICAgIF0s
DQogICAgICAgICAgICAgICAgImlkIjogImM1ZDkyNmQ1NjY0MjRlNGZiYTRmODBj
Mzc5MTZjZGU1IiwNCiAgICAgICAgICAgICAgICAidHlwZSI6ICJpZGVudGl0eSIs
DQogICAgICAgICAgICAgICAgIm5hbWUiOiAia2V5c3RvbmUiDQogICAgICAgICAg
ICB9DQogICAgICAgIF0sDQogICAgICAgICJ1c2VyIjogew0KICAgICAgICAgICAg
ImRvbWFpbiI6IHsNCiAgICAgICAgICAgICAgICAiaWQiOiAiZG9tYWluX2lkMSIs
DQogICAgICAgICAgICAgICAgIm5hbWUiOiAiZG9tYWluX25hbWUxIg0KICAgICAg
ICAgICAgfSwNCiAgICAgICAgICAgICJuYW1lIjogInVzZXJfbmFtZTEiLA0KICAg
ICAgICAgICAgImlkIjogInVzZXJfaWQxIg0KICAgICAgICB9DQogICAgfQ0KfQ0K
MYIByjCCAcYCAQEwgaQwgZ4xCjAIBgNVBAUTATUxCzAJBgNVBAYTAlVTMQswCQYD
VQQIEwJDQTESMBAGA1UEBxMJU3Vubnl2YWxlMRIwEAYDVQQKEwlPcGVuU3RhY2sx
ETAPBgNVBAsTCEtleXN0b25lMSUwIwYJKoZIhvcNAQkBFhZrZXlzdG9uZUBvcGVu
c3RhY2sub3JnMRQwEgYDVQQDEwtTZWxmIFNpZ25lZAIBETAHBgUrDgMCGjANBgkq
hkiG9w0BAQEFAASCAQCPCzpknZOfDONpHDWGrYTeyirjGGjrJem2EF2qsJ4K1x/V
guNLX1AfRnRUC95wSpGS5VCQ+OSfSFmLjJQOnMqLZ1L2MkVfn0CIkqig19sgRZ+O
hpi+0TpJ6XlCWRERJEICCOAHZ/M2iiiVFbFkIGtaJLw3HcXFreV+nEBuQSeIGH/H
FjnmocYu9vy612YT47HcyQKNMaku3QBLzFTSTiGkS4ft9yT2pNMbHZsMmysaRKWl
SfuA/DZHT6zi5D4lkxDBCexf3JAw4kOQSf/dirfDUKmIy4VPeAOuO1u86hN/coIS
JvgAJGOVUxtZCQ9256dUvKa1pLpQAgW/Ok3oPulS
-----END CMS-----

View File

@@ -1 +0,0 @@
PKIZ_eJylWEl34rwS3etXvH2fPu0BElh6wthBcmw8IO2wnWDLNiRh8PDrXwlId4bu70u_lyxycKBUt-reWyW-f4cf3bId8h8DL8WL7wg7Ds5b6t7tmFOcMqL5mbGDZ2vTMEzbNzTf6oxQm-ub6MXcYMPmhmHsfNtYPttLJ1WR6VtzbQ0Pt5G12Tx1D70rpcqhTkvnxpnvyzSJpbU9rbIeXs_2ZWbPhkzNT1njl6tlu0HO1j2ldjw4fLdJ1H25TvzSK7WScW1gTVB4Nh3REPfErEvcWCq2WYkT2pGBFURxFIQHq1wYWpk2swNbwimG26dKV-OlO10Y-q3T1JUI7jS0xQqraFg0nm0NmPtjyt0CkUFvKJ81OMwUGuYl4bhzyhY-MC7SJDpnkzXTPQud8jGW9nBA_TDXn7ImHlbKTELn6Nxp8bA5YNM64LCIMLducOjfYDNfn4NtdcjqqaaqgCeyCk5pMnsSdUKiUD9x29MDTerjSqkrvHTEaUeayPUFwvVD9fT87AJRKxFLxgVtupoZoupBnyeR-ODT-bXhSuL_6TZ4hEM-Qcvt-IhoMpZWyvnh9Q1BnSmkX690aZ1Mj-L0dBvv0_kZP6eroEjt6fa1ayKDKrOnT3DKm1aOJbZyG5qQa_qzKgVol3rEfXrJbpfPgxZ55eSEQ0ddcO2IjVHn8Y1KBnrKuXUiPChJQ4EPcPIQDcTEMguLgnDonEJHXuKWiHAghXLhArRm-5o2EKxmSn1Kq-mRXQp6rYszUB7XJIwk2pAG4dCSGHckzyQ1EKSjTaTSUJOxyao3ZDpCwXrWzPiVbAJynUFBEeAR0eWsac-VXc8DKTN3p8Vg9aQftWdo4W5EhkxZqLRbDFSinDXAypIqWAYq-wNJIuA7bRmk5AHnKYd_hXlxKdoVSnmhOUvyp1QZ36dNdIaNXklEwgD44PfMxhLh8Gu7BbFBPLzqSOiPhahYQgpmWuUjqBBUe4aBsoYVVLlyfh6XqV3z37XrT91CX23Xn7qF_qFdoH1LZQno3nY6yisJh5XiQXCiAEQT4EAHoY11zaBdwl2cbTDO7CvPQcK5ENKZ3ldP4JEKCuXQAQ7Bey_0VYQhElbgdyhqLyHQB0uhAyk-Cec14BY0AQp-lQD6XzXwWlT0q6oVpOQIDfwNrccIc0dUF_hdyXioJGJCIDMa0wZLXsIqYmYSuFXPyt_TGr3l9RdpPQZy9YLWAhr6N6r-snmnJSEdaBM0BLRA7LgBhy6Q8HicAFUTRyGDW0Jv31H135iK_kzVrxUVfTQLMsQNULcgopChL4GMBw-mkaA1CyHtwVFYWBf0Sj70ln3rRC6Y8h47DmOO-aygSaRQ7qig0BGzLRk3UQvGoyDPjsbYLOAN-OOI26b27CjwX2tSp03Qpgq0cY7FgElFndDHQtEkOKyT0TlYvnL3F0YWUj7Xbhb9pMM8EzWCllo3npmpiBhTBS9Hn6zqq06F_rX_SVAys25IqEP_qUpMBjIGBZtWB-41IJjM8AAMxP5z_68ig5nYfoKG_oTtq9DQ37rwKzQviWDhwBIiDR6BuwzQX3DfmlOOx4zH8FeTvLAoPbFY_AO10d-5sC-ofcTLiQI-CcYGEGC-cRJi0HwsRpsCSxRnfAN-AOLilkovVL9CGV1XnosuQmVco_emOasY10tiBhWkDj4ZVAAL2qjX2PYhOHimQmqw_d7Zyvl5MuXzur1Sl6eK3Oar4IMTuw0xNQlWvtIzxZQKIOPNGOy-pIPVwpbmgEi03kti_tGJ02aq0J8TOj6yuX4SnLgsnYezvaEPY7tgtiy2r69Y2wC1kxHpgTD950JFbwr1DlJjSSTUOjGVcAKTifKgxmEgPG-ExXLRsIaZG8Fz-XWIfJ4XF2cG6e_R1zggKByp4MTDR7YKT1z-ia5Y8WFmRLAXOyIzGKj-CCuO7NlBTZK4oGE0QsSGcc61v3Lit5mi96mCQEwN6Io_O3H9-_EGEJwRVXxJjDYyaGNsg3wVS_ZMv6eh0wsHBg20HgiNGZ_XANiRghrSfsrn1Tv8dIjPdGZmBJrwe4AKbBR7EwX1YrB_YGVDKsTC6KMbv7BVPeS2SPX1xHhgK-fTqi9ajP6fVV8ciq6nypkS9--39ivzzqe7l3V_e97YizwByLPpE4P5gMSAcGrGH2ZRv6zrLjaL-4eGxfGW9esqduPZdTZG4zi26juoV_RQTbpFXMTrIQ5RPK_LvHfP2l6vyJAncSXuQj-vQqbz-6vQVp5i6uioh4ukllFxwWw3a7_dsFFncM3RNyTWtSjUwqgzBs29vIZpWMch9vet4VMz9n0HWa1r-qG1xLpma3Jk6R12IzU-pttaoQlc_wKntbTzm--str7P4JoTqbAWK_vOCrV7dIm8Dw3rUD-sCNxax1DlqHXeXYcrHcbPr_ZG-kkEyiAQgkjHVHW3OPBba3M-ybTaQ8iSrnFm5IlBQAaIrHf3Z43om-q5qEobTVtJB_wzTVtCHfQyJe6ErBKVzTaj52ktJzfLb5v18ZmC1e32cXCI5qRZzslkMg823voBTYYq2m8GfXVblmo0dM3LjN3xqVkYCzr6sV1KyTqii1l6WDem8cKauebfL3da5hH1YX0kB3S3s8JH95Yrw_PIcQ-yjZenh4leSlL5GLTsx2EXLshhkdaPVjtbG_2tGo-Ml930B6tGP4y9h0aBP1TLYtZNb8udfW9NW0t2T1M6dLvxMdzcDMroZlOOlIexdFw6M4Ybgp-rKB-k7Y2fHb4hecCPk-Tbg_zUV3f7LNaH2_HtbLr99qwVnTxedF1u3DkvvgwjZpJr_SLQ_Uh6Zg-LZnFaqgnaNo8_3Nq_XZh-86yufGsepL68H-fDj6bFE6dcNhN0_rLDIuavLz7-Cz0ItdI=

View File

@@ -1 +0,0 @@
{"revoked": [{"expires": "2112-08-14T17:58:48Z", "id": "db98ed2af6c6707bec6dc6c6892789a0"}, {"expires": "2112-08-14T17:58:48Z", "id": "15ce05fd491b79791068ed80a9c7f5e7"}, {"expires": "2112-08-14T17:58:48Z", "id": "db98ed2af6c6707bec6dc6c6892789a0"}, {"expires": "2112-08-14T17:58:48Z", "id": "15ce05fd491b79791068ed80a9c7f5e7"}]}

View File

@@ -1,20 +0,0 @@
-----BEGIN CMS-----
MIIDTwYJKoZIhvcNAQcCoIIDQDCCAzwCAQExCTAHBgUrDgMCGjCCAVwGCSqGSIb3
DQEHAaCCAU0EggFJeyJyZXZva2VkIjogW3siZXhwaXJlcyI6ICIyMTEyLTA4LTE0
VDE3OjU4OjQ4WiIsICJpZCI6ICJkYjk4ZWQyYWY2YzY3MDdiZWM2ZGM2YzY4OTI3
ODlhMCJ9LCB7ImV4cGlyZXMiOiAiMjExMi0wOC0xNFQxNzo1ODo0OFoiLCAiaWQi
OiAiMTVjZTA1ZmQ0OTFiNzk3OTEwNjhlZDgwYTljN2Y1ZTcifSwgeyJleHBpcmVz
IjogIjIxMTItMDgtMTRUMTc6NTg6NDhaIiwgImlkIjogImRiOThlZDJhZjZjNjcw
N2JlYzZkYzZjNjg5Mjc4OWEwIn0sIHsiZXhwaXJlcyI6ICIyMTEyLTA4LTE0VDE3
OjU4OjQ4WiIsICJpZCI6ICIxNWNlMDVmZDQ5MWI3OTc5MTA2OGVkODBhOWM3ZjVl
NyJ9XX0xggHKMIIBxgIBATCBpDCBnjEKMAgGA1UEBRMBNTELMAkGA1UEBhMCVVMx
CzAJBgNVBAgTAkNBMRIwEAYDVQQHEwlTdW5ueXZhbGUxEjAQBgNVBAoTCU9wZW5T
dGFjazERMA8GA1UECxMIS2V5c3RvbmUxJTAjBgkqhkiG9w0BCQEWFmtleXN0b25l
QG9wZW5zdGFjay5vcmcxFDASBgNVBAMTC1NlbGYgU2lnbmVkAgERMAcGBSsOAwIa
MA0GCSqGSIb3DQEBAQUABIIBAGn4kryxJudTZYMf32gKnoNHeAXRb97CoCXiTgs2
gu/blX/fwMdrL8GLg2puYR07XBgjo56vMsD94ZIRyhcS1lFti9veQHt7Xp8kbR8l
nbx9fsOhMxUHLRnxioieA9T1ykP8ZvYV3hYCeXkIYhPgD4lAAAmNq99ZxBRS3csE
DP+Xz1+UYvT6Qm/NWRuj7WIjofneIB7gT6L5irsU0qtMCQeqI3dsP9GSsy4HJvBR
BBIzQ7fEMRCGTADbk4ml+6Dx+Jm5SO80NvinzxCjO3DbkcEG1pQ3RGVEn3gyzg2a
ssaRU4ycbYACA99K5UzCtSj8glGXFa1cnx42nSn2LbfJP1M=
-----END CMS-----

View File

@@ -1 +0,0 @@
PKIZ_eJx9VElzszgUvPMr5p5KBbPY5vAdtGAsYokAYr0ZbIvdSXDM8usHO1Uzl6lRlQ6v1dVPr6vrvb4uB5oWYX8h6j-KV4kSgvmQ2O_XlBT3nAE3R9cFczFCYB4QcM0RcbCHIvjGgiKrWvBwsJD_ZfkkUyXsmntwXMBANoXY2efJntI4vR-VsCbVVURqX6ZxMRxju8knsiaITJSb04ED7cBNWQqxqTpVoDmVq0Ul6QmyP1P0INp1UtVaGrlTEiVKMicqxacyjaiSWvRRaw4nquTgpqDINg4IbkgbarnVLD-gpVOCklbmSEt5cJA8sp07svm6cvBVdnbX8oBAeYzcUnoSeVilHKzS1pUdvivZXKsONwdWFU2KxZDwpmJKskp5Xl78QSxjNuc9_MzbcJYec5KKjJSTG8XiRrkXUJ6vGRdrhosjKQdB2ubpB2m90uEPUbtIq7RiVT5ITLGbZE7r5S6A0GmVa05kDqSTe7L_fwMf_kn_bSAZWcQaisM2xa5OI7KMlOuUA8WxwtrBsHAiqqZV2Ehsso04lkch9u9LJuAoCAQcwU-MYFeZ7xQIC6wCE3oUMm4eKKh_68X6MKSjhGZgQ8FCCAQHNYPUI4MJEhy67t4cGn6K9J9znBaZFYxmBdxf7pWjwBjSSOfSydpVx9n0KNg-ldFIia-Eeq5696wNRpuDCor6q6hLyxhkiFwz2rW35hwzOVP0RnKtp9L8FJr0e97m4w4D_7cT5WjFmsxKRKA0XdaGNRCPZrkF_d4BAzlKFMj_5HqJNQRuAODiBbA6rf6eRvvnxNOEXlRFvHdXtj-D2MuMDbqiuOSiVyTx85Y18dtloKfvw9Y6COXzJ_HkTQxFddXXd9pjQ0uJNxW5v2p2t9K4n939bRN_buvM2zZSl43GpXcKOgb7g9eN5bU8A4Ovpvpjm96TUC0SdI5rkhQfAmsNAKBlX4aRjtDz1bw3JfzxEs-rlyC587XbvrHI-6k20ZK7S3cmcCP4-qCX330gf90ocs9fRD31H4bl95O2t-_QkyAks7u5mNRDFgc4q7W2eVnj8cVudd_ZyuxedvOIKkdd3nLTWn26qmeFZqeKaRbKUer7oxdoU54lAAHDeNeDGd38aisaK94dV3k3akrnd8ohu9gfK_pHeu4hk-F_d9Lf2fB_ww==

View File

@@ -1,117 +0,0 @@
#!/usr/bin/python
# 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 json
import os
from keystoneclient.common import cms
from keystoneclient import utils
CURRENT_DIR = os.path.abspath(os.path.dirname(__file__))
def make_filename(*args):
return os.path.join(CURRENT_DIR, *args)
def generate_revocation_list():
REVOKED_TOKENS = ['auth_token_revoked', 'auth_v3_token_revoked']
revoked_list = []
for token in REVOKED_TOKENS:
with open(make_filename('cms', '%s.pkiz' % name), 'r') as f:
token_data = f.read()
id = utils.hash_signed_token(token_data.encode('utf-8'))
revoked_list.append({
'id': id,
"expires": "2112-08-14T17:58:48Z"
})
with open(make_filename('cms', '%s.pem' % name), 'r') as f:
pem_data = f.read()
token_data = cms.cms_to_token(pem_data).encode('utf-8')
id = utils.hash_signed_token(token_data)
revoked_list.append({
'id': id,
"expires": "2112-08-14T17:58:48Z"
})
revoked_json = json.dumps({"revoked": revoked_list})
with open(make_filename('cms', 'revocation_list.json'), 'w') as f:
f.write(revoked_json)
encoded = cms.pkiz_sign(revoked_json,
SIGNING_CERT_FILE_NAME,
SIGNING_KEY_FILE_NAME)
with open(make_filename('cms', 'revocation_list.pkiz'), 'w') as f:
f.write(encoded)
encoded = cms.cms_sign_data(revoked_json,
SIGNING_CERT_FILE_NAME,
SIGNING_KEY_FILE_NAME)
with open(make_filename('cms', 'revocation_list.pem'), 'w') as f:
f.write(encoded)
CA_CERT_FILE_NAME = make_filename('certs', 'cacert.pem')
SIGNING_CERT_FILE_NAME = make_filename('certs', 'signing_cert.pem')
SIGNING_KEY_FILE_NAME = make_filename('private', 'signing_key.pem')
EXAMPLE_TOKENS = ['auth_token_revoked',
'auth_token_unscoped',
'auth_token_scoped',
'auth_token_scoped_expired',
'auth_v3_token_scoped',
'auth_v3_token_revoked']
# Helper script to generate the sample data for testing
# the signed tokens using the existing JSON data for the
# MII-prefixed tokens. Uses the keys and certificates
# generated in gen_pki.sh.
def generate_der_form(name):
derfile = make_filename('cms', '%s.der' % name)
with open(derfile, 'w') as f:
derform = cms.cms_sign_data(text,
SIGNING_CERT_FILE_NAME,
SIGNING_KEY_FILE_NAME, cms.PKIZ_CMS_FORM)
f.write(derform)
for name in EXAMPLE_TOKENS:
json_file = make_filename('cms', name + '.json')
pkiz_file = make_filename('cms', name + '.pkiz')
with open(json_file, 'r') as f:
string_data = f.read()
# validate the JSON
try:
token_data = json.loads(string_data)
except ValueError as v:
raise SystemExit('%s while processing token data from %s: %s' %
(v, json_file, string_data))
text = json.dumps(token_data).encode('utf-8')
# Uncomment to record the token uncompressed,
# useful for debugging
# generate_der_form(name)
encoded = cms.pkiz_sign(text,
SIGNING_CERT_FILE_NAME,
SIGNING_KEY_FILE_NAME)
# verify before writing
cms.pkiz_verify(encoded,
SIGNING_CERT_FILE_NAME,
CA_CERT_FILE_NAME)
with open(pkiz_file, 'w') as f:
f.write(encoded)
generate_revocation_list()

View File

@@ -1,208 +0,0 @@
#!/bin/bash
# Copyright 2012 OpenStack Foundation
#
# 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.
# These functions generate the certificates and signed tokens for the tests.
DIR=`dirname "$0"`
CURRENT_DIR=`cd "$DIR" && pwd`
CERTS_DIR=$CURRENT_DIR/certs
PRIVATE_DIR=$CURRENT_DIR/private
CMS_DIR=$CURRENT_DIR/cms
function rm_old {
rm -rf $CERTS_DIR/*.pem
rm -rf $PRIVATE_DIR/*.pem
}
function cleanup {
rm -rf *.conf > /dev/null 2>&1
rm -rf index* > /dev/null 2>&1
rm -rf *.crt > /dev/null 2>&1
rm -rf newcerts > /dev/null 2>&1
rm -rf *.pem > /dev/null 2>&1
rm -rf serial* > /dev/null 2>&1
}
function generate_ca_conf {
echo '
[ req ]
default_bits = 2048
default_keyfile = cakey.pem
default_md = default
prompt = no
distinguished_name = ca_distinguished_name
x509_extensions = ca_extensions
[ ca_distinguished_name ]
serialNumber = 5
countryName = US
stateOrProvinceName = CA
localityName = Sunnyvale
organizationName = OpenStack
organizationalUnitName = Keystone
emailAddress = keystone@openstack.org
commonName = Self Signed
[ ca_extensions ]
basicConstraints = critical,CA:true
' > ca.conf
}
function generate_ssl_req_conf {
echo '
[ req ]
default_bits = 2048
default_keyfile = keystonekey.pem
default_md = default
prompt = no
distinguished_name = distinguished_name
[ distinguished_name ]
countryName = US
stateOrProvinceName = CA
localityName = Sunnyvale
organizationName = OpenStack
organizationalUnitName = Keystone
commonName = localhost
emailAddress = keystone@openstack.org
' > ssl_req.conf
}
function generate_cms_signing_req_conf {
echo '
[ req ]
default_bits = 2048
default_keyfile = keystonekey.pem
default_md = default
prompt = no
distinguished_name = distinguished_name
[ distinguished_name ]
countryName = US
stateOrProvinceName = CA
localityName = Sunnyvale
organizationName = OpenStack
organizationalUnitName = Keystone
commonName = Keystone
emailAddress = keystone@openstack.org
' > cms_signing_req.conf
}
function generate_signing_conf {
echo '
[ ca ]
default_ca = signing_ca
[ signing_ca ]
dir = .
database = $dir/index.txt
new_certs_dir = $dir/newcerts
certificate = $dir/certs/cacert.pem
serial = $dir/serial
private_key = $dir/private/cakey.pem
default_days = 21360
default_crl_days = 30
default_md = default
policy = policy_any
[ policy_any ]
countryName = supplied
stateOrProvinceName = supplied
localityName = optional
organizationName = supplied
organizationalUnitName = supplied
emailAddress = supplied
commonName = supplied
' > signing.conf
}
function setup {
touch index.txt
echo '10' > serial
generate_ca_conf
mkdir newcerts
}
function check_error {
if [ $1 != 0 ] ; then
echo "Failed! rc=${1}"
echo 'Bailing ...'
cleanup
exit $1
else
echo 'Done'
fi
}
function generate_ca {
echo 'Generating New CA Certificate ...'
openssl req -x509 -newkey rsa:2048 -days 21360 -out $CERTS_DIR/cacert.pem -keyout $PRIVATE_DIR/cakey.pem -outform PEM -config ca.conf -nodes
check_error $?
}
function ssl_cert_req {
echo 'Generating SSL Certificate Request ...'
generate_ssl_req_conf
openssl req -newkey rsa:2048 -keyout $PRIVATE_DIR/ssl_key.pem -keyform PEM -out ssl_req.pem -outform PEM -config ssl_req.conf -nodes
check_error $?
#openssl req -in req.pem -text -noout
}
function cms_signing_cert_req {
echo 'Generating CMS Signing Certificate Request ...'
generate_cms_signing_req_conf
openssl req -newkey rsa:2048 -keyout $PRIVATE_DIR/signing_key.pem -keyform PEM -out cms_signing_req.pem -outform PEM -config cms_signing_req.conf -nodes
check_error $?
#openssl req -in req.pem -text -noout
}
function issue_certs {
generate_signing_conf
echo 'Issuing SSL Certificate ...'
openssl ca -in ssl_req.pem -config signing.conf -batch
check_error $?
openssl x509 -in $CURRENT_DIR/newcerts/10.pem -out $CERTS_DIR/ssl_cert.pem
check_error $?
echo 'Issuing CMS Signing Certificate ...'
openssl ca -in cms_signing_req.pem -config signing.conf -batch
check_error $?
openssl x509 -in $CURRENT_DIR/newcerts/11.pem -out $CERTS_DIR/signing_cert.pem
check_error $?
}
function check_openssl {
echo 'Checking openssl availability ...'
which openssl
check_error $?
}
JSON_FILES="${CMS_DIR}/auth_token_revoked.json ${CMS_DIR}/auth_token_unscoped.json ${CMS_DIR}/auth_token_scoped.json ${CMS_DIR}/auth_token_scoped_expired.json ${CMS_DIR}/revocation_list.json ${CMS_DIR}/auth_v3_token_scoped.json ${CMS_DIR}/auth_v3_token_revoked.json"
function gen_sample_cms {
for json_file in $JSON_FILES
do
openssl cms -sign -in $json_file -nosmimecap -signer $CERTS_DIR/signing_cert.pem -inkey $PRIVATE_DIR/signing_key.pem -outform PEM -nodetach -nocerts -noattr -out ${json_file/.json/.pem}
done
}

View File

@@ -1,28 +0,0 @@
-----BEGIN PRIVATE KEY-----
MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCl8906EaRpibQF
cCBWfxzLi5x/XpZ9iL6UX92NrSJxcDbaGws7s+GtjgDy8UOEonesRWTeqQEZtHpC
3/UHHOnsA8F6ha/pq9LioqT7RehCnZCLBJwh5Ct+lclpWs15SkjJD2LTDkjox0eA
9nOBx+XDlWyU/GAyqx5Wsvg/Kxr0iod9/4IcJdnSdUjq4v0Cxg/zNk08XPJX+F0b
UDhgdUf7JrAmmS5LA8wphRnbIgtVsf6VN9HrbqtHAJDxh8gEfuwdhEW1df1fBtZ+
6WMIF3IRSbIsZELFB6sqcyRj7HhMoWMkdEyPb2f8mq61MzTgE6lJGIyTRvEoFie7
qtGADIofAgMBAAECggEBAJ47X3y2xaU7f0KQHsVafgI2JAnuDl+zusOOhJlJs8Wl
0Sc1EgjjAxOQiqcaE96rap//qqYDTuFLjCenkuItV32KNzizr3+GLZWaruRHS6X4
xpFG2/gUrsQL3fdudOxpP+01lmzW+f25xRvZ4VilWRabquSDntWxA0R3cOwKFbGD
uuwbTw3pBrRfCk/2IdpQtRrvvkVIFiYT6b/zeCQzhp4RETbC0oxqcEEOIUGmimAV
9cbwafinxCo54cOfX4JAh3j7Mp3eQUymoFk5gnmIeVe0QmpH2VkN7eItrhEvHKOk
On7a5xvQ8s3wqPV5ZawHQcqar/p3QnGkiT6a+8LkIMECgYEA2iJ2DprTGZFRN0M7
Yj4WLsSC3/GKK8eYsKG3TvMrmPqUDaiWLIvBoc1Le59x9eoF7Mha+WX+cAFL+GTg
1sB+PUZZStpf1R1tGvMldvpQ+5GplUBpuQe4J0n5rCG6+5jkvSr7xO+G1B+C3GFq
KR3iltiW5WJRVwh2k8yGvx3agyUCgYEAwsKFX82F7O+9IVud1JSQWmZMiyEK+DEX
JRnwx4HBuWr+AZqbb0grRRb6x8JTUOD4T7DZGxTaAdfzzRjKU2sBAO8VCgaj2Auv
5nsbvfXvrmDDCqwoaD2PMy+kgFvE0QTh65tzuGXl1IgpIYSC1JwnP6kOeUDbqE+k
UXzfVZzDdvMCgYByk9dfJIPt0h7O4Em4+NO+DQqRhtYE2PqjDM60cZZc7IIICp2X
GHHFA4i6jq3Vde9WyIbAqYpUWtoExzgylTm6BdGxN7NOxf4hQcZUEHepLIHfG85s
mlloibrTZ4RH06+SjZlhgE9Z7JNYHvMcVc5HXc0k/9ep15AxYiUFDjFQ4QKBgG7i
k089U4/X2wWgBNdgkmN1tQTNllJCmNvdzhG41dQ8j0vYe8C7BS+76qJLCGaW/6lX
lfRuRcUg78UI5UDjPloKxR7FMwmxdb+yvdPEr2bH3qQ36nWW/u30pSMTnJYownwD
MLp/AYCk2U4lBNwJ3+rF1ODCRY2pcnOWtg0nSL5zAoGAWRoOinogEnOodJzO7eB3
TmL6M9QMyrAPBDsCnduJ8yW5mMUNod139YbSDxZPYwTLhK/GiHP/7OvLV5hg0s4s
QKnNaMeEowX7dyEO4ehnbfzysxXPKLRVhWhN6MCUc71NMxqr7QkuCXAjJS6/G21+
Im3+Xb3Scq+UZghR+jiEZF0=
-----END PRIVATE KEY-----

View File

@@ -1,28 +0,0 @@
-----BEGIN PRIVATE KEY-----
MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQDM+VrILLl962VH
S8EKWVzdkaOy0OoxGZ63gajM7VTm8AbgtVnYibIOnVZQuz1XbftIGNXPFhYNUypr
LnMXrEEsnxgD4PvU/4bETG+stdricX6d1oKqsNFNR7F7zImiR/OzGhp7dONwccxf
kfX4QHA5Ogso+XMfSdC72SRDszeCeGUcjuo/w2WSLW95SuVvcZLqE/pk3Q2TkCZ1
8hvNfLoln43QpC469a7srUXATqOJ2mPNvL6E/wOyPefmAoCoG44lFoR3k2jZjBEI
hstJxmH7XgvqErBzpcWd29dms8xz5PNwYdns9CIfb3GaHvQ6r5RTl37/avDrGHOW
KOoD01xLAgMBAAECggEAaIi22qWsh+JYCW9B6NRAPyN6V8Sh2x6UykOO4cwb45b/
+vOh+YPn0fo9vfhvxTnq0A8SY4WBA5SpanYK7kTEDEyqw7em1y7l/RB6V5t7IMb+
6uIuS3zXkVEB3AApJSEK0Ql7/gBTydHPh+H5jnzWfujyLhhhtNBBarvH+drZcWio
lWx8RERN4cH+3DZD/xxjH2Ff+X1XMvb8Xcup7MlWi2FtREg7LttLNWNK25iWjciP
QwfWQIrURRJrD2IrOr9V2nuIEvRqRRBoO+pxJT2sC48NJ3hiKV2GtSQe2nRpQJ47
f9MEsF5KVQOOn+aQ60EKOI0MpNPmpiCZ5hFvBrNuOQKBgQD6vueEdI9eJgz5YN+t
XWdpNippv35RTD8R4bQcE6GqIUXOmtQFS2wPJLn7nisZUsGMNEs36Yl0T9iow63r
5GNAfgzpqN1XZqaSMwAdxKmlBNYpAkVXHhv+1jN+9diDYmoj9T+3Q6Zvk5e/Liyp
6i+TsDppwmmr2utWajhyJ7owFwKBgQDRROncTztGDYLfRcrIoYsPo79KQ8tqwd2a
07Usch2kplTqojCUmmhMMFgV2eZPPiCjnEy2bAYh9I/oj7xG6EwApXTshZdCpivC
rbUV64MakRTUP8IvM6PdI+apkJRsRUi/bSyIbcRlvEoCMNZhfj/5VY6w/jlwrPJj
oBOCXBlB7QKBgQDGEbEeX1i03UfYYh6uep7qbEAaooqsu5cCkBDPMO6+TmQvLPyY
Zhio6bEEQs/2w/lhwBk+xHqw5zXVMiWbtiB03F1k4eBeXxbrW+AWo7gCQ4zMfh+6
Dm284wVwn9D1D/OaDevT31uEvcjb2ySq3/PPLSEnU8xXVaoa6/NEsX8Q5wKBgQCm
2smULWBXZKJ6n00mVxdnqun0rsVcI6Mrta14+KwGAdEnG5achdivFsTE924YtLKV
gSPxN4RUQokTprc52jHvOf1WMNYAADpYCOSfy55G6nKvIP8VX5lB00Qw4uRUx5FP
gB7H0K2NaGmiAYqNRXqAtOUG3kyyOFMzeAjWIdTJqQKBgQCHzY1c7sS1vv7mPEkr
6CpwoaEbZeFnWoHBA8Rd82psqfYsVJIRwk5Id8zgDSEmoEi8hQ9UrYbrFpLK77xq
EYSxLQHTNlM0G3lyEsv/gJhwYYhdTYiW3Cx3F6Y++jyn9O/+hFMyQvuesAL7DUYE
ptEfvzFprpQUpByXkIpuJub6fg==
-----END PRIVATE KEY-----

View File

@@ -1,28 +0,0 @@
-----BEGIN PRIVATE KEY-----
MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDL06AaJROwHPgJ
9tcySSBepzJ81jYars2sMvLjyuvdiIBbhWvbS/a9Tw3WgL8H6OALkHiOU/f0A6Rp
v8dGDIDsxZQVjT/4SLaQUOeDM+9bfkKHpSd9G3CsdSSZgOH08n+MyZ7slPHfUHLY
Wso0SJD0vAi1gmGDlSM/mmhhHTpCDGo6Wbwqare6JNeTCGJTJYwrxtoMCh/W1Zrs
lPC5lFvlHD7KBBf6IU2A8Xh/dUa3p5pmQeHPW8Em90DzIB1qH0DRXl3KANc24xYR
R45pPCVkk6vFsy6P0JwwpnkszB+LcK6CEsJhLsOYvQFsiQfSZ8m7YGhgrMLxtop4
YEPirGGrAgMBAAECggEATwvbY0hNwlb5uqOIAXBqpUqiQdexU9fG26lGmSDxKBDv
9o5frcRgBDrMWwvDCgY+HT4CAvB9kJx4/qnpVjkzJp/ZNiJ5VIiehIlbv348rXbh
xkk+bz5dDATCFOXuu1fwL2FhyM5anwhMAav0DyK1VLQ3jGzr9GO6L8hqAn+bQFFu
6ngiODwfhBMl5aRoL9UOBEhccK07znrH0JGRz+3+5Cdz59Xw91Bv210LhNNDL58+
0JD0N+YztVOQd2bgwo0bQbOEijzmYq+0mjoqAnJh1/++y7PlIPs0AnPgqSnFPx9+
6FsQEVRgk5Uq3kvPLaP4nT2y6MDZSp+ujYldvJhyQQKBgQDuX2pZIJMZ4aFnkG+K
TmJ5wsLa/u9an0TmvAL9RLtBpVpQNKD8cQ+y8PUZavXDbAIt5NWqZVnTbCR79Dnd
mZKblwcHhtsyA5f89el5KcxY2BREWdHdTnJpNd7XRlUECmzvX1zGj77lA982PhII
yflRBRV3vqLkgC8vfoYgRyRElwKBgQDa5jnLdx/RahfYMOgn1HE5o4hMzLR4Y0Dd
+gELshcUbPqouoP5zOb8WOagVJIgZVOSN+/VqbilVYrqRiNTn2rnoxs+HHRdaJNN
3eXllD4J2HfC2BIj1xSpIdyh2XewAJqw9IToHNB29QUhxOtgwseHciPG6JaKH2ik
kqGKH/EKDQKBgFFAftygiOPCkCTgC9UmANUmOQsy6N2H+pF3tsEj43xt44oBVnqW
A1boYXNnjRwuvdNs9BPf9i1l6E3EItFRXrLgWQoMwryakv0ryYh+YeRKyyW9RBbe
fYs1TJ8unx4Ae79gTxxztQsVNcmkgLs0NWKTjAzEE3w14V+cDhYEie1DAoGBAJdI
V5cLrBzBstsB6eBlDR9lqrRRIUS2a8U9m+1mVlcSfiWQSdehSd4K3tDdwePLw3ch
W4qR8n+pYAlLEe0gFvUhn5lMdwt7U5qUCeehjUKmrRYm2FqWsbu2IFJnBjXIJSC4
zQXRrC0aZ0KQYpAL7XPpaVp1slyhGmPqxuO78Y0dAoGBAMHo3EIMwu9rfuGwFodr
GFsOZhfJqgo5GDNxxf89Q9WWpMDTCdX+wdBTrN/wsMbBuwIDHrUuRnk6D5CWRjSk
/ikCgHN3kOtrbL8zzqRomGAIIWKYGFEIGe1GHVGo5r//HXHdPxFXygvruQ/xbOA4
RGvmDiji8vVDq7Shho8I6KuT
-----END PRIVATE KEY-----

View File

@@ -1,30 +0,0 @@
#!/bin/bash -x
# Copyright 2012 OpenStack Foundation
#
# 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.
# This script generates the crypto necessary for the SSL tests.
. gen_pki.sh
check_openssl
rm_old
cleanup
setup
generate_ca
ssl_cert_req
cms_signing_cert_req
issue_certs
gen_sample_cms
cleanup

View File

@@ -1,78 +0,0 @@
# Copyright 2012 OpenStack Foundation
# 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.
"""The python bindings for the OpenStack Identity (Keystone) project.
A Client object will allow you to communicate with the Identity server. The
recommended way to get a Client object is to use
:py:func:`keystoneclient.client.Client()`. :py:func:`~.Client()` uses version
discovery to create a V3 or V2 client depending on what versions the Identity
server supports and what version is requested.
Identity V2 and V3 clients can also be created directly. See
:py:class:`keystoneclient.v3.client.Client` for the V3 client and
:py:class:`keystoneclient.v2_0.client.Client` for the V2 client.
"""
import importlib
import sys
import pbr.version
__version__ = pbr.version.VersionInfo('python-keystoneclient').version_string()
__all__ = (
# Modules
'generic',
'v2_0',
'v3',
# Packages
'access',
'client',
'exceptions',
'httpclient',
'service_catalog',
)
class _LazyImporter(object):
def __init__(self, module):
self._module = module
def __getattr__(self, name):
# NB: this is only called until the import has been done.
# These submodules are part of the API without explicit importing, but
# expensive to load, so we load them on-demand rather than up-front.
lazy_submodules = [
'access',
'client',
'exceptions',
'generic',
'httpclient',
'service_catalog',
'v2_0',
'v3',
]
if name in lazy_submodules:
return importlib.import_module('keystoneclient.%s' % name)
# Return module attributes like __all__ etc.
return getattr(self._module, name)
sys.modules[__name__] = _LazyImporter(sys.modules[__name__])

View File

@@ -1,333 +0,0 @@
# 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.
"""The passive components to version discovery.
The Discover object in discover.py contains functions that can create objects
on your behalf. These functions are not usable from within the keystoneclient
library because you will get dependency resolution issues.
The Discover object in this file provides the querying components of Discovery.
This includes functions like url_for which allow you to retrieve URLs and the
raw data specified in version discovery responses.
"""
import logging
import re
from positional import positional
from keystoneclient import exceptions
from keystoneclient.i18n import _
_LOGGER = logging.getLogger(__name__)
@positional()
def get_version_data(session, url, authenticated=None):
"""Retrieve raw version data from a url."""
headers = {'Accept': 'application/json'}
resp = session.get(url, headers=headers, authenticated=authenticated)
try:
body_resp = resp.json()
except ValueError: # nosec(cjschaef): raise a DiscoveryFailure below
pass
else:
# In the event of querying a root URL we will get back a list of
# available versions.
try:
return body_resp['versions']['values']
except (KeyError, TypeError): # nosec(cjschaef): attempt to return
# versions dict or query the endpoint or raise a DiscoveryFailure
pass
# Most servers don't have a 'values' element so accept a simple
# versions dict if available.
try:
return body_resp['versions']
except KeyError: # nosec(cjschaef): query the endpoint or raise a
# DiscoveryFailure
pass
# Otherwise if we query an endpoint like /v2.0 then we will get back
# just the one available version.
try:
return [body_resp['version']]
except KeyError: # nosec(cjschaef): raise a DiscoveryFailure
pass
err_text = resp.text[:50] + '...' if len(resp.text) > 50 else resp.text
msg = _('Invalid Response - Bad version data returned: %s') % err_text
raise exceptions.DiscoveryFailure(msg)
def normalize_version_number(version):
"""Turn a version representation into a tuple."""
# trim the v from a 'v2.0' or similar
try:
version = version.lstrip('v')
except AttributeError: # nosec(cjschaef): 'version' is not a str, try a
# different type or raise a TypeError
pass
# if it's an integer or a numeric as a string then normalize it
# to a string, this ensures 1 decimal point
try:
num = float(version)
except Exception: # nosec(cjschaef): 'version' is not a float, try a
# different type or raise a TypeError
pass
else:
version = str(num)
# if it's a string (or an integer) from above break it on .
try:
return tuple(map(int, version.split('.')))
except Exception: # nosec(cjschaef): 'version' is not str (or an int),
# try a different type or raise a TypeError
pass
# last attempt, maybe it's a list or iterable.
try:
return tuple(map(int, version))
except Exception: # nosec(cjschaef): 'version' is not an expected type,
# raise a TypeError
pass
raise TypeError(_('Invalid version specified: %s') % version)
def version_match(required, candidate):
"""Test that an available version satisfies the required version.
To be suitable a version must be of the same major version as required
and be at least a match in minor/patch level.
eg. 3.3 is a match for a required 3.1 but 4.1 is not.
:param tuple required: the version that must be met.
:param tuple candidate: the version to test against required.
:returns: True if candidate is suitable False otherwise.
:rtype: bool
"""
# major versions must be the same (e.g. even though v2 is a lower
# version than v3 we can't use it if v2 was requested)
if candidate[0] != required[0]:
return False
# prevent selecting a minor version less than what is required
if candidate < required:
return False
return True
class Discover(object):
CURRENT_STATUSES = ('stable', 'current', 'supported')
DEPRECATED_STATUSES = ('deprecated',)
EXPERIMENTAL_STATUSES = ('experimental',)
@positional()
def __init__(self, session, url, authenticated=None):
self._data = get_version_data(session, url,
authenticated=authenticated)
def raw_version_data(self, allow_experimental=False,
allow_deprecated=True, allow_unknown=False):
"""Get raw version information from URL.
Raw data indicates that only minimal validation processing is performed
on the data, so what is returned here will be the data in the same
format it was received from the endpoint.
:param bool allow_experimental: Allow experimental version endpoints.
:param bool allow_deprecated: Allow deprecated version endpoints.
:param bool allow_unknown: Allow endpoints with an unrecognised status.
:returns: The endpoints returned from the server that match the
criteria.
:rtype: list
"""
versions = []
for v in self._data:
try:
status = v['status']
except KeyError:
_LOGGER.warning('Skipping over invalid version data. '
'No stability status in version.')
continue
status = status.lower()
if status in self.CURRENT_STATUSES:
versions.append(v)
elif status in self.DEPRECATED_STATUSES:
if allow_deprecated:
versions.append(v)
elif status in self.EXPERIMENTAL_STATUSES:
if allow_experimental:
versions.append(v)
elif allow_unknown:
versions.append(v)
return versions
def version_data(self, **kwargs):
"""Get normalized version data.
Return version data in a structured way.
:returns: A list of version data dictionaries sorted by version number.
Each data element in the returned list is a dictionary
consisting of at least:
:version tuple: The normalized version of the endpoint.
:url str: The url for the endpoint.
:raw_status str: The status as provided by the server
:rtype: list(dict)
"""
if kwargs.pop('unstable', None):
kwargs.setdefault('allow_experimental', True)
kwargs.setdefault('allow_unknown', True)
data = self.raw_version_data(**kwargs)
versions = []
for v in data:
try:
version_str = v['id']
except KeyError:
_LOGGER.info('Skipping invalid version data. Missing ID.')
continue
try:
links = v['links']
except KeyError:
_LOGGER.info('Skipping invalid version data. Missing links')
continue
version_number = normalize_version_number(version_str)
for link in links:
try:
rel = link['rel']
url = link['href']
except (KeyError, TypeError):
_LOGGER.info('Skipping invalid version link. '
'Missing link URL or relationship.')
continue
if rel.lower() == 'self':
break
else:
_LOGGER.info('Skipping invalid version data. '
'Missing link to endpoint.')
continue
versions.append({'version': version_number,
'url': url,
'raw_status': v['status']})
versions.sort(key=lambda v: v['version'])
return versions
def data_for(self, version, **kwargs):
"""Return endpoint data for a version.
:param tuple version: The version is always a minimum version in the
same major release as there should be no compatibility issues with
using a version newer than the one asked for.
:returns: the endpoint data for a URL that matches the required version
(the format is described in version_data) or None if no
match.
:rtype: dict
"""
version = normalize_version_number(version)
version_data = self.version_data(**kwargs)
for data in reversed(version_data):
if version_match(version, data['version']):
return data
return None
def url_for(self, version, **kwargs):
"""Get the endpoint url for a version.
:param tuple version: The version is always a minimum version in the
same major release as there should be no compatibility issues with
using a version newer than the one asked for.
:returns: The url for the specified version or None if no match.
:rtype: str
"""
data = self.data_for(version, **kwargs)
return data['url'] if data else None
class _VersionHacks(object):
"""A container to abstract the list of version hacks.
This could be done as simply a dictionary but is abstracted like this to
make for easier testing.
"""
def __init__(self):
self._discovery_data = {}
def add_discover_hack(self, service_type, old, new=''):
"""Add a new hack for a service type.
:param str service_type: The service_type in the catalog.
:param re.RegexObject old: The pattern to use.
:param str new: What to replace the pattern with.
"""
hacks = self._discovery_data.setdefault(service_type, [])
hacks.append((old, new))
def get_discover_hack(self, service_type, url):
"""Apply the catalog hacks and figure out an unversioned endpoint.
:param str service_type: the service_type to look up.
:param str url: The original url that came from a service_catalog.
:returns: Either the unversioned url or the one from the catalog
to try.
"""
for old, new in self._discovery_data.get(service_type, []):
new_string, number_of_subs_made = old.subn(new, url)
if number_of_subs_made > 0:
return new_string
return url
_VERSION_HACKS = _VersionHacks()
_VERSION_HACKS.add_discover_hack('identity', re.compile('/v2.0/?$'), '/')
def get_catalog_discover_hack(service_type, url):
"""Apply the catalog hacks and figure out an unversioned endpoint.
This function is internal to keystoneclient.
:param str service_type: the service_type to look up.
:param str url: The original url that came from a service_catalog.
:returns: Either the unversioned url or the one from the catalog to try.
"""
return _VERSION_HACKS.get_discover_hack(service_type, url)

View File

@@ -1,886 +0,0 @@
# Copyright 2012 Nebula, Inc.
#
# 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.
import datetime
import warnings
from oslo_utils import timeutils
from keystoneclient.i18n import _
from keystoneclient import service_catalog
# gap, in seconds, to determine whether the given token is about to expire
STALE_TOKEN_DURATION = 30
class AccessInfo(dict):
"""Encapsulates a raw authentication token from keystone.
Provides helper methods for extracting useful values from that token.
"""
@classmethod
def factory(cls, resp=None, body=None, region_name=None, auth_token=None,
**kwargs):
"""Factory function to create a new AccessInfo object.
Create AccessInfo object given a successful auth response & body
or a user-provided dict.
.. warning::
Use of the region_name argument is deprecated as of the 1.7.0
release and may be removed in the 2.0.0 release.
"""
if region_name:
warnings.warn(
'Use of the region_name argument is deprecated as of the '
'1.7.0 release and may be removed in the 2.0.0 release.',
DeprecationWarning)
if body is not None or len(kwargs):
if AccessInfoV3.is_valid(body, **kwargs):
if resp and not auth_token:
auth_token = resp.headers['X-Subject-Token']
# NOTE(jamielennox): these return AccessInfo because they
# already have auth_token installed on them.
if body:
if region_name:
body['token']['region_name'] = region_name
return AccessInfoV3(auth_token, **body['token'])
else:
return AccessInfoV3(auth_token, **kwargs)
elif AccessInfoV2.is_valid(body, **kwargs):
if body:
if region_name:
body['access']['region_name'] = region_name
auth_ref = AccessInfoV2(**body['access'])
else:
auth_ref = AccessInfoV2(**kwargs)
else:
raise NotImplementedError(_('Unrecognized auth response'))
else:
auth_ref = AccessInfoV2(**kwargs)
if auth_token:
auth_ref.auth_token = auth_token
return auth_ref
def __init__(self, *args, **kwargs):
super(AccessInfo, self).__init__(*args, **kwargs)
self.service_catalog = service_catalog.ServiceCatalog.factory(
resource_dict=self, region_name=self._region_name)
@property
def _region_name(self):
return self.get('region_name')
def will_expire_soon(self, stale_duration=None):
"""Determine if expiration is about to occur.
:returns: true if expiration is within the given duration
:rtype: boolean
"""
stale_duration = (STALE_TOKEN_DURATION if stale_duration is None
else stale_duration)
norm_expires = timeutils.normalize_time(self.expires)
# (gyee) should we move auth_token.will_expire_soon() to timeutils
# instead of duplicating code here?
soon = (timeutils.utcnow() + datetime.timedelta(
seconds=stale_duration))
return norm_expires < soon
@classmethod
def is_valid(cls, body, **kwargs):
"""Determine if processing valid v2 or v3 token.
Validates from the auth body or a user-provided dict.
:returns: true if auth body matches implementing class
:rtype: boolean
"""
raise NotImplementedError()
def has_service_catalog(self):
"""Return true if the authorization token has a service catalog.
:returns: boolean
"""
raise NotImplementedError()
@property
def auth_token(self):
"""Return the token_id associated with the auth request.
To be used in headers for authenticating OpenStack API requests.
:returns: str
"""
return self['auth_token']
@auth_token.setter
def auth_token(self, value):
self['auth_token'] = value
@auth_token.deleter
def auth_token(self):
try:
del self['auth_token']
except KeyError: # nosec(cjschaef): 'auth_token' is not in the dict
pass
@property
def expires(self):
"""Return the token expiration (as datetime object).
:returns: datetime
"""
raise NotImplementedError()
@property
def issued(self):
"""Return the token issue time (as datetime object).
:returns: datetime
"""
raise NotImplementedError()
@property
def username(self):
"""Return the username associated with the auth request.
Follows the pattern defined in the V2 API of first looking for 'name',
returning that if available, and falling back to 'username' if name
is unavailable.
:returns: str
"""
raise NotImplementedError()
@property
def user_id(self):
"""Return the user id associated with the auth request.
:returns: str
"""
raise NotImplementedError()
@property
def user_domain_id(self):
"""Return the user's domain id associated with the auth request.
For v2, it always returns 'default' which may be different from the
Keystone configuration.
:returns: str
"""
raise NotImplementedError()
@property
def user_domain_name(self):
"""Return the user's domain name associated with the auth request.
For v2, it always returns 'Default' which may be different from the
Keystone configuration.
:returns: str
"""
raise NotImplementedError()
@property
def role_ids(self):
"""Return a list of user's role ids associated with the auth request.
:returns: a list of strings of role ids
"""
raise NotImplementedError()
@property
def role_names(self):
"""Return a list of user's role names associated with the auth request.
:returns: a list of strings of role names
"""
raise NotImplementedError()
@property
def domain_name(self):
"""Return the domain name associated with the auth request.
:returns: str or None (if no domain associated with the token)
"""
raise NotImplementedError()
@property
def domain_id(self):
"""Return the domain id associated with the auth request.
:returns: str or None (if no domain associated with the token)
"""
raise NotImplementedError()
@property
def project_name(self):
"""Return the project name associated with the auth request.
:returns: str or None (if no project associated with the token)
"""
raise NotImplementedError()
@property
def tenant_name(self):
"""Synonym for project_name."""
return self.project_name
@property
def scoped(self):
"""Return true if the auth token was scoped.
Return true if scoped to a tenant(project) or domain,
and contains a populated service catalog.
.. warning::
This is deprecated as of the 1.7.0 release in favor of
project_scoped and may be removed in the 2.0.0 release.
:returns: bool
"""
raise NotImplementedError()
@property
def project_scoped(self):
"""Return true if the auth token was scoped to a tenant(project).
:returns: bool
"""
raise NotImplementedError()
@property
def domain_scoped(self):
"""Return true if the auth token was scoped to a domain.
:returns: bool
"""
raise NotImplementedError()
@property
def trust_id(self):
"""Return the trust id associated with the auth request.
:returns: str or None (if no trust associated with the token)
"""
raise NotImplementedError()
@property
def trust_scoped(self):
"""Return true if the auth token was scoped from a delegated trust.
The trust delegation is via the OS-TRUST v3 extension.
:returns: bool
"""
raise NotImplementedError()
@property
def trustee_user_id(self):
"""Return the trustee user id associated with a trust.
:returns: str or None (if no trust associated with the token)
"""
raise NotImplementedError()
@property
def trustor_user_id(self):
"""Return the trustor user id associated with a trust.
:returns: str or None (if no trust associated with the token)
"""
raise NotImplementedError()
@property
def project_id(self):
"""Return the project ID associated with the auth request.
This returns None if the auth token wasn't scoped to a project.
:returns: str or None (if no project associated with the token)
"""
raise NotImplementedError()
@property
def tenant_id(self):
"""Synonym for project_id."""
return self.project_id
@property
def project_domain_id(self):
"""Return the project's domain id associated with the auth request.
For v2, it returns 'default' if a project is scoped or None which may
be different from the keystone configuration.
:returns: str
"""
raise NotImplementedError()
@property
def project_domain_name(self):
"""Return the project's domain name associated with the auth request.
For v2, it returns 'Default' if a project is scoped or None which may
be different from the keystone configuration.
:returns: str
"""
raise NotImplementedError()
@property
def auth_url(self):
"""Return a tuple of identity URLs.
The identity URLs are from publicURL and adminURL for the service
'identity' from the service catalog associated with the authorization
request. If the authentication request wasn't scoped to a tenant
(project), this property will return None.
DEPRECATED: this doesn't correctly handle region name. You should fetch
it from the service catalog yourself. This may be removed in the 2.0.0
release.
:returns: tuple of urls
"""
raise NotImplementedError()
@property
def management_url(self):
"""Return the first adminURL of the identity endpoint.
The identity endpoint is from the service catalog
associated with the authorization request, or None if the
authentication request wasn't scoped to a tenant (project).
DEPRECATED: this doesn't correctly handle region name. You should fetch
it from the service catalog yourself. This may be removed in the 2.0.0
release.
:returns: tuple of urls
"""
raise NotImplementedError()
@property
def version(self):
"""Return the version of the auth token from identity service.
:returns: str
"""
return self.get('version')
@property
def oauth_access_token_id(self):
"""Return the access token ID if OAuth authentication used.
:returns: str or None.
"""
raise NotImplementedError()
@property
def oauth_consumer_id(self):
"""Return the consumer ID if OAuth authentication used.
:returns: str or None.
"""
raise NotImplementedError()
@property
def is_federated(self):
"""Return true if federation was used to get the token.
:returns: boolean
"""
raise NotImplementedError()
@property
def audit_id(self):
"""Return the audit ID if present.
:returns: str or None.
"""
raise NotImplementedError()
@property
def audit_chain_id(self):
"""Return the audit chain ID if present.
In the event that a token was rescoped then this ID will be the
:py:attr:`audit_id` of the initial token. Returns None if no value
present.
:returns: str or None.
"""
raise NotImplementedError()
@property
def initial_audit_id(self):
"""The audit ID of the initially requested token.
This is the :py:attr:`audit_chain_id` if present or the
:py:attr:`audit_id`.
"""
return self.audit_chain_id or self.audit_id
class AccessInfoV2(AccessInfo):
"""An object for encapsulating raw v2 auth token from identity service."""
def __init__(self, *args, **kwargs):
super(AccessInfo, self).__init__(*args, **kwargs)
self.update(version='v2.0')
self.service_catalog = service_catalog.ServiceCatalog.factory(
resource_dict=self,
token=self['token']['id'],
region_name=self._region_name)
@classmethod
def is_valid(cls, body, **kwargs):
if body:
return 'access' in body
elif kwargs:
return kwargs.get('version') == 'v2.0'
else:
return False
def has_service_catalog(self):
return 'serviceCatalog' in self
@AccessInfo.auth_token.getter
def auth_token(self):
try:
return super(AccessInfoV2, self).auth_token
except KeyError:
return self['token']['id']
@property
def expires(self):
return timeutils.parse_isotime(self['token']['expires'])
@property
def issued(self):
return timeutils.parse_isotime(self['token']['issued_at'])
@property
def username(self):
return self['user'].get('name', self['user'].get('username'))
@property
def user_id(self):
return self['user']['id']
@property
def user_domain_id(self):
return 'default'
@property
def user_domain_name(self):
return 'Default'
@property
def role_ids(self):
return self.get('metadata', {}).get('roles', [])
@property
def role_names(self):
return [r['name'] for r in self['user'].get('roles', [])]
@property
def domain_name(self):
return None
@property
def domain_id(self):
return None
@property
def project_name(self):
try:
tenant_dict = self['token']['tenant']
except KeyError: # nosec(cjschaef): no 'token' key or 'tenant' key in
# token, return the name of the tenant or None
pass
else:
return tenant_dict.get('name')
# pre grizzly
try:
return self['user']['tenantName']
except KeyError: # nosec(cjschaef): no 'user' key or 'tenantName' in
# 'user', attempt 'tenantId' or return None
pass
# pre diablo, keystone only provided a tenantId
try:
return self['token']['tenantId']
except KeyError: # nosec(cjschaef): no 'token' key or 'tenantName' or
# 'tenantId' could be found, return None
pass
@property
def scoped(self):
"""Deprecated as of the 1.7.0 release.
Use project_scoped instead. It may be removed in the
2.0.0 release.
"""
warnings.warn(
'scoped is deprecated as of the 1.7.0 release in favor of '
'project_scoped and may be removed in the 2.0.0 release.',
DeprecationWarning)
if ('serviceCatalog' in self
and self['serviceCatalog']
and 'tenant' in self['token']):
return True
return False
@property
def project_scoped(self):
return 'tenant' in self['token']
@property
def domain_scoped(self):
return False
@property
def trust_id(self):
return self.get('trust', {}).get('id')
@property
def trust_scoped(self):
return 'trust' in self
@property
def trustee_user_id(self):
return self.get('trust', {}).get('trustee_user_id')
@property
def trustor_user_id(self):
# this information is not available in the v2 token bug: #1331882
return None
@property
def project_id(self):
try:
tenant_dict = self['token']['tenant']
except KeyError: # nosec(cjschaef): no 'token' key or 'tenant' dict,
# attempt to return 'tenantId' or return None
pass
else:
return tenant_dict.get('id')
# pre grizzly
try:
return self['user']['tenantId']
except KeyError: # nosec(cjschaef): no 'user' key or 'tenantId' in
# 'user', attempt to retrieve from 'token' or return None
pass
# pre diablo
try:
return self['token']['tenantId']
except KeyError: # nosec(cjschaef): no 'token' key or 'tenantId'
# could be found, return None
pass
@property
def project_domain_id(self):
if self.project_id:
return 'default'
@property
def project_domain_name(self):
if self.project_id:
return 'Default'
@property
def auth_url(self):
"""Deprecated as of the 1.7.0 release.
Use service_catalog.get_urls() instead. It may be removed in the
2.0.0 release.
"""
warnings.warn(
'auth_url is deprecated as of the 1.7.0 release in favor of '
'service_catalog.get_urls() and may be removed in the 2.0.0 '
'release.', DeprecationWarning)
if self.service_catalog:
return self.service_catalog.get_urls(service_type='identity',
endpoint_type='publicURL',
region_name=self._region_name)
else:
return None
@property
def management_url(self):
"""Deprecated as of the 1.7.0 release.
Use service_catalog.get_urls() instead. It may be removed in the
2.0.0 release.
"""
warnings.warn(
'management_url is deprecated as of the 1.7.0 release in favor of '
'service_catalog.get_urls() and may be removed in the 2.0.0 '
'release.', DeprecationWarning)
if self.service_catalog:
return self.service_catalog.get_urls(service_type='identity',
endpoint_type='adminURL',
region_name=self._region_name)
else:
return None
@property
def oauth_access_token_id(self):
return None
@property
def oauth_consumer_id(self):
return None
@property
def is_federated(self):
return False
@property
def audit_id(self):
try:
return self['token'].get('audit_ids', [])[0]
except IndexError:
return None
@property
def audit_chain_id(self):
try:
return self['token'].get('audit_ids', [])[1]
except IndexError:
return None
class AccessInfoV3(AccessInfo):
"""An object encapsulating raw v3 auth token from identity service."""
def __init__(self, token, *args, **kwargs):
super(AccessInfo, self).__init__(*args, **kwargs)
self.update(version='v3')
self.service_catalog = service_catalog.ServiceCatalog.factory(
resource_dict=self,
token=token,
region_name=self._region_name)
if token:
self.auth_token = token
@classmethod
def is_valid(cls, body, **kwargs):
if body:
return 'token' in body
elif kwargs:
return kwargs.get('version') == 'v3'
else:
return False
def has_service_catalog(self):
return 'catalog' in self
@property
def is_federated(self):
return 'OS-FEDERATION' in self['user']
@property
def expires(self):
return timeutils.parse_isotime(self['expires_at'])
@property
def issued(self):
return timeutils.parse_isotime(self['issued_at'])
@property
def user_id(self):
return self['user']['id']
@property
def user_domain_id(self):
try:
return self['user']['domain']['id']
except KeyError:
if self.is_federated:
return None
raise
@property
def user_domain_name(self):
try:
return self['user']['domain']['name']
except KeyError:
if self.is_federated:
return None
raise
@property
def role_ids(self):
return [r['id'] for r in self.get('roles', [])]
@property
def role_names(self):
return [r['name'] for r in self.get('roles', [])]
@property
def username(self):
return self['user']['name']
@property
def domain_name(self):
domain = self.get('domain')
if domain:
return domain['name']
@property
def domain_id(self):
domain = self.get('domain')
if domain:
return domain['id']
@property
def project_id(self):
project = self.get('project')
if project:
return project['id']
@property
def project_domain_id(self):
project = self.get('project')
if project:
return project['domain']['id']
@property
def project_domain_name(self):
project = self.get('project')
if project:
return project['domain']['name']
@property
def project_name(self):
project = self.get('project')
if project:
return project['name']
@property
def scoped(self):
"""Deprecated as of the 1.7.0 release.
Use project_scoped instead. It may be removed in the
2.0.0 release.
"""
warnings.warn(
'scoped is deprecated as of the 1.7.0 release in favor of '
'project_scoped and may be removed in the 2.0.0 release.',
DeprecationWarning)
return ('catalog' in self and self['catalog'] and 'project' in self)
@property
def project_scoped(self):
return 'project' in self
@property
def domain_scoped(self):
return 'domain' in self
@property
def trust_id(self):
return self.get('OS-TRUST:trust', {}).get('id')
@property
def trust_scoped(self):
return 'OS-TRUST:trust' in self
@property
def trustee_user_id(self):
return self.get('OS-TRUST:trust', {}).get('trustee_user', {}).get('id')
@property
def trustor_user_id(self):
return self.get('OS-TRUST:trust', {}).get('trustor_user', {}).get('id')
@property
def auth_url(self):
"""Deprecated as of the 1.7.0 release.
Use service_catalog.get_urls() instead. It may be removed in the
2.0.0 release.
"""
warnings.warn(
'auth_url is deprecated as of the 1.7.0 release in favor of '
'service_catalog.get_urls() and may be removed in the 2.0.0 '
'release.', DeprecationWarning)
if self.service_catalog:
return self.service_catalog.get_urls(service_type='identity',
endpoint_type='public',
region_name=self._region_name)
else:
return None
@property
def management_url(self):
"""Deprecated as of the 1.7.0 release.
Use service_catalog.get_urls() instead. It may be removed in the
2.0.0 release.
"""
warnings.warn(
'management_url is deprecated as of the 1.7.0 release in favor of '
'service_catalog.get_urls() and may be removed in the 2.0.0 '
'release.', DeprecationWarning)
if self.service_catalog:
return self.service_catalog.get_urls(service_type='identity',
endpoint_type='admin',
region_name=self._region_name)
else:
return None
@property
def oauth_access_token_id(self):
return self.get('OS-OAUTH1', {}).get('access_token_id')
@property
def oauth_consumer_id(self):
return self.get('OS-OAUTH1', {}).get('consumer_id')
@property
def audit_id(self):
try:
return self.get('audit_ids', [])[0]
except IndexError:
return None
@property
def audit_chain_id(self):
try:
return self.get('audit_ids', [])[1]
except IndexError:
return None

View File

@@ -1,223 +0,0 @@
# 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 warnings
from oslo_serialization import jsonutils
from positional import positional
class Adapter(object):
"""An instance of a session with local variables.
A session is a global object that is shared around amongst many clients. It
therefore contains state that is relevant to everyone. There is a lot of
state such as the service type and region_name that are only relevant to a
particular client that is using the session. An adapter provides a wrapper
of client local data around the global session object.
:param session: The session object to wrap.
:type session: keystoneclient.session.Session
:param str service_type: The default service_type for URL discovery.
:param str service_name: The default service_name for URL discovery.
:param str interface: The default interface for URL discovery.
:param str region_name: The default region_name for URL discovery.
:param str endpoint_override: Always use this endpoint URL for requests
for this client.
:param tuple version: The version that this API targets.
:param auth: An auth plugin to use instead of the session one.
:type auth: keystoneclient.auth.base.BaseAuthPlugin
:param str user_agent: The User-Agent string to set.
:param int connect_retries: the maximum number of retries that should
be attempted for connection errors.
Default None - use session default which
is don't retry.
:param logger: A logging object to use for requests that pass through this
adapter.
:type logger: logging.Logger
"""
@positional()
def __init__(self, session, service_type=None, service_name=None,
interface=None, region_name=None, endpoint_override=None,
version=None, auth=None, user_agent=None,
connect_retries=None, logger=None):
warnings.warn(
'keystoneclient.adapter.Adapter is deprecated as of the 2.1.0 '
'release in favor of keystoneauth1.adapter.Adapter. It will be '
'removed in future releases.', DeprecationWarning)
# NOTE(jamielennox): when adding new parameters to adapter please also
# add them to the adapter call in httpclient.HTTPClient.__init__
self.session = session
self.service_type = service_type
self.service_name = service_name
self.interface = interface
self.region_name = region_name
self.endpoint_override = endpoint_override
self.version = version
self.user_agent = user_agent
self.auth = auth
self.connect_retries = connect_retries
self.logger = logger
def _set_endpoint_filter_kwargs(self, kwargs):
if self.service_type:
kwargs.setdefault('service_type', self.service_type)
if self.service_name:
kwargs.setdefault('service_name', self.service_name)
if self.interface:
kwargs.setdefault('interface', self.interface)
if self.region_name:
kwargs.setdefault('region_name', self.region_name)
if self.version:
kwargs.setdefault('version', self.version)
def request(self, url, method, **kwargs):
endpoint_filter = kwargs.setdefault('endpoint_filter', {})
self._set_endpoint_filter_kwargs(endpoint_filter)
if self.endpoint_override:
kwargs.setdefault('endpoint_override', self.endpoint_override)
if self.auth:
kwargs.setdefault('auth', self.auth)
if self.user_agent:
kwargs.setdefault('user_agent', self.user_agent)
if self.connect_retries is not None:
kwargs.setdefault('connect_retries', self.connect_retries)
if self.logger:
kwargs.setdefault('logger', self.logger)
return self.session.request(url, method, **kwargs)
def get_token(self, auth=None):
"""Return a token as provided by the auth plugin.
:param auth: The auth plugin to use for token. Overrides the plugin
on the session. (optional)
:type auth: :class:`keystoneclient.auth.base.BaseAuthPlugin`
:raises keystoneclient.exceptions.AuthorizationFailure: if a new token
fetch fails.
:returns: A valid token.
:rtype: string
"""
return self.session.get_token(auth or self.auth)
def get_endpoint(self, auth=None, **kwargs):
"""Get an endpoint as provided by the auth plugin.
:param auth: The auth plugin to use for token. Overrides the plugin on
the session. (optional)
:type auth: :class:`keystoneclient.auth.base.BaseAuthPlugin`
:raises keystoneclient.exceptions.MissingAuthPlugin: if a plugin is not
available.
:returns: An endpoint if available or None.
:rtype: string
"""
if self.endpoint_override:
return self.endpoint_override
self._set_endpoint_filter_kwargs(kwargs)
return self.session.get_endpoint(auth or self.auth, **kwargs)
def invalidate(self, auth=None):
"""Invalidate an authentication plugin."""
return self.session.invalidate(auth or self.auth)
def get_user_id(self, auth=None):
"""Return the authenticated user_id as provided by the auth plugin.
:param auth: The auth plugin to use for token. Overrides the plugin
on the session. (optional)
:type auth: keystoneclient.auth.base.BaseAuthPlugin
:raises keystoneclient.exceptions.AuthorizationFailure:
if a new token fetch fails.
:raises keystoneclient.exceptions.MissingAuthPlugin:
if a plugin is not available.
:returns: Current `user_id` or None if not supported by plugin.
:rtype: string
"""
return self.session.get_user_id(auth or self.auth)
def get_project_id(self, auth=None):
"""Return the authenticated project_id as provided by the auth plugin.
:param auth: The auth plugin to use for token. Overrides the plugin
on the session. (optional)
:type auth: keystoneclient.auth.base.BaseAuthPlugin
:raises keystoneclient.exceptions.AuthorizationFailure:
if a new token fetch fails.
:raises keystoneclient.exceptions.MissingAuthPlugin:
if a plugin is not available.
:returns: Current `project_id` or None if not supported by plugin.
:rtype: string
"""
return self.session.get_project_id(auth or self.auth)
def get(self, url, **kwargs):
return self.request(url, 'GET', **kwargs)
def head(self, url, **kwargs):
return self.request(url, 'HEAD', **kwargs)
def post(self, url, **kwargs):
return self.request(url, 'POST', **kwargs)
def put(self, url, **kwargs):
return self.request(url, 'PUT', **kwargs)
def patch(self, url, **kwargs):
return self.request(url, 'PATCH', **kwargs)
def delete(self, url, **kwargs):
return self.request(url, 'DELETE', **kwargs)
class LegacyJsonAdapter(Adapter):
"""Make something that looks like an old HTTPClient.
A common case when using an adapter is that we want an interface similar to
the HTTPClients of old which returned the body as JSON as well.
You probably don't want this if you are starting from scratch.
"""
def request(self, *args, **kwargs):
headers = kwargs.setdefault('headers', {})
headers.setdefault('Accept', 'application/json')
try:
kwargs['json'] = kwargs.pop('body')
except KeyError: # nosec(cjschaef): kwargs doesn't contain a 'body'
# key, while 'json' is an optional argument for Session.request
pass
resp = super(LegacyJsonAdapter, self).request(*args, **kwargs)
body = None
if resp.text:
try:
body = jsonutils.loads(resp.text)
except ValueError: # nosec(cjschaef): return None for body as
# expected
pass
return resp, body

View File

@@ -1,38 +0,0 @@
# 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.
from keystoneclient.auth.base import * # noqa
from keystoneclient.auth.cli import * # noqa
from keystoneclient.auth.conf import * # noqa
__all__ = (
# auth.base
'AUTH_INTERFACE',
'BaseAuthPlugin',
'get_available_plugin_names',
'get_available_plugin_classes',
'get_plugin_class',
'IDENTITY_AUTH_HEADER_NAME',
'PLUGIN_NAMESPACE',
# auth.cli
'load_from_argparse_arguments',
'register_argparse_arguments',
# auth.conf
'get_common_conf_options',
'get_plugin_options',
'load_from_conf_options',
'register_conf_options',
)

View File

@@ -1,374 +0,0 @@
# 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
from debtcollector import removals
from keystoneauth1 import plugin
import six
import stevedore
from keystoneclient import exceptions
# NOTE(jamielennox): The AUTH_INTERFACE is a special value that can be
# requested from get_endpoint. If a plugin receives this as the value of
# 'interface' it should return the initial URL that was passed to the plugin.
AUTH_INTERFACE = plugin.AUTH_INTERFACE
PLUGIN_NAMESPACE = 'keystoneclient.auth.plugin'
IDENTITY_AUTH_HEADER_NAME = 'X-Auth-Token'
@removals.remove(
message='keystoneclient auth plugins are deprecated. Use keystoneauth.',
version='2.1.0',
removal_version='3.0.0'
)
def get_available_plugin_names():
"""Get the names of all the plugins that are available on the system.
This is particularly useful for help and error text to prompt a user for
example what plugins they may specify.
:returns: A list of names.
:rtype: frozenset
"""
mgr = stevedore.ExtensionManager(namespace=PLUGIN_NAMESPACE,
invoke_on_load=False)
return frozenset(mgr.names())
@removals.remove(
message='keystoneclient auth plugins are deprecated. Use keystoneauth.',
version='2.1.0',
removal_version='3.0.0'
)
def get_available_plugin_classes():
"""Retrieve all the plugin classes available on the system.
:returns: A dict with plugin entrypoint name as the key and the plugin
class as the value.
:rtype: dict
"""
mgr = stevedore.ExtensionManager(namespace=PLUGIN_NAMESPACE,
propagate_map_exceptions=True,
invoke_on_load=False)
return dict(mgr.map(lambda ext: (ext.entry_point.name, ext.plugin)))
@removals.remove(
message='keystoneclient auth plugins are deprecated. Use keystoneauth.',
version='2.1.0',
removal_version='3.0.0'
)
def get_plugin_class(name):
"""Retrieve a plugin class by its entrypoint name.
:param str name: The name of the object to get.
:returns: An auth plugin class.
:rtype: :py:class:`keystoneclient.auth.BaseAuthPlugin`
:raises keystoneclient.exceptions.NoMatchingPlugin: if a plugin cannot be
created.
"""
try:
mgr = stevedore.DriverManager(namespace=PLUGIN_NAMESPACE,
name=name,
invoke_on_load=False)
except RuntimeError:
raise exceptions.NoMatchingPlugin(name)
return mgr.driver
class BaseAuthPlugin(object):
"""The basic structure of an authentication plugin."""
def get_token(self, session, **kwargs):
"""Obtain a token.
How the token is obtained is up to the plugin. If it is still valid
it may be re-used, retrieved from cache or invoke an authentication
request against a server.
There are no required kwargs. They are passed directly to the auth
plugin and they are implementation specific.
Returning None will indicate that no token was able to be retrieved.
This function is misplaced as it should only be required for auth
plugins that use the 'X-Auth-Token' header. However due to the way
plugins evolved this method is required and often called to trigger an
authentication request on a new plugin.
When implementing a new plugin it is advised that you implement this
method, however if you don't require the 'X-Auth-Token' header override
the `get_headers` method instead.
:param session: A session object so the plugin can make HTTP calls.
:type session: keystoneclient.session.Session
:return: A token to use.
:rtype: string
"""
return None
def get_headers(self, session, **kwargs):
"""Fetch authentication headers for message.
This is a more generalized replacement of the older get_token to allow
plugins to specify different or additional authentication headers to
the OpenStack standard 'X-Auth-Token' header.
How the authentication headers are obtained is up to the plugin. If the
headers are still valid they may be re-used, retrieved from cache or
the plugin may invoke an authentication request against a server.
The default implementation of get_headers calls the `get_token` method
to enable older style plugins to continue functioning unchanged.
Subclasses should feel free to completely override this function to
provide the headers that they want.
There are no required kwargs. They are passed directly to the auth
plugin and they are implementation specific.
Returning None will indicate that no token was able to be retrieved and
that authorization was a failure. Adding no authentication data can be
achieved by returning an empty dictionary.
:param session: The session object that the auth_plugin belongs to.
:type session: keystoneclient.session.Session
:returns: Headers that are set to authenticate a message or None for
failure. Note that when checking this value that the empty
dict is a valid, non-failure response.
:rtype: dict
"""
token = self.get_token(session)
if not token:
return None
return {IDENTITY_AUTH_HEADER_NAME: token}
def get_endpoint(self, session, **kwargs):
"""Return an endpoint for the client.
There are no required keyword arguments to ``get_endpoint`` as a plugin
implementation should use best effort with the information available to
determine the endpoint. However there are certain standard options that
will be generated by the clients and should be used by plugins:
- ``service_type``: what sort of service is required.
- ``service_name``: the name of the service in the catalog.
- ``interface``: what visibility the endpoint should have.
- ``region_name``: the region the endpoint exists in.
:param session: The session object that the auth_plugin belongs to.
:type session: keystoneclient.session.Session
:returns: The base URL that will be used to talk to the required
service or None if not available.
:rtype: string
"""
return None
def get_connection_params(self, session, **kwargs):
"""Return any additional connection parameters required for the plugin.
:param session: The session object that the auth_plugin belongs to.
:type session: keystoneclient.session.Session
:returns: Headers that are set to authenticate a message or None for
failure. Note that when checking this value that the empty
dict is a valid, non-failure response.
:rtype: dict
"""
return {}
def invalidate(self):
"""Invalidate the current authentication data.
This should result in fetching a new token on next call.
A plugin may be invalidated if an Unauthorized HTTP response is
returned to indicate that the token may have been revoked or is
otherwise now invalid.
:returns: True if there was something that the plugin did to
invalidate. This means that it makes sense to try again. If
nothing happens returns False to indicate give up.
:rtype: bool
"""
return False
def get_user_id(self, session, **kwargs):
"""Return a unique user identifier of the plugin.
Wherever possible the user id should be inferred from the token however
there are certain URLs and other places that require access to the
currently authenticated user id.
:param session: A session object so the plugin can make HTTP calls.
:type session: keystoneclient.session.Session
:returns: A user identifier or None if one is not available.
:rtype: str
"""
return None
def get_project_id(self, session, **kwargs):
"""Return the project id that we are authenticated to.
Wherever possible the project id should be inferred from the token
however there are certain URLs and other places that require access to
the currently authenticated project id.
:param session: A session object so the plugin can make HTTP calls.
:type session: keystoneclient.session.Session
:returns: A project identifier or None if one is not available.
:rtype: str
"""
return None
@classmethod
def get_options(cls):
"""Return the list of parameters associated with the auth plugin.
This list may be used to generate CLI or config arguments.
:returns: A list of Param objects describing available plugin
parameters.
:rtype: List
"""
return []
@classmethod
def load_from_options(cls, **kwargs):
"""Create a plugin from the arguments retrieved from get_options.
A client can override this function to do argument validation or to
handle differences between the registered options and what is required
to create the plugin.
"""
return cls(**kwargs)
@classmethod
def register_argparse_arguments(cls, parser):
"""Register the CLI options provided by a specific plugin.
Given a plugin class convert it's options into argparse arguments and
add them to a parser.
:param parser: the parser to attach argparse options.
:type parser: argparse.ArgumentParser
"""
# NOTE(jamielennox): ideally oslo_config would be smart enough to
# handle all the Opt manipulation that goes on in this file. However it
# is currently not. Options are handled in as similar a way as
# possible to oslo_config such that when available we should be able to
# transition.
for opt in cls.get_options():
args = []
envs = []
for o in [opt] + opt.deprecated_opts:
args.append('--os-%s' % o.name)
envs.append('OS_%s' % o.name.replace('-', '_').upper())
# select the first ENV that is not false-y or return None
env_vars = (os.environ.get(e) for e in envs)
default = six.next(six.moves.filter(None, env_vars), None)
parser.add_argument(*args,
default=default or opt.default,
metavar=opt.metavar,
help=opt.help,
dest='os_%s' % opt.dest)
@classmethod
def load_from_argparse_arguments(cls, namespace, **kwargs):
"""Load a specific plugin object from an argparse result.
Convert the results of a parse into the specified plugin.
:param namespace: The result from CLI parsing.
:type namespace: argparse.Namespace
:returns: An auth plugin, or None if a name is not provided.
:rtype: :py:class:`keystoneclient.auth.BaseAuthPlugin`
"""
def _getter(opt):
return getattr(namespace, 'os_%s' % opt.dest)
return cls.load_from_options_getter(_getter, **kwargs)
@classmethod
def register_conf_options(cls, conf, group):
"""Register the oslo_config options that are needed for a plugin.
:param conf: A config object.
:type conf: oslo_config.cfg.ConfigOpts
:param string group: The group name that options should be read from.
"""
plugin_opts = cls.get_options()
conf.register_opts(plugin_opts, group=group)
@classmethod
def load_from_conf_options(cls, conf, group, **kwargs):
"""Load the plugin from a CONF object.
Convert the options already registered into a real plugin.
:param conf: A config object.
:type conf: oslo_config.cfg.ConfigOpts
:param string group: The group name that options should be read from.
:returns: An authentication Plugin.
:rtype: :py:class:`keystoneclient.auth.BaseAuthPlugin`
"""
def _getter(opt):
return conf[group][opt.dest]
return cls.load_from_options_getter(_getter, **kwargs)
@classmethod
def load_from_options_getter(cls, getter, **kwargs):
"""Load a plugin from a getter function returning appropriate values.
To handle cases other than the provided CONF and CLI loading you can
specify a custom loader function that will be queried for the option
value.
The getter is a function that takes one value, an
:py:class:`oslo_config.cfg.Opt` and returns a value to load with.
:param getter: A function that returns a value for the given opt.
:type getter: callable
:returns: An authentication Plugin.
:rtype: :py:class:`keystoneclient.auth.BaseAuthPlugin`
"""
plugin_opts = cls.get_options()
for opt in plugin_opts:
val = getter(opt)
if val is not None:
val = opt.type(val)
kwargs.setdefault(opt.dest, val)
return cls.load_from_options(**kwargs)

View File

@@ -1,97 +0,0 @@
# 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 argparse
import os
from debtcollector import removals
from positional import positional
from keystoneclient.auth import base
@removals.remove(
message='keystoneclient auth plugins are deprecated. Use keystoneauth.',
version='2.1.0',
removal_version='3.0.0'
)
@positional()
def register_argparse_arguments(parser, argv, default=None):
"""Register CLI options needed to create a plugin.
The function inspects the provided arguments so that it can also register
the options required for that specific plugin if available.
:param argparse.ArgumentParser: the parser to attach argparse options to.
:param List argv: the arguments provided to the application.
:param str/class default: a default plugin name or a plugin object to use
if one isn't specified by the CLI. default: None.
:returns: The plugin class that will be loaded or None if not provided.
:rtype: :py:class:`keystoneclient.auth.BaseAuthPlugin`
:raises keystoneclient.exceptions.NoMatchingPlugin: if a plugin cannot be
created.
"""
in_parser = argparse.ArgumentParser(add_help=False)
env_plugin = os.environ.get('OS_AUTH_PLUGIN', default)
for p in (in_parser, parser):
p.add_argument('--os-auth-plugin',
metavar='<name>',
default=env_plugin,
help='The auth plugin to load')
options, _args = in_parser.parse_known_args(argv)
if not options.os_auth_plugin:
return None
if isinstance(options.os_auth_plugin, type):
msg = 'Default Authentication options'
plugin = options.os_auth_plugin
else:
msg = 'Options specific to the %s plugin.' % options.os_auth_plugin
plugin = base.get_plugin_class(options.os_auth_plugin)
group = parser.add_argument_group('Authentication Options', msg)
plugin.register_argparse_arguments(group)
return plugin
@removals.remove(
message='keystoneclient auth plugins are deprecated. Use keystoneauth.',
version='2.1.0',
removal_version='3.0.0'
)
def load_from_argparse_arguments(namespace, **kwargs):
"""Retrieve the created plugin from the completed argparse results.
Loads and creates the auth plugin from the information parsed from the
command line by argparse.
:param Namespace namespace: The result from CLI parsing.
:returns: An auth plugin, or None if a name is not provided.
:rtype: :py:class:`keystoneclient.auth.BaseAuthPlugin`
:raises keystoneclient.exceptions.NoMatchingPlugin: if a plugin cannot be
created.
"""
if not namespace.os_auth_plugin:
return None
if isinstance(namespace.os_auth_plugin, type):
plugin = namespace.os_auth_plugin
else:
plugin = base.get_plugin_class(namespace.os_auth_plugin)
return plugin.load_from_argparse_arguments(namespace, **kwargs)

View File

@@ -1,132 +0,0 @@
# 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.
from debtcollector import removals
from oslo_config import cfg
from keystoneclient.auth import base
_AUTH_PLUGIN_OPT = cfg.StrOpt('auth_plugin', help='Name of the plugin to load')
_section_help = 'Config Section from which to load plugin specific options'
_AUTH_SECTION_OPT = cfg.StrOpt('auth_section', help=_section_help)
@removals.remove(
message='keystoneclient auth plugins are deprecated. Use keystoneauth.',
version='2.1.0',
removal_version='3.0.0'
)
def get_common_conf_options():
"""Get the oslo_config options common for all auth plugins.
These may be useful without being registered for config file generation
or to manipulate the options before registering them yourself.
The options that are set are:
:auth_plugin: The name of the plugin to load.
:auth_section: The config file section to load options from.
:returns: A list of oslo_config options.
"""
return [_AUTH_PLUGIN_OPT, _AUTH_SECTION_OPT]
@removals.remove(
message='keystoneclient auth plugins are deprecated. Use keystoneauth.',
version='2.1.0',
removal_version='3.0.0'
)
def get_plugin_options(name):
"""Get the oslo_config options for a specific plugin.
This will be the list of config options that is registered and loaded by
the specified plugin.
:returns: A list of oslo_config options.
"""
return base.get_plugin_class(name).get_options()
@removals.remove(
message='keystoneclient auth plugins are deprecated. Use keystoneauth.',
version='2.1.0',
removal_version='3.0.0'
)
def register_conf_options(conf, group):
"""Register the oslo_config options that are needed for a plugin.
This only registers the basic options shared by all plugins. Options that
are specific to a plugin are loaded just before they are read.
The defined options are:
- auth_plugin: the name of the auth plugin that will be used for
authentication.
- auth_section: the group from which further auth plugin options should be
taken. If section is not provided then the auth plugin options will be
taken from the same group as provided in the parameters.
:param conf: config object to register with.
:type conf: oslo_config.cfg.ConfigOpts
:param string group: The ini group to register options in.
"""
conf.register_opt(_AUTH_SECTION_OPT, group=group)
# NOTE(jamielennox): plugins are allowed to specify a 'section' which is
# the group that auth options should be taken from. If not present they
# come from the same as the base options were registered in. If present
# then the auth_plugin option may be read from that section so add that
# option.
if conf[group].auth_section:
group = conf[group].auth_section
conf.register_opt(_AUTH_PLUGIN_OPT, group=group)
@removals.remove(
message='keystoneclient auth plugins are deprecated. Use keystoneauth.',
version='2.1.0',
removal_version='3.0.0'
)
def load_from_conf_options(conf, group, **kwargs):
"""Load a plugin from an oslo_config CONF object.
Each plugin will register their own required options and so there is no
standard list and the plugin should be consulted.
The base options should have been registered with register_conf_options
before this function is called.
:param conf: A conf object.
:type conf: oslo_config.cfg.ConfigOpts
:param string group: The group name that options should be read from.
:returns: An authentication Plugin or None if a name is not provided
:rtype: :py:class:`keystoneclient.auth.BaseAuthPlugin`
:raises keystoneclient.exceptions.NoMatchingPlugin: if a plugin cannot be
created.
"""
# NOTE(jamielennox): plugins are allowed to specify a 'section' which is
# the group that auth options should be taken from. If not present they
# come from the same as the base options were registered in.
if conf[group].auth_section:
group = conf[group].auth_section
name = conf[group].auth_plugin
if not name:
return None
plugin_class = base.get_plugin_class(name)
plugin_class.register_conf_options(conf, group)
return plugin_class.load_from_conf_options(conf, group, **kwargs)

View File

@@ -1,37 +0,0 @@
# 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.
from keystoneclient.auth.identity import base
from keystoneclient.auth.identity import generic
from keystoneclient.auth.identity import v2
from keystoneclient.auth.identity import v3
BaseIdentityPlugin = base.BaseIdentityPlugin
V2Password = v2.Password
V2Token = v2.Token
V3Password = v3.Password
V3Token = v3.Token
Password = generic.Password
Token = generic.Token
__all__ = ('BaseIdentityPlugin',
'Password',
'Token',
'V2Password',
'V2Token',
'V3Password',
'V3Token')

View File

@@ -1,48 +0,0 @@
# 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.
from positional import positional
from keystoneclient.auth.identity import base
class AccessInfoPlugin(base.BaseIdentityPlugin):
"""A plugin that turns an existing AccessInfo object into a usable plugin.
There are cases where reuse of an auth_ref or AccessInfo object is
warranted such as from a cache, from auth_token middleware, or another
source.
Turn the existing access info object into an identity plugin. This plugin
cannot be refreshed as the AccessInfo object does not contain any
authorizing information.
:param auth_ref: the existing AccessInfo object.
:type auth_ref: keystoneclient.access.AccessInfo
:param auth_url: the url where this AccessInfo was retrieved from. Required
if using the AUTH_INTERFACE with get_endpoint. (optional)
"""
@positional()
def __init__(self, auth_ref, auth_url=None):
super(AccessInfoPlugin, self).__init__(auth_url=auth_url,
reauthenticate=False)
self.auth_ref = auth_ref
def get_auth_ref(self, session, **kwargs):
return self.auth_ref
def invalidate(self):
# NOTE(jamielennox): Don't allow the default invalidation to occur
# because on next authentication request we will only get the same
# auth_ref object again.
return False

View File

@@ -1,422 +0,0 @@
# 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 abc
import logging
import threading
import warnings
from oslo_config import cfg
from positional import positional
import six
from keystoneclient import _discover
from keystoneclient.auth import base
from keystoneclient import exceptions
LOG = logging.getLogger(__name__)
def get_options():
return [
cfg.StrOpt('auth-url', help='Authentication URL'),
]
@six.add_metaclass(abc.ABCMeta)
class BaseIdentityPlugin(base.BaseAuthPlugin):
# we count a token as valid (not needing refreshing) if it is valid for at
# least this many seconds before the token expiry time
MIN_TOKEN_LIFE_SECONDS = 120
def __init__(self,
auth_url=None,
username=None,
password=None,
token=None,
trust_id=None,
reauthenticate=True):
super(BaseIdentityPlugin, self).__init__()
warnings.warn(
'keystoneclient auth plugins are deprecated as of the 2.1.0 '
'release in favor of keystoneauth1 plugins. They will be removed '
'in future releases.', DeprecationWarning)
self.auth_url = auth_url
self.auth_ref = None
self.reauthenticate = reauthenticate
self._endpoint_cache = {}
self._lock = threading.Lock()
self._username = username
self._password = password
self._token = token
self._trust_id = trust_id
@property
def username(self):
"""Deprecated as of the 1.7.0 release.
It may be removed in the 2.0.0 release.
"""
warnings.warn(
'username is deprecated as of the 1.7.0 release and may be '
'removed in the 2.0.0 release.', DeprecationWarning)
return self._username
@username.setter
def username(self, value):
"""Deprecated as of the 1.7.0 release.
It may be removed in the 2.0.0 release.
"""
warnings.warn(
'username is deprecated as of the 1.7.0 release and may be '
'removed in the 2.0.0 release.', DeprecationWarning)
self._username = value
@property
def password(self):
"""Deprecated as of the 1.7.0 release.
It may be removed in the 2.0.0 release.
"""
warnings.warn(
'password is deprecated as of the 1.7.0 release and may be '
'removed in the 2.0.0 release.', DeprecationWarning)
return self._password
@password.setter
def password(self, value):
"""Deprecated as of the 1.7.0 release.
It may be removed in the 2.0.0 release.
"""
warnings.warn(
'password is deprecated as of the 1.7.0 release and may be '
'removed in the 2.0.0 release.', DeprecationWarning)
self._password = value
@property
def token(self):
"""Deprecated as of the 1.7.0 release.
It may be removed in the 2.0.0 release.
"""
warnings.warn(
'token is deprecated as of the 1.7.0 release and may be '
'removed in the 2.0.0 release.', DeprecationWarning)
return self._token
@token.setter
def token(self, value):
"""Deprecated as of the 1.7.0 release.
It may be removed in the 2.0.0 release.
"""
warnings.warn(
'token is deprecated as of the 1.7.0 release and may be '
'removed in the 2.0.0 release.', DeprecationWarning)
self._token = value
@property
def trust_id(self):
"""Deprecated as of the 1.7.0 release.
It may be removed in the 2.0.0 release.
"""
warnings.warn(
'trust_id is deprecated as of the 1.7.0 release and may be '
'removed in the 2.0.0 release.', DeprecationWarning)
return self._trust_id
@trust_id.setter
def trust_id(self, value):
"""Deprecated as of the 1.7.0 release.
It may be removed in the 2.0.0 release.
"""
warnings.warn(
'trust_id is deprecated as of the 1.7.0 release and may be '
'removed in the 2.0.0 release.', DeprecationWarning)
self._trust_id = value
@abc.abstractmethod
def get_auth_ref(self, session, **kwargs):
"""Obtain a token from an OpenStack Identity Service.
This method is overridden by the various token version plugins.
This method should not be called independently and is expected to be
invoked via the do_authenticate() method.
This method will be invoked if the AccessInfo object cached by the
plugin is not valid. Thus plugins should always fetch a new AccessInfo
when invoked. If you are looking to just retrieve the current auth data
then you should use get_access().
:param session: A session object that can be used for communication.
:type session: keystoneclient.session.Session
:raises keystoneclient.exceptions.InvalidResponse: The response
returned wasn't
appropriate.
:raises keystoneclient.exceptions.HttpError: An error from an invalid
HTTP response.
:returns: Token access information.
:rtype: :py:class:`keystoneclient.access.AccessInfo`
"""
pass # pragma: no cover
def get_token(self, session, **kwargs):
"""Return a valid auth token.
If a valid token is not present then a new one will be fetched.
:param session: A session object that can be used for communication.
:type session: keystoneclient.session.Session
:raises keystoneclient.exceptions.HttpError: An error from an invalid
HTTP response.
:return: A valid token.
:rtype: string
"""
return self.get_access(session).auth_token
def _needs_reauthenticate(self):
"""Return if the existing token needs to be re-authenticated.
The token should be refreshed if it is about to expire.
:returns: True if the plugin should fetch a new token. False otherwise.
"""
if not self.auth_ref:
# authentication was never fetched.
return True
if not self.reauthenticate:
# don't re-authenticate if it has been disallowed.
return False
if self.auth_ref.will_expire_soon(self.MIN_TOKEN_LIFE_SECONDS):
# if it's about to expire we should re-authenticate now.
return True
# otherwise it's fine and use the existing one.
return False
def get_access(self, session, **kwargs):
"""Fetch or return a current AccessInfo object.
If a valid AccessInfo is present then it is returned otherwise a new
one will be fetched.
:param session: A session object that can be used for communication.
:type session: keystoneclient.session.Session
:raises keystoneclient.exceptions.HttpError: An error from an invalid
HTTP response.
:returns: Valid AccessInfo
:rtype: :py:class:`keystoneclient.access.AccessInfo`
"""
# Hey Kids! Thread safety is important particularly in the case where
# a service is creating an admin style plugin that will then proceed
# to make calls from many threads. As a token expires all the threads
# will try and fetch a new token at once, so we want to ensure that
# only one thread tries to actually fetch from keystone at once.
with self._lock:
if self._needs_reauthenticate():
self.auth_ref = self.get_auth_ref(session)
return self.auth_ref
def invalidate(self):
"""Invalidate the current authentication data.
This should result in fetching a new token on next call.
A plugin may be invalidated if an Unauthorized HTTP response is
returned to indicate that the token may have been revoked or is
otherwise now invalid.
:returns: True if there was something that the plugin did to
invalidate. This means that it makes sense to try again. If
nothing happens returns False to indicate give up.
:rtype: bool
"""
if self.auth_ref:
self.auth_ref = None
return True
return False
def get_endpoint(self, session, service_type=None, interface=None,
region_name=None, service_name=None, version=None,
**kwargs):
"""Return a valid endpoint for a service.
If a valid token is not present then a new one will be fetched using
the session and kwargs.
:param session: A session object that can be used for communication.
:type session: keystoneclient.session.Session
:param string service_type: The type of service to lookup the endpoint
for. This plugin will return None (failure)
if service_type is not provided.
:param string interface: The exposure of the endpoint. Should be
`public`, `internal`, `admin`, or `auth`.
`auth` is special here to use the `auth_url`
rather than a URL extracted from the service
catalog. Defaults to `public`.
:param string region_name: The region the endpoint should exist in.
(optional)
:param string service_name: The name of the service in the catalog.
(optional)
:param tuple version: The minimum version number required for this
endpoint. (optional)
:raises keystoneclient.exceptions.HttpError: An error from an invalid
HTTP response.
:return: A valid endpoint URL or None if not available.
:rtype: string or None
"""
# NOTE(jamielennox): if you specifically ask for requests to be sent to
# the auth url then we can ignore many of the checks. Typically if you
# are asking for the auth endpoint it means that there is no catalog to
# query however we still need to support asking for a specific version
# of the auth_url for generic plugins.
if interface is base.AUTH_INTERFACE:
url = self.auth_url
service_type = service_type or 'identity'
else:
if not service_type:
LOG.warning(
'Plugin cannot return an endpoint without knowing the '
'service type that is required. Add service_type to '
'endpoint filtering data.')
return None
if not interface:
interface = 'public'
service_catalog = self.get_access(session).service_catalog
url = service_catalog.url_for(service_type=service_type,
endpoint_type=interface,
region_name=region_name,
service_name=service_name)
if not version:
# NOTE(jamielennox): This may not be the best thing to default to
# but is here for backwards compatibility. It may be worth
# defaulting to the most recent version.
return url
# NOTE(jamielennox): For backwards compatibility people might have a
# versioned endpoint in their catalog even though they want to use
# other endpoint versions. So we support a list of client defined
# situations where we can strip the version component from a URL before
# doing discovery.
hacked_url = _discover.get_catalog_discover_hack(service_type, url)
try:
disc = self.get_discovery(session, hacked_url, authenticated=False)
except (exceptions.DiscoveryFailure,
exceptions.HTTPError,
exceptions.ConnectionError):
# NOTE(jamielennox): Again if we can't contact the server we fall
# back to just returning the URL from the catalog. This may not be
# the best default but we need it for now.
LOG.warning(
'Failed to contact the endpoint at %s for discovery. Fallback '
'to using that endpoint as the base url.', url)
else:
url = disc.url_for(version)
return url
def get_user_id(self, session, **kwargs):
return self.get_access(session).user_id
def get_project_id(self, session, **kwargs):
return self.get_access(session).project_id
@positional()
def get_discovery(self, session, url, authenticated=None):
"""Return the discovery object for a URL.
Check the session and the plugin cache to see if we have already
performed discovery on the URL and if so return it, otherwise create
a new discovery object, cache it and return it.
This function is expected to be used by subclasses and should not
be needed by users.
:param session: A session object to discover with.
:type session: keystoneclient.session.Session
:param str url: The url to lookup.
:param bool authenticated: Include a token in the discovery call.
(optional) Defaults to None (use a token
if a plugin is installed).
:raises keystoneclient.exceptions.DiscoveryFailure: if for some reason
the lookup fails.
:raises keystoneclient.exceptions.HttpError: An error from an invalid
HTTP response.
:returns: A discovery object with the results of looking up that URL.
"""
# NOTE(jamielennox): we want to cache endpoints on the session as well
# so that they maintain sharing between auth plugins. Create a cache on
# the session if it doesn't exist already.
try:
session_endpoint_cache = session._identity_endpoint_cache
except AttributeError:
session_endpoint_cache = session._identity_endpoint_cache = {}
# NOTE(jamielennox): There is a cache located on both the session
# object and the auth plugin object so that they can be shared and the
# cache is still usable
for cache in (self._endpoint_cache, session_endpoint_cache):
disc = cache.get(url)
if disc:
break
else:
disc = _discover.Discover(session, url,
authenticated=authenticated)
self._endpoint_cache[url] = disc
session_endpoint_cache[url] = disc
return disc
@classmethod
def get_options(cls):
options = super(BaseIdentityPlugin, cls).get_options()
options.extend(get_options())
return options

View File

@@ -1,21 +0,0 @@
# 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.
from keystoneclient.auth.identity.generic.base import BaseGenericPlugin # noqa
from keystoneclient.auth.identity.generic.password import Password # noqa
from keystoneclient.auth.identity.generic.token import Token # noqa
__all__ = ('BaseGenericPlugin',
'Password',
'Token',
)

View File

@@ -1,192 +0,0 @@
# 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 abc
import logging
from oslo_config import cfg
import six
import six.moves.urllib.parse as urlparse
from keystoneclient import _discover
from keystoneclient.auth.identity import base
from keystoneclient import exceptions
from keystoneclient.i18n import _
LOG = logging.getLogger(__name__)
def get_options():
return [
cfg.StrOpt('domain-id', help='Domain ID to scope to'),
cfg.StrOpt('domain-name', help='Domain name to scope to'),
cfg.StrOpt('tenant-id', help='Tenant ID to scope to'),
cfg.StrOpt('tenant-name', help='Tenant name to scope to'),
cfg.StrOpt('project-id', help='Project ID to scope to'),
cfg.StrOpt('project-name', help='Project name to scope to'),
cfg.StrOpt('project-domain-id',
help='Domain ID containing project'),
cfg.StrOpt('project-domain-name',
help='Domain name containing project'),
cfg.StrOpt('trust-id', help='Trust ID'),
]
@six.add_metaclass(abc.ABCMeta)
class BaseGenericPlugin(base.BaseIdentityPlugin):
"""An identity plugin that is not version dependent.
Internally we will construct a version dependent plugin with the resolved
URL and then proxy all calls from the base plugin to the versioned one.
"""
def __init__(self, auth_url,
tenant_id=None,
tenant_name=None,
project_id=None,
project_name=None,
project_domain_id=None,
project_domain_name=None,
domain_id=None,
domain_name=None,
trust_id=None):
super(BaseGenericPlugin, self).__init__(auth_url=auth_url)
self._project_id = project_id or tenant_id
self._project_name = project_name or tenant_name
self._project_domain_id = project_domain_id
self._project_domain_name = project_domain_name
self._domain_id = domain_id
self._domain_name = domain_name
self._trust_id = trust_id
self._plugin = None
@property
def trust_id(self):
# Override to remove deprecation.
return self._trust_id
@trust_id.setter
def trust_id(self, value):
# Override to remove deprecation.
self._trust_id = value
@abc.abstractmethod
def create_plugin(self, session, version, url, raw_status=None):
"""Create a plugin from the given parameters.
This function will be called multiple times with the version and url
of a potential endpoint. If a plugin can be constructed that fits the
params then it should return it. If not return None and then another
call will be made with other available URLs.
:param session: A session object.
:type session: keystoneclient.session.Session
:param tuple version: A tuple of the API version at the URL.
:param string url: The base URL for this version.
:param string raw_status: The status that was in the discovery field.
:returns: A plugin that can match the parameters or None if nothing.
"""
return None # pragma: no cover
@property
def _has_domain_scope(self):
"""Are there domain parameters.
Domain parameters are v3 only so returns if any are set.
:returns: True if a domain parameter is set, false otherwise.
"""
return any([self._domain_id, self._domain_name,
self._project_domain_id, self._project_domain_name])
@property
def _v2_params(self):
"""Return parameters that are common to v2 plugins."""
return {'trust_id': self._trust_id,
'tenant_id': self._project_id,
'tenant_name': self._project_name}
@property
def _v3_params(self):
"""Return parameters that are common to v3 plugins."""
return {'trust_id': self._trust_id,
'project_id': self._project_id,
'project_name': self._project_name,
'project_domain_id': self._project_domain_id,
'project_domain_name': self._project_domain_name,
'domain_id': self._domain_id,
'domain_name': self._domain_name}
def _do_create_plugin(self, session):
plugin = None
try:
disc = self.get_discovery(session,
self.auth_url,
authenticated=False)
except (exceptions.DiscoveryFailure,
exceptions.HTTPError,
exceptions.ConnectionError):
LOG.warning('Discovering versions from the identity service '
'failed when creating the password plugin. '
'Attempting to determine version from URL.')
url_parts = urlparse.urlparse(self.auth_url)
path = url_parts.path.lower()
if path.startswith('/v2.0') and not self._has_domain_scope:
plugin = self.create_plugin(session, (2, 0), self.auth_url)
elif path.startswith('/v3'):
plugin = self.create_plugin(session, (3, 0), self.auth_url)
else:
disc_data = disc.version_data()
for data in disc_data:
version = data['version']
if (_discover.version_match((2,), version) and
self._has_domain_scope):
# NOTE(jamielennox): if there are domain parameters there
# is no point even trying against v2 APIs.
continue
plugin = self.create_plugin(session,
version,
data['url'],
raw_status=data['raw_status'])
if plugin:
break
if plugin:
return plugin
# so there were no URLs that i could use for auth of any version.
msg = _('Could not determine a suitable URL for the plugin')
raise exceptions.DiscoveryFailure(msg)
def get_auth_ref(self, session, **kwargs):
if not self._plugin:
self._plugin = self._do_create_plugin(session)
return self._plugin.get_auth_ref(session, **kwargs)
@classmethod
def get_options(cls):
options = super(BaseGenericPlugin, cls).get_options()
options.extend(get_options())
return options

View File

@@ -1,84 +0,0 @@
# 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.
from oslo_config import cfg
from positional import positional
from keystoneclient.auth.identity.generic import password
from keystoneclient import exceptions as exc
from keystoneclient.i18n import _
class DefaultCLI(password.Password):
"""A Plugin that provides typical authentication options for CLIs.
This plugin provides standard username and password authentication options
as well as allowing users to override with a custom token and endpoint.
"""
@positional()
def __init__(self, endpoint=None, token=None, **kwargs):
super(DefaultCLI, self).__init__(**kwargs)
self._token = token
self._endpoint = endpoint
@classmethod
def get_options(cls):
options = super(DefaultCLI, cls).get_options()
options.extend([cfg.StrOpt('endpoint',
help='A URL to use instead of a catalog'),
cfg.StrOpt('token',
secret=True,
help='Always use the specified token')])
return options
def get_token(self, *args, **kwargs):
if self._token:
return self._token
return super(DefaultCLI, self).get_token(*args, **kwargs)
def get_endpoint(self, *args, **kwargs):
if self._endpoint:
return self._endpoint
return super(DefaultCLI, self).get_endpoint(*args, **kwargs)
@classmethod
def load_from_argparse_arguments(cls, namespace, **kwargs):
token = kwargs.get('token') or namespace.os_token
endpoint = kwargs.get('endpoint') or namespace.os_endpoint
auth_url = kwargs.get('auth_url') or namespace.os_auth_url
if token and not endpoint:
# if a user provides a token then they must also provide an
# endpoint because we aren't fetching a token to get a catalog from
msg = _('A service URL must be provided with a token')
raise exc.CommandError(msg)
elif (not token) and (not auth_url):
# if you don't provide a token you are going to provide at least an
# auth_url with which to authenticate.
raise exc.CommandError(_('Expecting an auth URL via either '
'--os-auth-url or env[OS_AUTH_URL]'))
plugin = super(DefaultCLI, cls).load_from_argparse_arguments(namespace,
**kwargs)
if (not token) and (not plugin._password):
# we do this after the load so that the base plugin has an
# opportunity to prompt the user for a password
raise exc.CommandError(_('Expecting a password provided via '
'either --os-password, env[OS_PASSWORD], '
'or prompted response'))
return plugin

View File

@@ -1,89 +0,0 @@
# 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.
from oslo_config import cfg
from positional import positional
from keystoneclient import _discover
from keystoneclient.auth.identity.generic import base
from keystoneclient.auth.identity import v2
from keystoneclient.auth.identity import v3
from keystoneclient import utils
def get_options():
return [
cfg.StrOpt('user-id', help='User id'),
cfg.StrOpt('username', dest='username', help='Username',
deprecated_name='user-name'),
cfg.StrOpt('user-domain-id', help="User's domain id"),
cfg.StrOpt('user-domain-name', help="User's domain name"),
cfg.StrOpt('password', secret=True, help="User's password"),
]
class Password(base.BaseGenericPlugin):
"""A common user/password authentication plugin.
:param string username: Username for authentication.
:param string user_id: User ID for authentication.
:param string password: Password for authentication.
:param string user_domain_id: User's domain ID for authentication.
:param string user_domain_name: User's domain name for authentication.
"""
@positional()
def __init__(self, auth_url, username=None, user_id=None, password=None,
user_domain_id=None, user_domain_name=None, **kwargs):
super(Password, self).__init__(auth_url=auth_url, **kwargs)
self._username = username
self._user_id = user_id
self._password = password
self._user_domain_id = user_domain_id
self._user_domain_name = user_domain_name
def create_plugin(self, session, version, url, raw_status=None):
if _discover.version_match((2,), version):
if self._user_domain_id or self._user_domain_name:
# If you specify any domain parameters it won't work so quit.
return None
return v2.Password(auth_url=url,
user_id=self._user_id,
username=self._username,
password=self._password,
**self._v2_params)
elif _discover.version_match((3,), version):
return v3.Password(auth_url=url,
user_id=self._user_id,
username=self._username,
user_domain_id=self._user_domain_id,
user_domain_name=self._user_domain_name,
password=self._password,
**self._v3_params)
@classmethod
def get_options(cls):
options = super(Password, cls).get_options()
options.extend(get_options())
return options
@classmethod
def load_from_argparse_arguments(cls, namespace, **kwargs):
if not (kwargs.get('password') or namespace.os_password):
kwargs['password'] = utils.prompt_user_password()
return super(Password, cls).load_from_argparse_arguments(namespace,
**kwargs)

View File

@@ -1,48 +0,0 @@
# 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.
from oslo_config import cfg
from keystoneclient import _discover
from keystoneclient.auth.identity.generic import base
from keystoneclient.auth.identity import v2
from keystoneclient.auth.identity import v3
def get_options():
return [
cfg.StrOpt('token', secret=True, help='Token to authenticate with'),
]
class Token(base.BaseGenericPlugin):
"""Generic token auth plugin.
:param string token: Token for authentication.
"""
def __init__(self, auth_url, token=None, **kwargs):
super(Token, self).__init__(auth_url, **kwargs)
self._token = token
def create_plugin(self, session, version, url, raw_status=None):
if _discover.version_match((2,), version):
return v2.Token(url, self._token, **self._v2_params)
elif _discover.version_match((3,), version):
return v3.Token(url, self._token, **self._v3_params)
@classmethod
def get_options(cls):
options = super(Token, cls).get_options()
options.extend(get_options())
return options

View File

@@ -1,242 +0,0 @@
# 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 abc
import logging
from oslo_config import cfg
from positional import positional
import six
from keystoneclient import access
from keystoneclient.auth.identity import base
from keystoneclient import exceptions
from keystoneclient import utils
_logger = logging.getLogger(__name__)
@six.add_metaclass(abc.ABCMeta)
class Auth(base.BaseIdentityPlugin):
"""Identity V2 Authentication Plugin.
:param string auth_url: Identity service endpoint for authorization.
:param string trust_id: Trust ID for trust scoping.
:param string tenant_id: Tenant ID for project scoping.
:param string tenant_name: Tenant name for project scoping.
:param bool reauthenticate: Allow fetching a new token if the current one
is going to expire. (optional) default True
"""
@classmethod
def get_options(cls):
options = super(Auth, cls).get_options()
options.extend([
cfg.StrOpt('tenant-id', help='Tenant ID'),
cfg.StrOpt('tenant-name', help='Tenant Name'),
cfg.StrOpt('trust-id', help='Trust ID'),
])
return options
@positional()
def __init__(self, auth_url,
trust_id=None,
tenant_id=None,
tenant_name=None,
reauthenticate=True):
super(Auth, self).__init__(auth_url=auth_url,
reauthenticate=reauthenticate)
self._trust_id = trust_id
self.tenant_id = tenant_id
self.tenant_name = tenant_name
@property
def trust_id(self):
# Override to remove deprecation.
return self._trust_id
@trust_id.setter
def trust_id(self, value):
# Override to remove deprecation.
self._trust_id = value
def get_auth_ref(self, session, **kwargs):
headers = {'Accept': 'application/json'}
url = self.auth_url.rstrip('/') + '/tokens'
params = {'auth': self.get_auth_data(headers)}
if self.tenant_id:
params['auth']['tenantId'] = self.tenant_id
elif self.tenant_name:
params['auth']['tenantName'] = self.tenant_name
if self.trust_id:
params['auth']['trust_id'] = self.trust_id
_logger.debug('Making authentication request to %s', url)
resp = session.post(url, json=params, headers=headers,
authenticated=False, log=False)
try:
resp_data = resp.json()['access']
except (KeyError, ValueError):
raise exceptions.InvalidResponse(response=resp)
return access.AccessInfoV2(**resp_data)
@abc.abstractmethod
def get_auth_data(self, headers=None):
"""Return the authentication section of an auth plugin.
:param dict headers: The headers that will be sent with the auth
request if a plugin needs to add to them.
:return: A dict of authentication data for the auth type.
:rtype: dict
"""
pass # pragma: no cover
_NOT_PASSED = object()
class Password(Auth):
"""A plugin for authenticating with a username and password.
A username or user_id must be provided.
:param string auth_url: Identity service endpoint for authorization.
:param string username: Username for authentication.
:param string password: Password for authentication.
:param string user_id: User ID for authentication.
:param string trust_id: Trust ID for trust scoping.
:param string tenant_id: Tenant ID for tenant scoping.
:param string tenant_name: Tenant name for tenant scoping.
:param bool reauthenticate: Allow fetching a new token if the current one
is going to expire. (optional) default True
:raises TypeError: if a user_id or username is not provided.
"""
@positional(4)
def __init__(self, auth_url, username=_NOT_PASSED, password=None,
user_id=_NOT_PASSED, **kwargs):
super(Password, self).__init__(auth_url, **kwargs)
if username is _NOT_PASSED and user_id is _NOT_PASSED:
msg = 'You need to specify either a username or user_id'
raise TypeError(msg)
if username is _NOT_PASSED:
username = None
if user_id is _NOT_PASSED:
user_id = None
self.user_id = user_id
self._username = username
self._password = password
@property
def username(self):
# Override to remove deprecation.
return self._username
@username.setter
def username(self, value):
# Override to remove deprecation.
self._username = value
@property
def password(self):
# Override to remove deprecation.
return self._password
@password.setter
def password(self, value):
# Override to remove deprecation.
self._password = value
def get_auth_data(self, headers=None):
auth = {'password': self.password}
if self.username:
auth['username'] = self.username
elif self.user_id:
auth['userId'] = self.user_id
return {'passwordCredentials': auth}
@classmethod
def load_from_argparse_arguments(cls, namespace, **kwargs):
if not (kwargs.get('password') or namespace.os_password):
kwargs['password'] = utils.prompt_user_password()
return super(Password, cls).load_from_argparse_arguments(namespace,
**kwargs)
@classmethod
def get_options(cls):
options = super(Password, cls).get_options()
options.extend([
cfg.StrOpt('username',
dest='username',
deprecated_name='user-name',
help='Username to login with'),
cfg.StrOpt('user-id', help='User ID to login with'),
cfg.StrOpt('password', secret=True, help='Password to use'),
])
return options
class Token(Auth):
"""A plugin for authenticating with an existing token.
:param string auth_url: Identity service endpoint for authorization.
:param string token: Existing token for authentication.
:param string tenant_id: Tenant ID for tenant scoping.
:param string tenant_name: Tenant name for tenant scoping.
:param string trust_id: Trust ID for trust scoping.
:param bool reauthenticate: Allow fetching a new token if the current one
is going to expire. (optional) default True
"""
def __init__(self, auth_url, token, **kwargs):
super(Token, self).__init__(auth_url, **kwargs)
self._token = token
@property
def token(self):
# Override to remove deprecation.
return self._token
@token.setter
def token(self, value):
# Override to remove deprecation.
self._token = value
def get_auth_data(self, headers=None):
if headers is not None:
headers['X-Auth-Token'] = self.token
return {'token': {'id': self.token}}
@classmethod
def get_options(cls):
options = super(Token, cls).get_options()
options.extend([
cfg.StrOpt('token', secret=True, help='Token'),
])
return options

View File

@@ -1,30 +0,0 @@
# 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.
from keystoneclient.auth.identity.v3.base import * # noqa
from keystoneclient.auth.identity.v3.federated import * # noqa
from keystoneclient.auth.identity.v3.password import * # noqa
from keystoneclient.auth.identity.v3.token import * # noqa
__all__ = ('Auth',
'AuthConstructor',
'AuthMethod',
'BaseAuth',
'FederatedBaseAuth',
'Password',
'PasswordMethod',
'Token',
'TokenMethod')

View File

@@ -1,265 +0,0 @@
# 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 abc
import json
import logging
from oslo_config import cfg
from positional import positional
import six
from keystoneclient import access
from keystoneclient.auth.identity import base
from keystoneclient import exceptions
from keystoneclient.i18n import _
_logger = logging.getLogger(__name__)
__all__ = ('Auth', 'AuthMethod', 'AuthConstructor', 'BaseAuth')
@six.add_metaclass(abc.ABCMeta)
class BaseAuth(base.BaseIdentityPlugin):
"""Identity V3 Authentication Plugin.
:param string auth_url: Identity service endpoint for authentication.
:param List auth_methods: A collection of methods to authenticate with.
:param string trust_id: Trust ID for trust scoping.
:param string domain_id: Domain ID for domain scoping.
:param string domain_name: Domain name for domain scoping.
:param string project_id: Project ID for project scoping.
:param string project_name: Project name for project scoping.
:param string project_domain_id: Project's domain ID for project.
:param string project_domain_name: Project's domain name for project.
:param bool reauthenticate: Allow fetching a new token if the current one
is going to expire. (optional) default True
:param bool include_catalog: Include the service catalog in the returned
token. (optional) default True.
"""
@positional()
def __init__(self, auth_url,
trust_id=None,
domain_id=None,
domain_name=None,
project_id=None,
project_name=None,
project_domain_id=None,
project_domain_name=None,
reauthenticate=True,
include_catalog=True):
super(BaseAuth, self).__init__(auth_url=auth_url,
reauthenticate=reauthenticate)
self._trust_id = trust_id
self.domain_id = domain_id
self.domain_name = domain_name
self.project_id = project_id
self.project_name = project_name
self.project_domain_id = project_domain_id
self.project_domain_name = project_domain_name
self.include_catalog = include_catalog
@property
def trust_id(self):
# Override to remove deprecation.
return self._trust_id
@trust_id.setter
def trust_id(self, value):
# Override to remove deprecation.
self._trust_id = value
@property
def token_url(self):
"""The full URL where we will send authentication data."""
return '%s/auth/tokens' % self.auth_url.rstrip('/')
@abc.abstractmethod
def get_auth_ref(self, session, **kwargs):
return None # pragma: no cover
@classmethod
def get_options(cls):
options = super(BaseAuth, cls).get_options()
options.extend([
cfg.StrOpt('domain-id', help='Domain ID to scope to'),
cfg.StrOpt('domain-name', help='Domain name to scope to'),
cfg.StrOpt('project-id', help='Project ID to scope to'),
cfg.StrOpt('project-name', help='Project name to scope to'),
cfg.StrOpt('project-domain-id',
help='Domain ID containing project'),
cfg.StrOpt('project-domain-name',
help='Domain name containing project'),
cfg.StrOpt('trust-id', help='Trust ID'),
])
return options
class Auth(BaseAuth):
"""Identity V3 Authentication Plugin.
:param string auth_url: Identity service endpoint for authentication.
:param List auth_methods: A collection of methods to authenticate with.
:param string trust_id: Trust ID for trust scoping.
:param string domain_id: Domain ID for domain scoping.
:param string domain_name: Domain name for domain scoping.
:param string project_id: Project ID for project scoping.
:param string project_name: Project name for project scoping.
:param string project_domain_id: Project's domain ID for project.
:param string project_domain_name: Project's domain name for project.
:param bool reauthenticate: Allow fetching a new token if the current one
is going to expire. (optional) default True
:param bool include_catalog: Include the service catalog in the returned
token. (optional) default True.
:param bool unscoped: Force the return of an unscoped token. This will make
the keystone server return an unscoped token even if
a default_project_id is set for this user.
"""
def __init__(self, auth_url, auth_methods, **kwargs):
self.unscoped = kwargs.pop('unscoped', False)
super(Auth, self).__init__(auth_url=auth_url, **kwargs)
self.auth_methods = auth_methods
def get_auth_ref(self, session, **kwargs):
headers = {'Accept': 'application/json'}
body = {'auth': {'identity': {}}}
ident = body['auth']['identity']
rkwargs = {}
for method in self.auth_methods:
name, auth_data = method.get_auth_data(session,
self,
headers,
request_kwargs=rkwargs)
ident.setdefault('methods', []).append(name)
ident[name] = auth_data
if not ident:
raise exceptions.AuthorizationFailure(
_('Authentication method required (e.g. password)'))
mutual_exclusion = [bool(self.domain_id or self.domain_name),
bool(self.project_id or self.project_name),
bool(self.trust_id),
bool(self.unscoped)]
if sum(mutual_exclusion) > 1:
raise exceptions.AuthorizationFailure(
_('Authentication cannot be scoped to multiple targets. Pick '
'one of: project, domain, trust or unscoped'))
if self.domain_id:
body['auth']['scope'] = {'domain': {'id': self.domain_id}}
elif self.domain_name:
body['auth']['scope'] = {'domain': {'name': self.domain_name}}
elif self.project_id:
body['auth']['scope'] = {'project': {'id': self.project_id}}
elif self.project_name:
scope = body['auth']['scope'] = {'project': {}}
scope['project']['name'] = self.project_name
if self.project_domain_id:
scope['project']['domain'] = {'id': self.project_domain_id}
elif self.project_domain_name:
scope['project']['domain'] = {'name': self.project_domain_name}
elif self.trust_id:
body['auth']['scope'] = {'OS-TRUST:trust': {'id': self.trust_id}}
elif self.unscoped:
body['auth']['scope'] = {'unscoped': {}}
# NOTE(jamielennox): we add nocatalog here rather than in token_url
# directly as some federation plugins require the base token_url
token_url = self.token_url
if not self.include_catalog:
token_url += '?nocatalog'
_logger.debug('Making authentication request to %s', token_url)
resp = session.post(token_url, json=body, headers=headers,
authenticated=False, log=False, **rkwargs)
try:
_logger.debug(json.dumps(resp.json()))
resp_data = resp.json()['token']
except (KeyError, ValueError):
raise exceptions.InvalidResponse(response=resp)
return access.AccessInfoV3(resp.headers['X-Subject-Token'],
**resp_data)
@six.add_metaclass(abc.ABCMeta)
class AuthMethod(object):
"""One part of a V3 Authentication strategy.
V3 Tokens allow multiple methods to be presented when authentication
against the server. Each one of these methods is implemented by an
AuthMethod.
Note: When implementing an AuthMethod use the method_parameters
and do not use positional arguments. Otherwise they can't be picked up by
the factory method and don't work as well with AuthConstructors.
"""
_method_parameters = []
def __init__(self, **kwargs):
for param in self._method_parameters:
setattr(self, param, kwargs.pop(param, None))
if kwargs:
msg = _("Unexpected Attributes: %s") % ", ".join(kwargs)
raise AttributeError(msg)
@classmethod
def _extract_kwargs(cls, kwargs):
"""Remove parameters related to this method from other kwargs."""
return dict([(p, kwargs.pop(p, None))
for p in cls._method_parameters])
@abc.abstractmethod
def get_auth_data(self, session, auth, headers, **kwargs):
"""Return the authentication section of an auth plugin.
:param session: The communication session.
:type session: keystoneclient.session.Session
:param base.Auth auth: The auth plugin calling the method.
:param dict headers: The headers that will be sent with the auth
request if a plugin needs to add to them.
:return: The identifier of this plugin and a dict of authentication
data for the auth type.
:rtype: tuple(string, dict)
"""
pass # pragma: no cover
@six.add_metaclass(abc.ABCMeta)
class AuthConstructor(Auth):
"""Abstract base class for creating an Auth Plugin.
The Auth Plugin created contains only one authentication method. This
is generally the required usage.
An AuthConstructor creates an AuthMethod based on the method's
arguments and the auth_method_class defined by the plugin. It then
creates the auth plugin with only that authentication method.
"""
_auth_method_class = None
def __init__(self, auth_url, *args, **kwargs):
method_kwargs = self._auth_method_class._extract_kwargs(kwargs)
method = self._auth_method_class(*args, **method_kwargs)
super(AuthConstructor, self).__init__(auth_url, [method], **kwargs)

View File

@@ -1,121 +0,0 @@
# 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 abc
from oslo_config import cfg
import six
from keystoneclient.auth.identity.v3 import base
from keystoneclient.auth.identity.v3 import token
__all__ = ('FederatedBaseAuth',)
@six.add_metaclass(abc.ABCMeta)
class FederatedBaseAuth(base.BaseAuth):
rescoping_plugin = token.Token
def __init__(self, auth_url, identity_provider, protocol, **kwargs):
"""Class constructor for federated authentication plugins.
Accepting following parameters:
:param auth_url: URL of the Identity Service
:type auth_url: string
:param identity_provider: Name of the Identity Provider the client
will authenticate against. This parameter
will be used to build a dynamic URL used to
obtain unscoped OpenStack token.
:type identity_provider: string
:param protocol: Protocol name configured on the keystone service
provider side
:type protocol: string
"""
super(FederatedBaseAuth, self).__init__(auth_url=auth_url, **kwargs)
self.identity_provider = identity_provider
self.protocol = protocol
@classmethod
def get_options(cls):
options = super(FederatedBaseAuth, cls).get_options()
options.extend([
cfg.StrOpt('identity-provider',
help="Identity Provider's name"),
cfg.StrOpt('protocol', help="Name of the federated protocol used "
"for federated authentication. Must "
"match its counterpart name "
"configured at the keystone service "
"provider. Typically values would be "
"'saml2' or 'oidc'.")
])
return options
@property
def federated_token_url(self):
"""Full URL where authorization data is sent."""
values = {
'host': self.auth_url.rstrip('/'),
'identity_provider': self.identity_provider,
'protocol': self.protocol
}
url = ("%(host)s/OS-FEDERATION/identity_providers/"
"%(identity_provider)s/protocols/%(protocol)s/auth")
url = url % values
return url
def _get_scoping_data(self):
return {'trust_id': self.trust_id,
'domain_id': self.domain_id,
'domain_name': self.domain_name,
'project_id': self.project_id,
'project_name': self.project_name,
'project_domain_id': self.project_domain_id,
'project_domain_name': self.project_domain_name}
def get_auth_ref(self, session, **kwargs):
"""Authenticate retrieve token information.
This is a multi-step process where a client does federated authn
receives an unscoped token.
If an unscoped token is successfully received and scoping information
is present then the token is rescoped to that target.
:param session: a session object to send out HTTP requests.
:type session: keystoneclient.session.Session
:returns: a token data representation
:rtype: :py:class:`keystoneclient.access.AccessInfo`
"""
auth_ref = self.get_unscoped_auth_ref(session)
scoping = self._get_scoping_data()
if any(scoping.values()):
token_plugin = self.rescoping_plugin(self.auth_url,
token=auth_ref.auth_token,
**scoping)
auth_ref = token_plugin.get_auth_ref(session)
return auth_ref
@abc.abstractmethod
def get_unscoped_auth_ref(self, session, **kwargs):
"""Fetch unscoped federated token."""
pass # pragma: no cover

View File

@@ -1,97 +0,0 @@
# 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.
from oslo_config import cfg
from keystoneclient.auth.identity.v3 import base
from keystoneclient import utils
__all__ = ('PasswordMethod', 'Password')
class PasswordMethod(base.AuthMethod):
"""Construct a User/Password based authentication method.
:param string password: Password for authentication.
:param string username: Username for authentication.
:param string user_id: User ID for authentication.
:param string user_domain_id: User's domain ID for authentication.
:param string user_domain_name: User's domain name for authentication.
"""
_method_parameters = ['user_id',
'username',
'user_domain_id',
'user_domain_name',
'password']
def get_auth_data(self, session, auth, headers, **kwargs):
user = {'password': self.password}
if self.user_id:
user['id'] = self.user_id
elif self.username:
user['name'] = self.username
if self.user_domain_id:
user['domain'] = {'id': self.user_domain_id}
elif self.user_domain_name:
user['domain'] = {'name': self.user_domain_name}
return 'password', {'user': user}
class Password(base.AuthConstructor):
"""A plugin for authenticating with a username and password.
:param string auth_url: Identity service endpoint for authentication.
:param string password: Password for authentication.
:param string username: Username for authentication.
:param string user_id: User ID for authentication.
:param string user_domain_id: User's domain ID for authentication.
:param string user_domain_name: User's domain name for authentication.
:param string trust_id: Trust ID for trust scoping.
:param string domain_id: Domain ID for domain scoping.
:param string domain_name: Domain name for domain scoping.
:param string project_id: Project ID for project scoping.
:param string project_name: Project name for project scoping.
:param string project_domain_id: Project's domain ID for project.
:param string project_domain_name: Project's domain name for project.
:param bool reauthenticate: Allow fetching a new token if the current one
is going to expire. (optional) default True
"""
_auth_method_class = PasswordMethod
@classmethod
def get_options(cls):
options = super(Password, cls).get_options()
options.extend([
cfg.StrOpt('user-id', help='User ID'),
cfg.StrOpt('username', dest='username', help='Username',
deprecated_name='user-name'),
cfg.StrOpt('user-domain-id', help="User's domain id"),
cfg.StrOpt('user-domain-name', help="User's domain name"),
cfg.StrOpt('password', secret=True, help="User's password"),
])
return options
@classmethod
def load_from_argparse_arguments(cls, namespace, **kwargs):
if not (kwargs.get('password') or namespace.os_password):
kwargs['password'] = utils.prompt_user_password()
return super(Password, cls).load_from_argparse_arguments(namespace,
**kwargs)

View File

@@ -1,65 +0,0 @@
# 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.
from oslo_config import cfg
from keystoneclient.auth.identity.v3 import base
__all__ = ('TokenMethod', 'Token')
class TokenMethod(base.AuthMethod):
"""Construct an Auth plugin to fetch a token from a token.
:param string token: Token for authentication.
"""
_method_parameters = ['token']
def get_auth_data(self, session, auth, headers, **kwargs):
headers['X-Auth-Token'] = self.token
return 'token', {'id': self.token}
class Token(base.AuthConstructor):
"""A plugin for authenticating with an existing Token.
:param string auth_url: Identity service endpoint for authentication.
:param string token: Token for authentication.
:param string trust_id: Trust ID for trust scoping.
:param string domain_id: Domain ID for domain scoping.
:param string domain_name: Domain name for domain scoping.
:param string project_id: Project ID for project scoping.
:param string project_name: Project name for project scoping.
:param string project_domain_id: Project's domain ID for project.
:param string project_domain_name: Project's domain name for project.
:param bool reauthenticate: Allow fetching a new token if the current one
is going to expire. (optional) default True
"""
_auth_method_class = TokenMethod
def __init__(self, auth_url, token, **kwargs):
super(Token, self).__init__(auth_url, token=token, **kwargs)
@classmethod
def get_options(cls):
options = super(Token, cls).get_options()
options.extend([
cfg.StrOpt('token',
secret=True,
help='Token to authenticate with'),
])
return options

View File

@@ -1,62 +0,0 @@
# 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 warnings
from oslo_config import cfg
from keystoneclient.auth import base
class Token(base.BaseAuthPlugin):
"""A provider that will always use the given token and endpoint.
This is really only useful for testing and in certain CLI cases where you
have a known endpoint and admin token that you want to use.
"""
def __init__(self, endpoint, token):
# NOTE(jamielennox): endpoint is reserved for when plugins
# can be used to provide that information
warnings.warn(
'TokenEndpoint plugin is deprecated as of the 2.1.0 release in '
'favor of keystoneauth1.token_endpoint.Token. It will be removed '
'in future releases.',
DeprecationWarning)
self.endpoint = endpoint
self.token = token
def get_token(self, session):
return self.token
def get_endpoint(self, session, **kwargs):
"""Return the supplied endpoint.
Using this plugin the same endpoint is returned regardless of the
parameters passed to the plugin.
"""
return self.endpoint
@classmethod
def get_options(cls):
options = super(Token, cls).get_options()
options.extend([
cfg.StrOpt('endpoint',
help='The endpoint that will always be used'),
cfg.StrOpt('token',
secret=True,
help='The token that will always be used'),
])
return options

View File

@@ -1,549 +0,0 @@
# Copyright 2010 Jacob Kaplan-Moss
# Copyright 2011 OpenStack Foundation
# Copyright 2013 OpenStack Foundation
# 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 utilities to build API operation managers and objects on top of."""
import abc
import copy
import functools
import warnings
from keystoneauth1 import exceptions as ksa_exceptions
from keystoneauth1 import plugin
from oslo_utils import strutils
import six
from six.moves import urllib
from keystoneclient import exceptions as ksc_exceptions
from keystoneclient.i18n import _
def getid(obj):
"""Return id if argument is a Resource.
Abstracts the common pattern of allowing both an object or an object's ID
(UUID) as a parameter when dealing with relationships.
"""
try:
if obj.uuid:
return obj.uuid
except AttributeError: # nosec(cjschaef): 'obj' doesn't contain attribute
# 'uuid', return attribute 'id' or the 'obj'
pass
try:
return obj.id
except AttributeError:
return obj
def filter_none(**kwargs):
"""Remove any entries from a dictionary where the value is None."""
return dict((k, v) for k, v in kwargs.items() if v is not None)
def filter_kwargs(f):
@functools.wraps(f)
def func(*args, **kwargs):
new_kwargs = {}
for key, ref in kwargs.items():
if ref is None:
# drop null values
continue
id_value = getid(ref)
if id_value != ref:
# If an object with an id was passed, then use the id, e.g.:
# user: user(id=1) becomes user_id: 1
key = '%s_id' % key
new_kwargs[key] = id_value
return f(*args, **new_kwargs)
return func
class Manager(object):
"""Basic manager type providing common operations.
Managers interact with a particular type of API (servers, flavors, images,
etc.) and provide CRUD operations for them.
:param client: instance of BaseClient descendant for HTTP requests
"""
resource_class = None
def __init__(self, client):
super(Manager, self).__init__()
self.client = client
@property
def api(self):
"""The client.
.. warning::
This property is deprecated as of the 1.7.0 release in favor of
:meth:`client` and may be removed in the 2.0.0 release.
"""
warnings.warn(
'api is deprecated as of the 1.7.0 release in favor of client and '
'may be removed in the 2.0.0 release', DeprecationWarning)
return self.client
def _list(self, url, response_key, obj_class=None, body=None, **kwargs):
"""List the collection.
:param url: a partial URL, e.g., '/servers'
:param response_key: the key to be looked up in response dictionary,
e.g., 'servers'
:param obj_class: class for constructing the returned objects
(self.resource_class will be used by default)
:param body: data that will be encoded as JSON and passed in POST
request (GET will be sent by default)
:param kwargs: Additional arguments will be passed to the request.
"""
if body:
resp, body = self.client.post(url, body=body, **kwargs)
else:
resp, body = self.client.get(url, **kwargs)
if obj_class is None:
obj_class = self.resource_class
data = body[response_key]
# NOTE(ja): keystone returns values as list as {'values': [ ... ]}
# unlike other services which just return the list...
try:
data = data['values']
except (KeyError, TypeError): # nosec(cjschaef): keystone data values
# not as expected (see comment above), assumption is that values
# are already returned in a list (so simply utilize that list)
pass
return [obj_class(self, res, loaded=True) for res in data if res]
def _get(self, url, response_key, **kwargs):
"""Get an object from collection.
:param url: a partial URL, e.g., '/servers'
:param response_key: the key to be looked up in response dictionary,
e.g., 'server'
:param kwargs: Additional arguments will be passed to the request.
"""
resp, body = self.client.get(url, **kwargs)
return self.resource_class(self, body[response_key], loaded=True)
def _head(self, url, **kwargs):
"""Retrieve request headers for an object.
:param url: a partial URL, e.g., '/servers'
:param kwargs: Additional arguments will be passed to the request.
"""
resp, body = self.client.head(url, **kwargs)
return resp.status_code == 204
def _post(self, url, body, response_key, return_raw=False, **kwargs):
"""Create an object.
:param url: a partial URL, e.g., '/servers'
:param body: data that will be encoded as JSON and passed in POST
request (GET will be sent by default)
:param response_key: the key to be looked up in response dictionary,
e.g., 'servers'
:param return_raw: flag to force returning raw JSON instead of
Python object of self.resource_class
:param kwargs: Additional arguments will be passed to the request.
"""
resp, body = self.client.post(url, body=body, **kwargs)
if return_raw:
return body[response_key]
return self.resource_class(self, body[response_key])
def _put(self, url, body=None, response_key=None, **kwargs):
"""Update an object with PUT method.
:param url: a partial URL, e.g., '/servers'
:param body: data that will be encoded as JSON and passed in POST
request (GET will be sent by default)
:param response_key: the key to be looked up in response dictionary,
e.g., 'servers'
:param kwargs: Additional arguments will be passed to the request.
"""
resp, body = self.client.put(url, body=body, **kwargs)
# PUT requests may not return a body
if body is not None:
if response_key is not None:
return self.resource_class(self, body[response_key])
else:
return self.resource_class(self, body)
def _patch(self, url, body=None, response_key=None, **kwargs):
"""Update an object with PATCH method.
:param url: a partial URL, e.g., '/servers'
:param body: data that will be encoded as JSON and passed in POST
request (GET will be sent by default)
:param response_key: the key to be looked up in response dictionary,
e.g., 'servers'
:param kwargs: Additional arguments will be passed to the request.
"""
resp, body = self.client.patch(url, body=body, **kwargs)
if response_key is not None:
return self.resource_class(self, body[response_key])
else:
return self.resource_class(self, body)
def _delete(self, url, **kwargs):
"""Delete an object.
:param url: a partial URL, e.g., '/servers/my-server'
:param kwargs: Additional arguments will be passed to the request.
"""
return self.client.delete(url, **kwargs)
def _update(self, url, body=None, response_key=None, method="PUT",
**kwargs):
methods = {"PUT": self.client.put,
"POST": self.client.post,
"PATCH": self.client.patch}
try:
resp, body = methods[method](url, body=body,
**kwargs)
except KeyError:
raise ksc_exceptions.ClientException(_("Invalid update method: %s")
% method)
# PUT requests may not return a body
if body:
return self.resource_class(self, body[response_key])
@six.add_metaclass(abc.ABCMeta)
class ManagerWithFind(Manager):
"""Manager with additional `find()`/`findall()` methods."""
@abc.abstractmethod
def list(self):
pass # pragma: no cover
def find(self, **kwargs):
"""Find a single item with attributes matching ``**kwargs``.
This isn't very efficient: it loads the entire list then filters on
the Python side.
"""
rl = self.findall(**kwargs)
num = len(rl)
if num == 0:
msg = _("No %(name)s matching %(kwargs)s.") % {
'name': self.resource_class.__name__, 'kwargs': kwargs}
raise ksa_exceptions.NotFound(404, msg)
elif num > 1:
raise ksc_exceptions.NoUniqueMatch
else:
return rl[0]
def findall(self, **kwargs):
"""Find all items with attributes matching ``**kwargs``.
This isn't very efficient: it loads the entire list then filters on
the Python side.
"""
found = []
searches = kwargs.items()
for obj in self.list():
try:
if all(getattr(obj, attr) == value
for (attr, value) in searches):
found.append(obj)
except AttributeError:
continue
return found
class CrudManager(Manager):
"""Base manager class for manipulating Keystone entities.
Children of this class are expected to define a `collection_key` and `key`.
- `collection_key`: Usually a plural noun by convention (e.g. `entities`);
used to refer collections in both URL's (e.g. `/v3/entities`) and JSON
objects containing a list of member resources (e.g. `{'entities': [{},
{}, {}]}`).
- `key`: Usually a singular noun by convention (e.g. `entity`); used to
refer to an individual member of the collection.
"""
collection_key = None
key = None
base_url = None
def build_url(self, dict_args_in_out=None):
"""Build a resource URL for the given kwargs.
Given an example collection where `collection_key = 'entities'` and
`key = 'entity'`, the following URL's could be generated.
By default, the URL will represent a collection of entities, e.g.::
/entities
If kwargs contains an `entity_id`, then the URL will represent a
specific member, e.g.::
/entities/{entity_id}
If a `base_url` is provided, the generated URL will be appended to it.
If a 'tail' is provided, it will be appended to the end of the URL.
"""
if dict_args_in_out is None:
dict_args_in_out = {}
url = dict_args_in_out.pop('base_url', None) or self.base_url or ''
url += '/%s' % self.collection_key
# do we have a specific entity?
entity_id = dict_args_in_out.pop('%s_id' % self.key, None)
if entity_id is not None:
url += '/%s' % entity_id
if dict_args_in_out.get('tail'):
url += dict_args_in_out['tail']
return url
@filter_kwargs
def create(self, **kwargs):
url = self.build_url(dict_args_in_out=kwargs)
return self._post(
url,
{self.key: kwargs},
self.key)
@filter_kwargs
def get(self, **kwargs):
return self._get(
self.build_url(dict_args_in_out=kwargs),
self.key)
@filter_kwargs
def head(self, **kwargs):
return self._head(self.build_url(dict_args_in_out=kwargs))
def _build_query(self, params):
if params is None:
return ''
else:
return '?%s' % urllib.parse.urlencode(params, doseq=True)
def build_key_only_query(self, params_list):
"""Build a query that does not include values, just keys.
The Identity API has some calls that define queries without values,
this can not be accomplished by using urllib.parse.urlencode(). This
method builds a query using only the keys.
"""
return '?%s' % '&'.join(params_list) if params_list else ''
@filter_kwargs
def list(self, fallback_to_auth=False, **kwargs):
if 'id' in kwargs.keys():
# Ensure that users are not trying to call things like
# ``domains.list(id='default')`` when they should have used
# ``[domains.get(domain_id='default')]`` instead. Keystone supports
# ``GET /v3/domains/{domain_id}``, not ``GET
# /v3/domains?id={domain_id}``.
raise TypeError(
_("list() got an unexpected keyword argument 'id'. To "
"retrieve a single object using a globally unique "
"identifier, try using get() instead."))
url = self.build_url(dict_args_in_out=kwargs)
try:
query = self._build_query(kwargs)
url_query = '%(url)s%(query)s' % {'url': url, 'query': query}
return self._list(
url_query,
self.collection_key)
except ksa_exceptions.EmptyCatalog:
if fallback_to_auth:
return self._list(
url_query,
self.collection_key,
endpoint_filter={'interface': plugin.AUTH_INTERFACE})
else:
raise
@filter_kwargs
def put(self, **kwargs):
return self._update(
self.build_url(dict_args_in_out=kwargs),
method='PUT')
@filter_kwargs
def update(self, **kwargs):
url = self.build_url(dict_args_in_out=kwargs)
return self._update(
url,
{self.key: kwargs},
self.key,
method='PATCH')
@filter_kwargs
def delete(self, **kwargs):
return self._delete(
self.build_url(dict_args_in_out=kwargs))
@filter_kwargs
def find(self, **kwargs):
"""Find a single item with attributes matching ``**kwargs``."""
url = self.build_url(dict_args_in_out=kwargs)
query = self._build_query(kwargs)
url_query = '%(url)s%(query)s' % {
'url': url,
'query': query
}
elements = self._list(
url_query,
self.collection_key)
if not elements:
msg = _("No %(name)s matching %(kwargs)s.") % {
'name': self.resource_class.__name__, 'kwargs': kwargs}
raise ksa_exceptions.NotFound(404, msg)
elif len(elements) > 1:
raise ksc_exceptions.NoUniqueMatch
else:
return elements[0]
class Resource(object):
"""Base class for OpenStack resources (tenant, user, etc.).
This is pretty much just a bag for attributes.
"""
HUMAN_ID = False
NAME_ATTR = 'name'
def __init__(self, manager, info, loaded=False):
"""Populate and bind to a manager.
:param manager: BaseManager object
:param info: dictionary representing resource attributes
:param loaded: prevent lazy-loading if set to True
"""
self.manager = manager
self._info = info
self._add_details(info)
self._loaded = loaded
def __repr__(self):
"""Return string representation of resource attributes."""
reprkeys = sorted(k
for k in self.__dict__.keys()
if k[0] != '_' and k != 'manager')
info = ", ".join("%s=%s" % (k, getattr(self, k)) for k in reprkeys)
return "<%s %s>" % (self.__class__.__name__, info)
@property
def human_id(self):
"""Human-readable ID which can be used for bash completion."""
if self.HUMAN_ID:
name = getattr(self, self.NAME_ATTR, None)
if name is not None:
return strutils.to_slug(name)
return None
def _add_details(self, info):
for (k, v) in info.items():
try:
try:
setattr(self, k, v)
except UnicodeEncodeError:
# This happens when we're running with Python version that
# does not support Unicode identifiers (e.g. Python 2.7).
# In that case we can't help but not set this attrubute;
# it'll be available in a dict representation though
pass
self._info[k] = v
except AttributeError: # nosec(cjschaef): we already defined the
# attribute on the class
pass
def __getattr__(self, k):
"""Checking attrbiute existence."""
if k not in self.__dict__:
# NOTE(bcwaldon): disallow lazy-loading if already loaded once
if not self.is_loaded():
self.get()
return self.__getattr__(k)
raise AttributeError(k)
else:
return self.__dict__[k]
def get(self):
"""Support for lazy loading details.
Some clients, such as novaclient have the option to lazy load the
details, details which can be loaded with this function.
"""
# set_loaded() first ... so if we have to bail, we know we tried.
self.set_loaded(True)
if not hasattr(self.manager, 'get'):
return
new = self.manager.get(self.id)
if new:
self._add_details(new._info)
def __eq__(self, other):
"""Define equality for resources."""
if not isinstance(other, Resource):
return NotImplemented
# two resources of different types are not equal
if not isinstance(other, self.__class__):
return False
return self._info == other._info
def __ne__(self, other):
"""Define inequality for resources."""
return not self == other
def is_loaded(self):
return self._loaded
def set_loaded(self, val):
self._loaded = val
def to_dict(self):
return copy.deepcopy(self._info)
def delete(self):
return self.manager.delete(self)

View File

@@ -1,46 +0,0 @@
# 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 warnings
class Client(object):
def __init__(self, session):
warnings.warn(
'keystoneclient.baseclient.Client is deprecated as of the 2.1.0 '
'release. It will be removed in future releases.',
DeprecationWarning)
self.session = session
def request(self, url, method, **kwargs):
kwargs.setdefault('authenticated', True)
return self.session.request(url, method, **kwargs)
def get(self, url, **kwargs):
return self.request(url, 'GET', **kwargs)
def head(self, url, **kwargs):
return self.request(url, 'HEAD', **kwargs)
def post(self, url, **kwargs):
return self.request(url, 'POST', **kwargs)
def put(self, url, **kwargs):
return self.request(url, 'PUT', **kwargs)
def patch(self, url, **kwargs):
return self.request(url, 'PATCH', **kwargs)
def delete(self, url, **kwargs):
return self.request(url, 'DELETE', **kwargs)

View File

@@ -1,63 +0,0 @@
# 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.
from debtcollector import removals
from keystoneclient import discover
from keystoneclient import httpclient
from keystoneclient import session as client_session
@removals.remove(message='Use keystoneclient.httpclient.HTTPClient instead',
version='1.7.0', removal_version='2.0.0')
class HTTPClient(httpclient.HTTPClient):
"""Deprecated alias for httpclient.HTTPClient.
This class is deprecated as of the 1.7.0 release in favor of
:class:`keystoneclient.httpclient.HTTPClient` and may be removed in the
2.0.0 release.
"""
def Client(version=None, unstable=False, session=None, **kwargs):
"""Factory function to create a new identity service client.
The returned client will be either a V3 or V2 client. Check the version
using the :py:attr:`~keystoneclient.v3.client.Client.version` property or
the instance's class (with instanceof).
:param tuple version: The required version of the identity API. If
specified the client will be selected such that the
major version is equivalent and an endpoint provides
at least the specified minor version. For example to
specify the 3.1 API use ``(3, 1)``. (optional)
:param bool unstable: Accept endpoints not marked as 'stable'. (optional)
:param session: A session object to be used for communication. If one is
not provided it will be constructed from the provided
kwargs. (optional)
:type session: keystoneclient.session.Session
:param kwargs: Additional arguments are passed through to the client
that is being created.
:returns: New keystone client object.
:rtype: :py:class:`keystoneclient.v3.client.Client` or
:py:class:`keystoneclient.v2_0.client.Client`
:raises keystoneclient.exceptions.DiscoveryFailure: if the server's
response is invalid.
:raises keystoneclient.exceptions.VersionNotAvailable: if a suitable client
cannot be found.
"""
if not session:
session = client_session.Session._construct(kwargs)
d = discover.Discover(session=session, **kwargs)
return d.create_client(version=version, unstable=unstable)

View File

@@ -1,444 +0,0 @@
# 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.
"""Certificate signing functions.
Call set_subprocess() with the subprocess module. Either Python's
subprocess or eventlet.green.subprocess can be used.
If set_subprocess() is not called, this module will pick Python's subprocess
or eventlet.green.subprocess based on if os module is patched by eventlet.
"""
import base64
import errno
import hashlib
import logging
import zlib
from debtcollector import removals
import six
from keystoneclient import exceptions
from keystoneclient.i18n import _
subprocess = None
LOG = logging.getLogger(__name__)
PKI_ASN1_PREFIX = 'MII'
PKIZ_PREFIX = 'PKIZ_'
PKIZ_CMS_FORM = 'DER'
PKI_ASN1_FORM = 'PEM'
DEFAULT_TOKEN_DIGEST_ALGORITHM = 'sha256'
# The openssl cms command exits with these status codes.
# See https://www.openssl.org/docs/man1.1.0/apps/cms.html#EXIT-CODES
class OpensslCmsExitStatus(object):
SUCCESS = 0
COMMAND_OPTIONS_PARSING_ERROR = 1
INPUT_FILE_READ_ERROR = 2
CREATE_CMS_READ_MIME_ERROR = 3
def _ensure_subprocess():
# NOTE(vish): late loading subprocess so we can
# use the green version if we are in
# eventlet.
global subprocess
if not subprocess:
try:
from eventlet import patcher
if patcher.already_patched:
from eventlet.green import subprocess
else:
import subprocess # nosec(cjschaef): we must be careful when
# using subprocess.Popen with possibly untrusted data,
# assumption is that the certificate/key files provided are
# trustworthy
except ImportError:
import subprocess # noqa # nosec(cjschaef): we must be careful
# when using subprocess.Popen with possibly untrusted data,
# assumption is that the certificate/key files provided are
# trustworthy
def set_subprocess(_subprocess=None):
"""Set subprocess module to use.
The subprocess could be eventlet.green.subprocess if using eventlet,
or Python's subprocess otherwise.
"""
global subprocess
subprocess = _subprocess
def _check_files_accessible(files):
err = None
retcode = -1
try:
for try_file in files:
with open(try_file, 'r'):
pass
except IOError as e:
# Catching IOError means there is an issue with
# the given file.
err = try_file, e.strerror
# Emulate openssl behavior, which returns with code 2 when
# access to a file failed.
retcode = OpensslCmsExitStatus.INPUT_FILE_READ_ERROR
return retcode, err
def _process_communicate_handle_oserror(process, data, files):
"""Wrapper around process.communicate that checks for OSError."""
try:
output, err = process.communicate(data)
except OSError as e:
if e.errno != errno.EPIPE:
raise
# OSError with EPIPE only occurs with old Python 2.7.x versions
# http://bugs.python.org/issue10963
# The quick exit is typically caused by the openssl command not being
# able to read an input file, so check ourselves if can't read a file.
retcode, err = _check_files_accessible(files)
if process.stderr:
msg = process.stderr.read()
if isinstance(msg, six.binary_type):
msg = msg.decode('utf-8')
if err:
err = (_('Hit OSError in '
'_process_communicate_handle_oserror(): '
'%(stderr)s\nLikely due to %(file)s: %(error)s') %
{'stderr': msg,
'file': err[0],
'error': err[1]})
else:
err = (_('Hit OSError in '
'_process_communicate_handle_oserror(): %s') % msg)
output = ''
else:
retcode = process.poll()
if err is not None:
if isinstance(err, six.binary_type):
err = err.decode('utf-8')
return output, err, retcode
def _encoding_for_form(inform):
if inform == PKI_ASN1_FORM:
encoding = 'UTF-8'
elif inform == PKIZ_CMS_FORM:
encoding = 'hex'
else:
raise ValueError(
_('"inform" must be one of: %s') % ','.join((PKI_ASN1_FORM,
PKIZ_CMS_FORM)))
return encoding
def cms_verify(formatted, signing_cert_file_name, ca_file_name,
inform=PKI_ASN1_FORM):
"""Verify the signature of the contents IAW CMS syntax.
:raises subprocess.CalledProcessError:
:raises keystoneclient.exceptions.CertificateConfigError: if certificate
is not configured
properly.
"""
_ensure_subprocess()
if isinstance(formatted, six.string_types):
data = bytearray(formatted, _encoding_for_form(inform))
else:
data = formatted
process = subprocess.Popen(['openssl', 'cms', '-verify',
'-certfile', signing_cert_file_name,
'-CAfile', ca_file_name,
'-inform', 'PEM',
'-nosmimecap', '-nodetach',
'-nocerts', '-noattr'],
stdin=subprocess.PIPE,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
close_fds=True)
output, err, retcode = _process_communicate_handle_oserror(
process, data, (signing_cert_file_name, ca_file_name))
# Do not log errors, as some happen in the positive thread
# instead, catch them in the calling code and log them there.
# When invoke the openssl >= 1.1.0 with not exist file, return code should
# be 2 instead of 1 and error msg will be returned.
# You can get more from
# https://www.openssl.org/docs/man1.1.0/apps/cms.html#EXIT-CODES
#
# $ openssl cms -verify -certfile not_exist_file -CAfile
# not_exist_file -inform PEM -nosmimecap -nodetach
# -nocerts -noattr
# openssl < 1.1.0 returns
# Error opening certificate file not_exist_file
# openssl >= 1.1.0 returns
# cms: Cannot open input file not_exist_file, No such file or directory
#
if retcode == OpensslCmsExitStatus.INPUT_FILE_READ_ERROR:
if err.startswith('Error reading S/MIME message'):
raise exceptions.CMSError(err)
else:
raise exceptions.CertificateConfigError(err)
# workaround for OpenSSL >= 1.1.0,
# should return OpensslCmsExitStatus.INPUT_FILE_READ_ERROR
elif retcode == OpensslCmsExitStatus.COMMAND_OPTIONS_PARSING_ERROR:
if err.startswith('cms: Cannot open input file'):
raise exceptions.CertificateConfigError(err)
else:
raise subprocess.CalledProcessError(retcode, 'openssl', output=err)
elif retcode != OpensslCmsExitStatus.SUCCESS:
raise subprocess.CalledProcessError(retcode, 'openssl', output=err)
return output
def is_pkiz(token_text):
"""Determine if a token is PKIZ.
Checks if the string has the prefix that indicates it is a
Crypto Message Syntax, Z compressed token.
"""
return token_text.startswith(PKIZ_PREFIX)
def pkiz_sign(text,
signing_cert_file_name,
signing_key_file_name,
compression_level=6,
message_digest=DEFAULT_TOKEN_DIGEST_ALGORITHM):
signed = cms_sign_data(text,
signing_cert_file_name,
signing_key_file_name,
PKIZ_CMS_FORM,
message_digest=message_digest)
compressed = zlib.compress(signed, compression_level)
encoded = PKIZ_PREFIX + base64.urlsafe_b64encode(
compressed).decode('utf-8')
return encoded
def pkiz_uncompress(signed_text):
text = signed_text[len(PKIZ_PREFIX):].encode('utf-8')
unencoded = base64.urlsafe_b64decode(text)
uncompressed = zlib.decompress(unencoded)
return uncompressed
def pkiz_verify(signed_text, signing_cert_file_name, ca_file_name):
uncompressed = pkiz_uncompress(signed_text)
return cms_verify(uncompressed, signing_cert_file_name, ca_file_name,
inform=PKIZ_CMS_FORM)
def token_to_cms(signed_text):
"""Convert a custom formatted token to a PEM-formatted token.
See documentation for cms_to_token() for details on the custom formatting.
"""
copy_of_text = signed_text.replace('-', '/')
lines = ['-----BEGIN CMS-----']
lines += [copy_of_text[n:n + 64] for n in range(0, len(copy_of_text), 64)]
lines.append('-----END CMS-----\n')
return '\n'.join(lines)
def verify_token(token, signing_cert_file_name, ca_file_name):
return cms_verify(token_to_cms(token),
signing_cert_file_name,
ca_file_name)
def is_asn1_token(token):
"""Determine if a token appears to be PKI-based.
thx to ayoung for sorting this out.
base64 decoded hex representation of MII is 3082::
In [3]: binascii.hexlify(base64.b64decode('MII='))
Out[3]: '3082'
re: http://www.itu.int/ITU-T/studygroups/com17/languages/X.690-0207.pdf
::
pg4: For tags from 0 to 30 the first octet is the identfier
pg10: Hex 30 means sequence, followed by the length of that sequence.
pg5: Second octet is the length octet
first bit indicates short or long form, next 7 bits encode the
number of subsequent octets that make up the content length octets
as an unsigned binary int
82 = 10000010 (first bit indicates long form)
0000010 = 2 octets of content length
so read the next 2 octets to get the length of the content.
In the case of a very large content length there could be a requirement to
have more than 2 octets to designate the content length, therefore
requiring us to check for MIM, MIQ, etc.
::
In [4]: base64.b64encode(binascii.a2b_hex('3083'))
Out[4]: 'MIM='
In [5]: base64.b64encode(binascii.a2b_hex('3084'))
Out[5]: 'MIQ='
Checking for MI would become invalid at 16 octets of content length
10010000 = 90
In [6]: base64.b64encode(binascii.a2b_hex('3090'))
Out[6]: 'MJA='
Checking for just M is insufficient
But we will only check for MII:
Max length of the content using 2 octets is 3FFF or 16383.
It's not practical to support a token of this length or greater in http
therefore, we will check for MII only and ignore the case of larger tokens
"""
return token[:3] == PKI_ASN1_PREFIX
@removals.remove(message='Use is_asn1_token() instead.', version='1.7.0',
removal_version='2.0.0')
def is_ans1_token(token):
"""Deprecated.
This function is deprecated as of the 1.7.0 release in favor of
:func:`is_asn1_token` and may be removed in the 2.0.0 release.
"""
return is_asn1_token(token)
def cms_sign_text(data_to_sign, signing_cert_file_name, signing_key_file_name,
message_digest=DEFAULT_TOKEN_DIGEST_ALGORITHM):
return cms_sign_data(data_to_sign, signing_cert_file_name,
signing_key_file_name, message_digest=message_digest)
def cms_sign_data(data_to_sign, signing_cert_file_name, signing_key_file_name,
outform=PKI_ASN1_FORM,
message_digest=DEFAULT_TOKEN_DIGEST_ALGORITHM):
"""Use OpenSSL to sign a document.
Produces a Base64 encoding of a DER formatted CMS Document
http://en.wikipedia.org/wiki/Cryptographic_Message_Syntax
:param data_to_sign: data to sign
:param signing_cert_file_name: path to the X509 certificate containing
the public key associated with the private key used to sign the data
:param signing_key_file_name: path to the private key used to sign
the data
:param outform: Format for the signed document PKIZ_CMS_FORM or
PKI_ASN1_FORM
:param message_digest: Digest algorithm to use when signing or resigning
"""
_ensure_subprocess()
if isinstance(data_to_sign, six.string_types):
data = bytearray(data_to_sign, encoding='utf-8')
else:
data = data_to_sign
process = subprocess.Popen(['openssl', 'cms', '-sign',
'-signer', signing_cert_file_name,
'-inkey', signing_key_file_name,
'-outform', 'PEM',
'-nosmimecap', '-nodetach',
'-nocerts', '-noattr',
'-md', message_digest, ],
stdin=subprocess.PIPE,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
close_fds=True)
output, err, retcode = _process_communicate_handle_oserror(
process, data, (signing_cert_file_name, signing_key_file_name))
if retcode != OpensslCmsExitStatus.SUCCESS or ('Error' in err):
if retcode == OpensslCmsExitStatus.CREATE_CMS_READ_MIME_ERROR:
LOG.error('Signing error: Unable to load certificate - '
'ensure you have configured PKI with '
'"keystone-manage pki_setup"')
else:
LOG.error('Signing error: %s', err)
raise subprocess.CalledProcessError(retcode, 'openssl')
if outform == PKI_ASN1_FORM:
return output.decode('utf-8')
else:
return output
def cms_sign_token(text, signing_cert_file_name, signing_key_file_name,
message_digest=DEFAULT_TOKEN_DIGEST_ALGORITHM):
output = cms_sign_data(text, signing_cert_file_name, signing_key_file_name,
message_digest=message_digest)
return cms_to_token(output)
def cms_to_token(cms_text):
"""Convert a CMS-signed token in PEM format to a custom URL-safe format.
The conversion consists of replacing '/' char in the PEM-formatted token
with the '-' char and doing other such textual replacements to make the
result marshallable via HTTP. The return value can thus be used as the
value of a HTTP header such as "X-Auth-Token".
This ad-hoc conversion is an unfortunate oversight since the returned
value now does not conform to any of the standard variants of base64
encoding. It would have been better to use base64url encoding (either on
the PEM formatted text or, perhaps even better, on the inner CMS-signed
binary value without any PEM formatting). In any case, the same conversion
is done in reverse in the other direction (for token verification), so
there are no correctness issues here. Note that the non-standard encoding
of the token will be preserved so as to not break backward compatibility.
The conversion issue is detailed by the code author in a blog post at
http://adam.younglogic.com/2014/02/compressed-tokens/.
"""
start_delim = '-----BEGIN CMS-----'
end_delim = '-----END CMS-----'
signed_text = cms_text
signed_text = signed_text.replace('/', '-')
signed_text = signed_text.replace(start_delim, '')
signed_text = signed_text.replace(end_delim, '')
signed_text = signed_text.replace('\n', '')
return signed_text
def cms_hash_token(token_id, mode='md5'):
"""Hash PKI tokens.
return: for asn1 or pkiz tokens, returns the hash of the passed in token
otherwise, returns what it was passed in.
"""
if token_id is None:
return None
if is_asn1_token(token_id) or is_pkiz(token_id):
hasher = hashlib.new(mode)
if isinstance(token_id, six.text_type):
token_id = token_id.encode('utf-8')
hasher.update(token_id)
return hasher.hexdigest()
else:
return token_id

View File

@@ -1,211 +0,0 @@
# 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.
from oslo_config import cfg
from positional import positional
from keystoneclient import access
from keystoneclient.auth.identity.v3 import federated
class OidcPassword(federated.FederatedBaseAuth):
"""Implement authentication plugin for OpenID Connect protocol.
OIDC or OpenID Connect is a protocol for federated authentication.
The OpenID Connect specification can be found at::
``http://openid.net/specs/openid-connect-core-1_0.html``
"""
@classmethod
def get_options(cls):
options = super(OidcPassword, cls).get_options()
options.extend([
cfg.StrOpt('username', help='Username'),
cfg.StrOpt('password', secret=True, help='Password'),
cfg.StrOpt('client-id', help='OAuth 2.0 Client ID'),
cfg.StrOpt('client-secret', secret=True,
help='OAuth 2.0 Client Secret'),
cfg.StrOpt('access-token-endpoint',
help='OpenID Connect Provider Token Endpoint'),
cfg.StrOpt('scope', default="profile",
help='OpenID Connect scope that is requested from OP')
])
return options
@positional(4)
def __init__(self, auth_url, identity_provider, protocol,
username, password, client_id, client_secret,
access_token_endpoint, scope='profile',
grant_type='password'):
"""The OpenID Connect plugin.
It expects the following:
:param auth_url: URL of the Identity Service
:type auth_url: string
:param identity_provider: Name of the Identity Provider the client
will authenticate against
:type identity_provider: string
:param protocol: Protocol name as configured in keystone
:type protocol: string
:param username: Username used to authenticate
:type username: string
:param password: Password used to authenticate
:type password: string
:param client_id: OAuth 2.0 Client ID
:type client_id: string
:param client_secret: OAuth 2.0 Client Secret
:type client_secret: string
:param access_token_endpoint: OpenID Connect Provider Token Endpoint,
for example:
https://localhost:8020/oidc/OP/token
:type access_token_endpoint: string
:param scope: OpenID Connect scope that is requested from OP,
defaults to "profile", for example: "profile email"
:type scope: string
:param grant_type: OpenID Connect grant type, it represents the flow
that is used to talk to the OP. Valid values are:
"authorization_code", "refresh_token", or
"password".
:type grant_type: string
"""
super(OidcPassword, self).__init__(auth_url, identity_provider,
protocol)
self._username = username
self._password = password
self.client_id = client_id
self.client_secret = client_secret
self.access_token_endpoint = access_token_endpoint
self.scope = scope
self.grant_type = grant_type
@property
def username(self):
# Override to remove deprecation.
return self._username
@username.setter
def username(self, value):
# Override to remove deprecation.
self._username = value
@property
def password(self):
# Override to remove deprecation.
return self._password
@password.setter
def password(self, value):
# Override to remove deprecation.
self._password = value
def get_unscoped_auth_ref(self, session):
"""Authenticate with OpenID Connect and get back claims.
This is a multi-step process. First an access token must be retrieved,
to do this, the username and password, the OpenID Connect client ID
and secret, and the access token endpoint must be known.
Secondly, we then exchange the access token upon accessing the
protected Keystone endpoint (federated auth URL). This will trigger
the OpenID Connect Provider to perform a user introspection and
retrieve information (specified in the scope) about the user in
the form of an OpenID Connect Claim. These claims will be sent
to Keystone in the form of environment variables.
:param session: a session object to send out HTTP requests.
:type session: keystoneclient.session.Session
:returns: a token data representation
:rtype: :py:class:`keystoneclient.access.AccessInfo`
"""
# get an access token
client_auth = (self.client_id, self.client_secret)
payload = {'grant_type': self.grant_type, 'username': self.username,
'password': self.password, 'scope': self.scope}
response = self._get_access_token(session, client_auth, payload,
self.access_token_endpoint)
access_token = response.json()['access_token']
# use access token against protected URL
headers = {'Authorization': 'Bearer ' + access_token}
response = self._get_keystone_token(session, headers,
self.federated_token_url)
# grab the unscoped token
token = response.headers['X-Subject-Token']
token_json = response.json()['token']
return access.AccessInfoV3(token, **token_json)
def _get_access_token(self, session, client_auth, payload,
access_token_endpoint):
"""Exchange a variety of user supplied values for an access token.
:param session: a session object to send out HTTP requests.
:type session: keystoneclient.session.Session
:param client_auth: a tuple representing client id and secret
:type client_auth: tuple
:param payload: a dict containing various OpenID Connect values, for
example::
{'grant_type': 'password', 'username': self.username,
'password': self.password, 'scope': self.scope}
:type payload: dict
:param access_token_endpoint: URL to use to get an access token, for
example: https://localhost/oidc/token
:type access_token_endpoint: string
"""
op_response = session.post(self.access_token_endpoint,
requests_auth=client_auth,
data=payload,
authenticated=False)
return op_response
def _get_keystone_token(self, session, headers, federated_token_url):
r"""Exchange an acess token for a keystone token.
By Sending the access token in an `Authorization: Bearer` header, to
an OpenID Connect protected endpoint (Federated Token URL). The
OpenID Connect server will use the access token to look up information
about the authenticated user (this technique is called instrospection).
The output of the instrospection will be an OpenID Connect Claim, that
will be used against the mapping engine. Should the mapping engine
succeed, a Keystone token will be presented to the user.
:param session: a session object to send out HTTP requests.
:type session: keystoneclient.session.Session
:param headers: an Authorization header containing the access token.
:type headers_: dict
:param federated_auth_url: Protected URL for federated authentication,
for example: https://localhost:5000/v3/\
OS-FEDERATION/identity_providers/bluepages/\
protocols/oidc/auth
:type federated_auth_url: string
"""
auth_response = session.post(self.federated_token_url,
headers=headers,
authenticated=False)
return auth_response

View File

@@ -1,920 +0,0 @@
# 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 datetime
import uuid
from lxml import etree # nosec(cjschaef): used to create xml, not parse it
from oslo_config import cfg
from six.moves import urllib
from keystoneclient import access
from keystoneclient.auth.identity import v3
from keystoneclient import exceptions
from keystoneclient.i18n import _
class _BaseSAMLPlugin(v3.AuthConstructor):
HTTP_MOVED_TEMPORARILY = 302
HTTP_SEE_OTHER = 303
PROTOCOL = 'saml2'
@staticmethod
def _first(_list):
if len(_list) != 1:
raise IndexError(_("Only single element list is acceptable"))
return _list[0]
@staticmethod
def str_to_xml(content, msg=None, include_exc=True):
try:
return etree.XML(content)
except etree.XMLSyntaxError as e:
if not msg:
msg = str(e)
else:
msg = msg % e if include_exc else msg
raise exceptions.AuthorizationFailure(msg)
@staticmethod
def xml_to_str(content, **kwargs):
return etree.tostring(content, **kwargs)
@property
def token_url(self):
"""Return full URL where authorization data is sent."""
values = {
'host': self.auth_url.rstrip('/'),
'identity_provider': self.identity_provider,
'protocol': self.PROTOCOL
}
url = ("%(host)s/OS-FEDERATION/identity_providers/"
"%(identity_provider)s/protocols/%(protocol)s/auth")
url = url % values
return url
@classmethod
def get_options(cls):
options = super(_BaseSAMLPlugin, cls).get_options()
options.extend([
cfg.StrOpt('identity-provider', help="Identity Provider's name"),
cfg.StrOpt('identity-provider-url',
help="Identity Provider's URL"),
cfg.StrOpt('username', dest='username', help='Username',
deprecated_name='user-name'),
cfg.StrOpt('password', secret=True, help='Password')
])
return options
class Saml2UnscopedTokenAuthMethod(v3.AuthMethod):
_method_parameters = []
def get_auth_data(self, session, auth, headers, **kwargs):
raise exceptions.MethodNotImplemented(_('This method should never '
'be called'))
class Saml2UnscopedToken(_BaseSAMLPlugin):
r"""Implement authentication plugin for SAML2 protocol.
ECP stands for `Enhanced Client or Proxy` and is a SAML2 extension
for federated authentication where a transportation layer consists of
HTTP protocol and XML SOAP messages.
`Read for more information
<https://wiki.shibboleth.net/confluence/display/SHIB2/ECP>`_ on ECP.
Reference the `SAML2 ECP specification <https://www.oasis-open.org/\
committees/download.php/49979/saml-ecp-v2.0-wd09.pdf>`_.
Currently only HTTPBasicAuth mechanism is available for the IdP
authenication.
:param auth_url: URL of the Identity Service
:type auth_url: string
:param identity_provider: name of the Identity Provider the client will
authenticate against. This parameter will be used
to build a dynamic URL used to obtain unscoped
OpenStack token.
:type identity_provider: string
:param identity_provider_url: An Identity Provider URL, where the SAML2
authn request will be sent.
:type identity_provider_url: string
:param username: User's login
:type username: string
:param password: User's password
:type password: string
"""
_auth_method_class = Saml2UnscopedTokenAuthMethod
SAML2_HEADER_INDEX = 0
ECP_SP_EMPTY_REQUEST_HEADERS = {
'Accept': 'text/html, application/vnd.paos+xml',
'PAOS': ('ver="urn:liberty:paos:2003-08";"urn:oasis:names:tc:'
'SAML:2.0:profiles:SSO:ecp"')
}
ECP_SP_SAML2_REQUEST_HEADERS = {
'Content-Type': 'application/vnd.paos+xml'
}
ECP_SAML2_NAMESPACES = {
'ecp': 'urn:oasis:names:tc:SAML:2.0:profiles:SSO:ecp',
'S': 'http://schemas.xmlsoap.org/soap/envelope/',
'paos': 'urn:liberty:paos:2003-08'
}
ECP_RELAY_STATE = '//ecp:RelayState'
ECP_SERVICE_PROVIDER_CONSUMER_URL = ('/S:Envelope/S:Header/paos:Request/'
'@responseConsumerURL')
ECP_IDP_CONSUMER_URL = ('/S:Envelope/S:Header/ecp:Response/'
'@AssertionConsumerServiceURL')
SOAP_FAULT = """
<S:Envelope xmlns:S="http://schemas.xmlsoap.org/soap/envelope/">
<S:Body>
<S:Fault>
<faultcode>S:Server</faultcode>
<faultstring>responseConsumerURL from SP and
assertionConsumerServiceURL from IdP do not match
</faultstring>
</S:Fault>
</S:Body>
</S:Envelope>
"""
def __init__(self, auth_url,
identity_provider,
identity_provider_url,
username, password,
**kwargs):
super(Saml2UnscopedToken, self).__init__(auth_url=auth_url, **kwargs)
self.identity_provider = identity_provider
self.identity_provider_url = identity_provider_url
self._username, self._password = username, password
@property
def username(self):
# Override to remove deprecation.
return self._username
@username.setter
def username(self, value):
# Override to remove deprecation.
self._username = value
@property
def password(self):
# Override to remove deprecation.
return self._password
@password.setter
def password(self, value):
# Override to remove deprecation.
self._password = value
def _handle_http_ecp_redirect(self, session, response, method, **kwargs):
if response.status_code not in (self.HTTP_MOVED_TEMPORARILY,
self.HTTP_SEE_OTHER):
return response
location = response.headers['location']
return session.request(location, method, authenticated=False,
**kwargs)
def _prepare_idp_saml2_request(self, saml2_authn_request):
header = saml2_authn_request[self.SAML2_HEADER_INDEX]
saml2_authn_request.remove(header)
def _check_consumer_urls(self, session, sp_response_consumer_url,
idp_sp_response_consumer_url):
"""Check if consumer URLs issued by SP and IdP are equal.
In the initial SAML2 authn Request issued by a Service Provider
there is a url called ``consumer url``. A trusted Identity Provider
should issue identical url. If the URLs are not equal the federated
authn process should be interrupted and the user should be warned.
:param session: session object to send out HTTP requests.
:type session: keystoneclient.session.Session
:param sp_response_consumer_url: consumer URL issued by a SP
:type sp_response_consumer_url: string
:param idp_sp_response_consumer_url: consumer URL issued by an IdP
:type idp_sp_response_consumer_url: string
"""
if sp_response_consumer_url != idp_sp_response_consumer_url:
# send fault message to the SP, discard the response
session.post(sp_response_consumer_url, data=self.SOAP_FAULT,
headers=self.ECP_SP_SAML2_REQUEST_HEADERS,
authenticated=False)
# prepare error message and raise an exception.
msg = _("Consumer URLs from Service Provider %(service_provider)s "
"%(sp_consumer_url)s and Identity Provider "
"%(identity_provider)s %(idp_consumer_url)s are not equal")
msg = msg % {
'service_provider': self.token_url,
'sp_consumer_url': sp_response_consumer_url,
'identity_provider': self.identity_provider,
'idp_consumer_url': idp_sp_response_consumer_url
}
raise exceptions.ValidationError(msg)
def _send_service_provider_request(self, session):
"""Initial HTTP GET request to the SAML2 protected endpoint.
It's crucial to include HTTP headers indicating that the client is
willing to take advantage of the ECP SAML2 extension and receive data
as the SOAP.
Unlike standard authentication methods in the OpenStack Identity,
the client accesses::
``/v3/OS-FEDERATION/identity_providers/{identity_providers}/
protocols/{protocol}/auth``
After a successful HTTP call the HTTP response should include SAML2
authn request in the XML format.
If a HTTP response contains ``X-Subject-Token`` in the headers and
the response body is a valid JSON assume the user was already
authenticated and Keystone returned a valid unscoped token.
Return True indicating the user was already authenticated.
:param session: a session object to send out HTTP requests.
:type session: keystoneclient.session.Session
"""
sp_response = session.get(self.token_url,
headers=self.ECP_SP_EMPTY_REQUEST_HEADERS,
authenticated=False)
if 'X-Subject-Token' in sp_response.headers:
self.authenticated_response = sp_response
return True
try:
self.saml2_authn_request = etree.XML(sp_response.content)
except etree.XMLSyntaxError as e:
msg = _("SAML2: Error parsing XML returned "
"from Service Provider, reason: %s") % e
raise exceptions.AuthorizationFailure(msg)
relay_state = self.saml2_authn_request.xpath(
self.ECP_RELAY_STATE, namespaces=self.ECP_SAML2_NAMESPACES)
self.relay_state = self._first(relay_state)
sp_response_consumer_url = self.saml2_authn_request.xpath(
self.ECP_SERVICE_PROVIDER_CONSUMER_URL,
namespaces=self.ECP_SAML2_NAMESPACES)
self.sp_response_consumer_url = self._first(sp_response_consumer_url)
return False
def _send_idp_saml2_authn_request(self, session):
"""Present modified SAML2 authn assertion from the Service Provider."""
self._prepare_idp_saml2_request(self.saml2_authn_request)
idp_saml2_authn_request = self.saml2_authn_request
# Currently HTTPBasicAuth method is hardcoded into the plugin
idp_response = session.post(
self.identity_provider_url,
headers={'Content-type': 'text/xml'},
data=etree.tostring(idp_saml2_authn_request),
requests_auth=(self.username, self.password),
authenticated=False, log=False)
try:
self.saml2_idp_authn_response = etree.XML(idp_response.content)
except etree.XMLSyntaxError as e:
msg = _("SAML2: Error parsing XML returned "
"from Identity Provider, reason: %s") % e
raise exceptions.AuthorizationFailure(msg)
idp_response_consumer_url = self.saml2_idp_authn_response.xpath(
self.ECP_IDP_CONSUMER_URL,
namespaces=self.ECP_SAML2_NAMESPACES)
self.idp_response_consumer_url = self._first(idp_response_consumer_url)
self._check_consumer_urls(session, self.idp_response_consumer_url,
self.sp_response_consumer_url)
def _send_service_provider_saml2_authn_response(self, session):
"""Present SAML2 assertion to the Service Provider.
The assertion is issued by a trusted Identity Provider for the
authenticated user. This function directs the HTTP request to SP
managed URL, for instance: ``https://<host>:<port>/Shibboleth.sso/
SAML2/ECP``.
Upon success the there's a session created and access to the protected
resource is granted. Many implementations of the SP return HTTP 302/303
status code pointing to the protected URL (``https://<host>:<port>/v3/
OS-FEDERATION/identity_providers/{identity_provider}/protocols/
{protocol_id}/auth`` in this case). Saml2 plugin should point to that
URL again, with HTTP GET method, expecting an unscoped token.
:param session: a session object to send out HTTP requests.
"""
self.saml2_idp_authn_response[0][0] = self.relay_state
response = session.post(
self.idp_response_consumer_url,
headers=self.ECP_SP_SAML2_REQUEST_HEADERS,
data=etree.tostring(self.saml2_idp_authn_response),
authenticated=False, redirect=False)
# Don't follow HTTP specs - after the HTTP 302/303 response don't
# repeat the call directed to the Location URL. In this case, this is
# an indication that saml2 session is now active and protected resource
# can be accessed.
response = self._handle_http_ecp_redirect(
session, response, method='GET',
headers=self.ECP_SP_SAML2_REQUEST_HEADERS)
self.authenticated_response = response
def _get_unscoped_token(self, session):
"""Get unscoped OpenStack token after federated authentication.
This is a multi-step process including multiple HTTP requests.
The federated authentication consists of::
* HTTP GET request to the Identity Service (acting as a Service
Provider). Client utilizes URL::
``/v3/OS-FEDERATION/identity_providers/{identity_provider}/
protocols/saml2/auth``.
It's crucial to include HTTP headers indicating we are expecting
SOAP message in return.
Service Provider should respond with such SOAP message.
This step is handed by a method
``Saml2UnscopedToken_send_service_provider_request()``
* HTTP POST request to the external Identity Provider service with
ECP extension enabled. The content sent is a header removed SOAP
message returned from the Service Provider. It's also worth noting
that ECP extension to the SAML2 doesn't define authentication method.
The most popular is HttpBasicAuth with just user and password.
Other possibilities could be X509 certificates or Kerberos.
Upon successful authentication the user should receive a SAML2
assertion.
This step is handed by a method
``Saml2UnscopedToken_send_idp_saml2_authn_request(session)``
* HTTP POST request again to the Service Provider. The body of the
request includes SAML2 assertion issued by a trusted Identity
Provider. The request should be sent to the Service Provider
consumer url specified in the SAML2 assertion.
Providing the authentication was successful and both Service Provider
and Identity Providers are trusted to each other, the Service
Provider will issue an unscoped token with a list of groups the
federated user is a member of.
This step is handed by a method
``Saml2UnscopedToken_send_service_provider_saml2_authn_response()``
Unscoped token example::
{
"token": {
"methods": [
"saml2"
],
"user": {
"id": "username%40example.com",
"name": "username@example.com",
"OS-FEDERATION": {
"identity_provider": "ACME",
"protocol": "saml2",
"groups": [
{"id": "abc123"},
{"id": "bcd234"}
]
}
}
}
}
:param session : a session object to send out HTTP requests.
:type session: keystoneclient.session.Session
:returns: (token, token_json)
"""
saml_authenticated = self._send_service_provider_request(session)
if not saml_authenticated:
self._send_idp_saml2_authn_request(session)
self._send_service_provider_saml2_authn_response(session)
return (self.authenticated_response.headers['X-Subject-Token'],
self.authenticated_response.json()['token'])
def get_auth_ref(self, session, **kwargs):
"""Authenticate via SAML2 protocol and retrieve unscoped token.
This is a multi-step process where a client does federated authn
receives an unscoped token.
Federated authentication utilizing SAML2 Enhanced Client or Proxy
extension. See ``Saml2UnscopedToken_get_unscoped_token()``
for more information on that step.
Upon successful authentication and assertion mapping an
unscoped token is returned and stored within the plugin object for
further use.
:param session : a session object to send out HTTP requests.
:type session: keystoneclient.session.Session
:return: an object with scoped token's id and unscoped token json
included.
:rtype: :py:class:`keystoneclient.access.AccessInfoV3`
"""
token, token_json = self._get_unscoped_token(session)
return access.AccessInfoV3(token,
**token_json)
class ADFSUnscopedToken(_BaseSAMLPlugin):
"""Authentication plugin for Microsoft ADFS2.0 IdPs.
:param auth_url: URL of the Identity Service
:type auth_url: string
:param identity_provider: name of the Identity Provider the client will
authenticate against. This parameter will be used
to build a dynamic URL used to obtain unscoped
OpenStack token.
:type identity_provider: string
:param identity_provider_url: An Identity Provider URL, where the SAML2
authentication request will be sent.
:type identity_provider_url: string
:param service_provider_endpoint: Endpoint where an assertion is being
sent, for instance: ``https://host.domain/Shibboleth.sso/ADFS``
:type service_provider_endpoint: string
:param username: User's login
:type username: string
:param password: User's password
:type password: string
"""
_auth_method_class = Saml2UnscopedTokenAuthMethod
DEFAULT_ADFS_TOKEN_EXPIRATION = 120
HEADER_SOAP = {"Content-Type": "application/soap+xml; charset=utf-8"}
HEADER_X_FORM = {"Content-Type": "application/x-www-form-urlencoded"}
NAMESPACES = {
's': 'http://www.w3.org/2003/05/soap-envelope',
'a': 'http://www.w3.org/2005/08/addressing',
'u': ('http://docs.oasis-open.org/wss/2004/01/oasis-200401-'
'wss-wssecurity-utility-1.0.xsd')
}
ADFS_TOKEN_NAMESPACES = {
's': 'http://www.w3.org/2003/05/soap-envelope',
't': 'http://docs.oasis-open.org/ws-sx/ws-trust/200512'
}
ADFS_ASSERTION_XPATH = ('/s:Envelope/s:Body'
'/t:RequestSecurityTokenResponseCollection'
'/t:RequestSecurityTokenResponse')
def __init__(self, auth_url, identity_provider, identity_provider_url,
service_provider_endpoint, username, password, **kwargs):
super(ADFSUnscopedToken, self).__init__(auth_url=auth_url, **kwargs)
self.identity_provider = identity_provider
self.identity_provider_url = identity_provider_url
self.service_provider_endpoint = service_provider_endpoint
self._username, self._password = username, password
@property
def username(self):
# Override to remove deprecation.
return self._username
@username.setter
def username(self, value):
# Override to remove deprecation.
self._username = value
@property
def password(self):
# Override to remove deprecation.
return self._password
@password.setter
def password(self, value):
# Override to remove deprecation.
self._password = value
@classmethod
def get_options(cls):
options = super(ADFSUnscopedToken, cls).get_options()
options.extend([
cfg.StrOpt('service-provider-endpoint',
help="Service Provider's Endpoint")
])
return options
def _cookies(self, session):
"""Check if cookie jar is not empty.
keystoneclient.session.Session object doesn't have a cookies attribute.
We should then try fetching cookies from the underlying
requests.Session object. If that fails too, there is something wrong
and let Python raise the AttributeError.
:param session
:returns: True if cookie jar is nonempty, False otherwise
:raises AttributeError: in case cookies are not find anywhere
"""
try:
return bool(session.cookies)
except AttributeError: # nosec(cjschaef): fetch cookies from
# underylying requests.Session object, or fail trying
pass
return bool(session.session.cookies)
def _token_dates(self, fmt='%Y-%m-%dT%H:%M:%S.%fZ'):
"""Calculate created and expires datetime objects.
The method is going to be used for building ADFS Request Security
Token message. Time interval between ``created`` and ``expires``
dates is now static and equals to 120 seconds. ADFS security tokens
should not be live too long, as currently ``keystoneclient``
doesn't have mechanisms for reusing such tokens (every time ADFS authn
method is called, keystoneclient will login with the ADFS instance).
:param fmt: Datetime format for specifying string format of a date.
It should not be changed if the method is going to be used
for building the ADFS security token request.
:type fmt: string
"""
date_created = datetime.datetime.utcnow()
date_expires = date_created + datetime.timedelta(
seconds=self.DEFAULT_ADFS_TOKEN_EXPIRATION)
return [_time.strftime(fmt) for _time in (date_created, date_expires)]
def _prepare_adfs_request(self):
"""Build the ADFS Request Security Token SOAP message.
Some values like username or password are inserted in the request.
"""
WSS_SECURITY_NAMESPACE = {
'o': ('http://docs.oasis-open.org/wss/2004/01/oasis-200401-'
'wss-wssecurity-secext-1.0.xsd')
}
TRUST_NAMESPACE = {
'trust': 'http://docs.oasis-open.org/ws-sx/ws-trust/200512'
}
WSP_NAMESPACE = {
'wsp': 'http://schemas.xmlsoap.org/ws/2004/09/policy'
}
WSA_NAMESPACE = {
'wsa': 'http://www.w3.org/2005/08/addressing'
}
root = etree.Element(
'{http://www.w3.org/2003/05/soap-envelope}Envelope',
nsmap=self.NAMESPACES)
header = etree.SubElement(
root, '{http://www.w3.org/2003/05/soap-envelope}Header')
action = etree.SubElement(
header, "{http://www.w3.org/2005/08/addressing}Action")
action.set(
"{http://www.w3.org/2003/05/soap-envelope}mustUnderstand", "1")
action.text = ('http://docs.oasis-open.org/ws-sx/ws-trust/200512'
'/RST/Issue')
messageID = etree.SubElement(
header, '{http://www.w3.org/2005/08/addressing}MessageID')
messageID.text = 'urn:uuid:' + uuid.uuid4().hex
replyID = etree.SubElement(
header, '{http://www.w3.org/2005/08/addressing}ReplyTo')
address = etree.SubElement(
replyID, '{http://www.w3.org/2005/08/addressing}Address')
address.text = 'http://www.w3.org/2005/08/addressing/anonymous'
to = etree.SubElement(
header, '{http://www.w3.org/2005/08/addressing}To')
to.set("{http://www.w3.org/2003/05/soap-envelope}mustUnderstand", "1")
security = etree.SubElement(
header, '{http://docs.oasis-open.org/wss/2004/01/oasis-200401-'
'wss-wssecurity-secext-1.0.xsd}Security',
nsmap=WSS_SECURITY_NAMESPACE)
security.set(
"{http://www.w3.org/2003/05/soap-envelope}mustUnderstand", "1")
timestamp = etree.SubElement(
security, ('{http://docs.oasis-open.org/wss/2004/01/oasis-200401-'
'wss-wssecurity-utility-1.0.xsd}Timestamp'))
timestamp.set(
('{http://docs.oasis-open.org/wss/2004/01/oasis-200401-'
'wss-wssecurity-utility-1.0.xsd}Id'), '_0')
created = etree.SubElement(
timestamp, ('{http://docs.oasis-open.org/wss/2004/01/oasis-200401-'
'wss-wssecurity-utility-1.0.xsd}Created'))
expires = etree.SubElement(
timestamp, ('{http://docs.oasis-open.org/wss/2004/01/oasis-200401-'
'wss-wssecurity-utility-1.0.xsd}Expires'))
created.text, expires.text = self._token_dates()
usernametoken = etree.SubElement(
security, '{http://docs.oasis-open.org/wss/2004/01/oasis-200401-'
'wss-wssecurity-secext-1.0.xsd}UsernameToken')
usernametoken.set(
('{http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-'
'wssecurity-utility-1.0.xsd}u'), "uuid-%s-1" % uuid.uuid4().hex)
username = etree.SubElement(
usernametoken, ('{http://docs.oasis-open.org/wss/2004/01/oasis-'
'200401-wss-wssecurity-secext-1.0.xsd}Username'))
password = etree.SubElement(
usernametoken, ('{http://docs.oasis-open.org/wss/2004/01/oasis-'
'200401-wss-wssecurity-secext-1.0.xsd}Password'),
Type=('http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-'
'username-token-profile-1.0#PasswordText'))
body = etree.SubElement(
root, "{http://www.w3.org/2003/05/soap-envelope}Body")
request_security_token = etree.SubElement(
body, ('{http://docs.oasis-open.org/ws-sx/ws-trust/200512}'
'RequestSecurityToken'), nsmap=TRUST_NAMESPACE)
applies_to = etree.SubElement(
request_security_token,
'{http://schemas.xmlsoap.org/ws/2004/09/policy}AppliesTo',
nsmap=WSP_NAMESPACE)
endpoint_reference = etree.SubElement(
applies_to,
'{http://www.w3.org/2005/08/addressing}EndpointReference',
nsmap=WSA_NAMESPACE)
wsa_address = etree.SubElement(
endpoint_reference,
'{http://www.w3.org/2005/08/addressing}Address')
keytype = etree.SubElement(
request_security_token,
'{http://docs.oasis-open.org/ws-sx/ws-trust/200512}KeyType')
keytype.text = ('http://docs.oasis-open.org/ws-sx/'
'ws-trust/200512/Bearer')
request_type = etree.SubElement(
request_security_token,
'{http://docs.oasis-open.org/ws-sx/ws-trust/200512}RequestType')
request_type.text = ('http://docs.oasis-open.org/ws-sx/'
'ws-trust/200512/Issue')
token_type = etree.SubElement(
request_security_token,
'{http://docs.oasis-open.org/ws-sx/ws-trust/200512}TokenType')
token_type.text = 'urn:oasis:names:tc:SAML:1.0:assertion'
# After constructing the request, let's plug in some values
username.text = self.username
password.text = self.password
to.text = self.identity_provider_url
wsa_address.text = self.service_provider_endpoint
self.prepared_request = root
def _get_adfs_security_token(self, session):
"""Send ADFS Security token to the ADFS server.
Store the result in the instance attribute and raise an exception in
case the response is not valid XML data.
If a user cannot authenticate due to providing bad credentials, the
ADFS2.0 server will return a HTTP 500 response and a XML Fault message.
If ``exceptions.InternalServerError`` is caught, the method tries to
parse the XML response.
If parsing is unsuccessful, an ``exceptions.AuthorizationFailure`` is
raised with a reason from the XML fault. Otherwise an original
``exceptions.InternalServerError`` is re-raised.
:param session : a session object to send out HTTP requests.
:type session: keystoneclient.session.Session
:raises keystoneclient.exceptions.AuthorizationFailure: when HTTP
response from the ADFS server is not a valid XML ADFS security
token.
:raises keystoneclient.exceptions.InternalServerError: If response
status code is HTTP 500 and the response XML cannot be
recognized.
"""
def _get_failure(e):
xpath = '/s:Envelope/s:Body/s:Fault/s:Code/s:Subcode/s:Value'
content = e.response.content
try:
obj = self.str_to_xml(content).xpath(
xpath, namespaces=self.NAMESPACES)
obj = self._first(obj)
return obj.text
# NOTE(marek-denis): etree.Element.xpath() doesn't raise an
# exception, it just returns an empty list. In that case, _first()
# will raise IndexError and we should treat it as an indication XML
# is not valid. exceptions.AuthorizationFailure can be raised from
# str_to_xml(), however since server returned HTTP 500 we should
# re-raise exceptions.InternalServerError.
except (IndexError, exceptions.AuthorizationFailure):
raise e
request_security_token = self.xml_to_str(self.prepared_request)
try:
response = session.post(
url=self.identity_provider_url, headers=self.HEADER_SOAP,
data=request_security_token, authenticated=False)
except exceptions.InternalServerError as e:
reason = _get_failure(e)
raise exceptions.AuthorizationFailure(reason)
msg = _("Error parsing XML returned from "
"the ADFS Identity Provider, reason: %s")
self.adfs_token = self.str_to_xml(response.content, msg)
def _prepare_sp_request(self):
"""Prepare ADFS Security Token to be sent to the Service Provider.
The method works as follows:
* Extract SAML2 assertion from the ADFS Security Token.
* Replace namespaces
* urlencode assertion
* concatenate static string with the encoded assertion
"""
assertion = self.adfs_token.xpath(
self.ADFS_ASSERTION_XPATH, namespaces=self.ADFS_TOKEN_NAMESPACES)
assertion = self._first(assertion)
assertion = self.xml_to_str(assertion)
# TODO(marek-denis): Ideally no string replacement should occur.
# Unfortunately lxml doesn't allow for namespaces changing in-place and
# probably the only solution good for now is to build the assertion
# from scratch and reuse values from the adfs security token.
assertion = assertion.replace(
b'http://docs.oasis-open.org/ws-sx/ws-trust/200512',
b'http://schemas.xmlsoap.org/ws/2005/02/trust')
encoded_assertion = urllib.parse.quote(assertion)
self.encoded_assertion = 'wa=wsignin1.0&wresult=' + encoded_assertion
def _send_assertion_to_service_provider(self, session):
"""Send prepared assertion to a service provider.
As the assertion doesn't contain a protected resource, the value from
the ``location`` header is not valid and we should not let the Session
object get redirected there. The aim of this call is to get a cookie in
the response which is required for entering a protected endpoint.
:param session : a session object to send out HTTP requests.
:type session: keystoneclient.session.Session
:raises: Corresponding HTTP error exception
"""
session.post(
url=self.service_provider_endpoint, data=self.encoded_assertion,
headers=self.HEADER_X_FORM, redirect=False, authenticated=False)
def _access_service_provider(self, session):
"""Access protected endpoint and fetch unscoped token.
After federated authentication workflow a protected endpoint should be
accessible with the session object. The access is granted basing on the
cookies stored within the session object. If, for some reason no
cookies are present (quantity test) it means something went wrong and
user will not be able to fetch an unscoped token. In that case an
``exceptions.AuthorizationFailure` exception is raised and no HTTP call
is even made.
:param session : a session object to send out HTTP requests.
:type session: keystoneclient.session.Session
:raises keystoneclient.exceptions.AuthorizationFailure: in case session
object has empty cookie jar.
"""
if self._cookies(session) is False:
raise exceptions.AuthorizationFailure(
_("Session object doesn't contain a cookie, therefore you are "
"not allowed to enter the Identity Provider's protected "
"area."))
self.authenticated_response = session.get(self.token_url,
authenticated=False)
def _get_unscoped_token(self, session, *kwargs):
"""Retrieve unscoped token after authentcation with ADFS server.
This is a multistep process::
* Prepare ADFS Request Securty Token -
build an etree.XML object filling certain attributes with proper user
credentials, created/expires dates (ticket is be valid for 120 seconds
as currently we don't handle reusing ADFS issued security tokens) .
Step handled by ``ADFSUnscopedToken._prepare_adfs_request()`` method.
* Send ADFS Security token to the ADFS server. Step handled by
``ADFSUnscopedToken._get_adfs_security_token()`` method.
* Receive and parse security token, extract actual SAML assertion and
prepare a request addressed for the Service Provider endpoint.
This also includes changing namespaces in the XML document. Step
handled by ``ADFSUnscopedToken._prepare_sp_request()`` method.
* Send prepared assertion to the Service Provider endpoint. Usually
the server will respond with HTTP 301 code which should be ignored as
the 'location' header doesn't contain protected area. The goal of this
operation is fetching the session cookie which later allows for
accessing protected URL endpoints. Step handed by
``ADFSUnscopedToken._send_assertion_to_service_provider()`` method.
* Once the session cookie is issued, the protected endpoint can be
accessed and an unscoped token can be retrieved. Step handled by
``ADFSUnscopedToken._access_service_provider()`` method.
:param session : a session object to send out HTTP requests.
:type session: keystoneclient.session.Session
:returns: (Unscoped federated token, token JSON body)
"""
self._prepare_adfs_request()
self._get_adfs_security_token(session)
self._prepare_sp_request()
self._send_assertion_to_service_provider(session)
self._access_service_provider(session)
try:
return (self.authenticated_response.headers['X-Subject-Token'],
self.authenticated_response.json()['token'])
except (KeyError, ValueError):
raise exceptions.InvalidResponse(
response=self.authenticated_response)
def get_auth_ref(self, session, **kwargs):
token, token_json = self._get_unscoped_token(session)
return access.AccessInfoV3(token, **token_json)
class Saml2ScopedTokenMethod(v3.TokenMethod):
_method_name = 'saml2'
def get_auth_data(self, session, auth, headers, **kwargs):
"""Build and return request body for token scoping step."""
t = super(Saml2ScopedTokenMethod, self).get_auth_data(
session, auth, headers, **kwargs)
_token_method, token = t
return self._method_name, token
class Saml2ScopedToken(v3.Token):
"""Class for scoping unscoped saml2 token."""
_auth_method_class = Saml2ScopedTokenMethod
def __init__(self, auth_url, token, **kwargs):
super(Saml2ScopedToken, self).__init__(auth_url, token, **kwargs)
if not (self.project_id or self.domain_id):
raise exceptions.ValidationError(
_('Neither project nor domain specified'))

View File

@@ -1,287 +0,0 @@
# Copyright 2012 OpenStack Foundation
# Copyright 2010 United States Government as represented by the
# Administrator of the National Aeronautics and Space Administration.
# Copyright 2011 - 2012 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.
import base64
import hashlib
import hmac
import re
import six
from six.moves import urllib
from keystoneclient.i18n import _
class Ec2Signer(object):
"""Utility class for EC2 signing of request.
This allows a request to be signed with an AWS style signature,
which can then be used for authentication via the keystone ec2
authentication extension.
"""
def __init__(self, secret_key):
self.secret_key = secret_key.encode()
self.hmac = hmac.new(self.secret_key, digestmod=hashlib.sha1)
if hashlib.sha256:
self.hmac_256 = hmac.new(self.secret_key, digestmod=hashlib.sha256)
def _v4_creds(self, credentials):
"""Detect if the credentials are for a v4 signed request.
Check is needed since AWS removed the SignatureVersion field from
the v4 request spec...
This expects a dict of the request headers to be passed in the
credentials dict, since the recommended way to pass v4 creds is
via the 'Authorization' header
see http://docs.aws.amazon.com/general/latest/gr/
sigv4-signed-request-examples.html
Alternatively X-Amz-Algorithm can be specified as a query parameter,
and the authentication data can also passed as query parameters.
Note a hash of the request body is also required in the credentials
for v4 auth to work in the body_hash key, calculated via:
hashlib.sha256(req.body).hexdigest()
"""
try:
auth_str = credentials['headers']['Authorization']
if auth_str.startswith('AWS4-HMAC-SHA256'):
return True
except KeyError:
# Alternatively the Authorization data can be passed via
# the query params list, check X-Amz-Algorithm=AWS4-HMAC-SHA256
try:
if (credentials['params']['X-Amz-Algorithm'] ==
'AWS4-HMAC-SHA256'):
return True
except KeyError: # nosec(cjschaef): in cases of not finding
# entries, simply return False
pass
return False
def generate(self, credentials):
"""Generate auth string according to what SignatureVersion is given."""
signature_version = credentials['params'].get('SignatureVersion')
if signature_version == '0':
return self._calc_signature_0(credentials['params'])
if signature_version == '1':
return self._calc_signature_1(credentials['params'])
if signature_version == '2':
return self._calc_signature_2(credentials['params'],
credentials['verb'],
credentials['host'],
credentials['path'])
if self._v4_creds(credentials):
return self._calc_signature_4(credentials['params'],
credentials['verb'],
credentials['host'],
credentials['path'],
credentials['headers'],
credentials['body_hash'])
if signature_version is not None:
raise Exception(_('Unknown signature version: %s') %
signature_version)
else:
raise Exception(_('Unexpected signature format'))
@staticmethod
def _get_utf8_value(value):
"""Get the UTF8-encoded version of a value."""
if not isinstance(value, (six.binary_type, six.text_type)):
value = str(value)
if isinstance(value, six.text_type):
return value.encode('utf-8')
else:
return value
def _calc_signature_0(self, params):
"""Generate AWS signature version 0 string."""
s = (params['Action'] + params['Timestamp']).encode('utf-8')
self.hmac.update(s)
return base64.b64encode(self.hmac.digest()).decode('utf-8')
def _calc_signature_1(self, params):
"""Generate AWS signature version 1 string."""
keys = list(params)
keys.sort(key=six.text_type.lower)
for key in keys:
self.hmac.update(key.encode('utf-8'))
val = self._get_utf8_value(params[key])
self.hmac.update(val)
return base64.b64encode(self.hmac.digest()).decode('utf-8')
@staticmethod
def _canonical_qs(params):
"""Construct a sorted, correctly encoded query string.
This is required for _calc_signature_2 and _calc_signature_4.
"""
keys = list(params)
keys.sort()
pairs = []
for key in keys:
val = Ec2Signer._get_utf8_value(params[key])
val = urllib.parse.quote(val, safe='-_~')
pairs.append(urllib.parse.quote(key, safe='') + '=' + val)
qs = '&'.join(pairs)
return qs
def _calc_signature_2(self, params, verb, server_string, path):
"""Generate AWS signature version 2 string."""
string_to_sign = '%s\n%s\n%s\n' % (verb, server_string, path)
if self.hmac_256:
current_hmac = self.hmac_256
params['SignatureMethod'] = 'HmacSHA256'
else:
current_hmac = self.hmac
params['SignatureMethod'] = 'HmacSHA1'
string_to_sign += self._canonical_qs(params)
current_hmac.update(string_to_sign.encode('utf-8'))
b64 = base64.b64encode(current_hmac.digest()).decode('utf-8')
return b64
def _calc_signature_4(self, params, verb, server_string, path, headers,
body_hash):
"""Generate AWS signature version 4 string."""
def sign(key, msg):
return hmac.new(key, self._get_utf8_value(msg),
hashlib.sha256).digest()
def signature_key(datestamp, region_name, service_name):
"""Signature key derivation.
See http://docs.aws.amazon.com/general/latest/gr/
signature-v4-examples.html#signature-v4-examples-python
"""
k_date = sign(self._get_utf8_value(b"AWS4" + self.secret_key),
datestamp)
k_region = sign(k_date, region_name)
k_service = sign(k_region, service_name)
k_signing = sign(k_service, "aws4_request")
return k_signing
def auth_param(param_name):
"""Get specified auth parameter.
Provided via one of:
- the Authorization header
- the X-Amz-* query parameters
"""
try:
auth_str = headers['Authorization']
param_str = auth_str.partition(
'%s=' % param_name)[2].split(',')[0]
except KeyError:
param_str = params.get('X-Amz-%s' % param_name)
return param_str
def date_param():
"""Get the X-Amz-Date' value.
The value can be either a header or parameter.
Note AWS supports parsing the Date header also, but this is not
currently supported here as it will require some format mangling
So the X-Amz-Date value must be YYYYMMDDTHHMMSSZ format, then it
can be used to match against the YYYYMMDD format provided in the
credential scope.
see:
http://docs.aws.amazon.com/general/latest/gr/
sigv4-date-handling.html
"""
try:
return headers['X-Amz-Date']
except KeyError:
return params.get('X-Amz-Date')
def canonical_header_str():
# Get the list of headers to include, from either
# - the Authorization header (SignedHeaders key)
# - the X-Amz-SignedHeaders query parameter
headers_lower = dict((k.lower().strip(), v.strip())
for (k, v) in headers.items())
# Boto versions < 2.9.3 strip the port component of the host:port
# header, so detect the user-agent via the header and strip the
# port if we detect an old boto version. FIXME: remove when all
# distros package boto >= 2.9.3, this is a transitional workaround
user_agent = headers_lower.get('user-agent', '')
strip_port = re.match('Boto/2\.[0-9]\.[0-2]', user_agent)
header_list = []
sh_str = auth_param('SignedHeaders')
for h in sh_str.split(';'):
if h not in headers_lower:
continue
if h == 'host' and strip_port:
header_list.append('%s:%s' %
(h, headers_lower[h].split(':')[0]))
continue
header_list.append('%s:%s' % (h, headers_lower[h]))
return '\n'.join(header_list) + '\n'
def canonical_query_str(verb, params):
# POST requests pass parameters in through the request body
canonical_qs = ''
if verb.upper() != 'POST':
canonical_qs = self._canonical_qs(params)
return canonical_qs
# Create canonical request:
# http://docs.aws.amazon.com/general/latest/gr/
# sigv4-create-canonical-request.html
# Get parameters and headers in expected string format
cr = "\n".join((verb.upper(), path,
canonical_query_str(verb, params),
canonical_header_str(),
auth_param('SignedHeaders'),
body_hash))
# Check the date, reject any request where the X-Amz-Date doesn't
# match the credential scope
credential = auth_param('Credential')
credential_split = credential.split('/')
credential_scope = '/'.join(credential_split[1:])
credential_date = credential_split[1]
param_date = date_param()
if not param_date.startswith(credential_date):
raise Exception(_('Request date mismatch error'))
# Create the string to sign
# http://docs.aws.amazon.com/general/latest/gr/
# sigv4-create-string-to-sign.html
cr = cr.encode('utf-8')
string_to_sign = '\n'.join(('AWS4-HMAC-SHA256',
param_date,
credential_scope,
hashlib.sha256(cr).hexdigest()))
# Calculate the derived key, this requires a datestamp, region
# and service, which can be extracted from the credential scope
(req_region, req_service) = credential_split[2:4]
s_key = signature_key(credential_date, req_region, req_service)
# Finally calculate the signature!
signature = hmac.new(s_key, self._get_utf8_value(string_to_sign),
hashlib.sha256).hexdigest()
return signature

View File

@@ -1,364 +0,0 @@
# 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 warnings
from debtcollector import removals
from keystoneauth1 import plugin
from positional import positional
from keystoneclient import _discover
from keystoneclient import exceptions
from keystoneclient.i18n import _
from keystoneclient import session as client_session
from keystoneclient.v2_0 import client as v2_client
from keystoneclient.v3 import client as v3_client
_CLIENT_VERSIONS = {2: v2_client.Client,
3: v3_client.Client}
# functions needed from the private file that can be made public
def normalize_version_number(version):
"""Turn a version representation into a tuple.
Takes a string, tuple or float which represent version formats we can
handle and converts them into a (major, minor) version tuple that we can
actually use for discovery.
e.g. 'v3.3' gives (3, 3)
3.1 gives (3, 1)
:param version: Inputted version number to try and convert.
:returns: A usable version tuple
:rtype: tuple
:raises TypeError: if the inputted version cannot be converted to tuple.
"""
return _discover.normalize_version_number(version)
def version_match(required, candidate):
"""Test that an available version satisfies the required version.
To be suitable a version must be of the same major version as required
and be at least a match in minor/patch level.
eg. 3.3 is a match for a required 3.1 but 4.1 is not.
:param tuple required: the version that must be met.
:param tuple candidate: the version to test against required.
:returns: True if candidate is suitable False otherwise.
:rtype: bool
"""
return _discover.version_match(required, candidate)
def available_versions(url, session=None, **kwargs):
"""Retrieve raw version data from a url."""
if not session:
session = client_session.Session._construct(kwargs)
return _discover.get_version_data(session, url)
class Discover(_discover.Discover):
"""A means to discover and create clients.
Clients are created depending on the supported API versions on the server.
Querying the server is done on object creation and every subsequent method
operates upon the data that was retrieved.
The connection parameters associated with this method are the same format
and name as those used by a client (see
:py:class:`keystoneclient.v2_0.client.Client` and
:py:class:`keystoneclient.v3.client.Client`). If not overridden in
subsequent methods they will also be what is passed to the constructed
client.
In the event that auth_url and endpoint is provided then auth_url will be
used in accordance with how the client operates.
.. warning::
Creating an instance of this class without using the session argument
is deprecated as of the 1.7.0 release and may be removed in the 2.0.0
release.
:param session: A session object that will be used for communication.
Clients will also be constructed with this session.
:type session: keystoneclient.session.Session
:param string auth_url: Identity service endpoint for authorization.
(optional)
:param string endpoint: A user-supplied endpoint URL for the identity
service. (optional)
:param string original_ip: The original IP of the requesting user which
will be sent to identity service in a
'Forwarded' header. (optional) This is ignored
if a session is provided. Deprecated as of the
1.7.0 release and may be removed in the 2.0.0
release.
:param boolean debug: Enables debug logging of all request and responses to
the identity service. default False (optional)
This is ignored if a session is provided. Deprecated
as of the 1.7.0 release and may be removed in the
2.0.0 release.
:param string cacert: Path to the Privacy Enhanced Mail (PEM) file which
contains the trusted authority X.509 certificates
needed to established SSL connection with the
identity service. (optional) This is ignored if a
session is provided. Deprecated as of the 1.7.0
release and may be removed in the 2.0.0 release.
:param string key: Path to the Privacy Enhanced Mail (PEM) file which
contains the unencrypted client private key needed to
established two-way SSL connection with the identity
service. (optional) This is ignored if a session is
provided. Deprecated as of the 1.7.0 release and may be
removed in the 2.0.0 release.
:param string cert: Path to the Privacy Enhanced Mail (PEM) file which
contains the corresponding X.509 client certificate
needed to established two-way SSL connection with the
identity service. (optional) This is ignored if a
session is provided. Deprecated as of the 1.7.0 release
and may be removed in the 2.0.0 release.
:param boolean insecure: Does not perform X.509 certificate validation when
establishing SSL connection with identity service.
default: False (optional) This is ignored if a
session is provided. Deprecated as of the 1.7.0
release and may be removed in the 2.0.0 release.
:param bool authenticated: Should a token be used to perform the initial
discovery operations. default: None (attach a
token if an auth plugin is available).
"""
@positional(2)
def __init__(self, session=None, authenticated=None, **kwargs):
if not session:
warnings.warn(
'Constructing a Discover instance without using a session is '
'deprecated as of the 1.7.0 release and may be removed in the '
'2.0.0 release.', DeprecationWarning)
session = client_session.Session._construct(kwargs)
kwargs['session'] = session
url = None
endpoint = kwargs.pop('endpoint', None)
auth_url = kwargs.pop('auth_url', None)
if endpoint:
self._use_endpoint = True
url = endpoint
elif auth_url:
self._use_endpoint = False
url = auth_url
elif session.auth:
self._use_endpoint = False
url = session.get_endpoint(interface=plugin.AUTH_INTERFACE)
if not url:
raise exceptions.DiscoveryFailure(
_('Not enough information to determine URL. Provide'
' either a Session, or auth_url or endpoint'))
self._client_kwargs = kwargs
super(Discover, self).__init__(session, url,
authenticated=authenticated)
@removals.remove(message='Use raw_version_data instead.', version='1.7.0',
removal_version='2.0.0')
def available_versions(self, **kwargs):
"""Return a list of identity APIs available on the server.
The list returned includes the data associated with them.
.. warning::
This method is deprecated as of the 1.7.0 release in favor of
:meth:`raw_version_data` and may be removed in the 2.0.0 release.
:param bool unstable: Accept endpoints not marked 'stable'. (optional)
Equates to setting allow_experimental
and allow_unknown to True.
:param bool allow_experimental: Allow experimental version endpoints.
:param bool allow_deprecated: Allow deprecated version endpoints.
:param bool allow_unknown: Allow endpoints with an unrecognised status.
:returns: A List of dictionaries as presented by the server. Each dict
will contain the version and the URL to use for the version.
It is a direct representation of the layout presented by the
identity API.
"""
return self.raw_version_data(**kwargs)
@removals.removed_kwarg(
'unstable',
message='Use allow_experimental and allow_unknown instead.',
version='1.7.0', removal_version='2.0.0')
def raw_version_data(self, unstable=False, **kwargs):
"""Get raw version information from URL.
Raw data indicates that only minimal validation processing is performed
on the data, so what is returned here will be the data in the same
format it was received from the endpoint.
:param bool unstable: equates to setting allow_experimental and
allow_unknown. This argument is deprecated as of
the 1.7.0 release and may be removed in the 2.0.0
release.
:param bool allow_experimental: Allow experimental version endpoints.
:param bool allow_deprecated: Allow deprecated version endpoints.
:param bool allow_unknown: Allow endpoints with an unrecognised status.
:returns: The endpoints returned from the server that match the
criteria.
:rtype: List
Example::
>>> from keystoneclient import discover
>>> disc = discover.Discovery(auth_url='http://localhost:5000')
>>> disc.raw_version_data()
[{'id': 'v3.0',
'links': [{'href': u'http://127.0.0.1:5000/v3/',
'rel': u'self'}],
'media-types': [
{'base': 'application/json',
'type': 'application/vnd.openstack.identity-v3+json'},
{'base': 'application/xml',
'type': 'application/vnd.openstack.identity-v3+xml'}],
'status': 'stable',
'updated': '2013-03-06T00:00:00Z'},
{'id': 'v2.0',
'links': [{'href': u'http://127.0.0.1:5000/v2.0/',
'rel': u'self'},
{'href': u'...',
'rel': u'describedby',
'type': u'application/pdf'}],
'media-types': [
{'base': 'application/json',
'type': 'application/vnd.openstack.identity-v2.0+json'},
{'base': 'application/xml',
'type': 'application/vnd.openstack.identity-v2.0+xml'}],
'status': 'stable',
'updated': '2013-03-06T00:00:00Z'}]
"""
if unstable:
kwargs.setdefault('allow_experimental', True)
kwargs.setdefault('allow_unknown', True)
return super(Discover, self).raw_version_data(**kwargs)
def _calculate_version(self, version, unstable):
version_data = None
if version:
version_data = self.data_for(version)
else:
# if no version specified pick the latest one
all_versions = self.version_data(unstable=unstable)
if all_versions:
version_data = all_versions[-1]
if not version_data:
msg = _('Could not find a suitable endpoint')
if version:
msg = _('Could not find a suitable endpoint for client '
'version: %s') % str(version)
raise exceptions.VersionNotAvailable(msg)
return version_data
def _create_client(self, version_data, **kwargs):
# Get the client for the version requested that was returned
try:
client_class = _CLIENT_VERSIONS[version_data['version'][0]]
except KeyError:
version = '.'.join(str(v) for v in version_data['version'])
msg = _('No client available for version: %s') % version
raise exceptions.DiscoveryFailure(msg)
# kwargs should take priority over stored kwargs.
for k, v in self._client_kwargs.items():
kwargs.setdefault(k, v)
# restore the url to either auth_url or endpoint depending on what
# was initially given
if self._use_endpoint:
kwargs['auth_url'] = None
kwargs['endpoint'] = version_data['url']
else:
kwargs['auth_url'] = version_data['url']
kwargs['endpoint'] = None
return client_class(**kwargs)
def create_client(self, version=None, unstable=False, **kwargs):
"""Factory function to create a new identity service client.
:param tuple version: The required version of the identity API. If
specified the client will be selected such that
the major version is equivalent and an endpoint
provides at least the specified minor version.
For example to specify the 3.1 API use (3, 1).
(optional)
:param bool unstable: Accept endpoints not marked 'stable'. (optional)
:param kwargs: Additional arguments will override those provided to
this object's constructor.
:returns: An instantiated identity client object.
:raises keystoneclient.exceptions.DiscoveryFailure: if the server
response is invalid
:raises keystoneclient.exceptions.VersionNotAvailable: if a suitable
client cannot be found.
"""
version_data = self._calculate_version(version, unstable)
return self._create_client(version_data, **kwargs)
def add_catalog_discover_hack(service_type, old, new):
"""Add a version removal rule for a particular service.
Originally deployments of OpenStack would contain a versioned endpoint in
the catalog for different services. E.g. an identity service might look
like ``http://localhost:5000/v2.0``. This is a problem when we want to use
a different version like v3.0 as there is no way to tell where it is
located. We cannot simply change all service catalogs either so there must
be a way to handle the older style of catalog.
This function adds a rule for a given service type that if part of the URL
matches a given regular expression in *old* then it will be replaced with
the *new* value. This will replace all instances of old with new. It should
therefore contain a regex anchor.
For example the included rule states::
add_catalog_version_hack('identity', re.compile('/v2.0/?$'), '/')
so if the catalog retrieves an *identity* URL that ends with /v2.0 or
/v2.0/ then it should replace it simply with / to fix the user's catalog.
:param str service_type: The service type as defined in the catalog that
the rule will apply to.
:param re.RegexObject old: The regular expression to search for and replace
if found.
:param str new: The new string to replace the pattern with.
"""
_discover._VERSION_HACKS.add_discover_hack(service_type, old, new)

View File

@@ -1,437 +0,0 @@
# Copyright 2010 Jacob Kaplan-Moss
# Copyright 2011 Nebula, Inc.
#
# 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.
"""Exception definitions."""
from keystoneauth1 import exceptions as _exc
from keystoneclient.i18n import _
ClientException = _exc.ClientException
"""The base exception class for all exceptions this library raises.
An alias of :py:exc:`keystoneauth1.exceptions.base.ClientException`
"""
ConnectionError = _exc.ConnectionError
"""Cannot connect to API service.
An alias of :py:exc:`keystoneauth1.exceptions.connection.ConnectionError`
"""
ConnectionRefused = _exc.ConnectFailure
"""Connection refused while trying to connect to API service.
An alias of :py:exc:`keystoneauth1.exceptions.connection.ConnectFailure`
"""
SSLError = _exc.SSLError
"""An SSL error occurred.
An alias of :py:exc:`keystoneauth1.exceptions.connection.SSLError`
"""
AuthorizationFailure = _exc.AuthorizationFailure
"""Cannot authorize API client.
An alias of :py:exc:`keystoneauth1.exceptions.auth.AuthorizationFailure`
"""
class ValidationError(ClientException):
"""Error in validation on API client side."""
pass
class UnsupportedVersion(ClientException):
"""User is trying to use an unsupported version of the API."""
pass
class CommandError(ClientException):
"""Error in CLI tool."""
pass
class AuthPluginOptionsMissing(AuthorizationFailure):
"""Auth plugin misses some options."""
def __init__(self, opt_names):
super(AuthPluginOptionsMissing, self).__init__(
_("Authentication failed. Missing options: %s") %
", ".join(opt_names))
self.opt_names = opt_names
class AuthSystemNotFound(AuthorizationFailure):
"""User has specified an AuthSystem that is not installed."""
def __init__(self, auth_system):
super(AuthSystemNotFound, self).__init__(
_("AuthSystemNotFound: %r") % auth_system)
self.auth_system = auth_system
class NoUniqueMatch(ClientException):
"""Multiple entities found instead of one."""
pass
EndpointException = _exc.CatalogException
"""Something is rotten in Service Catalog.
An alias of :py:exc:`keystoneauth1.exceptions.catalog.CatalogException`
"""
EndpointNotFound = _exc.EndpointNotFound
"""Could not find requested endpoint in Service Catalog.
An alias of :py:exc:`keystoneauth1.exceptions.catalog.EndpointNotFound`
"""
class AmbiguousEndpoints(EndpointException):
"""Found more than one matching endpoint in Service Catalog."""
def __init__(self, endpoints=None):
super(AmbiguousEndpoints, self).__init__(
_("AmbiguousEndpoints: %r") % endpoints)
self.endpoints = endpoints
HttpError = _exc.HttpError
"""The base exception class for all HTTP exceptions.
An alias of :py:exc:`keystoneauth1.exceptions.http.HttpError`
"""
HTTPClientError = _exc.HTTPClientError
"""Client-side HTTP error.
Exception for cases in which the client seems to have erred.
An alias of :py:exc:`keystoneauth1.exceptions.http.HTTPClientError`
"""
HttpServerError = _exc.HttpServerError
"""Server-side HTTP error.
Exception for cases in which the server is aware that it has
erred or is incapable of performing the request.
An alias of :py:exc:`keystoneauth1.exceptions.http.HttpServerError`
"""
class HTTPRedirection(HttpError):
"""HTTP Redirection."""
message = _("HTTP Redirection")
class MultipleChoices(HTTPRedirection):
"""HTTP 300 - Multiple Choices.
Indicates multiple options for the resource that the client may follow.
"""
http_status = 300
message = _("Multiple Choices")
BadRequest = _exc.BadRequest
"""HTTP 400 - Bad Request.
The request cannot be fulfilled due to bad syntax.
An alias of :py:exc:`keystoneauth1.exceptions.http.BadRequest`
"""
Unauthorized = _exc.Unauthorized
"""HTTP 401 - Unauthorized.
Similar to 403 Forbidden, but specifically for use when authentication
is required and has failed or has not yet been provided.
An alias of :py:exc:`keystoneauth1.exceptions.http.Unauthorized`
"""
PaymentRequired = _exc.PaymentRequired
"""HTTP 402 - Payment Required.
Reserved for future use.
An alias of :py:exc:`keystoneauth1.exceptions.http.PaymentRequired`
"""
Forbidden = _exc.Forbidden
"""HTTP 403 - Forbidden.
The request was a valid request, but the server is refusing to respond
to it.
An alias of :py:exc:`keystoneauth1.exceptions.http.Forbidden`
"""
NotFound = _exc.NotFound
"""HTTP 404 - Not Found.
The requested resource could not be found but may be available again
in the future.
An alias of :py:exc:`keystoneauth1.exceptions.http.NotFound`
"""
MethodNotAllowed = _exc.MethodNotAllowed
"""HTTP 405 - Method Not Allowed.
A request was made of a resource using a request method not supported
by that resource.
An alias of :py:exc:`keystoneauth1.exceptions.http.MethodNotAllowed`
"""
NotAcceptable = _exc.NotAcceptable
"""HTTP 406 - Not Acceptable.
The requested resource is only capable of generating content not
acceptable according to the Accept headers sent in the request.
An alias of :py:exc:`keystoneauth1.exceptions.http.NotAcceptable`
"""
ProxyAuthenticationRequired = _exc.ProxyAuthenticationRequired
"""HTTP 407 - Proxy Authentication Required.
The client must first authenticate itself with the proxy.
An alias of :py:exc:`keystoneauth1.exceptions.http.ProxyAuthenticationRequired`
"""
RequestTimeout = _exc.RequestTimeout
"""HTTP 408 - Request Timeout.
The server timed out waiting for the request.
An alias of :py:exc:`keystoneauth1.exceptions.http.RequestTimeout`
"""
Conflict = _exc.Conflict
"""HTTP 409 - Conflict.
Indicates that the request could not be processed because of conflict
in the request, such as an edit conflict.
An alias of :py:exc:`keystoneauth1.exceptions.http.Conflict`
"""
Gone = _exc.Gone
"""HTTP 410 - Gone.
Indicates that the resource requested is no longer available and will
not be available again.
An alias of :py:exc:`keystoneauth1.exceptions.http.Gone`
"""
LengthRequired = _exc.LengthRequired
"""HTTP 411 - Length Required.
The request did not specify the length of its content, which is
required by the requested resource.
An alias of :py:exc:`keystoneauth1.exceptions.http.LengthRequired`
"""
PreconditionFailed = _exc.PreconditionFailed
"""HTTP 412 - Precondition Failed.
The server does not meet one of the preconditions that the requester
put on the request.
An alias of :py:exc:`keystoneauth1.exceptions.http.PreconditionFailed`
"""
RequestEntityTooLarge = _exc.RequestEntityTooLarge
"""HTTP 413 - Request Entity Too Large.
The request is larger than the server is willing or able to process.
An alias of :py:exc:`keystoneauth1.exceptions.http.RequestEntityTooLarge`
"""
RequestUriTooLong = _exc.RequestUriTooLong
"""HTTP 414 - Request-URI Too Long.
The URI provided was too long for the server to process.
An alias of :py:exc:`keystoneauth1.exceptions.http.RequestUriTooLong`
"""
UnsupportedMediaType = _exc.UnsupportedMediaType
"""HTTP 415 - Unsupported Media Type.
The request entity has a media type which the server or resource does
not support.
An alias of :py:exc:`keystoneauth1.exceptions.http.UnsupportedMediaType`
"""
RequestedRangeNotSatisfiable = _exc.RequestedRangeNotSatisfiable
"""HTTP 416 - Requested Range Not Satisfiable.
The client has asked for a portion of the file, but the server cannot
supply that portion.
An alias of
:py:exc:`keystoneauth1.exceptions.http.RequestedRangeNotSatisfiable`
"""
ExpectationFailed = _exc.ExpectationFailed
"""HTTP 417 - Expectation Failed.
The server cannot meet the requirements of the Expect request-header field.
An alias of :py:exc:`keystoneauth1.exceptions.http.ExpectationFailed`
"""
UnprocessableEntity = _exc.UnprocessableEntity
"""HTTP 422 - Unprocessable Entity.
The request was well-formed but was unable to be followed due to semantic
errors.
An alias of :py:exc:`keystoneauth1.exceptions.http.UnprocessableEntity`
"""
InternalServerError = _exc.InternalServerError
"""HTTP 500 - Internal Server Error.
A generic error message, given when no more specific message is suitable.
An alias of :py:exc:`keystoneauth1.exceptions.http.InternalServerError`
"""
HttpNotImplemented = _exc.HttpNotImplemented
"""HTTP 501 - Not Implemented.
The server either does not recognize the request method, or it lacks
the ability to fulfill the request.
An alias of :py:exc:`keystoneauth1.exceptions.http.HttpNotImplemented`
"""
BadGateway = _exc.BadGateway
"""HTTP 502 - Bad Gateway.
The server was acting as a gateway or proxy and received an invalid
response from the upstream server.
An alias of :py:exc:`keystoneauth1.exceptions.http.BadGateway`
"""
ServiceUnavailable = _exc.ServiceUnavailable
"""HTTP 503 - Service Unavailable.
The server is currently unavailable.
An alias of :py:exc:`keystoneauth1.exceptions.http.ServiceUnavailable`
"""
GatewayTimeout = _exc.GatewayTimeout
"""HTTP 504 - Gateway Timeout.
The server was acting as a gateway or proxy and did not receive a timely
response from the upstream server.
An alias of :py:exc:`keystoneauth1.exceptions.http.GatewayTimeout`
"""
HttpVersionNotSupported = _exc.HttpVersionNotSupported
"""HTTP 505 - HttpVersion Not Supported.
The server does not support the HTTP protocol version used in the request.
An alias of :py:exc:`keystoneauth1.exceptions.http.HttpVersionNotSupported`
"""
from_response = _exc.from_response
"""Return an instance of :class:`HttpError` or subclass based on response.
An alias of :py:func:`keystoneauth1.exceptions.http.from_response`
"""
# NOTE(akurilin): This alias should be left here to support backwards
# compatibility until we are sure that usage of these exceptions in
# projects is correct.
HTTPNotImplemented = HttpNotImplemented
Timeout = RequestTimeout
HTTPError = HttpError
class CertificateConfigError(Exception):
"""Error reading the certificate."""
def __init__(self, output):
self.output = output
msg = _('Unable to load certificate.')
super(CertificateConfigError, self).__init__(msg)
class CMSError(Exception):
"""Error reading the certificate."""
def __init__(self, output):
self.output = output
msg = _('Unable to sign or verify data.')
super(CMSError, self).__init__(msg)
EmptyCatalog = _exc.EmptyCatalog
"""The service catalog is empty.
An alias of :py:exc:`keystoneauth1.exceptions.catalog.EmptyCatalog`
"""
DiscoveryFailure = _exc.DiscoveryFailure
"""Discovery of client versions failed.
An alias of :py:exc:`keystoneauth1.exceptions.discovery.DiscoveryFailure`
"""
VersionNotAvailable = _exc.VersionNotAvailable
"""Discovery failed as the version you requested is not available.
An alias of :py:exc:`keystoneauth1.exceptions.discovery.VersionNotAvailable`
"""
class MethodNotImplemented(ClientException):
"""Method not implemented by the keystoneclient API."""
MissingAuthPlugin = _exc.MissingAuthPlugin
"""An authenticated request is required but no plugin available.
An alias of :py:exc:`keystoneauth1.exceptions.auth_plugins.MissingAuthPlugin`
"""
NoMatchingPlugin = _exc.NoMatchingPlugin
"""There were no auth plugins that could be created from the parameters
provided.
An alias of :py:exc:`keystoneauth1.exceptions.auth_plugins.NoMatchingPlugin`
"""
class UnsupportedParameters(ClientException):
"""A parameter that was provided or returned is not supported.
:param List(str) names: Names of the unsupported parameters.
.. py:attribute:: names
Names of the unsupported parameters.
"""
def __init__(self, names):
self.names = names
m = _('The following parameters were given that are unsupported: %s')
super(UnsupportedParameters, self).__init__(m % ', '.join(self.names))
class InvalidResponse(ClientException):
"""The response from the server is not valid for this request."""
def __init__(self, response):
super(InvalidResponse, self).__init__()
self.response = response

View File

@@ -1,56 +0,0 @@
# 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.
"""
Produce keystone compliant structures for testing.
The generators in this directory produce keystone compliant structures for
use in testing.
They should be considered part of the public API because they may be relied
upon to generate test tokens for other clients. However they should never be
imported into the main client (keystoneclient or other). Because of this there
may be dependencies from this module on libraries that are only available in
testing.
.. warning::
The keystoneclient.fixture package is deprecated in favor of
keystoneauth1.fixture and will not be supported.
"""
import warnings
from keystoneclient.fixture.discovery import * # noqa
from keystoneclient.fixture import exception
from keystoneclient.fixture import v2
from keystoneclient.fixture import v3
warnings.warn(
"The keystoneclient.fixture package is deprecated in favor of "
"keystoneauth1.fixture and will not be supported.", DeprecationWarning)
FixtureValidationError = exception.FixtureValidationError
V2Token = v2.Token
V3Token = v3.Token
V3FederationToken = v3.V3FederationToken
__all__ = ('DiscoveryList',
'FixtureValidationError',
'V2Discovery',
'V3Discovery',
'V2Token',
'V3Token',
'V3FederationToken',
)

View File

@@ -1,38 +0,0 @@
# 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.
from keystoneauth1.fixture import discovery
__all__ = ('DiscoveryList',
'V2Discovery',
'V3Discovery',
)
V2Discovery = discovery.V2Discovery
"""A Version element for a V2 identity service endpoint.
An alias of :py:exc:`keystoneauth1.fixture.discovery.V2Discovery`
"""
V3Discovery = discovery.V3Discovery
"""A Version element for a V3 identity service endpoint.
An alias of :py:exc:`keystoneauth1.fixture.discovery.V3Discovery`
"""
DiscoveryList = discovery.DiscoveryList
"""A List of version elements.
An alias of :py:exc:`keystoneauth1.fixture.discovery.DiscoveryList`
"""

View File

@@ -1,20 +0,0 @@
# 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.
from keystoneauth1.fixture import exception
FixtureValidationError = exception.FixtureValidationError
"""The token you created is not legitimate.
An alias of :py:exc:`keystoneauth1.fixture.exception.FixtureValidationError``
"""

View File

@@ -1,20 +0,0 @@
# 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.
from keystoneauth1.fixture import v2
Token = v2.Token
"""A V2 Keystone token that can be used for testing.
An alias of :py:exc:`keystoneauth1.fixture.v2.Token`
"""

View File

@@ -1,26 +0,0 @@
# 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.
from keystoneauth1.fixture import v3
Token = v3.Token
"""A V3 Keystone token that can be used for testing.
An alias of :py:exc:`keystoneauth1.fixture.v3.Token`
"""
V3FederationToken = v3.V3FederationToken
"""A V3 Keystone Federation token that can be used for testing.
An alias of :py:exc:`keystoneauth1.fixture.v3.V3FederationToken`
"""

View File

@@ -1,4 +0,0 @@
__all__ = (
'client',
)

View File

@@ -1,208 +0,0 @@
# Copyright 2010 OpenStack Foundation
# 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.
import logging
from debtcollector import removals
from six.moves.urllib import parse as urlparse
from keystoneclient import exceptions
from keystoneclient import httpclient
from keystoneclient.i18n import _
_logger = logging.getLogger(__name__)
# NOTE(jamielennox): To be removed after Pike.
@removals.removed_class('keystoneclient.generic.client.Client',
message='Use keystoneauth discovery',
version='3.9.0',
removal_version='4.0.0')
class Client(httpclient.HTTPClient):
"""Client for the OpenStack Keystone pre-version calls API.
:param string endpoint: A user-supplied endpoint URL for the keystone
service.
:param integer timeout: Allows customization of the timeout for client
http requests. (optional)
Example::
>>> from keystoneclient.generic import client
>>> root = client.Client(auth_url=KEYSTONE_URL)
>>> versions = root.discover()
...
>>> from keystoneclient.v2_0 import client as v2client
>>> keystone = v2client.Client(auth_url=versions['v2.0']['url'])
...
>>> user = keystone.users.get(USER_ID)
>>> user.delete()
"""
def __init__(self, endpoint=None, **kwargs):
"""Initialize a new client for the Keystone v2.0 API."""
super(Client, self).__init__(endpoint=endpoint, **kwargs)
self.endpoint = endpoint
def discover(self, url=None):
"""Discover Keystone servers and return API versions supported.
:param url: optional url to test (without version)
Returns::
{
'message': 'Keystone found at http://127.0.0.1:5000/',
'v2.0': {
'status': 'beta',
'url': 'http://127.0.0.1:5000/v2.0/',
'id': 'v2.0'
},
}
"""
if url:
return self._check_keystone_versions(url)
else:
return self._local_keystone_exists()
def _local_keystone_exists(self):
"""Check if Keystone is available on default local port 35357."""
results = self._check_keystone_versions("http://localhost:35357")
if results is None:
results = self._check_keystone_versions("https://localhost:35357")
return results
def _check_keystone_versions(self, url):
"""Call Keystone URL and detects the available API versions."""
try:
resp, body = self._request(url, "GET",
headers={'Accept':
'application/json'})
# Multiple Choices status code is returned by the root
# identity endpoint, with references to one or more
# Identity API versions -- v3 spec
# some cases we get No Content
if resp.status_code in (200, 204, 300):
try:
results = {}
if 'version' in body:
results['message'] = _("Keystone found at %s") % url
version = body['version']
# Stable/diablo incorrect format
id, status, version_url = (
self._get_version_info(version, url))
results[str(id)] = {"id": id,
"status": status,
"url": version_url}
return results
elif 'versions' in body:
# Correct format
results['message'] = _("Keystone found at %s") % url
for version in body['versions']['values']:
id, status, version_url = (
self._get_version_info(version, url))
results[str(id)] = {"id": id,
"status": status,
"url": version_url}
return results
else:
results['message'] = (
_("Unrecognized response from %s") % url)
return results
except KeyError:
raise exceptions.AuthorizationFailure()
elif resp.status_code == 305:
return self._check_keystone_versions(resp['location'])
else:
raise exceptions.from_response(resp, "GET", url)
except Exception:
_logger.exception('Failed to detect available versions.')
def discover_extensions(self, url=None):
"""Discover Keystone extensions supported.
:param url: optional url to test (should have a version in it)
Returns::
{
'message': 'Keystone extensions at http://127.0.0.1:35357/v2',
'OS-KSEC2': 'OpenStack EC2 Credentials Extension',
}
"""
if url:
return self._check_keystone_extensions(url)
def _check_keystone_extensions(self, url):
"""Call Keystone URL and detects the available extensions."""
try:
if not url.endswith("/"):
url += '/'
resp, body = self._request("%sextensions" % url, "GET",
headers={'Accept':
'application/json'})
if resp.status_code in (200, 204): # some cases we get No Content
if 'extensions' in body and 'values' in body['extensions']:
# Parse correct format (per contract)
extensions = body['extensions']['values']
elif 'extensions' in body:
# Support incorrect, but prevalent format
extensions = body['extensions']
else:
return dict(message=(
_('Unrecognized extensions response from %s') % url))
return dict(self._get_extension_info(e) for e in extensions)
elif resp.status_code == 305:
return self._check_keystone_extensions(resp['location'])
else:
raise exceptions.from_response(
resp, "GET", "%sextensions" % url)
except Exception:
_logger.exception('Failed to check keystone extensions.')
@staticmethod
def _get_version_info(version, root_url):
"""Parse version information.
:param version: a dict of a Keystone version response
:param root_url: string url used to construct
the version if no URL is provided.
:returns: tuple - (verionId, versionStatus, versionUrl)
"""
id = version['id']
status = version['status']
ref = urlparse.urljoin(root_url, id)
if 'links' in version:
for link in version['links']:
if link['rel'] == 'self':
ref = link['href']
break
return (id, status, ref)
@staticmethod
def _get_extension_info(extension):
"""Parse extension information.
:param extension: a dict of a Keystone extension response
:returns: tuple - (alias, name)
"""
alias = extension['alias']
name = extension['name']
return (alias, name)

View File

@@ -1,919 +0,0 @@
# Copyright 2010 Jacob Kaplan-Moss
# Copyright 2011 OpenStack Foundation
# Copyright 2011 Piston Cloud Computing, Inc.
# Copyright 2011 Nebula, Inc.
# 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.
"""OpenStack Client interface. Handles the REST calls and responses."""
import logging
import warnings
from debtcollector import removals
from debtcollector import renames
from keystoneauth1 import adapter
from oslo_serialization import jsonutils
import pkg_resources
from positional import positional
import requests
try:
import pickle # nosec(cjschaef): see bug 1534288 for details
# NOTE(sdague): The conditional keyring import needs to only
# trigger if it's a version of keyring that's supported in global
# requirements. Update _min and _bad when that changes.
keyring_v = pkg_resources.parse_version(
pkg_resources.get_distribution("keyring").version)
keyring_min = pkg_resources.parse_version('5.5.1')
# This is a list of versions, e.g., pkg_resources.parse_version('3.3')
keyring_bad = []
if keyring_v >= keyring_min and keyring_v not in keyring_bad:
import keyring
else:
keyring = None
except (ImportError, pkg_resources.DistributionNotFound):
keyring = None
pickle = None
from keystoneclient import _discover
from keystoneclient import access
from keystoneclient.auth import base
from keystoneclient import baseclient
from keystoneclient import exceptions
from keystoneclient.i18n import _
from keystoneclient import session as client_session
_logger = logging.getLogger(__name__)
USER_AGENT = client_session.USER_AGENT
"""Default user agent string.
This property is deprecated as of the 1.7.0 release in favor of
:data:`keystoneclient.session.USER_AGENT` and may be removed in the 2.0.0
release.
"""
@removals.remove(message='Use keystoneclient.session.request instead.',
version='1.7.0', removal_version='2.0.0')
def request(*args, **kwargs):
"""Make a request.
This function is deprecated as of the 1.7.0 release in favor of
:func:`keystoneclient.session.request` and may be removed in the
2.0.0 release.
"""
return client_session.request(*args, **kwargs)
class _FakeRequestSession(object):
"""This object is a temporary hack that should be removed later.
Keystoneclient has a cyclical dependency with its managers which is
preventing it from being cleaned up correctly. This is always bad but when
we switched to doing connection pooling this object wasn't getting cleaned
either and so we had left over TCP connections hanging around.
Until we can fix the client cleanup we rollback the use of a requests
session and do individual connections like we used to.
"""
def request(self, *args, **kwargs):
return requests.request(*args, **kwargs)
class _KeystoneAdapter(adapter.LegacyJsonAdapter):
"""A wrapper layer to interface keystoneclient with a session.
An adapter provides a generic interface between a client and the session to
provide client specific defaults. This object is passed to the managers.
Keystoneclient managers have some additional requirements of variables that
they expect to be present on the passed object.
Subclass the existing adapter to provide those values that keystoneclient
managers expect.
"""
@property
def user_id(self):
"""Best effort to retrieve the user_id from the plugin.
Some managers rely on being able to get the currently authenticated
user id. This is a problem when we are trying to abstract away the
details of an auth plugin.
For example changing a user's password can require access to the
currently authenticated user_id.
Perform a best attempt to fetch this data. It will work in the legacy
case and with identity plugins and be None otherwise which is the same
as the historical behavior.
"""
# the identity plugin case
try:
return self.session.auth.get_access(self.session).user_id
except AttributeError: # nosec(cjschaef): attempt legacy retrival, or
# return None
pass
# there is a case that we explicitly allow (tested by our unit tests)
# that says you should be able to set the user_id on a legacy client
# and it should overwrite the one retrieved via authentication. If it's
# a legacy then self.session.auth is a client and we retrieve user_id.
try:
return self.session.auth.user_id
except AttributeError: # nosec(cjschaef): retrivals failed, return
# None
pass
return None
class HTTPClient(baseclient.Client, base.BaseAuthPlugin):
"""HTTP client.
.. warning::
Creating an instance of this class without using the session argument
is deprecated as of the 1.7.0 release and may be removed in the 2.0.0
release.
:param string user_id: User ID for authentication. (optional)
:param string username: Username for authentication. (optional)
:param string user_domain_id: User's domain ID for authentication.
(optional)
:param string user_domain_name: User's domain name for authentication.
(optional)
:param string password: Password for authentication. (optional)
:param string domain_id: Domain ID for domain scoping. (optional)
:param string domain_name: Domain name for domain scoping. (optional)
:param string project_id: Project ID for project scoping. (optional)
:param string project_name: Project name for project scoping. (optional)
:param string project_domain_id: Project's domain ID for project scoping.
(optional)
:param string project_domain_name: Project's domain name for project
scoping. (optional)
:param string auth_url: Identity service endpoint for authorization.
:param string region_name: Name of a region to select when choosing an
endpoint from the service catalog.
:param integer timeout: This argument is deprecated as of the 1.7.0 release
in favor of session and may be removed in the 2.0.0
release. (optional)
:param string endpoint: A user-supplied endpoint URL for the identity
service. Lazy-authentication is possible for API
service calls if endpoint is set at instantiation.
(optional)
:param string token: Token for authentication. (optional)
:param string cacert: This argument is deprecated as of the 1.7.0 release
in favor of session and may be removed in the 2.0.0
release. (optional)
:param string key: This argument is deprecated as of the 1.7.0 release
in favor of session and may be removed in the 2.0.0
release. (optional)
:param string cert: This argument is deprecated as of the 1.7.0 release
in favor of session and may be removed in the 2.0.0
release. (optional)
:param boolean insecure: This argument is deprecated as of the 1.7.0
release in favor of session and may be removed in
the 2.0.0 release. (optional)
:param string original_ip: This argument is deprecated as of the 1.7.0
release in favor of session and may be removed
in the 2.0.0 release. (optional)
:param dict auth_ref: To allow for consumers of the client to manage their
own caching strategy, you may initialize a client
with a previously captured auth_reference (token). If
there are keyword arguments passed that also exist in
auth_ref, the value from the argument will take
precedence.
:param boolean use_keyring: Enables caching auth_ref into keyring.
default: False (optional)
:param boolean force_new_token: Keyring related parameter, forces request
for new token. default: False (optional)
:param integer stale_duration: Gap in seconds to determine if token from
keyring is about to expire. default: 30
(optional)
:param string tenant_name: Tenant name. (optional) The tenant_name keyword
argument is deprecated as of the 1.7.0 release
in favor of project_name and may be removed in
the 2.0.0 release.
:param string tenant_id: Tenant id. (optional) The tenant_id keyword
argument is deprecated as of the 1.7.0 release in
favor of project_id and may be removed in the
2.0.0 release.
:param string trust_id: Trust ID for trust scoping. (optional)
:param object session: A Session object to be used for
communicating with the identity service.
:type session: keystoneclient.session.Session
:param string service_name: The default service_name for URL discovery.
default: None (optional)
:param string interface: The default interface for URL discovery.
default: admin (optional)
:param string endpoint_override: Always use this endpoint URL for requests
for this client. (optional)
:param auth: An auth plugin to use instead of the session one. (optional)
:type auth: keystoneclient.auth.base.BaseAuthPlugin
:param string user_agent: The User-Agent string to set.
default: python-keystoneclient (optional)
:param int connect_retries: the maximum number of retries that should
be attempted for connection errors.
Default None - use session default which
is don't retry. (optional)
"""
version = None
@renames.renamed_kwarg('tenant_name', 'project_name', version='1.7.0',
removal_version='2.0.0')
@renames.renamed_kwarg('tenant_id', 'project_id', version='1.7.0',
removal_version='2.0.0')
@positional(enforcement=positional.WARN)
def __init__(self, username=None, tenant_id=None, tenant_name=None,
password=None, auth_url=None, region_name=None, endpoint=None,
token=None, auth_ref=None, use_keyring=False,
force_new_token=False, stale_duration=None, user_id=None,
user_domain_id=None, user_domain_name=None, domain_id=None,
domain_name=None, project_id=None, project_name=None,
project_domain_id=None, project_domain_name=None,
trust_id=None, session=None, service_name=None,
interface='admin', endpoint_override=None, auth=None,
user_agent=USER_AGENT, connect_retries=None, **kwargs):
# set baseline defaults
self.user_id = None
self.username = None
self.user_domain_id = None
self.user_domain_name = None
self.domain_id = None
self.domain_name = None
self.project_id = None
self.project_name = None
self.project_domain_id = None
self.project_domain_name = None
self.auth_url = None
self._endpoint = None
self._management_url = None
self.trust_id = None
# if loading from a dictionary passed in via auth_ref,
# load values from AccessInfo parsing that dictionary
if auth_ref:
self.auth_ref = access.AccessInfo.factory(**auth_ref)
self.version = self.auth_ref.version
self.user_id = self.auth_ref.user_id
self.username = self.auth_ref.username
self.user_domain_id = self.auth_ref.user_domain_id
self.domain_id = self.auth_ref.domain_id
self.domain_name = self.auth_ref.domain_name
self.project_id = self.auth_ref.project_id
self.project_name = self.auth_ref.project_name
self.project_domain_id = self.auth_ref.project_domain_id
auth_urls = self.auth_ref.service_catalog.get_urls(
service_type='identity', endpoint_type='public',
region_name=region_name)
self.auth_url = auth_urls[0]
management_urls = self.auth_ref.service_catalog.get_urls(
service_type='identity', endpoint_type='admin',
region_name=region_name)
self._management_url = management_urls[0]
self.auth_token_from_user = self.auth_ref.auth_token
self.trust_id = self.auth_ref.trust_id
# TODO(blk-u): Using self.auth_ref.service_catalog._region_name is
# deprecated and this code must be removed when the property is
# actually removed.
if self.auth_ref.has_service_catalog() and not region_name:
region_name = self.auth_ref.service_catalog._region_name
else:
self.auth_ref = None
# allow override of the auth_ref defaults from explicit
# values provided to the client
# apply deprecated variables first, so modern variables override them
if tenant_id:
self.project_id = tenant_id
if tenant_name:
self.project_name = tenant_name
# user-related attributes
self.password = password
if user_id:
self.user_id = user_id
if username:
self.username = username
if user_domain_id:
self.user_domain_id = user_domain_id
elif not (user_id or user_domain_name):
self.user_domain_id = 'default'
if user_domain_name:
self.user_domain_name = user_domain_name
# domain-related attributes
if domain_id:
self.domain_id = domain_id
if domain_name:
self.domain_name = domain_name
# project-related attributes
if project_id:
self.project_id = project_id
if project_name:
self.project_name = project_name
if project_domain_id:
self.project_domain_id = project_domain_id
elif not (project_id or project_domain_name):
self.project_domain_id = 'default'
if project_domain_name:
self.project_domain_name = project_domain_name
# trust-related attributes
if trust_id:
self.trust_id = trust_id
# endpoint selection
if auth_url:
self.auth_url = auth_url.rstrip('/')
if token:
self.auth_token_from_user = token
else:
self.auth_token_from_user = None
if endpoint:
self._endpoint = endpoint.rstrip('/')
self._auth_token = None
if not session:
warnings.warn(
'Constructing an HTTPClient instance without using a session '
'is deprecated as of the 1.7.0 release and may be removed in '
'the 2.0.0 release.', DeprecationWarning)
kwargs['session'] = _FakeRequestSession()
session = client_session.Session._construct(kwargs)
session.auth = self
self.session = session
self.domain = ''
# NOTE(jamielennox): unfortunately we can't just use **kwargs here as
# it would incompatibly limit the kwargs that can be passed to __init__
# try and keep this list in sync with adapter.Adapter.__init__
version = (
_discover.normalize_version_number(self.version) if self.version
else None)
self._adapter = _KeystoneAdapter(session,
service_type='identity',
service_name=service_name,
interface=interface,
region_name=region_name,
endpoint_override=endpoint_override,
version=version,
auth=auth,
user_agent=user_agent,
connect_retries=connect_retries)
# keyring setup
if use_keyring and keyring is None:
_logger.warning('Failed to load keyring modules.')
self.use_keyring = use_keyring and keyring is not None
self.force_new_token = force_new_token
self.stale_duration = stale_duration or access.STALE_TOKEN_DURATION
self.stale_duration = int(self.stale_duration)
def get_token(self, session, **kwargs):
return self.auth_token
@property
def auth_token(self):
if self._auth_token:
return self._auth_token
if self.auth_ref:
if self.auth_ref.will_expire_soon(self.stale_duration):
self.authenticate()
return self.auth_ref.auth_token
if self.auth_token_from_user:
return self.auth_token_from_user
def get_endpoint(self, session, interface=None, **kwargs):
if interface == 'public' or interface is base.AUTH_INTERFACE:
return self.auth_url
else:
return self.management_url
def get_user_id(self, session, **kwargs):
return self.auth_ref.user_id
def get_project_id(self, session, **kwargs):
return self.auth_ref.project_id
@auth_token.setter
def auth_token(self, value):
"""Override the auth_token.
If an application sets auth_token explicitly then it will always be
used and override any past or future retrieved token.
"""
self._auth_token = value
@auth_token.deleter
def auth_token(self):
self._auth_token = None
self.auth_token_from_user = None
@property
def service_catalog(self):
"""Return this client's service catalog."""
try:
return self.auth_ref.service_catalog
except AttributeError:
return None
def has_service_catalog(self):
"""Return True if this client provides a service catalog."""
return self.auth_ref and self.auth_ref.has_service_catalog()
@property
def tenant_id(self):
"""Provide read-only backwards compatibility for tenant_id.
.. warning::
This is deprecated as of the 1.7.0 release in favor of project_id
and may be removed in the 2.0.0 release.
"""
warnings.warn(
'tenant_id is deprecated as of the 1.7.0 release in favor of '
'project_id and may be removed in the 2.0.0 release.',
DeprecationWarning)
return self.project_id
@property
def tenant_name(self):
"""Provide read-only backwards compatibility for tenant_name.
.. warning::
This is deprecated as of the 1.7.0 release in favor of project_name
and may be removed in the 2.0.0 release.
"""
warnings.warn(
'tenant_name is deprecated as of the 1.7.0 release in favor of '
'project_name and may be removed in the 2.0.0 release.',
DeprecationWarning)
return self.project_name
@positional(enforcement=positional.WARN)
def authenticate(self, username=None, password=None, tenant_name=None,
tenant_id=None, auth_url=None, token=None,
user_id=None, domain_name=None, domain_id=None,
project_name=None, project_id=None, user_domain_id=None,
user_domain_name=None, project_domain_id=None,
project_domain_name=None, trust_id=None,
region_name=None):
"""Authenticate user.
Uses the data provided at instantiation to authenticate against
the Identity server. This may use either a username and password
or token for authentication. If a tenant name or id was provided
then the resulting authenticated client will be scoped to that
tenant and contain a service catalog of available endpoints.
With the v2.0 API, if a tenant name or ID is not provided, the
authentication token returned will be 'unscoped' and limited in
capabilities until a fully-scoped token is acquired.
With the v3 API, if a domain name or id was provided then the resulting
authenticated client will be scoped to that domain. If a project name
or ID is not provided, and the authenticating user has a default
project configured, the authentication token returned will be 'scoped'
to the default project. Otherwise, the authentication token returned
will be 'unscoped' and limited in capabilities until a fully-scoped
token is acquired.
With the v3 API, with the OS-TRUST extension enabled, the trust_id can
be provided to allow project-specific role delegation between users
If successful, sets the self.auth_ref and self.auth_token with
the returned token. If not already set, will also set
self.management_url from the details provided in the token.
:returns: ``True`` if authentication was successful.
:raises keystoneclient.exceptions.AuthorizationFailure: if unable to
authenticate or validate the existing authorization token
:raises keystoneclient.exceptions.ValueError: if insufficient
parameters are used.
If keyring is used, token is retrieved from keyring instead.
Authentication will only be necessary if any of the following
conditions are met:
* keyring is not used
* if token is not found in keyring
* if token retrieved from keyring is expired or about to
expired (as determined by stale_duration)
* if force_new_token is true
"""
auth_url = auth_url or self.auth_url
user_id = user_id or self.user_id
username = username or self.username
password = password or self.password
user_domain_id = user_domain_id or self.user_domain_id
user_domain_name = user_domain_name or self.user_domain_name
domain_id = domain_id or self.domain_id
domain_name = domain_name or self.domain_name
project_id = project_id or tenant_id or self.project_id
project_name = project_name or tenant_name or self.project_name
project_domain_id = project_domain_id or self.project_domain_id
project_domain_name = project_domain_name or self.project_domain_name
trust_id = trust_id or self.trust_id
region_name = region_name or self._adapter.region_name
if not token:
token = self.auth_token_from_user
if (not token and self.auth_ref and not
self.auth_ref.will_expire_soon(self.stale_duration)):
token = self.auth_ref.auth_token
kwargs = {
'auth_url': auth_url,
'user_id': user_id,
'username': username,
'user_domain_id': user_domain_id,
'user_domain_name': user_domain_name,
'domain_id': domain_id,
'domain_name': domain_name,
'project_id': project_id,
'project_name': project_name,
'project_domain_id': project_domain_id,
'project_domain_name': project_domain_name,
'token': token,
'trust_id': trust_id,
}
(keyring_key, auth_ref) = self.get_auth_ref_from_keyring(**kwargs)
new_token_needed = False
if auth_ref is None or self.force_new_token:
new_token_needed = True
kwargs['password'] = password
resp = self.get_raw_token_from_identity_service(**kwargs)
if isinstance(resp, access.AccessInfo):
self.auth_ref = resp
else:
self.auth_ref = access.AccessInfo.factory(*resp)
# NOTE(jamielennox): The original client relies on being able to
# push the region name into the service catalog but new auth
# it in.
if region_name:
self.auth_ref.service_catalog._region_name = region_name
else:
self.auth_ref = auth_ref
self.process_token(region_name=region_name)
if new_token_needed:
self.store_auth_ref_into_keyring(keyring_key)
return True
def _build_keyring_key(self, **kwargs):
"""Create a unique key for keyring.
Used to store and retrieve auth_ref from keyring.
Return a slash-separated string of values ordered by key name.
"""
return '/'.join([kwargs[k] or '?' for k in sorted(kwargs)])
def get_auth_ref_from_keyring(self, **kwargs):
"""Retrieve auth_ref from keyring.
If auth_ref is found in keyring, (keyring_key, auth_ref) is returned.
Otherwise, (keyring_key, None) is returned.
:returns: (keyring_key, auth_ref) or (keyring_key, None)
:returns: or (None, None) if use_keyring is not set in the object
"""
keyring_key = None
auth_ref = None
if self.use_keyring:
keyring_key = self._build_keyring_key(**kwargs)
try:
auth_ref = keyring.get_password("keystoneclient_auth",
keyring_key)
if auth_ref:
auth_ref = pickle.loads(auth_ref) # nosec(cjschaef): see
# bug 1534288
if auth_ref.will_expire_soon(self.stale_duration):
# token has expired, don't use it
auth_ref = None
except Exception as e:
auth_ref = None
_logger.warning('Unable to retrieve token from keyring %s', e)
return (keyring_key, auth_ref)
def store_auth_ref_into_keyring(self, keyring_key):
"""Store auth_ref into keyring."""
if self.use_keyring:
try:
keyring.set_password("keystoneclient_auth",
keyring_key,
pickle.dumps(self.auth_ref)) # nosec
# (cjschaef): see bug 1534288
except Exception as e:
_logger.warning("Failed to store token into keyring %s", e)
def _process_management_url(self, region_name):
try:
self._management_url = self.auth_ref.service_catalog.url_for(
service_type='identity',
endpoint_type='admin',
region_name=region_name)
except exceptions.EndpointNotFound as e:
_logger.debug("Failed to find endpoint for management url %s", e)
def process_token(self, region_name=None):
"""Extract and process information from the new auth_ref.
And set the relevant authentication information.
"""
# if we got a response without a service catalog, set the local
# list of tenants for introspection, and leave to client user
# to determine what to do. Otherwise, load up the service catalog
if self.auth_ref.project_scoped:
if not self.auth_ref.tenant_id:
raise exceptions.AuthorizationFailure(
_("Token didn't provide tenant_id"))
self._process_management_url(region_name)
self.project_name = self.auth_ref.tenant_name
self.project_id = self.auth_ref.tenant_id
if not self.auth_ref.user_id:
raise exceptions.AuthorizationFailure(
_("Token didn't provide user_id"))
self.user_id = self.auth_ref.user_id
self.auth_domain_id = self.auth_ref.domain_id
self.auth_tenant_id = self.auth_ref.tenant_id
self.auth_user_id = self.auth_ref.user_id
@property
def management_url(self):
return self._endpoint or self._management_url
@management_url.setter
def management_url(self, value):
# NOTE(jamielennox): it's debatable here whether we should set
# _endpoint or _management_url. As historically management_url was set
# permanently setting _endpoint would better match that behaviour.
self._endpoint = value
@positional(enforcement=positional.WARN)
def get_raw_token_from_identity_service(self, auth_url, username=None,
password=None, tenant_name=None,
tenant_id=None, token=None,
user_id=None, user_domain_id=None,
user_domain_name=None,
domain_id=None, domain_name=None,
project_id=None, project_name=None,
project_domain_id=None,
project_domain_name=None,
trust_id=None):
"""Authenticate against the Identity API and get a token.
Not implemented here because auth protocols should be API
version-specific.
Expected to authenticate or validate an existing authentication
reference already associated with the client. Invoking this call
*always* makes a call to the Identity service.
:returns: (``resp``, ``body``)
"""
raise NotImplementedError
def serialize(self, entity):
return jsonutils.dumps(entity)
@removals.remove(version='1.7.0', removal_version='2.0.0')
def request(self, *args, **kwargs):
"""Send an http request with the specified characteristics.
Wrapper around requests.request to handle tasks such as
setting headers, JSON encoding/decoding, and error handling.
.. warning::
*DEPRECATED*: This function is no longer used. It was designed to
be used only by the managers and the managers now receive an
adapter so this function is no longer on the standard request path.
This may be removed in the 2.0.0 release.
"""
return self._request(*args, **kwargs)
def _request(self, *args, **kwargs):
kwargs.setdefault('authenticated', False)
return self._adapter.request(*args, **kwargs)
def _cs_request(self, url, method, management=True, **kwargs):
"""Make an authenticated request to keystone endpoint.
Request are made to keystone endpoint by concatenating
self.management_url and url and passing in method and
any associated kwargs.
"""
if not management:
endpoint_filter = kwargs.setdefault('endpoint_filter', {})
endpoint_filter.setdefault('interface', 'public')
kwargs.setdefault('authenticated', None)
return self._request(url, method, **kwargs)
@removals.remove(version='1.7.0', removal_version='2.0.0')
def get(self, url, **kwargs):
"""Perform an authenticated GET request.
This calls :py:meth:`.request()` with ``method`` set to ``GET`` and an
authentication token if one is available.
.. warning::
*DEPRECATED*: This function is no longer used and is deprecated as
of the 1.7.0 release and may be removed in the 2.0.0 release. It
was designed to be used by the managers and the managers now
receive an adapter so this function is no longer on the standard
request path.
"""
return self._cs_request(url, 'GET', **kwargs)
@removals.remove(version='1.7.0', removal_version='2.0.0')
def head(self, url, **kwargs):
"""Perform an authenticated HEAD request.
This calls :py:meth:`.request()` with ``method`` set to ``HEAD`` and an
authentication token if one is available.
.. warning::
*DEPRECATED*: This function is no longer used and is deprecated as
of the 1.7.0 release and may be removed in the 2.0.0 release. It
was designed to be used by the managers and the managers now
receive an adapter so this function is no longer on the standard
request path.
"""
return self._cs_request(url, 'HEAD', **kwargs)
@removals.remove(version='1.7.0', removal_version='2.0.0')
def post(self, url, **kwargs):
"""Perform an authenticate POST request.
This calls :py:meth:`.request()` with ``method`` set to ``POST`` and an
authentication token if one is available.
.. warning::
*DEPRECATED*: This function is no longer used and is deprecated as
of the 1.7.0 release and may be removed in the 2.0.0 release. It
was designed to be used by the managers and the managers now
receive an adapter so this function is no longer on the standard
request path.
"""
return self._cs_request(url, 'POST', **kwargs)
@removals.remove(version='1.7.0', removal_version='2.0.0')
def put(self, url, **kwargs):
"""Perform an authenticate PUT request.
This calls :py:meth:`.request()` with ``method`` set to ``PUT`` and an
authentication token if one is available.
.. warning::
*DEPRECATED*: This function is no longer used and is deprecated as
of the 1.7.0 release and may be removed in the 2.0.0 release. It
was designed to be used by the managers and the managers now
receive an adapter so this function is no longer on the standard
request path.
"""
return self._cs_request(url, 'PUT', **kwargs)
@removals.remove(version='1.7.0', removal_version='2.0.0')
def patch(self, url, **kwargs):
"""Perform an authenticate PATCH request.
This calls :py:meth:`.request()` with ``method`` set to ``PATCH`` and
an authentication token if one is available.
.. warning::
*DEPRECATED*: This function is no longer used and is deprecated as
of the 1.7.0 release and may be removed in the 2.0.0 release. It
was designed to be used by the managers and the managers now
receive an adapter so this function is no longer on the standard
request path.
"""
return self._cs_request(url, 'PATCH', **kwargs)
@removals.remove(version='1.7.0', removal_version='2.0.0')
def delete(self, url, **kwargs):
"""Perform an authenticate DELETE request.
This calls :py:meth:`.request()` with ``method`` set to ``DELETE`` and
an authentication token if one is available.
.. warning::
*DEPRECATED*: This function is no longer used and is deprecated as
of the 1.7.0 release and may be removed in the 2.0.0 release. It
was designed to be used by the managers and the managers now
receive an adapter so this function is no longer on the standard
request path.
"""
return self._cs_request(url, 'DELETE', **kwargs)
deprecated_session_variables = {'original_ip': None,
'cert': None,
'timeout': None,
'verify_cert': 'verify'}
deprecated_adapter_variables = {'region_name': None}
def __getattr__(self, name):
"""Fetch deprecated session variables."""
try:
var_name = self.deprecated_session_variables[name]
except KeyError: # nosec(cjschaef): try adapter variable or raise
# an AttributeError
pass
else:
warnings.warn(
'The %s session variable is deprecated as of the 1.7.0 '
'release and may be removed in the 2.0.0 release' % name,
DeprecationWarning)
return getattr(self.session, var_name or name)
try:
var_name = self.deprecated_adapter_variables[name]
except KeyError: # nosec(cjschaef): raise an AttributeError
pass
else:
warnings.warn(
'The %s adapter variable is deprecated as of the 1.7.0 '
'release and may be removed in the 2.0.0 release' % name,
DeprecationWarning)
return getattr(self._adapter, var_name or name)
raise AttributeError(_("Unknown Attribute: %s") % name)
def __setattr__(self, name, val):
"""Assign value to deprecated seesion variables."""
try:
var_name = self.deprecated_session_variables[name]
except KeyError: # nosec(cjschaef): try adapter variable or call
# parent class's __setattr__
pass
else:
warnings.warn(
'The %s session variable is deprecated as of the 1.7.0 '
'release and may be removed in the 2.0.0 release' % name,
DeprecationWarning)
return setattr(self.session, var_name or name)
try:
var_name = self.deprecated_adapter_variables[name]
except KeyError: # nosec(cjschaef): call parent class's __setattr__
pass
else:
warnings.warn(
'The %s adapter variable is deprecated as of the 1.7.0 '
'release and may be removed in the 2.0.0 release' % name,
DeprecationWarning)
return setattr(self._adapter, var_name or name)
super(HTTPClient, self).__setattr__(name, val)

View File

@@ -1,27 +0,0 @@
# Copyright 2014 IBM Corp.
#
# 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.
"""oslo.i18n integration module.
See https://docs.openstack.org/oslo.i18n/latest/user/index.html .
"""
import oslo_i18n
_translators = oslo_i18n.TranslatorFactory(domain='keystoneclient')
# The primary translation function using the well-known name "_"
_ = _translators.primary

View File

@@ -1,432 +0,0 @@
# Copyright 2011 OpenStack Foundation
# Copyright 2011, Piston Cloud Computing, Inc.
# Copyright 2011 Nebula, Inc.
#
# 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.
import abc
import warnings
from positional import positional
import six
from keystoneclient import exceptions
from keystoneclient.i18n import _
@six.add_metaclass(abc.ABCMeta)
class ServiceCatalog(object):
"""Helper methods for dealing with a Keystone Service Catalog.
.. warning::
Setting region_name is deprecated in favor of passing the region name
as a parameter to calls made to the service catalog as of the 1.7.0
release and may be removed in the 2.0.0 release.
"""
@classmethod
def factory(cls, resource_dict, token=None, region_name=None):
"""Create ServiceCatalog object given an auth token.
.. warning::
Setting region_name is deprecated in favor of passing the region
name as a parameter to calls made to the service catalog as of the
1.7.0 release and may be removed in the 2.0.0 release.
"""
if ServiceCatalogV3.is_valid(resource_dict):
return ServiceCatalogV3(token, resource_dict, region_name)
elif ServiceCatalogV2.is_valid(resource_dict):
return ServiceCatalogV2(resource_dict, region_name)
else:
raise NotImplementedError(_('Unrecognized auth response'))
def __init__(self, region_name=None):
if region_name:
warnings.warn(
'Setting region_name on the service catalog is deprecated in '
'favor of passing the region name as a parameter to calls '
'made to the service catalog as of the 1.7.0 release and may '
'be removed in the 2.0.0 release.',
DeprecationWarning)
self._region_name = region_name
@property
def region_name(self):
"""Region name.
.. warning::
region_name is deprecated in favor of passing the region name as a
parameter to calls made to the service catalog as of the 1.7.0
release and may be removed in the 2.0.0 release.
"""
warnings.warn(
'region_name is deprecated in favor of passing the region name as '
'a parameter to calls made to the service catalog as of the 1.7.0 '
'release and may be removed in the 2.0.0 release.',
DeprecationWarning)
return self._region_name
def _get_endpoint_region(self, endpoint):
return endpoint.get('region_id') or endpoint.get('region')
@abc.abstractmethod
def get_token(self):
"""Fetch token details from service catalog.
Returns a dictionary containing the following::
- `id`: Token's ID
- `expires`: Token's expiration
- `user_id`: Authenticated user's ID
- `tenant_id`: Authorized project's ID
- `domain_id`: Authorized domain's ID
"""
raise NotImplementedError() # pragma: no cover
@abc.abstractmethod
def _is_endpoint_type_match(self, endpoint, endpoint_type):
"""Helper function to normalize endpoint matching across v2 and v3.
:returns: True if the provided endpoint matches the required
endpoint_type otherwise False.
"""
pass # pragma: no cover
@abc.abstractmethod
def _normalize_endpoint_type(self, endpoint_type):
"""Handle differences in the way v2 and v3 catalogs specify endpoint.
Both v2 and v3 must be able to handle the endpoint style of the other.
For example v2 must be able to handle a 'public' endpoint_type and
v3 must be able to handle a 'publicURL' endpoint_type.
:returns: the endpoint string in the format appropriate for this
service catalog.
"""
pass # pragma: no cover
def get_endpoints(self, service_type=None, endpoint_type=None,
region_name=None, service_name=None):
"""Fetch and filter endpoints for the specified service(s).
Returns endpoints for the specified service (or all) containing
the specified type (or all) and region (or all) and service name.
If there is no name in the service catalog the service_name check will
be skipped. This allows compatibility with services that existed
before the name was available in the catalog.
"""
endpoint_type = self._normalize_endpoint_type(endpoint_type)
region_name = region_name or self._region_name
sc = {}
for service in (self.get_data() or []):
try:
st = service['type']
except KeyError:
continue
if service_type and service_type != st:
continue
# NOTE(jamielennox): service_name is different. It is not available
# in API < v3.3. If it is in the catalog then we enforce it, if it
# is not then we don't because the name could be correct we just
# don't have that information to check against.
if service_name:
try:
sn = service['name']
except KeyError: # nosec(cjschaef)
# assume that we're in v3.0-v3.2 and don't have the name in
# the catalog. Skip the check.
pass
else:
if service_name != sn:
continue
endpoints = sc.setdefault(st, [])
for endpoint in service.get('endpoints', []):
if (endpoint_type and not
self._is_endpoint_type_match(endpoint, endpoint_type)):
continue
if (region_name and
region_name != self._get_endpoint_region(endpoint)):
continue
endpoints.append(endpoint)
return sc
def _get_service_endpoints(self, attr, filter_value, service_type,
endpoint_type, region_name, service_name):
"""Fetch the endpoints of a particular service_type.
Endpoints returned are also filtered based on the attr and
filter_value provided.
"""
sc_endpoints = self.get_endpoints(service_type=service_type,
endpoint_type=endpoint_type,
region_name=region_name,
service_name=service_name)
try:
endpoints = sc_endpoints[service_type]
except KeyError:
return
if attr and not filter_value:
warnings.warn(
'Providing attr without filter_value to get_urls() is '
'deprecated as of the 1.7.0 release and may be removed in the '
'2.0.0 release. Either both should be provided or neither '
'should be provided.')
if filter_value:
return [endpoint for endpoint in endpoints
if endpoint.get(attr) == filter_value]
return endpoints
@abc.abstractmethod
@positional(enforcement=positional.WARN)
def get_urls(self, attr=None, filter_value=None,
service_type='identity', endpoint_type='publicURL',
region_name=None, service_name=None):
"""Fetch endpoint urls from the service catalog.
Fetch the endpoints from the service catalog for a particular
endpoint attribute. If no attribute is given, return the first
endpoint of the specified type.
:param string attr: Endpoint attribute name.
:param string filter_value: Endpoint attribute value.
:param string service_type: Service type of the endpoint.
:param string endpoint_type: Type of endpoint.
Possible values: public or publicURL,
internal or internalURL, admin or
adminURL
:param string region_name: Region of the endpoint.
:param string service_name: The assigned name of the service.
:returns: tuple of urls or None (if no match found)
"""
raise NotImplementedError() # pragma: no cover
@positional(3, enforcement=positional.WARN)
def url_for(self, attr=None, filter_value=None,
service_type='identity', endpoint_type='publicURL',
region_name=None, service_name=None):
"""Fetch an endpoint from the service catalog.
Fetch the specified endpoint from the service catalog for
a particular endpoint attribute. If no attribute is given, return
the first endpoint of the specified type.
Valid endpoint types: `public` or `publicURL`,
`internal` or `internalURL`,
`admin` or 'adminURL`
:param string attr: Endpoint attribute name.
:param string filter_value: Endpoint attribute value.
:param string service_type: Service type of the endpoint.
:param string endpoint_type: Type of endpoint.
:param string region_name: Region of the endpoint.
:param string service_name: The assigned name of the service.
"""
if not self.get_data():
raise exceptions.EmptyCatalog(_('The service catalog is empty.'))
urls = self.get_urls(attr=attr,
filter_value=filter_value,
service_type=service_type,
endpoint_type=endpoint_type,
region_name=region_name,
service_name=service_name)
try:
return urls[0]
except Exception:
if service_name and region_name:
msg = (_('%(endpoint_type)s endpoint for %(service_type)s '
'service named %(service_name)s in %(region_name)s '
'region not found') %
{'endpoint_type': endpoint_type,
'service_type': service_type,
'service_name': service_name,
'region_name': region_name})
elif service_name:
msg = (_('%(endpoint_type)s endpoint for %(service_type)s '
'service named %(service_name)s not found') %
{'endpoint_type': endpoint_type,
'service_type': service_type,
'service_name': service_name})
elif region_name:
msg = (_('%(endpoint_type)s endpoint for %(service_type)s '
'service in %(region_name)s region not found') %
{'endpoint_type': endpoint_type,
'service_type': service_type,
'region_name': region_name})
else:
msg = (_('%(endpoint_type)s endpoint for %(service_type)s '
'service not found') %
{'endpoint_type': endpoint_type,
'service_type': service_type})
raise exceptions.EndpointNotFound(msg)
@abc.abstractmethod
def get_data(self):
"""Get the raw catalog structure.
Get the version dependent catalog structure as it is presented within
the resource.
:returns: list containing raw catalog data entries or None
"""
raise NotImplementedError() # pragma: no cover
class ServiceCatalogV2(ServiceCatalog):
"""An object for encapsulating the v2 service catalog.
The object is created using raw v2 auth token from Keystone.
"""
def __init__(self, resource_dict, region_name=None):
self.catalog = resource_dict
super(ServiceCatalogV2, self).__init__(region_name=region_name)
@classmethod
def is_valid(cls, resource_dict):
# This class is also used for reading token info of an unscoped token.
# Unscoped token does not have 'serviceCatalog' in V2, checking this
# will not work. Use 'token' attribute instead.
return 'token' in resource_dict
def _normalize_endpoint_type(self, endpoint_type):
if endpoint_type and 'URL' not in endpoint_type:
endpoint_type += 'URL'
return endpoint_type
def _is_endpoint_type_match(self, endpoint, endpoint_type):
return endpoint_type in endpoint
def get_data(self):
return self.catalog.get('serviceCatalog')
def get_token(self):
token = {'id': self.catalog['token']['id'],
'expires': self.catalog['token']['expires']}
try:
token['user_id'] = self.catalog['user']['id']
token['tenant_id'] = self.catalog['token']['tenant']['id']
except KeyError: # nosec(cjschaef)
# just leave the tenant and user out if it doesn't exist
pass
return token
@positional(enforcement=positional.WARN)
def get_urls(self, attr=None, filter_value=None,
service_type='identity', endpoint_type='publicURL',
region_name=None, service_name=None):
endpoint_type = self._normalize_endpoint_type(endpoint_type)
endpoints = self._get_service_endpoints(attr=attr,
filter_value=filter_value,
service_type=service_type,
endpoint_type=endpoint_type,
region_name=region_name,
service_name=service_name)
if endpoints:
return tuple([endpoint[endpoint_type] for endpoint in endpoints])
else:
return None
class ServiceCatalogV3(ServiceCatalog):
"""An object for encapsulating the v3 service catalog.
The object is created using raw v3 auth token from Keystone.
"""
def __init__(self, token, resource_dict, region_name=None):
super(ServiceCatalogV3, self).__init__(region_name=region_name)
self._auth_token = token
self.catalog = resource_dict
@classmethod
def is_valid(cls, resource_dict):
# This class is also used for reading token info of an unscoped token.
# Unscoped token does not have 'catalog', checking this
# will not work. Use 'methods' attribute instead.
return 'methods' in resource_dict
def _normalize_endpoint_type(self, endpoint_type):
if endpoint_type:
endpoint_type = endpoint_type.rstrip('URL')
return endpoint_type
def _is_endpoint_type_match(self, endpoint, endpoint_type):
try:
return endpoint_type == endpoint['interface']
except KeyError:
return False
def get_data(self):
return self.catalog.get('catalog')
def get_token(self):
token = {'id': self._auth_token,
'expires': self.catalog['expires_at']}
try:
token['user_id'] = self.catalog['user']['id']
domain = self.catalog.get('domain')
if domain:
token['domain_id'] = domain['id']
project = self.catalog.get('project')
if project:
token['tenant_id'] = project['id']
except KeyError: # nosec(cjschaef)
# just leave the domain, project and user out if it doesn't exist
pass
return token
@positional(enforcement=positional.WARN)
def get_urls(self, attr=None, filter_value=None,
service_type='identity', endpoint_type='public',
region_name=None, service_name=None):
endpoints = self._get_service_endpoints(attr=attr,
filter_value=filter_value,
service_type=service_type,
endpoint_type=endpoint_type,
region_name=region_name,
service_name=service_name)
if endpoints:
return tuple([endpoint['url'] for endpoint in endpoints])
else:
return None

File diff suppressed because it is too large Load Diff

Some files were not shown because too many files have changed in this diff Show More