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: I338ace932cd0e20dd0bfdb69c783c7c831c0baf9
This commit is contained in:
parent
3fa43f1431
commit
f61e5a13b5
14
.gitignore
vendored
14
.gitignore
vendored
@ -1,14 +0,0 @@
|
||||
*.pyc
|
||||
*.egg
|
||||
*.egg-info
|
||||
*.mo
|
||||
.DS_STORE
|
||||
doc/build
|
||||
build
|
||||
dist
|
||||
.tox
|
||||
AUTHORS
|
||||
ChangeLog
|
||||
.coverage
|
||||
reports
|
||||
coverage.xml
|
@ -1,4 +0,0 @@
|
||||
[gerrit]
|
||||
host=review.openstack.org
|
||||
port=29418
|
||||
project=openstack/django_openstack_auth.git
|
5
.mailmap
5
.mailmap
@ -1,5 +0,0 @@
|
||||
<david.lyle@hp.com> <david-lyle@davidlyle-VirtualBox.(none)>
|
||||
<lin-hua.cheng@hp.com> <lin_hua_cheng@yahoo.com>
|
||||
Eric Peterson <ericpeterson@hp.com> ericpeterson-l <ericpeterson@hp.com>
|
||||
Eric Peterson <ericpeterson@hp.com> erpet <ericpeterson@hp.com>
|
||||
Lin Hua Cheng <lin-hua.cheng@hp.com> linhuacheng <lin-hua.cheng@hp.com>
|
@ -1,17 +0,0 @@
|
||||
If you would like to contribute to the development of OpenStack,
|
||||
you must follow the steps documented at:
|
||||
|
||||
http://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:
|
||||
|
||||
http://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/django-openstack-auth
|
176
LICENSE
176
LICENSE
@ -1,176 +0,0 @@
|
||||
|
||||
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.
|
||||
|
@ -1,6 +0,0 @@
|
||||
include AUTHORS
|
||||
include ChangeLog
|
||||
exclude .gitignore
|
||||
exclude .gitreview
|
||||
|
||||
global-exclude *.pyc
|
14
README
Normal file
14
README
Normal 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.
|
27
README.rst
27
README.rst
@ -1,27 +0,0 @@
|
||||
========================
|
||||
Team and repository tags
|
||||
========================
|
||||
|
||||
.. image:: http://governance.openstack.org/badges/django_openstack_auth.svg
|
||||
:target: http://governance.openstack.org/reference/tags/index.html
|
||||
|
||||
.. Change things from this point on
|
||||
|
||||
=====================
|
||||
Django OpenStack Auth
|
||||
=====================
|
||||
|
||||
Django OpenStack Auth is a pluggable Django authentication backend that
|
||||
works with Django's ``contrib.auth`` framework to authenticate a user against
|
||||
OpenStack's Keystone Identity API.
|
||||
|
||||
The current version is designed to work with the Keystone v2.0 and v3 API.
|
||||
|
||||
You can `view the installation instructions`_ on Read The Docs.
|
||||
|
||||
.. _view the installation instructions: http://docs.openstack.org/developer/django_openstack_auth/
|
||||
|
||||
* License: Apache License, Version 2.0
|
||||
* Documentation: http://django-openstack-auth.readthedocs.org/en/latest/
|
||||
* Source: http://git.openstack.org/cgit/openstack/django_openstack_auth/
|
||||
* Bugs: https://bugs.launchpad.net/django-openstack-auth
|
@ -1 +0,0 @@
|
||||
[python: **.py]
|
@ -1,170 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Django OpenStack Auth documentation build configuration file, created by
|
||||
# sphinx-quickstart on Sun Jul 8 15:13:36 2012.
|
||||
#
|
||||
# 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.
|
||||
|
||||
import os
|
||||
|
||||
import django
|
||||
|
||||
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'openstack_auth.tests.settings')
|
||||
|
||||
django.setup()
|
||||
|
||||
# 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.insert(0, os.path.abspath('.'))
|
||||
|
||||
# -- General configuration -----------------------------------------------------
|
||||
|
||||
# If your documentation needs a minimal Sphinx version, state it here.
|
||||
#needs_sphinx = '1.0'
|
||||
|
||||
# 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.viewcode', 'openstackdocstheme']
|
||||
|
||||
# openstackdocstheme options
|
||||
repository_name = 'openstack/django_openstack_auth'
|
||||
bug_project = 'django-openstack-auth'
|
||||
bug_tag = ''
|
||||
html_last_updated_fmt = '%Y-%m-%d %H:%M'
|
||||
|
||||
# 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-sig'
|
||||
|
||||
# The master toctree document.
|
||||
master_doc = 'index'
|
||||
|
||||
# General information about the project.
|
||||
project = u'Django OpenStack Auth'
|
||||
copyright = u'2012, Gabriel Hurley'
|
||||
|
||||
# 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 patterns, relative to source directory, that match files and
|
||||
# directories to ignore when looking for source files.
|
||||
exclude_patterns = ['_build']
|
||||
|
||||
# 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 = ['openstack_auth.']
|
||||
|
||||
|
||||
# -- Options for HTML output ---------------------------------------------------
|
||||
|
||||
# The theme to use for HTML and HTML Help pages. See the documentation for
|
||||
# a list of builtin themes.
|
||||
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 = []
|
||||
|
||||
# If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
|
||||
# using the given strftime format.
|
||||
#html_last_updated_fmt = '%b %d, %Y'
|
||||
|
||||
# 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_domain_indices = 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, "Created using Sphinx" is shown in the HTML footer. Default is True.
|
||||
#html_show_sphinx = True
|
||||
|
||||
# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True.
|
||||
#html_show_copyright = 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 = ''
|
||||
|
||||
# This is the file name suffix for HTML files (e.g. ".xhtml").
|
||||
#html_file_suffix = None
|
||||
|
||||
# Output file base name for HTML help builder.
|
||||
htmlhelp_basename = 'DjangoOpenStackAuthdoc'
|
@ -1,425 +0,0 @@
|
||||
=============
|
||||
Configuration
|
||||
=============
|
||||
|
||||
Django OpenStack Auth is configured through Django ``settings.py`` file.
|
||||
In most cases it is used combined with the OpenStack Dashboard,
|
||||
so the settings file will be ``local/local_settings.py`` file
|
||||
in your OpenStack Dashboard deployment.
|
||||
|
||||
This page covers the configuration options referred by Django OpenStack Auth.
|
||||
|
||||
:ref:`Some settings <settings-shared-with-horizon>` are also referred to
|
||||
by Horizon. Configure them carefully.
|
||||
|
||||
General settings
|
||||
================
|
||||
|
||||
``AUTHENTICATION_PLUGINS``
|
||||
--------------------------
|
||||
|
||||
Default: ``['openstack_auth.plugin.password.PasswordPlugin', 'openstack_auth.plugin.token.TokenPlugin']``
|
||||
|
||||
A list of authentication plugins to be used.
|
||||
In most cases, there is no need to configure this.
|
||||
|
||||
``AVAILABLE_REGIONS``
|
||||
---------------------
|
||||
|
||||
Default: ``None``
|
||||
|
||||
A list of tuples which define multiple regions. The tuple format is
|
||||
``('http://{{ keystone_host }}:5000/v2.0', '{{ region_name }}')``. If any regions
|
||||
are specified the login form will have a dropdown selector for authenticating
|
||||
to the appropriate region, and there will be a region switcher dropdown in
|
||||
the site header when logged in.
|
||||
|
||||
You should also define ``OPENSTACK_KEYSTONE_URL`` to indicate which of
|
||||
the regions is the default one.
|
||||
|
||||
|
||||
``DEFAULT_SERVICE_REGIONS``
|
||||
---------------------------
|
||||
|
||||
Default: ``{}``
|
||||
|
||||
The default service region is set on a per-endpoint basis, meaning that once
|
||||
the user logs into some Keystone endpoint, if a default service region is
|
||||
defined for it in this setting and exists within Keystone catalog, it will be
|
||||
set as the initial service region in this endpoint. By default it is an empty
|
||||
dictionary because upstream can neither predict service region names in a
|
||||
specific deployment, nor tell whether this behavior is desired. The key of the
|
||||
dictionary is a full url of a Keystone endpoint with version suffix, the value
|
||||
is a region name.
|
||||
|
||||
Example::
|
||||
|
||||
DEFAULT_SERVICE_REGIONS = {
|
||||
OPENSTACK_KEYSTONE_URL: 'RegionOne'
|
||||
}
|
||||
|
||||
|
||||
``OPENSTACK_API_VERSIONS``
|
||||
--------------------------
|
||||
|
||||
Default::
|
||||
|
||||
{
|
||||
"identity": 2.0,
|
||||
...,
|
||||
}
|
||||
|
||||
Overrides for OpenStack API versions. Use this setting to force the
|
||||
OpenStack dashboard to use a specific API version for a given service API.
|
||||
Django OpenStack Auth refers to only the ``"identity"`` entry.
|
||||
The current valid values are "2.0" or "3".
|
||||
|
||||
.. note::
|
||||
|
||||
See `Horizon settings
|
||||
<https://docs.openstack.org/developer/horizon/install/settings.html#openstack-api-versions>`__
|
||||
for the full description of this setting.
|
||||
|
||||
``OPENSTACK_ENDPOINT_TYPE``
|
||||
---------------------------
|
||||
|
||||
Default: ``"publicURL"``
|
||||
|
||||
A string which specifies the endpoint type to use for the endpoints in the
|
||||
Keystone service catalog. The default value for all services except for
|
||||
identity is ``"publicURL"``. The default value for the identity service is
|
||||
``"internalURL"``.
|
||||
|
||||
``OPENSTACK_KEYSTONE_ADMIN_ROLES``
|
||||
----------------------------------
|
||||
|
||||
Default: ``["admin"]``
|
||||
|
||||
The list of roles that have administrator privileges in this OpenStack
|
||||
installation. This check is very basic and essentially only works with
|
||||
keystone v2.0 and v3 with the default policy file. The setting assumes there
|
||||
is a common ``admin`` like role(s) across services. Example uses of this
|
||||
setting are:
|
||||
|
||||
* to rename the ``admin`` role to ``cloud-admin``
|
||||
* allowing multiple roles to have administrative privileges, like
|
||||
``["admin", "cloud-admin", "net-op"]``
|
||||
|
||||
``OPENSTACK_KEYSTONE_DEFAULT_DOMAIN``
|
||||
-------------------------------------
|
||||
|
||||
Default: ``"Default"``
|
||||
|
||||
Overrides the default domain used when running on single-domain model
|
||||
with Keystone V3. All entities will be created in the default domain.
|
||||
|
||||
.. note::
|
||||
|
||||
This value must be the name of the default domain, NOT the ID.
|
||||
Also, you will most likely have a value in the keystone policy file like
|
||||
``"cloud_admin": "rule:admin_required and domain_id:<your domain id>"``.
|
||||
This value must be the name of the domain whose ID is specified there.
|
||||
|
||||
``OPENSTACK_KEYSTONE_DOMAIN_CHOICES``
|
||||
-------------------------------------
|
||||
|
||||
.. versionadded:: 12.0.0(Pike)
|
||||
|
||||
Default::
|
||||
|
||||
(
|
||||
('Default', 'Default'),
|
||||
)
|
||||
|
||||
If OPENSTACK_KEYSTONE_DOMAIN_DROPDOWN is enabled, this option can be used to
|
||||
set the available domains to choose from. This is a list of pairs whose first
|
||||
value is the domain name and the second is the display name.
|
||||
|
||||
``OPENSTACK_KEYSTONE_DOMAIN_DROPDOWN``
|
||||
--------------------------------------
|
||||
|
||||
.. versionadded:: 12.0.0(Pike)
|
||||
|
||||
Default: ``False``
|
||||
Set this to True if you want available domains displayed as a dropdown menu on
|
||||
the login screen. It is strongly advised NOT to enable this for public clouds,
|
||||
as advertising enabled domains to unauthenticated customers irresponsibly
|
||||
exposes private information. This should only be used for private clouds where
|
||||
the dashboard sits behind a corporate firewall.
|
||||
|
||||
``OPENSTACK_KEYSTONE_MULTIDOMAIN_SUPPORT``
|
||||
------------------------------------------
|
||||
|
||||
Default: ``False``
|
||||
|
||||
Set this to True if running on multi-domain model. When this is enabled, it
|
||||
will require user to enter the Domain name in addition to username for login.
|
||||
|
||||
``OPENSTACK_KEYSTONE_URL``
|
||||
--------------------------
|
||||
|
||||
Default: ``"http://%s:5000/v2.0" % OPENSTACK_HOST``
|
||||
|
||||
The full URL for the Keystone endpoint used for authentication. Unless you
|
||||
are using HTTPS, running your Keystone server on a nonstandard port, or using
|
||||
a nonstandard URL scheme you shouldn't need to touch this setting.
|
||||
|
||||
``OPENSTACK_SSL_CACERT``
|
||||
------------------------
|
||||
|
||||
Default: ``None``
|
||||
|
||||
When unset or set to ``None`` the default CA certificate on the system is used
|
||||
for SSL verification.
|
||||
|
||||
When set with the path to a custom CA certificate file, this overrides use of
|
||||
the default system CA certificate. This custom certificate is used to verify all
|
||||
connections to openstack services when making API calls.
|
||||
|
||||
``OPENSTACK_SSL_NO_VERIFY``
|
||||
---------------------------
|
||||
|
||||
Default: ``False``
|
||||
|
||||
Disable SSL certificate checks in the OpenStack clients (useful for self-signed
|
||||
certificates).
|
||||
|
||||
``OPENSTACK_TOKEN_HASH_ALGORITHM``
|
||||
----------------------------------
|
||||
|
||||
Default: ``"md5"``
|
||||
|
||||
The hash algorithm to use for authentication tokens. This must match the hash
|
||||
algorithm that the identity (Keystone) server and the auth_token middleware
|
||||
are using. Allowed values are the algorithms supported by Python's hashlib
|
||||
library.
|
||||
|
||||
``OPENSTACK_TOKEN_HASH_ENABLED``
|
||||
--------------------------------
|
||||
|
||||
(Deprecated)
|
||||
|
||||
Default: ``True``
|
||||
|
||||
Hashing tokens from Keystone keeps the Horizon session data smaller, but it
|
||||
doesn't work in some cases when using PKI tokens. Uncomment this value and
|
||||
set it to False if using PKI tokens and there are 401 errors due to token
|
||||
hashing.
|
||||
|
||||
This option is now marked as "deprecated" and will be removed in Ocata or a
|
||||
later release. PKI tokens currently work with hashing, and Keystone will soon
|
||||
deprecate usage of PKI tokens.
|
||||
|
||||
``PASSWORD_EXPIRES_WARNING_THRESHOLD_DAYS``
|
||||
-------------------------------------------
|
||||
|
||||
Default: ``-1``
|
||||
|
||||
Password will have an expiration date when using keystone v3 and enabling the
|
||||
feature. This setting allows you to set the number of days that the user will
|
||||
be alerted prior to the password expiration. Once the password expires keystone
|
||||
will deny the access and users must contact an admin to change their password.
|
||||
Setting this value to ``N`` days means the user will be alerted when the
|
||||
password expires in less than ``N+1`` days. ``-1`` disables the feature.
|
||||
|
||||
``POLICY_FILES``
|
||||
----------------
|
||||
|
||||
Default: ``{'identity': 'keystone_policy.json', 'compute': 'nova_policy.json'}``
|
||||
|
||||
This should essentially be the mapping of the contents of ``POLICY_FILES_PATH``
|
||||
to service types. When policy.json files are added to ``POLICY_FILES_PATH``,
|
||||
they should be included here too.
|
||||
|
||||
``POLICY_FILES_PATH``
|
||||
---------------------
|
||||
|
||||
Default: ``os.path.join(ROOT_PATH, "conf")``
|
||||
|
||||
Specifies where service based policy files are located. These are used to
|
||||
define the policy rules actions are verified against.
|
||||
|
||||
``SECURE_PROXY_ADDR_HEADER``
|
||||
----------------------------
|
||||
|
||||
Default: ``False``
|
||||
|
||||
If horizon is behind a proxy server and the proxy is configured, the IP address
|
||||
from request is passed using header variables inside the request. The header
|
||||
name depends on a proxy or a load-balancer. This setting specifies the name of
|
||||
the header with remote IP address. The main use is for authentication log
|
||||
(success or fail) displaing the IP address of the user.
|
||||
The commom value for this setting is ``HTTP_X_REAL_IP`` or
|
||||
``HTTP_X_FORWARDED_FOR``.
|
||||
If not present, then ``REMOTE_ADDR`` header is used. (``REMOTE_ADDR`` is the
|
||||
field of Django HttpRequest object which contains IP address of the client.)
|
||||
|
||||
``SESSION_TIMEOUT``
|
||||
-------------------
|
||||
|
||||
Default: ``"3600"``
|
||||
|
||||
This ``SESSION_TIMEOUT`` is a method to supercede the token timeout with a
|
||||
shorter horizon session timeout (in seconds). So if your token expires in
|
||||
60 minutes, a value of 1800 will log users out after 30 minutes.
|
||||
|
||||
``TOKEN_DELETION_DISABLED``
|
||||
---------------------------
|
||||
|
||||
Default: ``False``
|
||||
|
||||
This setting allows deployers to control whether a token is deleted on log out.
|
||||
This can be helpful when there are often long running processes being run
|
||||
in the Horizon environment.
|
||||
|
||||
``TOKEN_TIMEOUT_MARGIN``
|
||||
------------------------
|
||||
|
||||
Default: ``0``
|
||||
|
||||
A time margin in seconds to subtract from the real token's validity.
|
||||
An example usage is that the token can be valid once the middleware
|
||||
passed, and invalid (timed-out) during a view rendering and this
|
||||
generates authorization errors during the view rendering.
|
||||
By setting this value to some smaller seconds, you can avoid token
|
||||
expiration during a view rendering.
|
||||
|
||||
``WEBROOT``
|
||||
-----------
|
||||
|
||||
Default: ``"/"``
|
||||
|
||||
Specifies the location where the access to the dashboard is configured in
|
||||
the web server.
|
||||
|
||||
For example, if you're accessing the Dashboard via
|
||||
https://<your server>/dashboard, you would set this to ``"/dashboard/"``.
|
||||
|
||||
.. note::
|
||||
|
||||
Additional settings may be required in the config files of your webserver
|
||||
of choice. For example to make ``"/dashboard/"`` the web root in Apache,
|
||||
the ``"sites-available/horizon.conf"`` requires a couple of additional
|
||||
aliases set::
|
||||
|
||||
Alias /dashboard/static %HORIZON_DIR%/static
|
||||
|
||||
Alias /dashboard/media %HORIZON_DIR%/openstack_dashboard/static
|
||||
|
||||
Apache also requires changing your WSGIScriptAlias to reflect the desired
|
||||
path. For example, you'd replace ``/`` with ``/dashboard`` for the
|
||||
alias.
|
||||
|
||||
Web SSO (Single Sign On) settings
|
||||
=================================
|
||||
|
||||
``WEBSSO_ENABLED``
|
||||
------------------
|
||||
|
||||
Default: ``False``
|
||||
|
||||
Enables keystone web single-sign-on if set to True. For this feature to work,
|
||||
make sure that you are using Keystone V3 and Django OpenStack Auth V1.2.0 or
|
||||
later.
|
||||
|
||||
``WEBSSO_INITIAL_CHOICE``
|
||||
-------------------------
|
||||
|
||||
Default: ``"credentials"``
|
||||
|
||||
Determines the default authentication mechanism. When user lands on the login
|
||||
page, this is the first choice they will see.
|
||||
|
||||
``WEBSSO_CHOICES``
|
||||
------------------
|
||||
|
||||
Default::
|
||||
|
||||
(
|
||||
("credentials", _("Keystone Credentials")),
|
||||
("oidc", _("OpenID Connect")),
|
||||
("saml2", _("Security Assertion Markup Language"))
|
||||
)
|
||||
|
||||
This is the list of authentication mechanisms available to the user. It
|
||||
includes Keystone federation protocols such as OpenID Connect and SAML, and
|
||||
also keys that map to specific identity provider and federation protocol
|
||||
combinations (as defined in ``WEBSSO_IDP_MAPPING``). The list of choices is
|
||||
completely configurable, so as long as the id remains intact. Do not remove
|
||||
the credentials mechanism unless you are sure. Once removed, even admins will
|
||||
have no way to log into the system via the dashboard.
|
||||
|
||||
``WEBSSO_IDP_MAPPING``
|
||||
----------------------
|
||||
|
||||
Default: ``{}``
|
||||
|
||||
A dictionary of specific identity provider and federation protocol combinations.
|
||||
From the selected authentication mechanism, the value will be looked up as keys
|
||||
in the dictionary. If a match is found, it will redirect the user to a identity
|
||||
provider and federation protocol specific WebSSO endpoint in keystone, otherwise
|
||||
it will use the value as the protocol_id when redirecting to the WebSSO by
|
||||
protocol endpoint.
|
||||
|
||||
Example::
|
||||
|
||||
WEBSSO_CHOICES = (
|
||||
("credentials", _("Keystone Credentials")),
|
||||
("oidc", _("OpenID Connect")),
|
||||
("saml2", _("Security Assertion Markup Language")),
|
||||
("acme_oidc", "ACME - OpenID Connect"),
|
||||
("acme_saml2", "ACME - SAML2")
|
||||
)
|
||||
|
||||
WEBSSO_IDP_MAPPING = {
|
||||
"acme_oidc": ("acme", "oidc"),
|
||||
"acme_saml2": ("acme", "saml2")
|
||||
}
|
||||
|
||||
.. note::
|
||||
The value is expected to be a tuple formatted as: (<idp_id>, <protocol_id>).
|
||||
|
||||
K2K (Keystone to Keystone) Federation settings
|
||||
==============================================
|
||||
|
||||
``KEYSTONE_PROVIDER_IDP_NAME``
|
||||
------------------------------
|
||||
|
||||
Default: ``Local Keystone``
|
||||
|
||||
The Keystone Provider drop down uses Keystone to Keystone federation
|
||||
to switch between Keystone service providers.
|
||||
This sets display name for Identity Provider (dropdown display name).
|
||||
|
||||
``KEYSTONE_PROVIDER_IDP_ID``
|
||||
----------------------------
|
||||
|
||||
Default:: ``localkeystone``
|
||||
|
||||
This ID is used for only for comparison with the service provider IDs.
|
||||
This ID should not match any service provider IDs.
|
||||
|
||||
.. _settings-shared-with-horizon:
|
||||
|
||||
Settings shared with Horizon
|
||||
============================
|
||||
|
||||
The following settings in Django OpenStack Auth are also used by Horizon.
|
||||
|
||||
* ``AVAILABLE_REGIONS``
|
||||
* ``OPENSTACK_API_VERSIONS``
|
||||
* ``OPENSTACK_KEYSTONE_URL``
|
||||
* ``OPENSTACK_ENDPOINT_TYPE``
|
||||
* ``OPENSTACK_SSL_CACERT``
|
||||
* ``OPENSTACK_SSL_NO_VERIFY``
|
||||
* ``WEBROOT``
|
||||
|
||||
Django OpenStack Auth also refers to the following Django settings.
|
||||
For more detail, see `Django settings documentation
|
||||
<https://docs.djangoproject.com/en/1.11/ref/settings/#auth>`__.
|
||||
They are usually configured as part of Horizon settings.
|
||||
|
||||
* ``LOGIN_REDIRECT_URL``
|
||||
* ``LOGIN_URL``
|
||||
* ``SESSION_ENGINE``
|
||||
* ``USE_TZ``
|
@ -1,21 +0,0 @@
|
||||
=====================
|
||||
Django OpenStack Auth
|
||||
=====================
|
||||
|
||||
Django OpenStack Auth is a pluggable Django authentication backend that
|
||||
works with Django's ``contrib.auth`` framework to authenticate a user against
|
||||
OpenStack's Keystone Identity API.
|
||||
|
||||
The current version is designed to work with the Keystone V2 or V3 API.
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 2
|
||||
|
||||
install/index
|
||||
configuration/index
|
||||
reference/index
|
||||
|
||||
* :ref:`genindex`
|
||||
* :ref:`modindex`
|
||||
* :ref:`search`
|
||||
|
@ -1,63 +0,0 @@
|
||||
===============
|
||||
Getting Started
|
||||
===============
|
||||
|
||||
Installation
|
||||
============
|
||||
|
||||
Installing is quick and easy:
|
||||
|
||||
#. Run ``pip install django_openstack_auth``.
|
||||
|
||||
#. Add ``openstack_auth`` to ``settings.INSTALLED_APPS``.
|
||||
|
||||
#. Add ``'openstack_auth.backend.KeystoneBackend'`` to your
|
||||
``settings.AUTHENTICATION_BACKENDS``, e.g.::
|
||||
|
||||
AUTHENTICATION_BACKENDS = ('openstack_auth.backend.KeystoneBackend',)
|
||||
|
||||
#. Configure your API endpoint(s) in ``settings.py``::
|
||||
|
||||
OPENSTACK_KEYSTONE_URL = "http://example.com:5000/v3"
|
||||
|
||||
#. Include ``'openstack_auth.urls'`` somewhere in your ``urls.py`` file.
|
||||
|
||||
#. Use it as you would any other Django auth backend.
|
||||
|
||||
Running Tests
|
||||
=============
|
||||
|
||||
Before running tests, you should have ``tox`` installed and available in your
|
||||
environment:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
$ pip install tox
|
||||
|
||||
.. NOTE::
|
||||
|
||||
You may need to perform both the above operation and the next inside a
|
||||
python virtualenv, or prefix the above command with ``sudo``, depending on
|
||||
your preference.
|
||||
|
||||
To execute the full suite of tests maintained within the project, simply run:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
$ tox
|
||||
|
||||
.. NOTE::
|
||||
|
||||
The first time you run ``tox``, it will take additional time to build
|
||||
virtualenvs. You can later use the ``-r`` option with ``tox`` to rebuild
|
||||
your virtualenv in a similar manner.
|
||||
|
||||
To run tests for one or more specific test environments (for example, the most
|
||||
common configuration of Python 2.7 and PEP-8), list the environments with the
|
||||
``-e`` option, separated by spaces:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
$ tox -e py27,pep8
|
||||
|
||||
See ``tox.ini`` for the full list of available test environments.
|
@ -1,6 +0,0 @@
|
||||
==================
|
||||
The Backend Module
|
||||
==================
|
||||
|
||||
.. automodule:: openstack_auth.backend
|
||||
:members:
|
@ -1,6 +0,0 @@
|
||||
================
|
||||
The Forms Module
|
||||
================
|
||||
|
||||
.. automodule:: openstack_auth.forms
|
||||
:members:
|
@ -1,13 +0,0 @@
|
||||
=====================================
|
||||
Django OpenStack Auth API Reference
|
||||
=====================================
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 2
|
||||
|
||||
user
|
||||
views
|
||||
forms
|
||||
backend
|
||||
utils
|
||||
|
@ -1,6 +0,0 @@
|
||||
==============
|
||||
The User Class
|
||||
==============
|
||||
|
||||
.. automodule:: openstack_auth.user
|
||||
:members:
|
@ -1,6 +0,0 @@
|
||||
================
|
||||
The Utils Module
|
||||
================
|
||||
|
||||
.. automodule:: openstack_auth.utils
|
||||
:members:
|
@ -1,6 +0,0 @@
|
||||
================
|
||||
The Views Module
|
||||
================
|
||||
|
||||
.. automodule:: openstack_auth.views
|
||||
:members:
|
@ -1,17 +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 pbr.version
|
||||
|
||||
|
||||
__version__ = pbr.version.VersionInfo('django_openstack_auth').version_string()
|
@ -1,282 +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.
|
||||
|
||||
""" Module defining the Django auth backend class for the Keystone API. """
|
||||
|
||||
import datetime
|
||||
import logging
|
||||
import pytz
|
||||
|
||||
from django.conf import settings
|
||||
from django.utils.module_loading import import_string
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
from openstack_auth import exceptions
|
||||
from openstack_auth import user as auth_user
|
||||
from openstack_auth import utils
|
||||
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
KEYSTONE_CLIENT_ATTR = "_keystoneclient"
|
||||
|
||||
|
||||
class KeystoneBackend(object):
|
||||
"""Django authentication backend for use with ``django.contrib.auth``."""
|
||||
|
||||
def __init__(self):
|
||||
self._auth_plugins = None
|
||||
|
||||
@property
|
||||
def auth_plugins(self):
|
||||
if self._auth_plugins is None:
|
||||
plugins = getattr(
|
||||
settings,
|
||||
'AUTHENTICATION_PLUGINS',
|
||||
['openstack_auth.plugin.password.PasswordPlugin',
|
||||
'openstack_auth.plugin.token.TokenPlugin'])
|
||||
|
||||
self._auth_plugins = [import_string(p)() for p in plugins]
|
||||
|
||||
return self._auth_plugins
|
||||
|
||||
def check_auth_expiry(self, auth_ref, margin=None):
|
||||
if not utils.is_token_valid(auth_ref, margin):
|
||||
msg = _("The authentication token issued by the Identity service "
|
||||
"has expired.")
|
||||
LOG.warning("The authentication token issued by the Identity "
|
||||
"service appears to have expired before it was "
|
||||
"issued. This may indicate a problem with either your "
|
||||
"server or client configuration.")
|
||||
raise exceptions.KeystoneAuthException(msg)
|
||||
return True
|
||||
|
||||
def get_user(self, user_id):
|
||||
"""Returns the current user from the session data.
|
||||
|
||||
If authenticated, this return the user object based on the user ID
|
||||
and session data.
|
||||
|
||||
.. note::
|
||||
|
||||
This required monkey-patching the ``contrib.auth`` middleware
|
||||
to make the ``request`` object available to the auth backend class.
|
||||
|
||||
"""
|
||||
if (hasattr(self, 'request') and
|
||||
user_id == self.request.session["user_id"]):
|
||||
token = self.request.session['token']
|
||||
endpoint = self.request.session['region_endpoint']
|
||||
services_region = self.request.session['services_region']
|
||||
user = auth_user.create_user_from_token(self.request, token,
|
||||
endpoint, services_region)
|
||||
return user
|
||||
else:
|
||||
return None
|
||||
|
||||
def authenticate(self, auth_url=None, **kwargs):
|
||||
"""Authenticates a user via the Keystone Identity API."""
|
||||
LOG.debug('Beginning user authentication')
|
||||
|
||||
if not auth_url:
|
||||
auth_url = settings.OPENSTACK_KEYSTONE_URL
|
||||
|
||||
auth_url, url_fixed = utils.fix_auth_url_version_prefix(auth_url)
|
||||
if url_fixed:
|
||||
LOG.warning("The OPENSTACK_KEYSTONE_URL setting points to a v2.0 "
|
||||
"Keystone endpoint, but v3 is specified as the API "
|
||||
"version to use by Horizon. Using v3 endpoint for "
|
||||
"authentication.")
|
||||
|
||||
for plugin in self.auth_plugins:
|
||||
unscoped_auth = plugin.get_plugin(auth_url=auth_url, **kwargs)
|
||||
|
||||
if unscoped_auth:
|
||||
break
|
||||
else:
|
||||
msg = _('No authentication backend could be determined to '
|
||||
'handle the provided credentials.')
|
||||
LOG.warning('No authentication backend could be determined to '
|
||||
'handle the provided credentials. This is likely a '
|
||||
'configuration error that should be addressed.')
|
||||
raise exceptions.KeystoneAuthException(msg)
|
||||
|
||||
# the recent project id a user might have set in a cookie
|
||||
recent_project = None
|
||||
request = kwargs.get('request')
|
||||
if request:
|
||||
# Grab recent_project found in the cookie, try to scope
|
||||
# to the last project used.
|
||||
recent_project = request.COOKIES.get('recent_project')
|
||||
unscoped_auth_ref = plugin.get_access_info(unscoped_auth)
|
||||
|
||||
# Check expiry for our unscoped auth ref.
|
||||
self.check_auth_expiry(unscoped_auth_ref)
|
||||
|
||||
domain_name = kwargs.get('user_domain_name', None)
|
||||
domain_auth, domain_auth_ref = plugin.get_domain_scoped_auth(
|
||||
unscoped_auth, unscoped_auth_ref, domain_name)
|
||||
scoped_auth, scoped_auth_ref = plugin.get_project_scoped_auth(
|
||||
unscoped_auth, unscoped_auth_ref, recent_project=recent_project)
|
||||
|
||||
# Abort if there are no projects for this user and a valid domain
|
||||
# token has not been obtained
|
||||
#
|
||||
# The valid use cases for a user login are:
|
||||
# Keystone v2: user must have a role on a project and be able
|
||||
# to obtain a project scoped token
|
||||
# Keystone v3: 1) user can obtain a domain scoped token (user
|
||||
# has a role on the domain they authenticated to),
|
||||
# only, no roles on a project
|
||||
# 2) user can obtain a domain scoped token and has
|
||||
# a role on a project in the domain they
|
||||
# authenticated to (and can obtain a project scoped
|
||||
# token)
|
||||
# 3) user cannot obtain a domain scoped token, but can
|
||||
# obtain a project scoped token
|
||||
if not scoped_auth_ref and domain_auth_ref:
|
||||
# if the user can't obtain a project scoped token, set the scoped
|
||||
# token to be the domain token, if valid
|
||||
scoped_auth = domain_auth
|
||||
scoped_auth_ref = domain_auth_ref
|
||||
elif not scoped_auth_ref and not domain_auth_ref:
|
||||
msg = _('You are not authorized for any projects.')
|
||||
if utils.get_keystone_version() >= 3:
|
||||
msg = _('You are not authorized for any projects or domains.')
|
||||
raise exceptions.KeystoneAuthException(msg)
|
||||
|
||||
# Check expiry for our new scoped token.
|
||||
self.check_auth_expiry(scoped_auth_ref)
|
||||
|
||||
# We want to try to use the same region we just logged into
|
||||
# which may or may not be the default depending upon the order
|
||||
# keystone uses
|
||||
region_name = None
|
||||
id_endpoints = scoped_auth_ref.service_catalog.\
|
||||
get_endpoints(service_type='identity')
|
||||
for id_endpoint in [cat for cat in id_endpoints['identity']]:
|
||||
if auth_url in id_endpoint.values():
|
||||
region_name = id_endpoint['region']
|
||||
break
|
||||
|
||||
interface = getattr(settings, 'OPENSTACK_ENDPOINT_TYPE', 'public')
|
||||
|
||||
endpoint, url_fixed = utils.fix_auth_url_version_prefix(
|
||||
scoped_auth_ref.service_catalog.url_for(
|
||||
service_type='identity',
|
||||
interface=interface,
|
||||
region_name=region_name))
|
||||
if url_fixed:
|
||||
LOG.warning("The Keystone URL in service catalog points to a v2.0 "
|
||||
"Keystone endpoint, but v3 is specified as the API "
|
||||
"version to use by Horizon. Using v3 endpoint for "
|
||||
"authentication.")
|
||||
|
||||
# If we made it here we succeeded. Create our User!
|
||||
unscoped_token = unscoped_auth_ref.auth_token
|
||||
|
||||
user = auth_user.create_user_from_token(
|
||||
request,
|
||||
auth_user.Token(scoped_auth_ref, unscoped_token=unscoped_token),
|
||||
endpoint,
|
||||
services_region=region_name)
|
||||
|
||||
if request is not None:
|
||||
# if no k2k providers exist then the function returns quickly
|
||||
utils.store_initial_k2k_session(auth_url, request, scoped_auth_ref,
|
||||
unscoped_auth_ref)
|
||||
request.session['unscoped_token'] = unscoped_token
|
||||
if domain_auth_ref:
|
||||
# check django session engine, if using cookies, this will not
|
||||
# work, as it will overflow the cookie so don't add domain
|
||||
# scoped token to the session and put error in the log
|
||||
if utils.using_cookie_backed_sessions():
|
||||
LOG.error('Using signed cookies as SESSION_ENGINE with '
|
||||
'OPENSTACK_KEYSTONE_MULTIDOMAIN_SUPPORT is '
|
||||
'enabled. This disables the ability to '
|
||||
'perform identity operations due to cookie size '
|
||||
'constraints.')
|
||||
else:
|
||||
request.session['domain_token'] = domain_auth_ref
|
||||
|
||||
request.user = user
|
||||
timeout = getattr(settings, "SESSION_TIMEOUT", 3600)
|
||||
token_life = user.token.expires - datetime.datetime.now(pytz.utc)
|
||||
session_time = min(timeout, int(token_life.total_seconds()))
|
||||
request.session.set_expiry(session_time)
|
||||
|
||||
keystone_client_class = utils.get_keystone_client().Client
|
||||
session = utils.get_session()
|
||||
scoped_client = keystone_client_class(session=session,
|
||||
auth=scoped_auth)
|
||||
|
||||
# Support client caching to save on auth calls.
|
||||
setattr(request, KEYSTONE_CLIENT_ATTR, scoped_client)
|
||||
|
||||
LOG.debug('Authentication completed.')
|
||||
return user
|
||||
|
||||
def get_group_permissions(self, user, obj=None):
|
||||
"""Returns an empty set since Keystone doesn't support "groups"."""
|
||||
# Keystone V3 added "groups". The Auth token response includes the
|
||||
# roles from the user's Group assignment. It should be fine just
|
||||
# returning an empty set here.
|
||||
return set()
|
||||
|
||||
def get_all_permissions(self, user, obj=None):
|
||||
"""Returns a set of permission strings that the user has.
|
||||
|
||||
This permission available to the user is derived from the user's
|
||||
Keystone "roles".
|
||||
|
||||
The permissions are returned as ``"openstack.{{ role.name }}"``.
|
||||
"""
|
||||
if user.is_anonymous() or obj is not None:
|
||||
return set()
|
||||
# TODO(gabrielhurley): Integrate policy-driven RBAC
|
||||
# when supported by Keystone.
|
||||
role_perms = {utils.get_role_permission(role['name'])
|
||||
for role in user.roles}
|
||||
|
||||
services = []
|
||||
for service in user.service_catalog:
|
||||
try:
|
||||
service_type = service['type']
|
||||
except KeyError:
|
||||
continue
|
||||
service_regions = [utils.get_endpoint_region(endpoint) for endpoint
|
||||
in service.get('endpoints', [])]
|
||||
if user.services_region in service_regions:
|
||||
services.append(service_type.lower())
|
||||
service_perms = {"openstack.services.%s" % service
|
||||
for service in services}
|
||||
return role_perms | service_perms
|
||||
|
||||
def has_perm(self, user, perm, obj=None):
|
||||
"""Returns True if the given user has the specified permission."""
|
||||
if not user.is_active:
|
||||
return False
|
||||
return perm in self.get_all_permissions(user, obj)
|
||||
|
||||
def has_module_perms(self, user, app_label):
|
||||
"""Returns True if user has any permissions in the given app_label.
|
||||
|
||||
Currently this matches for the app_label ``"openstack"``.
|
||||
"""
|
||||
if not user.is_active:
|
||||
return False
|
||||
for perm in self.get_all_permissions(user):
|
||||
if perm[:perm.index('.')] == app_label:
|
||||
return True
|
||||
return False
|
@ -1,17 +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.
|
||||
|
||||
|
||||
class KeystoneAuthException(Exception):
|
||||
"""Generic error class to identify and catch our own errors."""
|
||||
pass
|
@ -1,154 +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 collections
|
||||
import logging
|
||||
|
||||
from django.conf import settings
|
||||
from django.contrib.auth import authenticate
|
||||
from django.contrib.auth import forms as django_auth_forms
|
||||
from django import forms
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
from django.views.decorators.debug import sensitive_variables
|
||||
|
||||
from openstack_auth import exceptions
|
||||
from openstack_auth import utils
|
||||
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class Login(django_auth_forms.AuthenticationForm):
|
||||
"""Form used for logging in a user.
|
||||
|
||||
Handles authentication with Keystone by providing the domain name, username
|
||||
and password. A scoped token is fetched after successful authentication.
|
||||
|
||||
A domain name is required if authenticating with Keystone V3 running
|
||||
multi-domain configuration.
|
||||
|
||||
If the user authenticated has a default project set, the token will be
|
||||
automatically scoped to their default project.
|
||||
|
||||
If the user authenticated has no default project set, the authentication
|
||||
backend will try to scope to the projects returned from the user's assigned
|
||||
projects. The first successful project scoped will be returned.
|
||||
|
||||
Inherits from the base ``django.contrib.auth.forms.AuthenticationForm``
|
||||
class for added security features.
|
||||
"""
|
||||
region = forms.ChoiceField(label=_("Region"), required=False)
|
||||
username = forms.CharField(
|
||||
label=_("User Name"),
|
||||
widget=forms.TextInput(attrs={"autofocus": "autofocus"}))
|
||||
password = forms.CharField(label=_("Password"),
|
||||
widget=forms.PasswordInput(render_value=False))
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(Login, self).__init__(*args, **kwargs)
|
||||
fields_ordering = ['username', 'password', 'region']
|
||||
if getattr(settings,
|
||||
'OPENSTACK_KEYSTONE_MULTIDOMAIN_SUPPORT',
|
||||
False):
|
||||
last_domain = self.request.COOKIES.get('login_domain', None)
|
||||
if getattr(settings,
|
||||
'OPENSTACK_KEYSTONE_DOMAIN_DROPDOWN',
|
||||
False):
|
||||
self.fields['domain'] = forms.ChoiceField(
|
||||
label=_("Domain"),
|
||||
initial=last_domain,
|
||||
required=True,
|
||||
choices=getattr(settings,
|
||||
'OPENSTACK_KEYSTONE_DOMAIN_CHOICES',
|
||||
()))
|
||||
else:
|
||||
self.fields['domain'] = forms.CharField(
|
||||
initial=last_domain,
|
||||
label=_("Domain"),
|
||||
required=True,
|
||||
widget=forms.TextInput(attrs={"autofocus": "autofocus"}))
|
||||
self.fields['username'].widget = forms.widgets.TextInput()
|
||||
fields_ordering = ['domain', 'username', 'password', 'region']
|
||||
self.fields['region'].choices = self.get_region_choices()
|
||||
if len(self.fields['region'].choices) == 1:
|
||||
self.fields['region'].initial = self.fields['region'].choices[0][0]
|
||||
self.fields['region'].widget = forms.widgets.HiddenInput()
|
||||
elif len(self.fields['region'].choices) > 1:
|
||||
self.fields['region'].initial = self.request.COOKIES.get(
|
||||
'login_region')
|
||||
|
||||
# if websso is enabled and keystone version supported
|
||||
# prepend the websso_choices select input to the form
|
||||
if utils.is_websso_enabled():
|
||||
initial = getattr(settings, 'WEBSSO_INITIAL_CHOICE', 'credentials')
|
||||
self.fields['auth_type'] = forms.ChoiceField(
|
||||
label=_("Authenticate using"),
|
||||
choices=getattr(settings, 'WEBSSO_CHOICES', ()),
|
||||
required=False,
|
||||
initial=initial)
|
||||
# add auth_type to the top of the list
|
||||
fields_ordering.insert(0, 'auth_type')
|
||||
|
||||
# websso is enabled, but keystone version is not supported
|
||||
elif getattr(settings, 'WEBSSO_ENABLED', False):
|
||||
msg = ("Websso is enabled but horizon is not configured to work " +
|
||||
"with keystone version 3 or above.")
|
||||
LOG.warning(msg)
|
||||
self.fields = collections.OrderedDict(
|
||||
(key, self.fields[key]) for key in fields_ordering)
|
||||
|
||||
@staticmethod
|
||||
def get_region_choices():
|
||||
default_region = (settings.OPENSTACK_KEYSTONE_URL, "Default Region")
|
||||
regions = getattr(settings, 'AVAILABLE_REGIONS', [])
|
||||
if not regions:
|
||||
regions = [default_region]
|
||||
return regions
|
||||
|
||||
@sensitive_variables()
|
||||
def clean(self):
|
||||
default_domain = getattr(settings,
|
||||
'OPENSTACK_KEYSTONE_DEFAULT_DOMAIN',
|
||||
'Default')
|
||||
username = self.cleaned_data.get('username')
|
||||
password = self.cleaned_data.get('password')
|
||||
region = self.cleaned_data.get('region')
|
||||
domain = self.cleaned_data.get('domain', default_domain)
|
||||
|
||||
if not (username and password):
|
||||
# Don't authenticate, just let the other validators handle it.
|
||||
return self.cleaned_data
|
||||
|
||||
try:
|
||||
self.user_cache = authenticate(request=self.request,
|
||||
username=username,
|
||||
password=password,
|
||||
user_domain_name=domain,
|
||||
auth_url=region)
|
||||
msg = 'Login successful for user "%(username)s", remote address '\
|
||||
'%(remote_ip)s.' % {
|
||||
'username': username,
|
||||
'remote_ip': utils.get_client_ip(self.request)
|
||||
}
|
||||
LOG.info(msg)
|
||||
except exceptions.KeystoneAuthException as exc:
|
||||
msg = 'Login failed for user "%(username)s", remote address '\
|
||||
'%(remote_ip)s.' % {
|
||||
'username': username,
|
||||
'remote_ip': utils.get_client_ip(self.request)
|
||||
}
|
||||
LOG.warning(msg)
|
||||
raise forms.ValidationError(exc)
|
||||
if hasattr(self, 'check_for_test_cookie'): # Dropped in django 1.7
|
||||
self.check_for_test_cookie()
|
||||
return self.cleaned_data
|
@ -1,74 +0,0 @@
|
||||
# Translations template for django_openstack_auth.
|
||||
# Copyright (C) 2015 ORGANIZATION
|
||||
# This file is distributed under the same license as the
|
||||
# django_openstack_auth project.
|
||||
#
|
||||
# Translators:
|
||||
# Zbyněk Schwarz <zbynek.schwarz@gmail.com>, 2014-2015
|
||||
# Stanislav Ulrych <stanislav.ulrych@ultimum.io>, 2016. #zanata
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: django_openstack_auth 3.1.1.dev1\n"
|
||||
"Report-Msgid-Bugs-To: https://bugs.launchpad.net/openstack-i18n/\n"
|
||||
"POT-Creation-Date: 2017-01-25 19:41+0000\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"PO-Revision-Date: 2016-06-09 07:46+0000\n"
|
||||
"Last-Translator: Lenka Husáková <lenka.husakova@ultimum.io>\n"
|
||||
"Language: cs\n"
|
||||
"Plural-Forms: nplurals=3; plural=(n==1) ? 0 : (n>=2 && n<=4) ? 1 : 2;\n"
|
||||
"Generated-By: Babel 2.0\n"
|
||||
"X-Generator: Zanata 3.7.3\n"
|
||||
"Language-Team: Czech\n"
|
||||
|
||||
msgid "An error occurred authenticating. Please try again later."
|
||||
msgstr "Při ověřování se vyskytla chyba. Zkuste to prosím znovu později."
|
||||
|
||||
msgid "Authenticate using"
|
||||
msgstr "Přihlásit se pomocí"
|
||||
|
||||
msgid "Domain"
|
||||
msgstr "Doména"
|
||||
|
||||
msgid "Invalid credentials."
|
||||
msgstr "Neplatné přihlašovací údaje."
|
||||
|
||||
msgid ""
|
||||
"No authentication backend could be determined to handle the provided "
|
||||
"credentials."
|
||||
msgstr ""
|
||||
"Nebyl rozpoznán žádný vhodný autentizační backend pro ověření zadaných "
|
||||
"přihlašovacích údajů."
|
||||
|
||||
msgid "Password"
|
||||
msgstr "Heslo"
|
||||
|
||||
#, python-format
|
||||
msgid "Project switch failed for user \"%(username)s\"."
|
||||
msgstr "Změna projektu selhala u uživatele \"%(username)s\"."
|
||||
|
||||
msgid "Region"
|
||||
msgstr "Region"
|
||||
|
||||
#, python-format
|
||||
msgid "Switch to project \"%(project_name)s\" successful."
|
||||
msgstr "Změna projektu \"%(project_name)s\" byla úspěšná."
|
||||
|
||||
msgid "The authentication token issued by the Identity service has expired."
|
||||
msgstr "Autentizační token poskytnutý službou identit vypršel."
|
||||
|
||||
msgid "Unable to establish connection to keystone endpoint."
|
||||
msgstr "Nelze se připojit ke keystone endpointu."
|
||||
|
||||
msgid "Unable to retrieve authorized projects."
|
||||
msgstr "Nelze získat oprávněné projekty."
|
||||
|
||||
msgid "User Name"
|
||||
msgstr "Uživatelské jméno"
|
||||
|
||||
msgid "You are not authorized for any projects or domains."
|
||||
msgstr "Nemáte oprávnění k žádným projektům či doménám."
|
||||
|
||||
msgid "You are not authorized for any projects."
|
||||
msgstr "Nemáte oprávnění k žádným projektům."
|
@ -1,103 +0,0 @@
|
||||
# Translations template for django_openstack_auth.
|
||||
# Copyright (C) 2015 ORGANIZATION
|
||||
# This file is distributed under the same license as the
|
||||
# django_openstack_auth project.
|
||||
#
|
||||
# Translators:
|
||||
# Andreas Jaeger <jaegerandi@gmail.com>, 2014
|
||||
# Robert Simai, 2015
|
||||
# Robert Simai, 2015
|
||||
# Frank Kloeker <eumel@arcor.de>, 2016. #zanata
|
||||
# Andreas Jaeger <jaegerandi@gmail.com>, 2017. #zanata
|
||||
# Robert Simai <robert.simai@suse.com>, 2017. #zanata
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: django_openstack_auth 3.1.2.dev14\n"
|
||||
"Report-Msgid-Bugs-To: https://bugs.launchpad.net/openstack-i18n/\n"
|
||||
"POT-Creation-Date: 2017-04-07 13:52+0000\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"PO-Revision-Date: 2017-03-28 08:35+0000\n"
|
||||
"Last-Translator: Robert Simai <robert.simai@suse.com>\n"
|
||||
"Language: de\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||
"Generated-By: Babel 2.0\n"
|
||||
"X-Generator: Zanata 3.9.6\n"
|
||||
"Language-Team: German\n"
|
||||
|
||||
msgid "An error occurred authenticating. Please try again later."
|
||||
msgstr ""
|
||||
"Während der Authentifizierung ist ein Fehler aufgetreten. Bitte versuchen "
|
||||
"Sie es später noch einmal."
|
||||
|
||||
msgid "Authenticate using"
|
||||
msgstr "Authentifizieren mit"
|
||||
|
||||
msgid "Could not find service provider ID on Keystone."
|
||||
msgstr "Dienstanbieter ID in Keystone nicht gefunden."
|
||||
|
||||
msgid "Domain"
|
||||
msgstr "Domäne"
|
||||
|
||||
msgid "Invalid credentials."
|
||||
msgstr "Unzureichende Berechtigung."
|
||||
|
||||
msgid "K2K Federation not setup for this session"
|
||||
msgstr "K2K Verbund ist für diese Sitzung nicht eingerichtet"
|
||||
|
||||
msgid ""
|
||||
"No authentication backend could be determined to handle the provided "
|
||||
"credentials."
|
||||
msgstr ""
|
||||
"Es konnte kein Authentifizierungsbackend für die angegebene Legitimierung "
|
||||
"gefunden werden."
|
||||
|
||||
msgid "Password"
|
||||
msgstr "Passwort"
|
||||
|
||||
#, python-format
|
||||
msgid "Please consider changing your password, it will expire in %s minutes"
|
||||
msgstr "Bitte ändern Sie Ihr Passwort. Es läuft in %s Minuten ab."
|
||||
|
||||
#, python-format
|
||||
msgid "Project switch failed for user \"%(username)s\"."
|
||||
msgstr "Projektumschaltung für Benutzer \"%(username)s\" fehlgeschlagen."
|
||||
|
||||
msgid "Region"
|
||||
msgstr "Region"
|
||||
|
||||
#, python-format
|
||||
msgid "Service provider authentication failed. %s"
|
||||
msgstr "Dienstanbieter Authentifizierung fehlgeschlagen. %s"
|
||||
|
||||
#, python-format
|
||||
msgid "Switch to Keystone Provider \"%(keystone_provider)s\"successful."
|
||||
msgstr ""
|
||||
"Umschalten zum Keystone Anbieter \"%(keystone_provider)s\" erfolgreich."
|
||||
|
||||
#, python-format
|
||||
msgid "Switch to project \"%(project_name)s\" successful."
|
||||
msgstr "Umschalten zum Projekt \"%(project_name)s\" erfolgreich."
|
||||
|
||||
msgid "The authentication token issued by the Identity service has expired."
|
||||
msgstr ""
|
||||
"Das vom Identitätsdienst ausgegebene Authentifizierungs-Token ist abgelaufen."
|
||||
|
||||
msgid "Unable to establish connection to keystone endpoint."
|
||||
msgstr "Es kann keine Verbindung zum Keystone Endpunkt aufgebaut werden."
|
||||
|
||||
msgid "Unable to retrieve authorized domains."
|
||||
msgstr "Die authorisierten Domänen können nicht abgerufen werden."
|
||||
|
||||
msgid "Unable to retrieve authorized projects."
|
||||
msgstr "Authorisierte Projekte können nicht abgerufen werden."
|
||||
|
||||
msgid "User Name"
|
||||
msgstr "Benutzername"
|
||||
|
||||
msgid "You are not authorized for any projects or domains."
|
||||
msgstr "Sie sind nicht autorisiert für irgendein Projekt oder eine Domäne."
|
||||
|
||||
msgid "You are not authorized for any projects."
|
||||
msgstr "Sie sind für kein Projekt berechtigt."
|
@ -1,75 +0,0 @@
|
||||
# Translations template for django_openstack_auth.
|
||||
# Copyright (C) 2015 ORGANIZATION
|
||||
# This file is distributed under the same license as the
|
||||
# django_openstack_auth project.
|
||||
#
|
||||
# Translators:
|
||||
# Tom Fifield <tom@openstack.org>, 2015
|
||||
# Andreas Jaeger <jaegerandi@gmail.com>, 2016. #zanata
|
||||
# Tom Fifield <tom@openstack.org>, 2016. #zanata
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: django_openstack_auth 3.1.1.dev1\n"
|
||||
"Report-Msgid-Bugs-To: https://bugs.launchpad.net/openstack-i18n/\n"
|
||||
"POT-Creation-Date: 2017-01-25 19:41+0000\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"PO-Revision-Date: 2016-09-19 04:53+0000\n"
|
||||
"Last-Translator: Tom Fifield <tom@openstack.org>\n"
|
||||
"Language: en-AU\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||
"Generated-By: Babel 2.0\n"
|
||||
"X-Generator: Zanata 3.7.3\n"
|
||||
"Language-Team: English (Australia)\n"
|
||||
|
||||
msgid "An error occurred authenticating. Please try again later."
|
||||
msgstr "An error occurred authenticating. Please try again later."
|
||||
|
||||
msgid "Authenticate using"
|
||||
msgstr "Authenticate using"
|
||||
|
||||
msgid "Domain"
|
||||
msgstr "Domain"
|
||||
|
||||
msgid "Invalid credentials."
|
||||
msgstr "Invalid credentials."
|
||||
|
||||
msgid ""
|
||||
"No authentication backend could be determined to handle the provided "
|
||||
"credentials."
|
||||
msgstr ""
|
||||
"No authentication backend could be determined to handle the provided "
|
||||
"credentials."
|
||||
|
||||
msgid "Password"
|
||||
msgstr "Password"
|
||||
|
||||
#, python-format
|
||||
msgid "Project switch failed for user \"%(username)s\"."
|
||||
msgstr "Project switch failed for user \"%(username)s\"."
|
||||
|
||||
msgid "Region"
|
||||
msgstr "Region"
|
||||
|
||||
#, python-format
|
||||
msgid "Switch to project \"%(project_name)s\" successful."
|
||||
msgstr "Switch to project \"%(project_name)s\" successful."
|
||||
|
||||
msgid "The authentication token issued by the Identity service has expired."
|
||||
msgstr "The authentication token issued by the Identity service has expired."
|
||||
|
||||
msgid "Unable to establish connection to keystone endpoint."
|
||||
msgstr "Unable to establish connection to keystone endpoint."
|
||||
|
||||
msgid "Unable to retrieve authorized projects."
|
||||
msgstr "Unable to retrieve authorized projects."
|
||||
|
||||
msgid "User Name"
|
||||
msgstr "User Name"
|
||||
|
||||
msgid "You are not authorized for any projects or domains."
|
||||
msgstr "You are not authorised for any projects or domains."
|
||||
|
||||
msgid "You are not authorized for any projects."
|
||||
msgstr "You are not authorized for any projects."
|
@ -1,75 +0,0 @@
|
||||
# Translations template for django_openstack_auth.
|
||||
# Copyright (C) 2015 ORGANIZATION
|
||||
# This file is distributed under the same license as the
|
||||
# django_openstack_auth project.
|
||||
#
|
||||
# Translators:
|
||||
# Rob Cresswell <robert.cresswell@outlook.com>, 2015
|
||||
# Andi Chandler <andi@gowling.com>, 2016. #zanata
|
||||
# Andreas Jaeger <jaegerandi@gmail.com>, 2016. #zanata
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: django_openstack_auth 3.1.1.dev1\n"
|
||||
"Report-Msgid-Bugs-To: https://bugs.launchpad.net/openstack-i18n/\n"
|
||||
"POT-Creation-Date: 2017-01-25 19:41+0000\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"PO-Revision-Date: 2016-06-09 10:53+0000\n"
|
||||
"Last-Translator: Andi Chandler <andi@gowling.com>\n"
|
||||
"Language: en-GB\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||
"Generated-By: Babel 2.0\n"
|
||||
"X-Generator: Zanata 3.7.3\n"
|
||||
"Language-Team: English (United Kingdom)\n"
|
||||
|
||||
msgid "An error occurred authenticating. Please try again later."
|
||||
msgstr "An error occurred authenticating. Please try again later."
|
||||
|
||||
msgid "Authenticate using"
|
||||
msgstr "Authenticate using"
|
||||
|
||||
msgid "Domain"
|
||||
msgstr "Domain"
|
||||
|
||||
msgid "Invalid credentials."
|
||||
msgstr "Invalid credentials."
|
||||
|
||||
msgid ""
|
||||
"No authentication backend could be determined to handle the provided "
|
||||
"credentials."
|
||||
msgstr ""
|
||||
"No authentication backend could be determined to handle the provided "
|
||||
"credentials."
|
||||
|
||||
msgid "Password"
|
||||
msgstr "Password"
|
||||
|
||||
#, python-format
|
||||
msgid "Project switch failed for user \"%(username)s\"."
|
||||
msgstr "Project switch failed for user \"%(username)s\"."
|
||||
|
||||
msgid "Region"
|
||||
msgstr "Region"
|
||||
|
||||
#, python-format
|
||||
msgid "Switch to project \"%(project_name)s\" successful."
|
||||
msgstr "Switch to project \"%(project_name)s\" successful."
|
||||
|
||||
msgid "The authentication token issued by the Identity service has expired."
|
||||
msgstr "The authentication token issued by the Identity service has expired."
|
||||
|
||||
msgid "Unable to establish connection to keystone endpoint."
|
||||
msgstr "Unable to establish connection to keystone endpoint."
|
||||
|
||||
msgid "Unable to retrieve authorized projects."
|
||||
msgstr "Unable to retrieve authorised projects."
|
||||
|
||||
msgid "User Name"
|
||||
msgstr "User Name"
|
||||
|
||||
msgid "You are not authorized for any projects or domains."
|
||||
msgstr "You are not authorised for any projects or domains."
|
||||
|
||||
msgid "You are not authorized for any projects."
|
||||
msgstr "You are not authorised for any projects."
|
@ -1,78 +0,0 @@
|
||||
# Translations template for django_openstack_auth.
|
||||
# Copyright (C) 2015 ORGANIZATION
|
||||
# This file is distributed under the same license as the
|
||||
# django_openstack_auth project.
|
||||
#
|
||||
# Translators:
|
||||
# Adriana Chisco Landazábal <achisco94@gmail.com>, 2015
|
||||
# Alberto Molina Coballes <alb.molina@gmail.com>, 2014
|
||||
# dario hereñu <magallania@gmail.com>, 2015
|
||||
# luis gil, 2015
|
||||
# Marian Tort <marian.tort@gmail.com>, 2014
|
||||
# Andreas Jaeger <jaegerandi@gmail.com>, 2016. #zanata
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: django_openstack_auth 3.1.1.dev1\n"
|
||||
"Report-Msgid-Bugs-To: https://bugs.launchpad.net/openstack-i18n/\n"
|
||||
"POT-Creation-Date: 2017-01-25 19:41+0000\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"PO-Revision-Date: 2016-03-31 08:04+0000\n"
|
||||
"Last-Translator: Eugènia Torrella <tester03@es.ibm.com>\n"
|
||||
"Language: es\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||
"Generated-By: Babel 2.0\n"
|
||||
"X-Generator: Zanata 3.7.3\n"
|
||||
"Language-Team: Spanish\n"
|
||||
|
||||
msgid "An error occurred authenticating. Please try again later."
|
||||
msgstr "Se produjo un error de autenticación. Inténtelo de nuevo más tarde."
|
||||
|
||||
msgid "Authenticate using"
|
||||
msgstr "Autenticación con"
|
||||
|
||||
msgid "Domain"
|
||||
msgstr "Dominio"
|
||||
|
||||
msgid "Invalid credentials."
|
||||
msgstr "Credenciales no válidas"
|
||||
|
||||
msgid ""
|
||||
"No authentication backend could be determined to handle the provided "
|
||||
"credentials."
|
||||
msgstr ""
|
||||
"No se ha podido determinar un backend de autenticación para gestionar las "
|
||||
"credenciales proporcionadas."
|
||||
|
||||
msgid "Password"
|
||||
msgstr "Contraseña"
|
||||
|
||||
#, python-format
|
||||
msgid "Project switch failed for user \"%(username)s\"."
|
||||
msgstr "Intercambio de projecto fracasó para el usuario \"%(username)s\"."
|
||||
|
||||
msgid "Region"
|
||||
msgstr "Región"
|
||||
|
||||
#, python-format
|
||||
msgid "Switch to project \"%(project_name)s\" successful."
|
||||
msgstr "Cambio al proyecto \"%(project_name)s\" de manera exitosa."
|
||||
|
||||
msgid "The authentication token issued by the Identity service has expired."
|
||||
msgstr "El token de autenticación emitido por Identity Service ha expirado."
|
||||
|
||||
msgid "Unable to establish connection to keystone endpoint."
|
||||
msgstr "No ha sido posible establecer conexión con el endpoint de keystone."
|
||||
|
||||
msgid "Unable to retrieve authorized projects."
|
||||
msgstr "No ha sido posible obtener los proyectos autorizados."
|
||||
|
||||
msgid "User Name"
|
||||
msgstr "Usuario"
|
||||
|
||||
msgid "You are not authorized for any projects or domains."
|
||||
msgstr "No tiene autorización para ningún proyecto ni dominio."
|
||||
|
||||
msgid "You are not authorized for any projects."
|
||||
msgstr "No está autorizado en ningún proyecto."
|
@ -1,74 +0,0 @@
|
||||
# Translations template for django_openstack_auth.
|
||||
# Copyright (C) 2015 ORGANIZATION
|
||||
# This file is distributed under the same license as the
|
||||
# django_openstack_auth project.
|
||||
#
|
||||
# Translators:
|
||||
# Heleno Jimenez de la Cruz <heleno.jimenez@gmail.com>, 2015
|
||||
# Andreas Jaeger <jaegerandi@gmail.com>, 2016. #zanata
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: django_openstack_auth 3.1.1.dev1\n"
|
||||
"Report-Msgid-Bugs-To: https://bugs.launchpad.net/openstack-i18n/\n"
|
||||
"POT-Creation-Date: 2017-01-25 19:41+0000\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"PO-Revision-Date: 2015-09-18 03:50+0000\n"
|
||||
"Last-Translator: Heleno Jimenez de la Cruz <heleno.jimenez@gmail.com>\n"
|
||||
"Language: es-MX\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||
"Generated-By: Babel 2.0\n"
|
||||
"X-Generator: Zanata 3.7.3\n"
|
||||
"Language-Team: Spanish (Mexico)\n"
|
||||
|
||||
msgid "An error occurred authenticating. Please try again later."
|
||||
msgstr ""
|
||||
"Se ha producido un error de autenticación. Por favor inténtelo de nuevo más "
|
||||
"tarde."
|
||||
|
||||
msgid "Authenticate using"
|
||||
msgstr "Autentificar usando"
|
||||
|
||||
msgid "Domain"
|
||||
msgstr "Dominio"
|
||||
|
||||
msgid "Invalid credentials."
|
||||
msgstr "Credenciales inválidas."
|
||||
|
||||
msgid ""
|
||||
"No authentication backend could be determined to handle the provided "
|
||||
"credentials."
|
||||
msgstr ""
|
||||
"Autentificación de soporte no puede ser determinada para manejar las "
|
||||
"credenciales proveidas."
|
||||
|
||||
msgid "Password"
|
||||
msgstr "Contraseña"
|
||||
|
||||
#, python-format
|
||||
msgid "Project switch failed for user \"%(username)s\"."
|
||||
msgstr "Cambio de proyecto fallido para el usuario \"%(username)s\"."
|
||||
|
||||
msgid "Region"
|
||||
msgstr "Región."
|
||||
|
||||
#, python-format
|
||||
msgid "Switch to project \"%(project_name)s\" successful."
|
||||
msgstr "Cambio al proyecto \"%(project_name)s\" satisfactorio."
|
||||
|
||||
msgid "The authentication token issued by the Identity service has expired."
|
||||
msgstr ""
|
||||
"El token de autenticación otorgado por el servicio de identidad ha expirado."
|
||||
|
||||
msgid "Unable to establish connection to keystone endpoint."
|
||||
msgstr "No es posible establecer conexión a punto final de la clave base."
|
||||
|
||||
msgid "Unable to retrieve authorized projects."
|
||||
msgstr "No se pueden recuperar los proyectos autorizados."
|
||||
|
||||
msgid "User Name"
|
||||
msgstr "Nombre de Usuario"
|
||||
|
||||
msgid "You are not authorized for any projects."
|
||||
msgstr "No estás autorizado para algunos proyectos."
|
@ -1,105 +0,0 @@
|
||||
# Translations template for django_openstack_auth.
|
||||
# Copyright (C) 2015 ORGANIZATION
|
||||
# This file is distributed under the same license as the
|
||||
# django_openstack_auth project.
|
||||
#
|
||||
# Translators:
|
||||
# Cédric Savignan <liced@liced.fr>, 2015
|
||||
# Corinne Verheyde <cverheyd@hotmail.com>, 2015
|
||||
# François Bureau, 2015
|
||||
# Maxime COQUEREL <max.coquerel@gmail.com>, 2015
|
||||
# Patte D <pattedeph@gmail.com>, 2015
|
||||
# Xavier Gauvrit <xavier.gauvrit@cloudwatt.com>, 2014
|
||||
# Andreas Jaeger <jaegerandi@gmail.com>, 2016. #zanata
|
||||
# Gérald LONLAS <g.lonlas@gmail.com>, 2016. #zanata
|
||||
# JF Taltavull <jftalta@gmail.com>, 2017. #zanata
|
||||
# Loic Nicolle <loic.nicolle@orange.com>, 2017. #zanata
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: django_openstack_auth 3.4.1.dev1\n"
|
||||
"Report-Msgid-Bugs-To: https://bugs.launchpad.net/openstack-i18n/\n"
|
||||
"POT-Creation-Date: 2017-07-19 17:16+0000\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"PO-Revision-Date: 2017-07-19 09:06+0000\n"
|
||||
"Last-Translator: Loic Nicolle <loic.nicolle@orange.com>\n"
|
||||
"Language: fr\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n > 1);\n"
|
||||
"Generated-By: Babel 2.0\n"
|
||||
"X-Generator: Zanata 3.9.6\n"
|
||||
"Language-Team: French\n"
|
||||
|
||||
msgid "An error occurred authenticating. Please try again later."
|
||||
msgstr ""
|
||||
"Une erreur s'est produite pendant l'authentification. Veuillez réessayer "
|
||||
"ultérieurement."
|
||||
|
||||
msgid "Authenticate using"
|
||||
msgstr "Mode d'authentification"
|
||||
|
||||
msgid "Could not find service provider ID on Keystone."
|
||||
msgstr "Impossible de trouver l'ID du fournisseur de services sur Keystone."
|
||||
|
||||
msgid "Domain"
|
||||
msgstr "Domaine"
|
||||
|
||||
msgid "Invalid credentials."
|
||||
msgstr "Informations d'authentification non valides."
|
||||
|
||||
msgid "K2K Federation not setup for this session"
|
||||
msgstr "La fédération K2K n'a pas été configurée pour cette session"
|
||||
|
||||
msgid ""
|
||||
"No authentication backend could be determined to handle the provided "
|
||||
"credentials."
|
||||
msgstr "Aucun backend d'authentification pour gérer les informations fournies."
|
||||
|
||||
msgid "Password"
|
||||
msgstr "Mot de passe"
|
||||
|
||||
#, python-format
|
||||
msgid "Please consider changing your password, it will expire in %s minutes"
|
||||
msgstr "Merci de changer votre mot de passe, il va expirer dans %s minutes"
|
||||
|
||||
#, python-format
|
||||
msgid "Project switch failed for user \"%(username)s\"."
|
||||
msgstr "La bascule de projet a échoué pour l'utilisateur \"%(username)s\"."
|
||||
|
||||
msgid "Region"
|
||||
msgstr "Région"
|
||||
|
||||
#, python-format
|
||||
msgid "Service provider authentication failed. %s"
|
||||
msgstr "L'authentification du fournisseur de services a échoué. %s"
|
||||
|
||||
#, python-format
|
||||
msgid "Switch to Keystone Provider \"%(keystone_provider)s\"successful."
|
||||
msgstr ""
|
||||
"Bascule vers le fournisseur Keystone \"%(keystone_provider)s\" réussie."
|
||||
|
||||
#, python-format
|
||||
msgid "Switch to project \"%(project_name)s\" successful."
|
||||
msgstr "Bascule vers le projet \"%(project_name)s\" réussie. "
|
||||
|
||||
msgid "The authentication token issued by the Identity service has expired."
|
||||
msgstr ""
|
||||
"Le jeton d'authentification délivré par le service d'Identité a expiré."
|
||||
|
||||
msgid "Unable to establish connection to keystone endpoint."
|
||||
msgstr "Impossible d'établir la connexion avec le endpoint keystone."
|
||||
|
||||
msgid "Unable to retrieve authorized domains."
|
||||
msgstr "Impossible de récupérer les domaines autorisés."
|
||||
|
||||
msgid "Unable to retrieve authorized projects."
|
||||
msgstr "Impossible de récupérer les projets autorisés."
|
||||
|
||||
msgid "User Name"
|
||||
msgstr "Nom d'utilisateur"
|
||||
|
||||
msgid "You are not authorized for any projects or domains."
|
||||
msgstr "Vous n'êtes autorisé sur aucun projet ou domaine."
|
||||
|
||||
msgid "You are not authorized for any projects."
|
||||
msgstr "Vous n'êtes autorisé sur aucun projet."
|
@ -1,98 +0,0 @@
|
||||
# Translations template for django_openstack_auth.
|
||||
# Copyright (C) 2015 ORGANIZATION
|
||||
# This file is distributed under the same license as the
|
||||
# django_openstack_auth project.
|
||||
#
|
||||
# Translators:
|
||||
# Andreas Jaeger <jaegerandi@gmail.com>, 2016. #zanata
|
||||
# suhartono <cloudsuhartono@gmail.com>, 2016. #zanata
|
||||
# suhartono <cloudsuhartono@gmail.com>, 2017. #zanata
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: django_openstack_auth 3.1.2.dev15\n"
|
||||
"Report-Msgid-Bugs-To: https://bugs.launchpad.net/openstack-i18n/\n"
|
||||
"POT-Creation-Date: 2017-04-10 01:40+0000\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"PO-Revision-Date: 2017-04-11 03:47+0000\n"
|
||||
"Last-Translator: suhartono <cloudsuhartono@gmail.com>\n"
|
||||
"Language: id\n"
|
||||
"Plural-Forms: nplurals=1; plural=0;\n"
|
||||
"Generated-By: Babel 2.0\n"
|
||||
"X-Generator: Zanata 3.9.6\n"
|
||||
"Language-Team: Indonesian\n"
|
||||
|
||||
msgid "An error occurred authenticating. Please try again later."
|
||||
msgstr "Terjadi kesalahan otentikasi. Silakan coba lagi nanti."
|
||||
|
||||
msgid "Authenticate using"
|
||||
msgstr "Otentikasi penggunaan"
|
||||
|
||||
msgid "Could not find service provider ID on Keystone."
|
||||
msgstr "Tidak dapat menemukan provider layanan ID di Keystone."
|
||||
|
||||
msgid "Domain"
|
||||
msgstr "Domain"
|
||||
|
||||
msgid "Invalid credentials."
|
||||
msgstr "Kredensial tidak valid."
|
||||
|
||||
msgid "K2K Federation not setup for this session"
|
||||
msgstr "K2K Federation tidak menyiapkan untuk sesi ini"
|
||||
|
||||
msgid ""
|
||||
"No authentication backend could be determined to handle the provided "
|
||||
"credentials."
|
||||
msgstr ""
|
||||
"Tidak ada otentikasi backend dapat ditentukan untuk menangani kredensial "
|
||||
"yang disediakan."
|
||||
|
||||
msgid "Password"
|
||||
msgstr "Password"
|
||||
|
||||
#, python-format
|
||||
msgid "Please consider changing your password, it will expire in %s minutes"
|
||||
msgstr ""
|
||||
"Silakan mempertimbangkan mengubah sandi Anda, inu akan berakhir pada %s menit"
|
||||
|
||||
#, python-format
|
||||
msgid "Project switch failed for user \"%(username)s\"."
|
||||
msgstr "Switch proyek gagal untuk pengguna \"%(username)s\"."
|
||||
|
||||
msgid "Region"
|
||||
msgstr "Region"
|
||||
|
||||
#, python-format
|
||||
msgid "Service provider authentication failed. %s"
|
||||
msgstr "Ootentikasi provider layanan gagal. %s"
|
||||
|
||||
#, python-format
|
||||
msgid "Switch to Keystone Provider \"%(keystone_provider)s\"successful."
|
||||
msgstr "Switch ke Keystone Provider \"%(keystone_provider)s\" berhasil."
|
||||
|
||||
#, python-format
|
||||
msgid "Switch to project \"%(project_name)s\" successful."
|
||||
msgstr "Switch proyek \"%(project_name)s\" sukses."
|
||||
|
||||
msgid "The authentication token issued by the Identity service has expired."
|
||||
msgstr ""
|
||||
"Token otentikasi yang dikeluarkan oleh layanan Identity telah berakhir."
|
||||
|
||||
msgid "Unable to establish connection to keystone endpoint."
|
||||
msgstr "Tidak dapat melakukan koneksi ke endpoint keystone."
|
||||
|
||||
msgid "Unable to retrieve authorized domains."
|
||||
msgstr "Tidak dapat mengambil domain yang berwenang."
|
||||
|
||||
msgid "Unable to retrieve authorized projects."
|
||||
msgstr "Tidak dapat mengambil proyek yang berwenang (authorized)"
|
||||
|
||||
msgid "User Name"
|
||||
msgstr "nama pengguna"
|
||||
|
||||
msgid "You are not authorized for any projects or domains."
|
||||
msgstr "Anda tidak berwenang untuk setiap proyek atau domain."
|
||||
|
||||
msgid "You are not authorized for any projects."
|
||||
msgstr "Anda tidak berwenang untuk setiap proyek."
|
@ -1,76 +0,0 @@
|
||||
# Translations template for django_openstack_auth.
|
||||
# Copyright (C) 2015 ORGANIZATION
|
||||
# This file is distributed under the same license as the
|
||||
# django_openstack_auth project.
|
||||
#
|
||||
# Translators:
|
||||
# Salvatore Davide Rapisarda <sdrapisarda@gmail.com>, 2014
|
||||
# Andreas Jaeger <jaegerandi@gmail.com>, 2016. #zanata
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: django_openstack_auth 3.1.1.dev1\n"
|
||||
"Report-Msgid-Bugs-To: https://bugs.launchpad.net/openstack-i18n/\n"
|
||||
"POT-Creation-Date: 2017-01-25 19:41+0000\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"PO-Revision-Date: 2016-03-26 09:13+0000\n"
|
||||
"Last-Translator: Remo Mattei <remo@rm.ht>\n"
|
||||
"Language: it\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||
"Generated-By: Babel 2.0\n"
|
||||
"X-Generator: Zanata 3.7.3\n"
|
||||
"Language-Team: Italian\n"
|
||||
|
||||
msgid "An error occurred authenticating. Please try again later."
|
||||
msgstr "Si è verificato un errore. Riprova più tardi."
|
||||
|
||||
msgid "Authenticate using"
|
||||
msgstr "Usando l'autenticazione "
|
||||
|
||||
msgid "Domain"
|
||||
msgstr "Dominio"
|
||||
|
||||
msgid "Invalid credentials."
|
||||
msgstr "Le credenziali non sono valide "
|
||||
|
||||
msgid ""
|
||||
"No authentication backend could be determined to handle the provided "
|
||||
"credentials."
|
||||
msgstr ""
|
||||
"Non è possibile comunicare con nessuno dei back-end di autenticazione per "
|
||||
"verificare le credenziali. "
|
||||
|
||||
msgid "Password"
|
||||
msgstr "Password"
|
||||
|
||||
#, python-format
|
||||
msgid "Project switch failed for user \"%(username)s\"."
|
||||
msgstr ""
|
||||
"Il passaggio al progetto non è riuscito per l'utente \"%(username)s\". "
|
||||
|
||||
msgid "Region"
|
||||
msgstr "Regione"
|
||||
|
||||
#, python-format
|
||||
msgid "Switch to project \"%(project_name)s\" successful."
|
||||
msgstr ""
|
||||
"Il passaggio al progetto \"%(project_name)s\" è riuscito con successo. "
|
||||
|
||||
msgid "The authentication token issued by the Identity service has expired."
|
||||
msgstr "Il token di autenticazione che stai usando è scaduto."
|
||||
|
||||
msgid "Unable to establish connection to keystone endpoint."
|
||||
msgstr "Impossibile stabilire una connessione con gli endpoint di keystone. "
|
||||
|
||||
msgid "Unable to retrieve authorized projects."
|
||||
msgstr "Impossibile ottenere l'accesso ai progetti."
|
||||
|
||||
msgid "User Name"
|
||||
msgstr "Nome Utente"
|
||||
|
||||
msgid "You are not authorized for any projects or domains."
|
||||
msgstr "Non sei autorizzato ad entrare a nessuno dei progetti o domini. "
|
||||
|
||||
msgid "You are not authorized for any projects."
|
||||
msgstr "Non sei abilitato a nessun progetto."
|
@ -1,102 +0,0 @@
|
||||
# Translations template for django_openstack_auth.
|
||||
# Copyright (C) 2015 ORGANIZATION
|
||||
# This file is distributed under the same license as the
|
||||
# django_openstack_auth project.
|
||||
#
|
||||
# Translators:
|
||||
# Akihiro Motoki <amotoki@gmail.com>, 2015
|
||||
# Shu Muto <shu-mutou@rf.jp.nec.com>, 2015
|
||||
# ykatabam <ykatabam@redhat.com>, 2015
|
||||
# ykatabam <ykatabam@redhat.com>, 2015
|
||||
# Mie Yamamoto <myamamot@redhat.com>, 2016. #zanata
|
||||
# Yuko Katabami <yukokatabami@gmail.com>, 2016. #zanata
|
||||
# Akihiro Motoki <amotoki@gmail.com>, 2017. #zanata
|
||||
# Yuko Katabami <yukokatabami@gmail.com>, 2017. #zanata
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: django_openstack_auth 3.3.1.dev1\n"
|
||||
"Report-Msgid-Bugs-To: https://bugs.launchpad.net/openstack-i18n/\n"
|
||||
"POT-Creation-Date: 2017-07-12 14:10+0000\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"PO-Revision-Date: 2017-07-11 02:05+0000\n"
|
||||
"Last-Translator: Akihiro Motoki <amotoki@gmail.com>\n"
|
||||
"Language: ja\n"
|
||||
"Plural-Forms: nplurals=1; plural=0;\n"
|
||||
"Generated-By: Babel 2.0\n"
|
||||
"X-Generator: Zanata 3.9.6\n"
|
||||
"Language-Team: Japanese\n"
|
||||
|
||||
msgid "An error occurred authenticating. Please try again later."
|
||||
msgstr "認証中にエラーが発生しました。後ほどもう一度お試しください。"
|
||||
|
||||
msgid "Authenticate using"
|
||||
msgstr "使用する認証方法"
|
||||
|
||||
msgid "Could not find service provider ID on Keystone."
|
||||
msgstr "Keystone でサービスプロバイダー ID を見つけることができませんでした。"
|
||||
|
||||
msgid "Domain"
|
||||
msgstr "ドメイン"
|
||||
|
||||
msgid "Invalid credentials."
|
||||
msgstr "無効な認証情報です。"
|
||||
|
||||
msgid "K2K Federation not setup for this session"
|
||||
msgstr "このセッションには K2K フェデレーションは設定されていません。"
|
||||
|
||||
msgid ""
|
||||
"No authentication backend could be determined to handle the provided "
|
||||
"credentials."
|
||||
msgstr "指定された認証情報を処理する認証バックエンドが決定できませんでした。"
|
||||
|
||||
msgid "Password"
|
||||
msgstr "パスワード"
|
||||
|
||||
#, python-format
|
||||
msgid "Please consider changing your password, it will expire in %s minutes"
|
||||
msgstr ""
|
||||
"パスワードの変更をお薦めします。今のパスワードはあと %s 分で無効になります。"
|
||||
|
||||
#, python-format
|
||||
msgid "Project switch failed for user \"%(username)s\"."
|
||||
msgstr "ユーザー \"%(username)s\" のプロジェクト切り替えに失敗しました。"
|
||||
|
||||
msgid "Region"
|
||||
msgstr "リージョン"
|
||||
|
||||
#, python-format
|
||||
msgid "Service provider authentication failed. %s"
|
||||
msgstr "サービスプロバイダーの認証に失敗しました。%s"
|
||||
|
||||
#, python-format
|
||||
msgid "Switch to Keystone Provider \"%(keystone_provider)s\"successful."
|
||||
msgstr ""
|
||||
"Keystone プロバイダー \"%(keystone_provider)s\" への切り替えが正常に完了しま"
|
||||
"した。"
|
||||
|
||||
#, python-format
|
||||
msgid "Switch to project \"%(project_name)s\" successful."
|
||||
msgstr "プロジェクト \"%(project_name)s\" へ正常に切り替えました。"
|
||||
|
||||
msgid "The authentication token issued by the Identity service has expired."
|
||||
msgstr "Identity サービスにより発行された認証トークンの期限が切れました。"
|
||||
|
||||
msgid "Unable to establish connection to keystone endpoint."
|
||||
msgstr "Keystone エンドポイントへの接続を確立できません。"
|
||||
|
||||
msgid "Unable to retrieve authorized domains."
|
||||
msgstr "認証されたドメインを取得できません。"
|
||||
|
||||
msgid "Unable to retrieve authorized projects."
|
||||
msgstr "権限を持っているプロジェクトの情報を取得できません。"
|
||||
|
||||
msgid "User Name"
|
||||
msgstr "ユーザー名"
|
||||
|
||||
msgid "You are not authorized for any projects or domains."
|
||||
msgstr "どのプロジェクトやドメインに対しても権限がありません。"
|
||||
|
||||
msgid "You are not authorized for any projects."
|
||||
msgstr "どのプロジェクトに対しても権限がありません。"
|
@ -1,98 +0,0 @@
|
||||
# Translations template for django_openstack_auth.
|
||||
# Copyright (C) 2015 ORGANIZATION
|
||||
# This file is distributed under the same license as the
|
||||
# django_openstack_auth project.
|
||||
#
|
||||
# Translators:
|
||||
# Ian Y. Choi <ianyrchoi@gmail.com>, 2015
|
||||
# Sungjin Kang <potopro@gmail.com>, 2015
|
||||
# Andrea Young Oak Li <youli@redhat.com>, 2016. #zanata
|
||||
# Andreas Jaeger <jaegerandi@gmail.com>, 2016. #zanata
|
||||
# Jun-Sik Shin <jsshin@smartx.kr>, 2017. #zanata
|
||||
# Sungjin Kang <gang.sungjin@gmail.com>, 2017. #zanata
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: django_openstack_auth 3.2.1.dev2\n"
|
||||
"Report-Msgid-Bugs-To: https://bugs.launchpad.net/openstack-i18n/\n"
|
||||
"POT-Creation-Date: 2017-06-07 17:46+0000\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"PO-Revision-Date: 2017-06-08 01:53+0000\n"
|
||||
"Last-Translator: Jun-Sik Shin <jsshin@smartx.kr>\n"
|
||||
"Language: ko-KR\n"
|
||||
"Plural-Forms: nplurals=1; plural=0;\n"
|
||||
"Generated-By: Babel 2.0\n"
|
||||
"X-Generator: Zanata 3.9.6\n"
|
||||
"Language-Team: Korean (South Korea)\n"
|
||||
|
||||
msgid "An error occurred authenticating. Please try again later."
|
||||
msgstr "인증 중 오류가 발생했습니다. 잠시 후 다시 시도하세요."
|
||||
|
||||
msgid "Authenticate using"
|
||||
msgstr "인증 방법"
|
||||
|
||||
msgid "Could not find service provider ID on Keystone."
|
||||
msgstr "Keystone 에서 서비스 프로바이더 ID 를 찾지 못하였습니다."
|
||||
|
||||
msgid "Domain"
|
||||
msgstr "도메인"
|
||||
|
||||
msgid "Invalid credentials."
|
||||
msgstr "올바르지 않은 인증정보입니다."
|
||||
|
||||
msgid "K2K Federation not setup for this session"
|
||||
msgstr "이 세션에는 K2K Federation 이 설정되지 않았습니다."
|
||||
|
||||
msgid ""
|
||||
"No authentication backend could be determined to handle the provided "
|
||||
"credentials."
|
||||
msgstr ""
|
||||
"제공된 인증정보에 대한 처리를 할 수 있는 인증 백엔드가 존재하지 않습니다."
|
||||
|
||||
msgid "Password"
|
||||
msgstr "암호"
|
||||
|
||||
#, python-format
|
||||
msgid "Please consider changing your password, it will expire in %s minutes"
|
||||
msgstr "암호는 %s 분 후에 만료되므로 변경해 주시기 바랍니다."
|
||||
|
||||
#, python-format
|
||||
msgid "Project switch failed for user \"%(username)s\"."
|
||||
msgstr "사용자 \"%(username)s\" 에 대한 프로젝트 전환에 실패했습니다."
|
||||
|
||||
msgid "Region"
|
||||
msgstr "지역"
|
||||
|
||||
#, python-format
|
||||
msgid "Service provider authentication failed. %s"
|
||||
msgstr "서비스 프로바이더 인증에 실패했습니다. %s"
|
||||
|
||||
#, python-format
|
||||
msgid "Switch to Keystone Provider \"%(keystone_provider)s\"successful."
|
||||
msgstr "Keystone 프로바이더 \"%(keystone_provider)s\" 로 변경하였습니다."
|
||||
|
||||
#, python-format
|
||||
msgid "Switch to project \"%(project_name)s\" successful."
|
||||
msgstr "프로젝트 \"%(project_name)s\" 로 전환에 성공하였습니다."
|
||||
|
||||
msgid "The authentication token issued by the Identity service has expired."
|
||||
msgstr "Identity 서비스에서 발급한 인증 토큰이 만료되었습니다."
|
||||
|
||||
msgid "Unable to establish connection to keystone endpoint."
|
||||
msgstr "Keystone 엔드포인트에 접속할 수 없습니다."
|
||||
|
||||
msgid "Unable to retrieve authorized domains."
|
||||
msgstr "인증된 도메인을 찾지 못했습니다."
|
||||
|
||||
msgid "Unable to retrieve authorized projects."
|
||||
msgstr "인증된 프로젝트를 가져올 수 없습니다."
|
||||
|
||||
msgid "User Name"
|
||||
msgstr "사용자 이름"
|
||||
|
||||
msgid "You are not authorized for any projects or domains."
|
||||
msgstr "프로젝트나 도메인에 대한 권한이 없습니다."
|
||||
|
||||
msgid "You are not authorized for any projects."
|
||||
msgstr "모든 프로젝트에 대한 권한이 없습니다."
|
@ -1,52 +0,0 @@
|
||||
# Translations template for django_openstack_auth.
|
||||
# Copyright (C) 2015 ORGANIZATION
|
||||
# This file is distributed under the same license as the
|
||||
# django_openstack_auth project.
|
||||
#
|
||||
# Translators:
|
||||
# Evelijn Saaltink <evelijnsaaltink@gmail.com>, 2015
|
||||
# Andreas Jaeger <jaegerandi@gmail.com>, 2016. #zanata
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: django_openstack_auth 3.1.1.dev1\n"
|
||||
"Report-Msgid-Bugs-To: https://bugs.launchpad.net/openstack-i18n/\n"
|
||||
"POT-Creation-Date: 2017-01-25 19:41+0000\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"PO-Revision-Date: 2015-08-17 09:33+0000\n"
|
||||
"Last-Translator: Evelijn Saaltink <evelijnsaaltink@gmail.com>\n"
|
||||
"Language: nl-NL\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||
"Generated-By: Babel 2.0\n"
|
||||
"X-Generator: Zanata 3.7.3\n"
|
||||
"Language-Team: Dutch (Netherlands)\n"
|
||||
|
||||
msgid "An error occurred authenticating. Please try again later."
|
||||
msgstr ""
|
||||
"Er is een fout opgetreden bij de verificatie. Probeert u het later nog eens."
|
||||
|
||||
msgid "Authenticate using"
|
||||
msgstr "Gebruik authentificeren"
|
||||
|
||||
msgid "Domain"
|
||||
msgstr "Domein"
|
||||
|
||||
msgid "Password"
|
||||
msgstr "Wachtwoord"
|
||||
|
||||
msgid "Region"
|
||||
msgstr "Regio"
|
||||
|
||||
msgid "The authentication token issued by the Identity service has expired."
|
||||
msgstr ""
|
||||
"De door de identiteitsdienst verstrekte authenticatietoken is verlopen."
|
||||
|
||||
msgid "Unable to retrieve authorized projects."
|
||||
msgstr "Niet in staat om de geauthentificeerde projecten op te halen."
|
||||
|
||||
msgid "User Name"
|
||||
msgstr "Gebruikersnaam"
|
||||
|
||||
msgid "You are not authorized for any projects."
|
||||
msgstr "U bent voor geen enkel project gemachtigd."
|
@ -1,71 +0,0 @@
|
||||
# Translations template for django_openstack_auth.
|
||||
# Copyright (C) 2015 ORGANIZATION
|
||||
# This file is distributed under the same license as the
|
||||
# django_openstack_auth project.
|
||||
#
|
||||
# Translators:
|
||||
# Andreas Jaeger <jaegerandi@gmail.com>, 2016. #zanata
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: django_openstack_auth 3.1.1.dev1\n"
|
||||
"Report-Msgid-Bugs-To: https://bugs.launchpad.net/openstack-i18n/\n"
|
||||
"POT-Creation-Date: 2017-01-25 19:41+0000\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"PO-Revision-Date: 2015-12-28 03:33+0000\n"
|
||||
"Last-Translator: A S Alam <aalam@fedoraproject.org>\n"
|
||||
"Language: pa-IN\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||
"Generated-By: Babel 2.0\n"
|
||||
"X-Generator: Zanata 3.7.3\n"
|
||||
"Language-Team: Punjabi (India)\n"
|
||||
|
||||
msgid "An error occurred authenticating. Please try again later."
|
||||
msgstr "ਪ੍ਰਮਾਣਿਕਤਾ ਵੇਲੇ ਇੱਕ ਗਲਤੀ ਵਾਪਰੀ। ਕਿਰਪਾ ਕਰ ਕੇ ਬਾਅਦ ਵਿੱਚ ਮੁੜ ਕੋਸ਼ਿਸ਼ ਕਰੋ।"
|
||||
|
||||
msgid "Authenticate using"
|
||||
msgstr "ਇਹ ਵਰਤ ਕੇ ਪ੍ਰਮਾਣਿਤ ਕਰੋ"
|
||||
|
||||
msgid "Domain"
|
||||
msgstr "ਡੋਮੇਨ"
|
||||
|
||||
msgid "Invalid credentials."
|
||||
msgstr "ਗਲਤ ਵੇਰਵੇ।"
|
||||
|
||||
msgid ""
|
||||
"No authentication backend could be determined to handle the provided "
|
||||
"credentials."
|
||||
msgstr "ਦਿੱਤੀਆਂ ਸਨਦਾਂ ਨੂੰ ਨਿਪਟਾਉਣ ਲਈ ਕੋਈ ਪਰਮਾਣਕਿਤਾ ਬੈਕਐਂਡ ਪਤਾ ਨਹੀਂ ਕੀਤਾ ਜਾ ਸਕਿਆ।"
|
||||
|
||||
msgid "Password"
|
||||
msgstr "ਪਾਸਵਰਡ"
|
||||
|
||||
#, python-format
|
||||
msgid "Project switch failed for user \"%(username)s\"."
|
||||
msgstr "\"%(username)s\" ਵਰਤੋਂ ਕਾਰ ਲਈ ਪ੍ਰੋਜੈਕਟ ਬਦਲਣਾ ਫੇਲ੍ਹ ਹੈ।"
|
||||
|
||||
msgid "Region"
|
||||
msgstr "ਖੇਤਰ"
|
||||
|
||||
#, python-format
|
||||
msgid "Switch to project \"%(project_name)s\" successful."
|
||||
msgstr "\"%(project_name)s\" ਪ੍ਰੋਜੈਕਟ ਲਈ ਬਦਲਣਾ ਕਾਮਯਾਬ ਹੈ।"
|
||||
|
||||
msgid "The authentication token issued by the Identity service has expired."
|
||||
msgstr "ਪਛਾਣ ਸੇਵਾ ਵਲੋਂ ਜਾਰੀ ਕੀਤੇ ਪਰਮਾਣਕਿਤਾ ਟੋਕਨ ਦੀ ਮਿਆਦ ਪੁੱਗ ਚੁੱਕੀ ਹੈ।"
|
||||
|
||||
msgid "Unable to establish connection to keystone endpoint."
|
||||
msgstr "ਕੀਅਸਟੋਨ ਅੰਤ ਬਿੰਦੂ ਨਾਲ ਸੰਬੰਧ ਸਥਾਪਤ ਕਰਨ ਤੋਂ ਅਸਮਰੱਥ।"
|
||||
|
||||
msgid "Unable to retrieve authorized projects."
|
||||
msgstr "ਅਧਿਕਾਰਤ ਪ੍ਰੋਜੈਕਟਾਂ ਨੂੰ ਪ੍ਰਾਪਤ ਕਰਨ ਤੋਂ ਅਸਮਰੱਥ।"
|
||||
|
||||
msgid "User Name"
|
||||
msgstr "ਵਰਤੋਂਕਾਰ ਨਾਂ"
|
||||
|
||||
msgid "You are not authorized for any projects or domains."
|
||||
msgstr "ਤੁਸੀਂ ਕਿਸੇ ਵੀ ਪ੍ਰੋਜੈਕਟ ਜਾਂ ਡੋਮੇਨ ਲਈ ਅਧਿਕਾਰਿਤ ਨਹੀਂ ਹੋ।"
|
||||
|
||||
msgid "You are not authorized for any projects."
|
||||
msgstr "ਤੁਸੀਂ ਕੋਈ ਵੀ ਪ੍ਰਾਜੈਕਟਾਂ ਲਈ ਅਧਿਕਾਰਤ ਨਹੀਂ ਹੋ।"
|
@ -1,75 +0,0 @@
|
||||
# Translations template for django_openstack_auth.
|
||||
# Copyright (C) 2015 ORGANIZATION
|
||||
# This file is distributed under the same license as the
|
||||
# django_openstack_auth project.
|
||||
#
|
||||
# Translators:
|
||||
# Łukasz Jernaś <deejay1@srem.org>, 2015
|
||||
# Andreas Jaeger <jaegerandi@gmail.com>, 2016. #zanata
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: django_openstack_auth 3.1.1.dev1\n"
|
||||
"Report-Msgid-Bugs-To: https://bugs.launchpad.net/openstack-i18n/\n"
|
||||
"POT-Creation-Date: 2017-01-25 19:41+0000\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"PO-Revision-Date: 2016-03-21 09:03+0000\n"
|
||||
"Last-Translator: Łukasz Jernas <deejay1@srem.org>\n"
|
||||
"Language: pl-PL\n"
|
||||
"Plural-Forms: nplurals=3; plural=(n==1 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 "
|
||||
"|| n%100>=20) ? 1 : 2);\n"
|
||||
"Generated-By: Babel 2.0\n"
|
||||
"X-Generator: Zanata 3.7.3\n"
|
||||
"Language-Team: Polish (Poland)\n"
|
||||
|
||||
msgid "An error occurred authenticating. Please try again later."
|
||||
msgstr "Wystąpił błąd w trakcie uwierzytelniania. Proszę spróbować później."
|
||||
|
||||
msgid "Authenticate using"
|
||||
msgstr "Uwierzytelnij się pużywając"
|
||||
|
||||
msgid "Domain"
|
||||
msgstr "Domena"
|
||||
|
||||
msgid "Invalid credentials."
|
||||
msgstr "Błędne dane logowania."
|
||||
|
||||
msgid ""
|
||||
"No authentication backend could be determined to handle the provided "
|
||||
"credentials."
|
||||
msgstr ""
|
||||
"Nie można określić silnika uwierzytelniania, który by mógł obsłużyć podane "
|
||||
"dane uwierzytelniające."
|
||||
|
||||
msgid "Password"
|
||||
msgstr "Hasło"
|
||||
|
||||
#, python-format
|
||||
msgid "Project switch failed for user \"%(username)s\"."
|
||||
msgstr "Nie można przełączyć projektu dla użytkownika „%(username)s”."
|
||||
|
||||
msgid "Region"
|
||||
msgstr "Region"
|
||||
|
||||
#, python-format
|
||||
msgid "Switch to project \"%(project_name)s\" successful."
|
||||
msgstr "Przełączono na projekt „%(project_name)s”."
|
||||
|
||||
msgid "The authentication token issued by the Identity service has expired."
|
||||
msgstr "Token uwierzytelniania wydany przez usługę uwierzytelniania wygasł."
|
||||
|
||||
msgid "Unable to establish connection to keystone endpoint."
|
||||
msgstr "Nie można nawiązać połączenia z usługą keystone."
|
||||
|
||||
msgid "Unable to retrieve authorized projects."
|
||||
msgstr "Nie można pobrać uwierzytelnionych projektów."
|
||||
|
||||
msgid "User Name"
|
||||
msgstr "Nazwa użytkownika"
|
||||
|
||||
msgid "You are not authorized for any projects or domains."
|
||||
msgstr "Brak upoważnień dla projektów lub domen."
|
||||
|
||||
msgid "You are not authorized for any projects."
|
||||
msgstr "Nie upoważniono do żadnego projektu."
|
@ -1,100 +0,0 @@
|
||||
# Translations template for django_openstack_auth.
|
||||
# Copyright (C) 2015 ORGANIZATION
|
||||
# This file is distributed under the same license as the
|
||||
# django_openstack_auth project.
|
||||
#
|
||||
# Translators:
|
||||
# Andre Campos Bezerra <andrecbezerra@gmail.com>, 2015
|
||||
# Gabriel Wainer, 2015
|
||||
# Andreas Jaeger <jaegerandi@gmail.com>, 2016. #zanata
|
||||
# Fernando Pimenta <fernando.c.pimenta@gmail.com>, 2017. #zanata
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: django_openstack_auth 3.2.1.dev5\n"
|
||||
"Report-Msgid-Bugs-To: https://bugs.launchpad.net/openstack-i18n/\n"
|
||||
"POT-Creation-Date: 2017-06-28 12:08+0000\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"PO-Revision-Date: 2017-06-28 12:37+0000\n"
|
||||
"Last-Translator: Fernando Pimenta <fernando.c.pimenta@gmail.com>\n"
|
||||
"Language: pt-BR\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n > 1);\n"
|
||||
"Generated-By: Babel 2.0\n"
|
||||
"X-Generator: Zanata 3.9.6\n"
|
||||
"Language-Team: Portuguese (Brazil)\n"
|
||||
|
||||
msgid "An error occurred authenticating. Please try again later."
|
||||
msgstr "Um erro ocorreu autenticando. Por favor tente novamente mais tarde."
|
||||
|
||||
msgid "Authenticate using"
|
||||
msgstr "Autenticar usando"
|
||||
|
||||
msgid "Could not find service provider ID on Keystone."
|
||||
msgstr "Não consegui encontrar o ID do provedor de serviço no Keystone."
|
||||
|
||||
msgid "Domain"
|
||||
msgstr "Domínio"
|
||||
|
||||
msgid "Invalid credentials."
|
||||
msgstr "Credencial Inválida"
|
||||
|
||||
msgid "K2K Federation not setup for this session"
|
||||
msgstr "K2K Federation não está configurada para esta sessão"
|
||||
|
||||
msgid ""
|
||||
"No authentication backend could be determined to handle the provided "
|
||||
"credentials."
|
||||
msgstr ""
|
||||
"Nenhuma autenticação poderia ser determinada para lidar com as credenciais "
|
||||
"fornecidas."
|
||||
|
||||
msgid "Password"
|
||||
msgstr "Senha"
|
||||
|
||||
#, python-format
|
||||
msgid "Please consider changing your password, it will expire in %s minutes"
|
||||
msgstr ""
|
||||
"Por favor, considere a alteração de sua senha, ela irá expirar em %s minutos"
|
||||
|
||||
#, python-format
|
||||
msgid "Project switch failed for user \"%(username)s\"."
|
||||
msgstr "A troca de projeto falhou para o usuário \"%(username)s\"."
|
||||
|
||||
msgid "Region"
|
||||
msgstr "Região"
|
||||
|
||||
#, python-format
|
||||
msgid "Service provider authentication failed. %s"
|
||||
msgstr "Autenticação do provedor de serviço falhou. %s"
|
||||
|
||||
#, python-format
|
||||
msgid "Switch to Keystone Provider \"%(keystone_provider)s\"successful."
|
||||
msgstr ""
|
||||
"Troca para o Provedor Keystone \"%(keystone_provider)s\" realizada com "
|
||||
"sucesso."
|
||||
|
||||
#, python-format
|
||||
msgid "Switch to project \"%(project_name)s\" successful."
|
||||
msgstr "Troca para o projeto \"%(project_name)s\" realizada com sucesso."
|
||||
|
||||
msgid "The authentication token issued by the Identity service has expired."
|
||||
msgstr "O token de autenticação emitido pelo serviço de identidade expirou."
|
||||
|
||||
msgid "Unable to establish connection to keystone endpoint."
|
||||
msgstr "Não foi possível estabelecer conexão com o endpoint do Keystone."
|
||||
|
||||
msgid "Unable to retrieve authorized domains."
|
||||
msgstr "Não foi possível recuperar domínios autorizados."
|
||||
|
||||
msgid "Unable to retrieve authorized projects."
|
||||
msgstr "Não foi possível obter os projetos autorizados."
|
||||
|
||||
msgid "User Name"
|
||||
msgstr "Nome do Usuário"
|
||||
|
||||
msgid "You are not authorized for any projects or domains."
|
||||
msgstr "Você não está autorizado para nenhum projeto ou domínio."
|
||||
|
||||
msgid "You are not authorized for any projects."
|
||||
msgstr "Você não está autorizado para nenhum projeto."
|
@ -1,87 +0,0 @@
|
||||
# Translations template for django_openstack_auth.
|
||||
# Copyright (C) 2015 ORGANIZATION
|
||||
# This file is distributed under the same license as the
|
||||
# django_openstack_auth project.
|
||||
#
|
||||
# Translators:
|
||||
# Alexander Vasiliev <avasiliev@asdco.ru>, 2015
|
||||
# Fedor Tarasenko <feodor.tarasenko@gmail.com>, 2015
|
||||
# Ilya Alekseyev <ilyaalekseyev@acm.org>, 2015
|
||||
# Ilya Shakhat <shakhat@gmail.com>, 2015
|
||||
# Nikita Burtsev, 2015
|
||||
# Andreas Jaeger <jaegerandi@gmail.com>, 2016. #zanata
|
||||
# Artem <amikhalev90@gmail.com>, 2017. #zanata
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: django_openstack_auth 3.1.1.dev1\n"
|
||||
"Report-Msgid-Bugs-To: https://bugs.launchpad.net/openstack-i18n/\n"
|
||||
"POT-Creation-Date: 2017-01-25 19:41+0000\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"PO-Revision-Date: 2017-01-23 12:43+0000\n"
|
||||
"Last-Translator: Artem <amikhalev90@gmail.com>\n"
|
||||
"Language: ru\n"
|
||||
"Plural-Forms: nplurals=4; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n"
|
||||
"%10<=4 && (n%100<12 || n%100>14) ? 1 : n%10==0 || (n%10>=5 && n%10<=9) || (n"
|
||||
"%100>=11 && n%100<=14)? 2 : 3);\n"
|
||||
"Generated-By: Babel 2.0\n"
|
||||
"X-Generator: Zanata 3.7.3\n"
|
||||
"Language-Team: Russian\n"
|
||||
|
||||
msgid "An error occurred authenticating. Please try again later."
|
||||
msgstr "Произошла ошибка аутентификации. Пожалуйста, повторите попытку позже."
|
||||
|
||||
msgid "Authenticate using"
|
||||
msgstr "Аутентификация с использованием"
|
||||
|
||||
msgid "Could not find service provider ID on Keystone."
|
||||
msgstr "Не удалось найти ИД поставщика служб в Keystone."
|
||||
|
||||
msgid "Domain"
|
||||
msgstr "Домен"
|
||||
|
||||
msgid "Invalid credentials."
|
||||
msgstr "Неправильные учётные данные."
|
||||
|
||||
msgid ""
|
||||
"No authentication backend could be determined to handle the provided "
|
||||
"credentials."
|
||||
msgstr ""
|
||||
"Невозможно определить backend для обработки предоставленных учётных данных."
|
||||
|
||||
msgid "Password"
|
||||
msgstr "Пароль"
|
||||
|
||||
#, python-format
|
||||
msgid "Project switch failed for user \"%(username)s\"."
|
||||
msgstr "Не удалось переключить проект для пользователя \"%(username)s\"."
|
||||
|
||||
msgid "Region"
|
||||
msgstr "Регион"
|
||||
|
||||
#, python-format
|
||||
msgid "Service provider authentication failed. %s"
|
||||
msgstr "Неудачная аутентикация поставщика службы. %s"
|
||||
|
||||
#, python-format
|
||||
msgid "Switch to project \"%(project_name)s\" successful."
|
||||
msgstr "Переключение на проект \"%(project_name)s\" выполнено успешно."
|
||||
|
||||
msgid "The authentication token issued by the Identity service has expired."
|
||||
msgstr "Время действия токена, выданного сервисом идентификации, истекло."
|
||||
|
||||
msgid "Unable to establish connection to keystone endpoint."
|
||||
msgstr "Невозможно установить соединение с точкой доступа keystone."
|
||||
|
||||
msgid "Unable to retrieve authorized projects."
|
||||
msgstr "Невозможно получить авторизованные проекты"
|
||||
|
||||
msgid "User Name"
|
||||
msgstr "Имя пользователя"
|
||||
|
||||
msgid "You are not authorized for any projects or domains."
|
||||
msgstr "Вы не авторизованы ни в одном проекте или домене."
|
||||
|
||||
msgid "You are not authorized for any projects."
|
||||
msgstr "Вы не авторизованы ни в одном проекте."
|
@ -1,98 +0,0 @@
|
||||
# Translations template for django_openstack_auth.
|
||||
# Copyright (C) 2015 ORGANIZATION
|
||||
# This file is distributed under the same license as the
|
||||
# django_openstack_auth project.
|
||||
#
|
||||
# Translators:
|
||||
# İşbaran Akçayır <isbaran@gmail.com>, 2015
|
||||
# Andreas Jaeger <jaegerandi@gmail.com>, 2016. #zanata
|
||||
# işbaran akçayır <isbaran@gmail.com>, 2017. #zanata
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: django_openstack_auth 3.1.2.dev20\n"
|
||||
"Report-Msgid-Bugs-To: https://bugs.launchpad.net/openstack-i18n/\n"
|
||||
"POT-Creation-Date: 2017-05-18 13:23+0000\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"PO-Revision-Date: 2017-05-22 09:01+0000\n"
|
||||
"Last-Translator: Copied by Zanata <copied-by-zanata@zanata.org>\n"
|
||||
"Language: tr-TR\n"
|
||||
"Plural-Forms: nplurals=1; plural=0;\n"
|
||||
"Generated-By: Babel 2.0\n"
|
||||
"X-Generator: Zanata 3.9.6\n"
|
||||
"Language-Team: Turkish (Turkey)\n"
|
||||
|
||||
msgid "An error occurred authenticating. Please try again later."
|
||||
msgstr "Bilinmeyen bir hata oluştu. Lütfen daha sonra tekrar deneyin."
|
||||
|
||||
msgid "Authenticate using"
|
||||
msgstr "Kimlik doğrulama"
|
||||
|
||||
msgid "Could not find service provider ID on Keystone."
|
||||
msgstr "Keystone için servis sağlayıcı ID'si bulunamadı."
|
||||
|
||||
msgid "Domain"
|
||||
msgstr "Domain"
|
||||
|
||||
msgid "Invalid credentials."
|
||||
msgstr "Geçersiz giriş bilgileri."
|
||||
|
||||
msgid "K2K Federation not setup for this session"
|
||||
msgstr "K2K Birleşimi bu oturum için kurulmamış"
|
||||
|
||||
msgid ""
|
||||
"No authentication backend could be determined to handle the provided "
|
||||
"credentials."
|
||||
msgstr ""
|
||||
"Sağlanan kimlik bilgilerini işleyecek kimlik doğrulama uygulaması tespit "
|
||||
"edilemedi."
|
||||
|
||||
msgid "Password"
|
||||
msgstr "Şifre"
|
||||
|
||||
#, python-format
|
||||
msgid "Please consider changing your password, it will expire in %s minutes"
|
||||
msgstr ""
|
||||
"Lütfen parolanızı değiştirmeyin düşünün, %s dakikada süresini dolduracak"
|
||||
|
||||
#, python-format
|
||||
msgid "Project switch failed for user \"%(username)s\"."
|
||||
msgstr "\"%(username)s\" kullanıcısı için proje geçişi başarısız."
|
||||
|
||||
msgid "Region"
|
||||
msgstr "Bölge"
|
||||
|
||||
#, python-format
|
||||
msgid "Service provider authentication failed. %s"
|
||||
msgstr "Servis sağlayıcı kimlik doğrulama başarısız. %s"
|
||||
|
||||
#, python-format
|
||||
msgid "Switch to Keystone Provider \"%(keystone_provider)s\"successful."
|
||||
msgstr "\"%(keystone_provider)s\" Keystone Sağlayıcısına geçiş başarılı."
|
||||
|
||||
#, python-format
|
||||
msgid "Switch to project \"%(project_name)s\" successful."
|
||||
msgstr "\"%(project_name)s\" projesine geçiş başarılı."
|
||||
|
||||
msgid "The authentication token issued by the Identity service has expired."
|
||||
msgstr ""
|
||||
"Kimlik servisi tarafından verilen kimlik doğrulama belirtecinin süresi doldu."
|
||||
|
||||
msgid "Unable to establish connection to keystone endpoint."
|
||||
msgstr "Keystone uç noktasına bağlantı kurulamadı."
|
||||
|
||||
msgid "Unable to retrieve authorized domains."
|
||||
msgstr "Yetkili alanlar alınamadı."
|
||||
|
||||
msgid "Unable to retrieve authorized projects."
|
||||
msgstr "Yetkiniz bulunan projeler alınamadı."
|
||||
|
||||
msgid "User Name"
|
||||
msgstr "Kullanıcı Adı"
|
||||
|
||||
msgid "You are not authorized for any projects or domains."
|
||||
msgstr "Herhangi bir proje veya domain için yetkili değilsiniz."
|
||||
|
||||
msgid "You are not authorized for any projects."
|
||||
msgstr "Herhangi bir proje için yetkili değilsiniz."
|
@ -1,50 +0,0 @@
|
||||
# Ukrainian translations for django_openstack_auth.
|
||||
# Copyright (C) 2015 ORGANIZATION
|
||||
# This file is distributed under the same license as the
|
||||
# django_openstack_auth project.
|
||||
#
|
||||
# Translators:
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: Horizon\n"
|
||||
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
|
||||
"POT-Creation-Date: 2015-08-04 06:25+0000\n"
|
||||
"PO-Revision-Date: 2015-07-16 01:34+0000\n"
|
||||
"Last-Translator: openstackjenkins <jenkins@openstack.org>\n"
|
||||
"Language-Team: Ukrainian (http://www.transifex.com/openstack/horizon/"
|
||||
"language/uk/)\n"
|
||||
"Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n"
|
||||
"%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2)\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=utf-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Generated-By: Babel 2.0\n"
|
||||
|
||||
msgid "An error occurred authenticating. Please try again later."
|
||||
msgstr "Помилка у автентифікації. Будь-ласка, спробуйте пізніше."
|
||||
|
||||
msgid "Domain"
|
||||
msgstr "Домен"
|
||||
|
||||
msgid "Password"
|
||||
msgstr "Пароль"
|
||||
|
||||
msgid "Region"
|
||||
msgstr "Регіон"
|
||||
|
||||
msgid "The authentication token issued by the Identity service has expired."
|
||||
msgstr ""
|
||||
"Автентифікаційний токен, виданий Ідентифікаційним сервісом, більше не "
|
||||
"дійсний."
|
||||
|
||||
msgid "Unable to authenticate to any available projects."
|
||||
msgstr "Неможливо автентифікуватися щодо жодного проекту."
|
||||
|
||||
msgid "Unable to retrieve authorized projects."
|
||||
msgstr "Неможливо отримати авторизовані проекти."
|
||||
|
||||
msgid "User Name"
|
||||
msgstr "Ім’я Користувача"
|
||||
|
||||
msgid "You are not authorized for any projects."
|
||||
msgstr "Ви не авторизовані до жодного проекту."
|
@ -1,94 +0,0 @@
|
||||
# Translations template for django_openstack_auth.
|
||||
# Copyright (C) 2015 ORGANIZATION
|
||||
# This file is distributed under the same license as the
|
||||
# django_openstack_auth project.
|
||||
#
|
||||
# Translators:
|
||||
# Allerson Yao, 2015
|
||||
# johnwoo_lee <lijiangsheng1@gmail.com>, 2015
|
||||
# LIU Yulong <dragon889@163.com>, 2015
|
||||
# Ying Chun Guo <daisy.ycguo@gmail.com>, 2015
|
||||
# 刘俊朋 <liujunpeng@inspur.com>, 2015
|
||||
# Andreas Jaeger <jaegerandi@gmail.com>, 2016. #zanata
|
||||
# liujunpeng <liujunpeng@inspur.com>, 2017. #zanata
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: django_openstack_auth 3.1.2.dev10\n"
|
||||
"Report-Msgid-Bugs-To: https://bugs.launchpad.net/openstack-i18n/\n"
|
||||
"POT-Creation-Date: 2017-03-14 12:20+0000\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"PO-Revision-Date: 2017-02-10 12:14+0000\n"
|
||||
"Last-Translator: liujunpeng <liujunpeng@inspur.com>\n"
|
||||
"Language: zh-CN\n"
|
||||
"Plural-Forms: nplurals=1; plural=0;\n"
|
||||
"Generated-By: Babel 2.0\n"
|
||||
"X-Generator: Zanata 3.9.6\n"
|
||||
"Language-Team: Chinese (China)\n"
|
||||
|
||||
msgid "An error occurred authenticating. Please try again later."
|
||||
msgstr "认证出现错误。请稍后重试。"
|
||||
|
||||
msgid "Authenticate using"
|
||||
msgstr "使用认证"
|
||||
|
||||
msgid "Could not find service provider ID on Keystone."
|
||||
msgstr "在Keystone上不能找到服务提供者ID。"
|
||||
|
||||
msgid "Domain"
|
||||
msgstr "域"
|
||||
|
||||
msgid "Invalid credentials."
|
||||
msgstr "凭据无效."
|
||||
|
||||
msgid "K2K Federation not setup for this session"
|
||||
msgstr "会话没有建立K2K联邦"
|
||||
|
||||
msgid ""
|
||||
"No authentication backend could be determined to handle the provided "
|
||||
"credentials."
|
||||
msgstr "没有认证后端可处理提供的凭据。"
|
||||
|
||||
msgid "Password"
|
||||
msgstr "密码"
|
||||
|
||||
#, python-format
|
||||
msgid "Project switch failed for user \"%(username)s\"."
|
||||
msgstr "为用户“%(username)s”切换项目失败"
|
||||
|
||||
msgid "Region"
|
||||
msgstr "区域"
|
||||
|
||||
#, python-format
|
||||
msgid "Service provider authentication failed. %s"
|
||||
msgstr "服务提供者认证失败。%s"
|
||||
|
||||
#, python-format
|
||||
msgid "Switch to Keystone Provider \"%(keystone_provider)s\"successful."
|
||||
msgstr "成功切换到 Keystone 提供者\"%(keystone_provider)s\"."
|
||||
|
||||
#, python-format
|
||||
msgid "Switch to project \"%(project_name)s\" successful."
|
||||
msgstr "成功切换到项目\"%(project_name)s\""
|
||||
|
||||
msgid "The authentication token issued by the Identity service has expired."
|
||||
msgstr "身份认证令牌已过期。"
|
||||
|
||||
msgid "Unable to establish connection to keystone endpoint."
|
||||
msgstr "无法连接keystone端点。"
|
||||
|
||||
msgid "Unable to retrieve authorized domains."
|
||||
msgstr "无法获取授权域。"
|
||||
|
||||
msgid "Unable to retrieve authorized projects."
|
||||
msgstr "无法获取授权的项目。"
|
||||
|
||||
msgid "User Name"
|
||||
msgstr "用户名"
|
||||
|
||||
msgid "You are not authorized for any projects or domains."
|
||||
msgstr "您未被授权访问任何项目或域。"
|
||||
|
||||
msgid "You are not authorized for any projects."
|
||||
msgstr "您未被授权访问任何项目。"
|
@ -1,90 +0,0 @@
|
||||
# Translations template for django_openstack_auth.
|
||||
# Copyright (C) 2015 ORGANIZATION
|
||||
# This file is distributed under the same license as the
|
||||
# django_openstack_auth project.
|
||||
#
|
||||
# Translators:
|
||||
# Zhang Xiaowei <zero00072@gmail.com>, 2015
|
||||
# Andreas Jaeger <jaegerandi@gmail.com>, 2016. #zanata
|
||||
# Ching Kuo <gene@openstack.org>, 2017. #zanata
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: django_openstack_auth 3.1.1.dev12\n"
|
||||
"Report-Msgid-Bugs-To: https://bugs.launchpad.net/openstack-i18n/\n"
|
||||
"POT-Creation-Date: 2017-02-02 16:33+0000\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"PO-Revision-Date: 2017-02-02 07:53+0000\n"
|
||||
"Last-Translator: Ching Kuo <gene@openstack.org>\n"
|
||||
"Language: zh-TW\n"
|
||||
"Plural-Forms: nplurals=1; plural=0;\n"
|
||||
"Generated-By: Babel 2.0\n"
|
||||
"X-Generator: Zanata 3.7.3\n"
|
||||
"Language-Team: Chinese (Taiwan)\n"
|
||||
|
||||
msgid "An error occurred authenticating. Please try again later."
|
||||
msgstr "認證發生錯誤。請稍後再試。"
|
||||
|
||||
msgid "Authenticate using"
|
||||
msgstr "認證使用"
|
||||
|
||||
msgid "Could not find service provider ID on Keystone."
|
||||
msgstr "在Keystone找不到服務提供者ID"
|
||||
|
||||
msgid "Domain"
|
||||
msgstr "地域"
|
||||
|
||||
msgid "Invalid credentials."
|
||||
msgstr "無效的認證。"
|
||||
|
||||
msgid "K2K Federation not setup for this session"
|
||||
msgstr "這個session並未設定K2K聯合"
|
||||
|
||||
msgid ""
|
||||
"No authentication backend could be determined to handle the provided "
|
||||
"credentials."
|
||||
msgstr "沒有選擇認證後端來處理提供的憑證。"
|
||||
|
||||
msgid "Password"
|
||||
msgstr "密碼"
|
||||
|
||||
#, python-format
|
||||
msgid "Project switch failed for user \"%(username)s\"."
|
||||
msgstr "使用者 \"%(username)s\" 的專案切換失敗。"
|
||||
|
||||
msgid "Region"
|
||||
msgstr "區域"
|
||||
|
||||
#, python-format
|
||||
msgid "Service provider authentication failed. %s"
|
||||
msgstr "服務來源認證錯誤。%s"
|
||||
|
||||
#, python-format
|
||||
msgid "Switch to Keystone Provider \"%(keystone_provider)s\"successful."
|
||||
msgstr "成功切換認證來源到 \"%(keystone_provider)s\""
|
||||
|
||||
#, python-format
|
||||
msgid "Switch to project \"%(project_name)s\" successful."
|
||||
msgstr "成功切換至專案 \"%(project_name)s\"。"
|
||||
|
||||
msgid "The authentication token issued by the Identity service has expired."
|
||||
msgstr "認證伺服器發佈的門票已經過期。"
|
||||
|
||||
msgid "Unable to establish connection to keystone endpoint."
|
||||
msgstr "無法建立與 Keystone 端點的連線。"
|
||||
|
||||
msgid "Unable to retrieve authorized domains."
|
||||
msgstr "無法獲得認證過的地域。"
|
||||
|
||||
msgid "Unable to retrieve authorized projects."
|
||||
msgstr "無法取回已授權的專案。"
|
||||
|
||||
msgid "User Name"
|
||||
msgstr "用戶名稱"
|
||||
|
||||
msgid "You are not authorized for any projects or domains."
|
||||
msgstr "您未獲授權對任何專案或網域執行動作。"
|
||||
|
||||
msgid "You are not authorized for any projects."
|
||||
msgstr "沒有授權您任何專案。"
|
@ -1,18 +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 the User model in here so Django can find it
|
||||
from openstack_auth.user import User
|
||||
|
||||
|
||||
__all__ = ['User']
|
@ -1,22 +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 openstack_auth.plugin.base import BasePlugin
|
||||
from openstack_auth.plugin.k2k import K2KAuthPlugin
|
||||
from openstack_auth.plugin.password import PasswordPlugin
|
||||
from openstack_auth.plugin.token import TokenPlugin
|
||||
|
||||
|
||||
__all__ = ['BasePlugin',
|
||||
'PasswordPlugin',
|
||||
'TokenPlugin',
|
||||
'K2KAuthPlugin']
|
@ -1,241 +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 django.utils.translation import ugettext_lazy as _
|
||||
from keystoneauth1 import exceptions as keystone_exceptions
|
||||
from keystoneclient.v2_0 import client as v2_client
|
||||
from keystoneclient.v3 import client as v3_client
|
||||
import six
|
||||
|
||||
from openstack_auth import exceptions
|
||||
from openstack_auth import utils
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
__all__ = ['BasePlugin']
|
||||
|
||||
|
||||
@six.add_metaclass(abc.ABCMeta)
|
||||
class BasePlugin(object):
|
||||
"""Base plugin to provide ways to log in to dashboard.
|
||||
|
||||
Provides a framework for keystoneclient plugins that can be used with the
|
||||
information provided to return an unscoped token.
|
||||
"""
|
||||
|
||||
@abc.abstractmethod
|
||||
def get_plugin(self, auth_url=None, **kwargs):
|
||||
"""Create a new plugin to attempt to authenticate.
|
||||
|
||||
Given the information provided by the login providers attempt to create
|
||||
an authentication plugin that can be used to authenticate the user.
|
||||
|
||||
If the provided login information does not contain enough information
|
||||
for this plugin to proceed then it should return None.
|
||||
|
||||
:param str auth_url: The URL to authenticate against.
|
||||
|
||||
:returns: A plugin that will be used to authenticate or None if the
|
||||
plugin cannot authenticate with the data provided.
|
||||
:rtype: keystoneclient.auth.BaseAuthPlugin
|
||||
"""
|
||||
return None
|
||||
|
||||
@property
|
||||
def keystone_version(self):
|
||||
"""The Identity API version as specified in the settings file."""
|
||||
return utils.get_keystone_version()
|
||||
|
||||
def list_projects(self, session, auth_plugin, auth_ref=None):
|
||||
"""List the projects that are accessible to this plugin.
|
||||
|
||||
Query the keystone server for all projects that this authentication
|
||||
token can be rescoped to.
|
||||
|
||||
This function is overrideable by plugins if they use a non-standard
|
||||
mechanism to determine projects.
|
||||
|
||||
:param session: A session object for communication:
|
||||
:type session: keystoneclient.session.Session
|
||||
:param auth_plugin: The auth plugin returned by :py:meth:`get_plugin`.
|
||||
:type auth_plugin: keystoneclient.auth.BaseAuthPlugin
|
||||
:param auth_ref: The current authentication data. This is optional as
|
||||
future auth plugins may not have auth_ref data and all
|
||||
the required information should be available via the
|
||||
auth_plugin.
|
||||
:type auth_ref: keystoneclient.access.AccessInfo` or None.
|
||||
|
||||
:raises: exceptions.KeystoneAuthException on lookup failure.
|
||||
|
||||
:returns: A list of projects. This currently accepts returning both v2
|
||||
or v3 keystoneclient projects objects.
|
||||
"""
|
||||
try:
|
||||
if self.keystone_version >= 3:
|
||||
client = v3_client.Client(session=session, auth=auth_plugin)
|
||||
if auth_ref.is_federated:
|
||||
return client.federation.projects.list()
|
||||
else:
|
||||
return client.projects.list(user=auth_ref.user_id)
|
||||
|
||||
else:
|
||||
client = v2_client.Client(session=session, auth=auth_plugin)
|
||||
return client.tenants.list()
|
||||
|
||||
except (keystone_exceptions.ClientException,
|
||||
keystone_exceptions.AuthorizationFailure):
|
||||
msg = _('Unable to retrieve authorized projects.')
|
||||
raise exceptions.KeystoneAuthException(msg)
|
||||
|
||||
def list_domains(self, session, auth_plugin, auth_ref=None):
|
||||
try:
|
||||
if self.keystone_version >= 3:
|
||||
client = v3_client.Client(session=session, auth=auth_plugin)
|
||||
return client.auth.domains()
|
||||
else:
|
||||
return []
|
||||
except (keystone_exceptions.ClientException,
|
||||
keystone_exceptions.AuthorizationFailure):
|
||||
msg = _('Unable to retrieve authorized domains.')
|
||||
raise exceptions.KeystoneAuthException(msg)
|
||||
|
||||
def get_access_info(self, keystone_auth):
|
||||
"""Get the access info from an unscoped auth
|
||||
|
||||
This function provides the base functionality that the
|
||||
plugins will use to authenticate and get the access info object.
|
||||
|
||||
:param keystone_auth: keystoneauth1 identity plugin
|
||||
:raises: exceptions.KeystoneAuthException on auth failure
|
||||
:returns: keystoneclient.access.AccessInfo
|
||||
"""
|
||||
session = utils.get_session()
|
||||
|
||||
try:
|
||||
unscoped_auth_ref = keystone_auth.get_access(session)
|
||||
except keystone_exceptions.ConnectFailure as exc:
|
||||
LOG.error(str(exc))
|
||||
msg = _('Unable to establish connection to keystone endpoint.')
|
||||
raise exceptions.KeystoneAuthException(msg)
|
||||
except (keystone_exceptions.Unauthorized,
|
||||
keystone_exceptions.Forbidden,
|
||||
keystone_exceptions.NotFound) as exc:
|
||||
LOG.debug(str(exc))
|
||||
raise exceptions.KeystoneAuthException(_('Invalid credentials.'))
|
||||
except (keystone_exceptions.ClientException,
|
||||
keystone_exceptions.AuthorizationFailure) as exc:
|
||||
msg = _("An error occurred authenticating. "
|
||||
"Please try again later.")
|
||||
LOG.debug(str(exc))
|
||||
raise exceptions.KeystoneAuthException(msg)
|
||||
return unscoped_auth_ref
|
||||
|
||||
def get_project_scoped_auth(self, unscoped_auth, unscoped_auth_ref,
|
||||
recent_project=None):
|
||||
"""Get the project scoped keystone auth and access info
|
||||
|
||||
This function returns a project scoped keystone token plugin
|
||||
and AccessInfo object.
|
||||
|
||||
:param unscoped_auth: keystone auth plugin
|
||||
:param unscoped_auth_ref: keystoneclient.access.AccessInfo` or None.
|
||||
:param recent_project: project that we should try to scope to
|
||||
:return: keystone token auth plugin, AccessInfo object
|
||||
"""
|
||||
auth_url = unscoped_auth.auth_url
|
||||
session = utils.get_session()
|
||||
|
||||
projects = self.list_projects(
|
||||
session, unscoped_auth, unscoped_auth_ref)
|
||||
# Attempt to scope only to enabled projects
|
||||
projects = [project for project in projects if project.enabled]
|
||||
|
||||
# if a most recent project was found, try using it first
|
||||
if recent_project:
|
||||
for pos, project in enumerate(projects):
|
||||
if project.id == recent_project:
|
||||
# move recent project to the beginning
|
||||
projects.pop(pos)
|
||||
projects.insert(0, project)
|
||||
break
|
||||
|
||||
scoped_auth = None
|
||||
scoped_auth_ref = None
|
||||
for project in projects:
|
||||
token = unscoped_auth_ref.auth_token
|
||||
scoped_auth = utils.get_token_auth_plugin(auth_url,
|
||||
token=token,
|
||||
project_id=project.id)
|
||||
try:
|
||||
scoped_auth_ref = scoped_auth.get_access(session)
|
||||
except (keystone_exceptions.ClientException,
|
||||
keystone_exceptions.AuthorizationFailure):
|
||||
LOG.info('Attempted scope to project %s failed, will attempt'
|
||||
'to scope to another project.' % project.name)
|
||||
pass
|
||||
else:
|
||||
break
|
||||
|
||||
return scoped_auth, scoped_auth_ref
|
||||
|
||||
def get_domain_scoped_auth(self, unscoped_auth, unscoped_auth_ref,
|
||||
domain_name=None):
|
||||
"""Get the domain scoped keystone auth and access info
|
||||
|
||||
This function returns a domain scoped keystone token plugin
|
||||
and AccessInfo object.
|
||||
|
||||
:param unscoped_auth: keystone auth plugin
|
||||
:param unscoped_auth_ref: keystoneclient.access.AccessInfo` or None.
|
||||
:param domain_name: domain that we should try to scope to
|
||||
:return: keystone token auth plugin, AccessInfo object
|
||||
"""
|
||||
session = utils.get_session()
|
||||
auth_url = unscoped_auth.auth_url
|
||||
|
||||
if utils.get_keystone_version() < 3:
|
||||
return None, None
|
||||
if domain_name:
|
||||
domains = [domain_name]
|
||||
else:
|
||||
domains = self.list_domains(session,
|
||||
unscoped_auth,
|
||||
unscoped_auth_ref)
|
||||
domains = [domain.name for domain in domains if domain.enabled]
|
||||
|
||||
# domain support can require domain scoped tokens to perform
|
||||
# identity operations depending on the policy files being used
|
||||
# for keystone.
|
||||
domain_auth = None
|
||||
domain_auth_ref = None
|
||||
for domain_name in domains:
|
||||
token = unscoped_auth_ref.auth_token
|
||||
domain_auth = utils.get_token_auth_plugin(
|
||||
auth_url,
|
||||
token,
|
||||
domain_name=domain_name)
|
||||
try:
|
||||
domain_auth_ref = domain_auth.get_access(session)
|
||||
except (keystone_exceptions.ClientException,
|
||||
keystone_exceptions.AuthorizationFailure):
|
||||
LOG.info('Attempted scope to domain %s failed, will attempt'
|
||||
'to scope to another domain.' % domain_name)
|
||||
pass
|
||||
else:
|
||||
if len(domains) > 1:
|
||||
LOG.info("More than one valid domain found for user %s,"
|
||||
" scoping to %s" %
|
||||
(unscoped_auth_ref.user_id, domain_name))
|
||||
break
|
||||
return domain_auth, domain_auth_ref
|
@ -1,107 +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 logging
|
||||
|
||||
from django.conf import settings
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
from keystoneauth1.identity import v3 as v3_auth
|
||||
|
||||
from openstack_auth import exceptions
|
||||
from openstack_auth.plugin import base
|
||||
from openstack_auth import utils
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
__all__ = ['K2KAuthPlugin']
|
||||
|
||||
|
||||
class K2KAuthPlugin(base.BasePlugin):
|
||||
|
||||
def get_plugin(self, service_provider=None, auth_url=None, plugins=None,
|
||||
**kwargs):
|
||||
"""Authenticate using keystone to keystone federation.
|
||||
|
||||
This plugin uses other v3 plugins to authenticate a user to a
|
||||
identity provider in order to authenticate the user to a service
|
||||
provider
|
||||
|
||||
:param service_provider: service provider ID
|
||||
:param auth_url: Keystone auth url
|
||||
:param plugins: list of openstack_auth plugins to check
|
||||
:returns Keystone2Keystone keystone auth plugin
|
||||
"""
|
||||
|
||||
# Avoid mutable default arg for plugins
|
||||
plugins = plugins or []
|
||||
|
||||
# service_provider being None prevents infinite recursion
|
||||
if utils.get_keystone_version() < 3 or not service_provider:
|
||||
return None
|
||||
|
||||
keystone_idp_id = getattr(settings, 'KEYSTONE_PROVIDER_IDP_ID',
|
||||
'localkeystone')
|
||||
if service_provider == keystone_idp_id:
|
||||
return None
|
||||
|
||||
for plugin in plugins:
|
||||
unscoped_idp_auth = plugin.get_plugin(plugins=plugins,
|
||||
auth_url=auth_url, **kwargs)
|
||||
if unscoped_idp_auth:
|
||||
break
|
||||
else:
|
||||
LOG.debug('Could not find base authentication backend for '
|
||||
'K2K plugin with the provided credentials.')
|
||||
return None
|
||||
|
||||
idp_exception = None
|
||||
scoped_idp_auth = None
|
||||
unscoped_auth_ref = base.BasePlugin.get_access_info(
|
||||
self, unscoped_idp_auth)
|
||||
try:
|
||||
scoped_idp_auth, __ = self.get_project_scoped_auth(
|
||||
unscoped_idp_auth, unscoped_auth_ref)
|
||||
except exceptions.KeystoneAuthException as idp_excp:
|
||||
idp_exception = idp_excp
|
||||
|
||||
if not scoped_idp_auth or idp_exception:
|
||||
msg = 'Identity provider authentication Failed.'
|
||||
raise exceptions.KeystoneAuthException(msg)
|
||||
|
||||
session = utils.get_session()
|
||||
|
||||
if scoped_idp_auth.get_sp_auth_url(session, service_provider) is None:
|
||||
msg = _('Could not find service provider ID on Keystone.')
|
||||
raise exceptions.KeystoneAuthException(msg)
|
||||
|
||||
unscoped_auth = v3_auth.Keystone2Keystone(
|
||||
base_plugin=scoped_idp_auth,
|
||||
service_provider=service_provider)
|
||||
return unscoped_auth
|
||||
|
||||
def get_access_info(self, unscoped_auth):
|
||||
"""Get the access info object
|
||||
|
||||
We attempt to get the auth ref. If it fails and if the K2K auth plugin
|
||||
was being used then we will prepend a message saying that the error was
|
||||
on the service provider side.
|
||||
:param: unscoped_auth: Keystone auth plugin for unscoped user
|
||||
:returns: keystoneclient.access.AccessInfo object
|
||||
"""
|
||||
try:
|
||||
unscoped_auth_ref = base.BasePlugin.get_access_info(
|
||||
self, unscoped_auth)
|
||||
except exceptions.KeystoneAuthException as excp:
|
||||
msg = _('Service provider authentication failed. %s')
|
||||
raise exceptions.KeystoneAuthException(msg % str(excp))
|
||||
return unscoped_auth_ref
|
@ -1,51 +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 logging
|
||||
|
||||
from keystoneauth1.identity import v2 as v2_auth
|
||||
from keystoneauth1.identity import v3 as v3_auth
|
||||
|
||||
from openstack_auth.plugin import base
|
||||
from openstack_auth import utils
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
__all__ = ['PasswordPlugin']
|
||||
|
||||
|
||||
class PasswordPlugin(base.BasePlugin):
|
||||
"""Authenticate against keystone given a username and password.
|
||||
|
||||
This is the default login mechanism. Given a username and password inputted
|
||||
from a login form returns a v2 or v3 keystone Password plugin for
|
||||
authentication.
|
||||
"""
|
||||
|
||||
def get_plugin(self, auth_url=None, username=None, password=None,
|
||||
user_domain_name=None, **kwargs):
|
||||
if not all((auth_url, username, password)):
|
||||
return None
|
||||
|
||||
LOG.debug('Attempting to authenticate for %s', username)
|
||||
|
||||
if utils.get_keystone_version() >= 3:
|
||||
return v3_auth.Password(auth_url=auth_url,
|
||||
username=username,
|
||||
password=password,
|
||||
user_domain_name=user_domain_name,
|
||||
unscoped=True)
|
||||
|
||||
else:
|
||||
return v2_auth.Password(auth_url=auth_url,
|
||||
username=username,
|
||||
password=password)
|
@ -1,41 +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.identity import v2 as v2_auth
|
||||
from keystoneauth1.identity import v3 as v3_auth
|
||||
|
||||
from openstack_auth.plugin import base
|
||||
from openstack_auth import utils
|
||||
|
||||
|
||||
__all__ = ['TokenPlugin']
|
||||
|
||||
|
||||
class TokenPlugin(base.BasePlugin):
|
||||
"""Authenticate against keystone with an existing token."""
|
||||
|
||||
def get_plugin(self, auth_url=None, token=None, project_id=None,
|
||||
**kwargs):
|
||||
if not all((auth_url, token)):
|
||||
return None
|
||||
|
||||
if utils.get_keystone_version() >= 3:
|
||||
return v3_auth.Token(auth_url=auth_url,
|
||||
token=token,
|
||||
project_id=project_id,
|
||||
reauthenticate=False)
|
||||
|
||||
else:
|
||||
return v2_auth.Token(auth_url=auth_url,
|
||||
token=token,
|
||||
tenant_id=project_id,
|
||||
reauthenticate=False)
|
@ -1,224 +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.
|
||||
|
||||
"""Policy engine for openstack_auth"""
|
||||
|
||||
import logging
|
||||
import os.path
|
||||
|
||||
from django.conf import settings
|
||||
from oslo_config import cfg
|
||||
from oslo_policy import opts as policy_opts
|
||||
from oslo_policy import policy
|
||||
|
||||
from openstack_auth import user as auth_user
|
||||
from openstack_auth import utils as auth_utils
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
_ENFORCER = None
|
||||
_BASE_PATH = getattr(settings, 'POLICY_FILES_PATH', '')
|
||||
|
||||
|
||||
def _get_policy_conf():
|
||||
conf = cfg.ConfigOpts()
|
||||
# Passing [] is required. Otherwise oslo.config looks up sys.argv.
|
||||
conf([])
|
||||
policy_opts.set_defaults(conf)
|
||||
# Policy Enforcer has been updated to take in a policy directory
|
||||
# as a config option. However, the default value in is set to
|
||||
# ['policy.d'] which causes the code to break. Set the default
|
||||
# value to empty list for now.
|
||||
conf.set_default('policy_dirs', [], 'oslo_policy')
|
||||
return conf
|
||||
|
||||
|
||||
def _get_enforcer():
|
||||
global _ENFORCER
|
||||
if not _ENFORCER:
|
||||
_ENFORCER = {}
|
||||
policy_files = getattr(settings, 'POLICY_FILES', {})
|
||||
conf = _get_policy_conf()
|
||||
for service in policy_files.keys():
|
||||
policy_file = os.path.join(_BASE_PATH, policy_files[service])
|
||||
enforcer = policy.Enforcer(conf, policy_file)
|
||||
# Ensure enforcer.policy_path is populated.
|
||||
enforcer.load_rules()
|
||||
if os.path.isfile(enforcer.policy_path):
|
||||
LOG.debug("adding enforcer for service: %s" % service)
|
||||
_ENFORCER[service] = enforcer
|
||||
else:
|
||||
LOG.warning("policy file for service: %s not found at %s" %
|
||||
(service, enforcer.policy_path))
|
||||
return _ENFORCER
|
||||
|
||||
|
||||
def reset():
|
||||
global _ENFORCER
|
||||
_ENFORCER = None
|
||||
|
||||
|
||||
def check(actions, request, target=None):
|
||||
"""Check user permission.
|
||||
|
||||
Check if the user has permission to the action according
|
||||
to policy setting.
|
||||
|
||||
:param actions: list of scope and action to do policy checks on,
|
||||
the composition of which is (scope, action). Multiple actions
|
||||
are treated as a logical AND.
|
||||
|
||||
* scope: service type managing the policy for action
|
||||
|
||||
* action: string representing the action to be checked
|
||||
|
||||
this should be colon separated for clarity.
|
||||
i.e.
|
||||
|
||||
| compute:create_instance
|
||||
| compute:attach_volume
|
||||
| volume:attach_volume
|
||||
|
||||
for a policy action that requires a single action, actions
|
||||
should look like
|
||||
|
||||
| "(("compute", "compute:create_instance"),)"
|
||||
|
||||
for a multiple action check, actions should look like
|
||||
| "(("identity", "identity:list_users"),
|
||||
| ("identity", "identity:list_roles"))"
|
||||
|
||||
:param request: django http request object. If not specified, credentials
|
||||
must be passed.
|
||||
:param target: dictionary representing the object of the action
|
||||
for object creation this should be a dictionary
|
||||
representing the location of the object e.g.
|
||||
{'project_id': object.project_id}
|
||||
:returns: boolean if the user has permission or not for the actions.
|
||||
"""
|
||||
if target is None:
|
||||
target = {}
|
||||
user = auth_utils.get_user(request)
|
||||
|
||||
# Several service policy engines default to a project id check for
|
||||
# ownership. Since the user is already scoped to a project, if a
|
||||
# different project id has not been specified use the currently scoped
|
||||
# project's id.
|
||||
#
|
||||
# The reason is the operator can edit the local copies of the service
|
||||
# policy file. If a rule is removed, then the default rule is used. We
|
||||
# don't want to block all actions because the operator did not fully
|
||||
# understand the implication of editing the policy file. Additionally,
|
||||
# the service APIs will correct us if we are too permissive.
|
||||
if target.get('project_id') is None:
|
||||
target['project_id'] = user.project_id
|
||||
if target.get('tenant_id') is None:
|
||||
target['tenant_id'] = target['project_id']
|
||||
# same for user_id
|
||||
if target.get('user_id') is None:
|
||||
target['user_id'] = user.id
|
||||
|
||||
domain_id_keys = [
|
||||
'domain_id',
|
||||
'project.domain_id',
|
||||
'user.domain_id',
|
||||
'group.domain_id'
|
||||
]
|
||||
# populates domain id keys with user's current domain id
|
||||
for key in domain_id_keys:
|
||||
if target.get(key) is None:
|
||||
target[key] = user.user_domain_id
|
||||
|
||||
credentials = _user_to_credentials(user)
|
||||
domain_credentials = _domain_to_credentials(request, user)
|
||||
# if there is a domain token use the domain_id instead of the user's domain
|
||||
if domain_credentials:
|
||||
credentials['domain_id'] = domain_credentials.get('domain_id')
|
||||
|
||||
enforcer = _get_enforcer()
|
||||
|
||||
for action in actions:
|
||||
scope, action = action[0], action[1]
|
||||
if scope in enforcer:
|
||||
# this is for handling the v3 policy file and will only be
|
||||
# needed when a domain scoped token is present
|
||||
if scope == 'identity' and domain_credentials:
|
||||
# use domain credentials
|
||||
if not _check_credentials(enforcer[scope],
|
||||
action,
|
||||
target,
|
||||
domain_credentials):
|
||||
return False
|
||||
|
||||
# use project credentials
|
||||
if not _check_credentials(enforcer[scope],
|
||||
action, target, credentials):
|
||||
return False
|
||||
|
||||
# if no policy for scope, allow action, underlying API will
|
||||
# ultimately block the action if not permitted, treat as though
|
||||
# allowed
|
||||
return True
|
||||
|
||||
|
||||
def _check_credentials(enforcer_scope, action, target, credentials):
|
||||
is_valid = True
|
||||
if not enforcer_scope.enforce(action, target, credentials):
|
||||
# to match service implementations, if a rule is not found,
|
||||
# use the default rule for that service policy
|
||||
#
|
||||
# waiting to make the check because the first call to
|
||||
# enforce loads the rules
|
||||
if action not in enforcer_scope.rules:
|
||||
if not enforcer_scope.enforce('default', target, credentials):
|
||||
is_valid = False
|
||||
else:
|
||||
is_valid = False
|
||||
return is_valid
|
||||
|
||||
|
||||
def _user_to_credentials(user):
|
||||
if not hasattr(user, "_credentials"):
|
||||
roles = [role['name'] for role in user.roles]
|
||||
user._credentials = {'user_id': user.id,
|
||||
'token': user.token,
|
||||
'username': user.username,
|
||||
'project_id': user.project_id,
|
||||
'tenant_id': user.project_id,
|
||||
'project_name': user.project_name,
|
||||
'domain_id': user.user_domain_id,
|
||||
'is_admin': user.is_superuser,
|
||||
'roles': roles}
|
||||
return user._credentials
|
||||
|
||||
|
||||
def _domain_to_credentials(request, user):
|
||||
if not hasattr(user, "_domain_credentials"):
|
||||
try:
|
||||
domain_auth_ref = request.session.get('domain_token')
|
||||
|
||||
# no domain role or not running on V3
|
||||
if not domain_auth_ref:
|
||||
return None
|
||||
domain_user = auth_user.create_user_from_token(
|
||||
request, auth_user.Token(domain_auth_ref),
|
||||
domain_auth_ref.service_catalog.url_for(interface=None))
|
||||
user._domain_credentials = _user_to_credentials(domain_user)
|
||||
|
||||
# uses the domain_id associated with the domain_user
|
||||
user._domain_credentials['domain_id'] = domain_user.domain_id
|
||||
|
||||
except Exception:
|
||||
LOG.warning("Failed to create user from domain scoped token.")
|
||||
return None
|
||||
return user._domain_credentials
|
@ -1,146 +0,0 @@
|
||||
{
|
||||
"admin_required": "role:admin or is_admin:1",
|
||||
"service_role": "role:service",
|
||||
"service_or_admin": "rule:admin_required or rule:service_role",
|
||||
"owner" : "user_id:%(user_id)s",
|
||||
"admin_or_owner": "rule:admin_required or rule:owner",
|
||||
|
||||
"default": "rule:admin_required",
|
||||
|
||||
"identity:get_region": "",
|
||||
"identity:list_regions": "",
|
||||
"identity:create_region": "rule:admin_required",
|
||||
"identity:update_region": "rule:admin_required",
|
||||
"identity:delete_region": "rule:admin_required",
|
||||
|
||||
"identity:get_service": "rule:admin_required",
|
||||
"identity:list_services": "rule:admin_required",
|
||||
"identity:create_service": "rule:admin_required",
|
||||
"identity:update_service": "rule:admin_required",
|
||||
"identity:delete_service": "rule:admin_required",
|
||||
|
||||
"identity:get_endpoint": "rule:admin_required",
|
||||
"identity:list_endpoints": "rule:admin_required",
|
||||
"identity:create_endpoint": "rule:admin_required",
|
||||
"identity:update_endpoint": "rule:admin_required",
|
||||
"identity:delete_endpoint": "rule:admin_required",
|
||||
|
||||
"identity:get_catalog": "",
|
||||
|
||||
"identity:get_domain": "rule:admin_required",
|
||||
"identity:list_domains": "rule:admin_required",
|
||||
"identity:create_domain": "rule:admin_required",
|
||||
"identity:update_domain": "rule:admin_required",
|
||||
"identity:delete_domain": "rule:admin_required",
|
||||
|
||||
"identity:get_project": "rule:admin_required",
|
||||
"identity:list_projects": "rule:admin_required",
|
||||
"identity:list_user_projects": "rule:admin_or_owner",
|
||||
"identity:create_project": "rule:admin_required",
|
||||
"identity:update_project": "rule:admin_required",
|
||||
"identity:delete_project": "rule:admin_required",
|
||||
|
||||
"identity:get_user": "rule:admin_required",
|
||||
"identity:list_users": "rule:admin_required",
|
||||
"identity:create_user": "rule:admin_required",
|
||||
"identity:update_user": "rule:admin_required",
|
||||
"identity:delete_user": "rule:admin_required",
|
||||
"identity:change_password": "rule:admin_or_owner",
|
||||
|
||||
"identity:get_group": "rule:admin_required",
|
||||
"identity:list_groups": "rule:admin_required",
|
||||
"identity:list_groups_for_user": "rule:admin_or_owner",
|
||||
"identity:create_group": "rule:admin_required",
|
||||
"identity:update_group": "rule:admin_required",
|
||||
"identity:delete_group": "rule:admin_required",
|
||||
"identity:list_users_in_group": "rule:admin_required",
|
||||
"identity:remove_user_from_group": "rule:admin_required",
|
||||
"identity:check_user_in_group": "rule:admin_required",
|
||||
"identity:add_user_to_group": "rule:admin_required",
|
||||
|
||||
"identity:get_credential": "rule:admin_required",
|
||||
"identity:list_credentials": "rule:admin_required",
|
||||
"identity:create_credential": "rule:admin_required",
|
||||
"identity:update_credential": "rule:admin_required",
|
||||
"identity:delete_credential": "rule:admin_required",
|
||||
|
||||
"identity:ec2_get_credential": "rule:admin_or_owner",
|
||||
"identity:ec2_list_credentials": "rule:admin_or_owner",
|
||||
"identity:ec2_create_credential": "rule:admin_or_owner",
|
||||
"identity:ec2_delete_credential": "rule:admin_required or (rule:owner and user_id:%(target.credential.user_id)s)",
|
||||
|
||||
"identity:get_role": "rule:admin_required",
|
||||
"identity:list_roles": "rule:admin_required",
|
||||
"identity:create_role": "rule:admin_required",
|
||||
"identity:update_role": "rule:admin_required",
|
||||
"identity:delete_role": "rule:admin_required",
|
||||
|
||||
"identity:check_grant": "rule:admin_required",
|
||||
"identity:list_grants": "rule:admin_required",
|
||||
"identity:create_grant": "rule:admin_required",
|
||||
"identity:revoke_grant": "rule:admin_required",
|
||||
|
||||
"identity:list_role_assignments": "rule:admin_required",
|
||||
|
||||
"identity:get_policy": "rule:admin_required",
|
||||
"identity:list_policies": "rule:admin_required",
|
||||
"identity:create_policy": "rule:admin_required",
|
||||
"identity:update_policy": "rule:admin_required",
|
||||
"identity:delete_policy": "rule:admin_required",
|
||||
|
||||
"identity:check_token": "rule:admin_required",
|
||||
"identity:validate_token": "rule:service_or_admin",
|
||||
"identity:validate_token_head": "rule:service_or_admin",
|
||||
"identity:revocation_list": "rule:service_or_admin",
|
||||
"identity:revoke_token": "rule:admin_or_owner",
|
||||
|
||||
"identity:create_trust": "user_id:%(trust.trustor_user_id)s",
|
||||
"identity:get_trust": "rule:admin_or_owner",
|
||||
"identity:list_trusts": "",
|
||||
"identity:list_roles_for_trust": "",
|
||||
"identity:check_role_for_trust": "",
|
||||
"identity:get_role_for_trust": "",
|
||||
"identity:delete_trust": "",
|
||||
|
||||
"identity:create_consumer": "rule:admin_required",
|
||||
"identity:get_consumer": "rule:admin_required",
|
||||
"identity:list_consumers": "rule:admin_required",
|
||||
"identity:delete_consumer": "rule:admin_required",
|
||||
"identity:update_consumer": "rule:admin_required",
|
||||
|
||||
"identity:authorize_request_token": "rule:admin_required",
|
||||
"identity:list_access_token_roles": "rule:admin_required",
|
||||
"identity:get_access_token_role": "rule:admin_required",
|
||||
"identity:list_access_tokens": "rule:admin_required",
|
||||
"identity:get_access_token": "rule:admin_required",
|
||||
"identity:delete_access_token": "rule:admin_required",
|
||||
|
||||
"identity:list_projects_for_endpoint": "rule:admin_required",
|
||||
"identity:add_endpoint_to_project": "rule:admin_required",
|
||||
"identity:check_endpoint_in_project": "rule:admin_required",
|
||||
"identity:list_endpoints_for_project": "rule:admin_required",
|
||||
"identity:remove_endpoint_from_project": "rule:admin_required",
|
||||
|
||||
"identity:create_identity_provider": "rule:admin_required",
|
||||
"identity:list_identity_providers": "rule:admin_required",
|
||||
"identity:get_identity_providers": "rule:admin_required",
|
||||
"identity:update_identity_provider": "rule:admin_required",
|
||||
"identity:delete_identity_provider": "rule:admin_required",
|
||||
|
||||
"identity:create_protocol": "rule:admin_required",
|
||||
"identity:update_protocol": "rule:admin_required",
|
||||
"identity:get_protocol": "rule:admin_required",
|
||||
"identity:list_protocols": "rule:admin_required",
|
||||
"identity:delete_protocol": "rule:admin_required",
|
||||
|
||||
"identity:create_mapping": "rule:admin_required",
|
||||
"identity:get_mapping": "rule:admin_required",
|
||||
"identity:list_mappings": "rule:admin_required",
|
||||
"identity:delete_mapping": "rule:admin_required",
|
||||
"identity:update_mapping": "rule:admin_required",
|
||||
|
||||
"identity:list_projects_for_groups": "",
|
||||
"identity:list_domains_for_groups": "",
|
||||
|
||||
"identity:list_revoke_events": ""
|
||||
}
|
@ -1,274 +0,0 @@
|
||||
{
|
||||
"context_is_admin": "role:admin",
|
||||
"admin_or_owner": "is_admin:True or project_id:%(project_id)s",
|
||||
"default": "rule:admin_or_owner",
|
||||
|
||||
"cells_scheduler_filter:TargetCellFilter": "is_admin:True",
|
||||
|
||||
"compute:create": "",
|
||||
"compute:create:attach_network": "",
|
||||
"compute:create:attach_volume": "",
|
||||
"compute:create:forced_host": "is_admin:True",
|
||||
"compute:delete": "rule:default",
|
||||
"compute:get_all": "",
|
||||
"compute:get_all_tenants": "",
|
||||
"compute:reboot": "rule:default",
|
||||
"compute:rebuild": "rule:default",
|
||||
"compute:snapshot": "rule:default",
|
||||
"compute:start": "rule:default",
|
||||
"compute:stop": "rule:default",
|
||||
"compute:unlock_override": "rule:admin_api",
|
||||
"compute:attach_volume" : "rule:default",
|
||||
"compute:detach_volume" : "rule:default",
|
||||
"compute:update": "rule:default",
|
||||
|
||||
"compute:resize": "rule:default",
|
||||
"compute:confirm_resize": "rule:default",
|
||||
"compute:revert_resize": "rule:default",
|
||||
|
||||
"compute:shelve": "",
|
||||
"compute:shelve_offload": "",
|
||||
"compute:unshelve": "",
|
||||
|
||||
"admin_api": "is_admin:True",
|
||||
"compute_extension:accounts": "rule:admin_api",
|
||||
"compute_extension:admin_actions": "rule:admin_api",
|
||||
"compute_extension:admin_actions:pause": "rule:admin_or_owner",
|
||||
"compute_extension:admin_actions:unpause": "rule:admin_or_owner",
|
||||
"compute_extension:admin_actions:suspend": "rule:admin_or_owner",
|
||||
"compute_extension:admin_actions:resume": "rule:admin_or_owner",
|
||||
"compute_extension:admin_actions:lock": "rule:admin_or_owner",
|
||||
"compute_extension:admin_actions:unlock": "rule:admin_or_owner",
|
||||
"compute_extension:admin_actions:resetNetwork": "rule:admin_api",
|
||||
"compute_extension:admin_actions:injectNetworkInfo": "rule:admin_api",
|
||||
"compute_extension:admin_actions:createBackup": "rule:admin_or_owner",
|
||||
"compute_extension:admin_actions:migrateLive": "rule:admin_api",
|
||||
"compute_extension:admin_actions:resetState": "rule:admin_api",
|
||||
"compute_extension:admin_actions:migrate": "rule:admin_api",
|
||||
"compute_extension:v3:os-admin-actions": "rule:admin_api",
|
||||
"compute_extension:v3:os-admin-actions:pause": "rule:admin_or_owner",
|
||||
"compute_extension:v3:os-admin-actions:unpause": "rule:admin_or_owner",
|
||||
"compute_extension:v3:os-admin-actions:suspend": "rule:admin_or_owner",
|
||||
"compute_extension:v3:os-admin-actions:resume": "rule:admin_or_owner",
|
||||
"compute_extension:v3:os-admin-actions:lock": "rule:admin_or_owner",
|
||||
"compute_extension:v3:os-admin-actions:unlock": "rule:admin_or_owner",
|
||||
"compute_extension:v3:os-admin-actions:reset_network": "rule:admin_api",
|
||||
"compute_extension:v3:os-admin-actions:inject_network_info": "rule:admin_api",
|
||||
"compute_extension:v3:os-admin-actions:create_backup": "rule:admin_or_owner",
|
||||
"compute_extension:v3:os-admin-actions:migrate_live": "rule:admin_api",
|
||||
"compute_extension:v3:os-admin-actions:reset_state": "rule:admin_api",
|
||||
"compute_extension:v3:os-admin-actions:migrate": "rule:admin_api",
|
||||
"compute_extension:v3:os-admin-password": "",
|
||||
"compute_extension:aggregates": "rule:admin_api",
|
||||
"compute_extension:v3:os-aggregates": "rule:admin_api",
|
||||
"compute_extension:agents": "rule:admin_api",
|
||||
"compute_extension:v3:os-agents": "rule:admin_api",
|
||||
"compute_extension:attach_interfaces": "",
|
||||
"compute_extension:v3:os-attach-interfaces": "",
|
||||
"compute_extension:baremetal_nodes": "rule:admin_api",
|
||||
"compute_extension:v3:os-baremetal-nodes": "rule:admin_api",
|
||||
"compute_extension:cells": "rule:admin_api",
|
||||
"compute_extension:v3:os-cells": "rule:admin_api",
|
||||
"compute_extension:certificates": "",
|
||||
"compute_extension:v3:os-certificates": "",
|
||||
"compute_extension:cloudpipe": "rule:admin_api",
|
||||
"compute_extension:cloudpipe_update": "rule:admin_api",
|
||||
"compute_extension:console_output": "",
|
||||
"compute_extension:v3:consoles:discoverable": "",
|
||||
"compute_extension:v3:os-console-output": "",
|
||||
"compute_extension:consoles": "",
|
||||
"compute_extension:v3:os-remote-consoles": "",
|
||||
"compute_extension:coverage_ext": "rule:admin_api",
|
||||
"compute_extension:v3:os-coverage": "rule:admin_api",
|
||||
"compute_extension:createserverext": "",
|
||||
"compute_extension:deferred_delete": "",
|
||||
"compute_extension:v3:os-deferred-delete": "",
|
||||
"compute_extension:disk_config": "",
|
||||
"compute_extension:evacuate": "rule:admin_api",
|
||||
"compute_extension:v3:os-evacuate": "rule:admin_api",
|
||||
"compute_extension:extended_server_attributes": "rule:admin_api",
|
||||
"compute_extension:v3:os-extended-server-attributes": "rule:admin_api",
|
||||
"compute_extension:extended_status": "",
|
||||
"compute_extension:v3:os-extended-status": "",
|
||||
"compute_extension:extended_availability_zone": "",
|
||||
"compute_extension:v3:os-extended-availability-zone": "",
|
||||
"compute_extension:extended_ips": "",
|
||||
"compute_extension:extended_ips_mac": "",
|
||||
"compute_extension:extended_vif_net": "",
|
||||
"compute_extension:v3:extension_info:discoverable": "",
|
||||
"compute_extension:extended_volumes": "",
|
||||
"compute_extension:v3:os-extended-volumes": "",
|
||||
"compute_extension:v3:os-extended-volumes:attach": "",
|
||||
"compute_extension:v3:os-extended-volumes:detach": "",
|
||||
"compute_extension:fixed_ips": "rule:admin_api",
|
||||
"compute_extension:v3:os-fixed-ips:discoverable": "",
|
||||
"compute_extension:v3:os-fixed-ips": "rule:admin_api",
|
||||
"compute_extension:flavor_access": "",
|
||||
"compute_extension:v3:os-flavor-access": "",
|
||||
"compute_extension:flavor_disabled": "",
|
||||
"compute_extension:v3:os-flavor-disabled": "",
|
||||
"compute_extension:flavor_rxtx": "",
|
||||
"compute_extension:v3:os-flavor-rxtx": "",
|
||||
"compute_extension:flavor_swap": "",
|
||||
"compute_extension:flavorextradata": "",
|
||||
"compute_extension:flavorextraspecs:index": "",
|
||||
"compute_extension:flavorextraspecs:show": "",
|
||||
"compute_extension:flavorextraspecs:create": "rule:admin_api",
|
||||
"compute_extension:flavorextraspecs:update": "rule:admin_api",
|
||||
"compute_extension:flavorextraspecs:delete": "rule:admin_api",
|
||||
"compute_extension:v3:flavor-extra-specs:index": "",
|
||||
"compute_extension:v3:flavor-extra-specs:show": "",
|
||||
"compute_extension:v3:flavor-extra-specs:create": "rule:admin_api",
|
||||
"compute_extension:v3:flavor-extra-specs:update": "rule:admin_api",
|
||||
"compute_extension:v3:flavor-extra-specs:delete": "rule:admin_api",
|
||||
"compute_extension:flavormanage": "rule:admin_api",
|
||||
"compute_extension:floating_ip_dns": "",
|
||||
"compute_extension:floating_ip_pools": "",
|
||||
"compute_extension:floating_ips": "",
|
||||
"compute_extension:floating_ips_bulk": "rule:admin_api",
|
||||
"compute_extension:fping": "",
|
||||
"compute_extension:fping:all_tenants": "rule:admin_api",
|
||||
"compute_extension:hide_server_addresses": "is_admin:False",
|
||||
"compute_extension:v3:os-hide-server-addresses": "is_admin:False",
|
||||
"compute_extension:hosts": "rule:admin_api",
|
||||
"compute_extension:v3:os-hosts": "rule:admin_api",
|
||||
"compute_extension:hypervisors": "rule:admin_api",
|
||||
"compute_extension:v3:os-hypervisors": "rule:admin_api",
|
||||
"compute_extension:image_size": "",
|
||||
"compute_extension:v3:os-image-metadata": "",
|
||||
"compute_extension:v3:os-images": "",
|
||||
"compute_extension:instance_actions": "",
|
||||
"compute_extension:v3:os-instance-actions": "",
|
||||
"compute_extension:instance_actions:events": "rule:admin_api",
|
||||
"compute_extension:v3:os-instance-actions:events": "rule:admin_api",
|
||||
"compute_extension:instance_usage_audit_log": "rule:admin_api",
|
||||
"compute_extension:v3:os-instance-usage-audit-log": "rule:admin_api",
|
||||
"compute_extension:v3:ips:discoverable": "",
|
||||
"compute_extension:keypairs": "",
|
||||
"compute_extension:keypairs:index": "",
|
||||
"compute_extension:keypairs:show": "",
|
||||
"compute_extension:keypairs:create": "",
|
||||
"compute_extension:keypairs:delete": "",
|
||||
"compute_extension:v3:os-keypairs:discoverable": "",
|
||||
"compute_extension:v3:os-keypairs": "",
|
||||
"compute_extension:v3:os-keypairs:index": "",
|
||||
"compute_extension:v3:os-keypairs:show": "",
|
||||
"compute_extension:v3:os-keypairs:create": "",
|
||||
"compute_extension:v3:os-keypairs:delete": "",
|
||||
"compute_extension:multinic": "",
|
||||
"compute_extension:v3:os-multinic": "",
|
||||
"compute_extension:networks": "rule:admin_api",
|
||||
"compute_extension:networks:view": "",
|
||||
"compute_extension:networks_associate": "rule:admin_api",
|
||||
"compute_extension:quotas:show": "",
|
||||
"compute_extension:quotas:update": "rule:admin_api",
|
||||
"compute_extension:quotas:delete": "rule:admin_api",
|
||||
"compute_extension:v3:os-quota-sets:show": "",
|
||||
"compute_extension:v3:os-quota-sets:update": "rule:admin_api",
|
||||
"compute_extension:v3:os-quota-sets:delete": "rule:admin_api",
|
||||
"compute_extension:quota_classes": "",
|
||||
"compute_extension:v3:os-quota-class-sets": "",
|
||||
"compute_extension:rescue": "",
|
||||
"compute_extension:v3:os-rescue": "",
|
||||
"compute_extension:security_group_default_rules": "rule:admin_api",
|
||||
"compute_extension:security_groups": "",
|
||||
"compute_extension:v3:os-security-groups": "",
|
||||
"compute_extension:server_diagnostics": "rule:admin_api",
|
||||
"compute_extension:v3:os-server-diagnostics": "rule:admin_api",
|
||||
"compute_extension:server_password": "",
|
||||
"compute_extension:v3:os-server-password": "",
|
||||
"compute_extension:server_usage": "",
|
||||
"compute_extension:v3:os-server-usage": "",
|
||||
"compute_extension:services": "rule:admin_api",
|
||||
"compute_extension:v3:os-services": "rule:admin_api",
|
||||
"compute_extension:v3:servers:discoverable": "",
|
||||
"compute_extension:shelve": "",
|
||||
"compute_extension:shelveOffload": "rule:admin_api",
|
||||
"compute_extension:v3:os-shelve:shelve": "",
|
||||
"compute_extension:v3:os-shelve:shelve_offload": "rule:admin_api",
|
||||
"compute_extension:simple_tenant_usage:show": "rule:admin_or_owner",
|
||||
"compute_extension:v3:os-simple-tenant-usage:show": "rule:admin_or_owner",
|
||||
"compute_extension:simple_tenant_usage:list": "rule:admin_api",
|
||||
"compute_extension:v3:os-simple-tenant-usage:list": "rule:admin_api",
|
||||
"compute_extension:unshelve": "",
|
||||
"compute_extension:v3:os-shelve:unshelve": "",
|
||||
"compute_extension:users": "rule:admin_api",
|
||||
"compute_extension:virtual_interfaces": "",
|
||||
"compute_extension:virtual_storage_arrays": "",
|
||||
"compute_extension:volumes": "",
|
||||
"compute_extension:volume_attachments:index": "",
|
||||
"compute_extension:volume_attachments:show": "",
|
||||
"compute_extension:volume_attachments:create": "",
|
||||
"compute_extension:volume_attachments:update": "",
|
||||
"compute_extension:volume_attachments:delete": "",
|
||||
"compute_extension:volumetypes": "",
|
||||
"compute_extension:availability_zone:list": "",
|
||||
"compute_extension:v3:os-availability-zone:list": "",
|
||||
"compute_extension:availability_zone:detail": "rule:admin_api",
|
||||
"compute_extension:v3:os-availability-zone:detail": "rule:admin_api",
|
||||
"compute_extension:used_limits_for_admin": "rule:admin_api",
|
||||
"compute_extension:v3:os-used-limits": "",
|
||||
"compute_extension:v3:os-used-limits:tenant": "rule:admin_api",
|
||||
"compute_extension:migrations:index": "rule:admin_api",
|
||||
"compute_extension:v3:os-migrations:index": "rule:admin_api",
|
||||
|
||||
|
||||
"volume:create": "",
|
||||
"volume:get_all": "",
|
||||
"volume:get_volume_metadata": "",
|
||||
"volume:get_snapshot": "",
|
||||
"volume:get_all_snapshots": "",
|
||||
|
||||
|
||||
"volume_extension:types_manage": "rule:admin_api",
|
||||
"volume_extension:types_extra_specs": "rule:admin_api",
|
||||
"volume_extension:volume_admin_actions:reset_status": "rule:admin_api",
|
||||
"volume_extension:snapshot_admin_actions:reset_status": "rule:admin_api",
|
||||
"volume_extension:volume_admin_actions:force_delete": "rule:admin_api",
|
||||
|
||||
|
||||
"network:get_all": "",
|
||||
"network:get": "",
|
||||
"network:create": "",
|
||||
"network:delete": "",
|
||||
"network:associate": "",
|
||||
"network:disassociate": "",
|
||||
"network:get_vifs_by_instance": "",
|
||||
"network:allocate_for_instance": "",
|
||||
"network:deallocate_for_instance": "",
|
||||
"network:validate_networks": "",
|
||||
"network:get_instance_uuids_by_ip_filter": "",
|
||||
"network:get_instance_id_by_floating_address": "",
|
||||
"network:setup_networks_on_host": "",
|
||||
"network:get_backdoor_port": "",
|
||||
|
||||
"network:get_floating_ip": "",
|
||||
"network:get_floating_ip_pools": "",
|
||||
"network:get_floating_ip_by_address": "",
|
||||
"network:get_floating_ips_by_project": "",
|
||||
"network:get_floating_ips_by_fixed_address": "",
|
||||
"network:allocate_floating_ip": "",
|
||||
"network:deallocate_floating_ip": "",
|
||||
"network:associate_floating_ip": "",
|
||||
"network:disassociate_floating_ip": "",
|
||||
"network:release_floating_ip": "",
|
||||
"network:migrate_instance_start": "",
|
||||
"network:migrate_instance_finish": "",
|
||||
|
||||
"network:get_fixed_ip": "",
|
||||
"network:get_fixed_ip_by_address": "",
|
||||
"network:add_fixed_ip_to_instance": "",
|
||||
"network:remove_fixed_ip_from_instance": "",
|
||||
"network:add_network_to_project": "",
|
||||
"network:get_instance_nw_info": "",
|
||||
|
||||
"network:get_dns_domains": "",
|
||||
"network:add_dns_entry": "",
|
||||
"network:modify_dns_entry": "",
|
||||
"network:delete_dns_entry": "",
|
||||
"network:get_dns_entries_by_address": "",
|
||||
"network:get_dns_entries_by_name": "",
|
||||
"network:create_private_dns_domain": "",
|
||||
"network:create_public_dns_domain": "",
|
||||
"network:delete_dns_domain": ""
|
||||
}
|
@ -1,195 +0,0 @@
|
||||
{
|
||||
"admin_required": "role:admin",
|
||||
"cloud_admin": "rule:admin_required and domain_id:admin_domain_id",
|
||||
"service_role": "role:service",
|
||||
"service_or_admin": "rule:admin_required or rule:service_role",
|
||||
"owner" : "user_id:%(user_id)s or user_id:%(target.token.user_id)s",
|
||||
"admin_or_owner": "(rule:admin_required and domain_id:%(target.token.user.domain.id)s) or rule:owner",
|
||||
"admin_or_cloud_admin": "rule:admin_required or rule:cloud_admin",
|
||||
"admin_and_matching_domain_id": "rule:admin_required and domain_id:%(domain_id)s",
|
||||
"service_admin_or_owner": "rule:service_or_admin or rule:owner",
|
||||
|
||||
"default": "rule:admin_required",
|
||||
|
||||
"identity:get_region": "",
|
||||
"identity:list_regions": "",
|
||||
"identity:create_region": "rule:cloud_admin",
|
||||
"identity:update_region": "rule:cloud_admin",
|
||||
"identity:delete_region": "rule:cloud_admin",
|
||||
|
||||
"identity:get_service": "rule:admin_or_cloud_admin",
|
||||
"identity:list_services": "rule:admin_or_cloud_admin",
|
||||
"identity:create_service": "rule:cloud_admin",
|
||||
"identity:update_service": "rule:cloud_admin",
|
||||
"identity:delete_service": "rule:cloud_admin",
|
||||
|
||||
"identity:get_endpoint": "rule:admin_or_cloud_admin",
|
||||
"identity:list_endpoints": "rule:admin_or_cloud_admin",
|
||||
"identity:create_endpoint": "rule:cloud_admin",
|
||||
"identity:update_endpoint": "rule:cloud_admin",
|
||||
"identity:delete_endpoint": "rule:cloud_admin",
|
||||
|
||||
"identity:get_domain": "rule:cloud_admin or rule:admin_and_matching_domain_id",
|
||||
"identity:list_domains": "rule:cloud_admin",
|
||||
"identity:create_domain": "rule:cloud_admin",
|
||||
"identity:update_domain": "rule:cloud_admin",
|
||||
"identity:delete_domain": "rule:cloud_admin",
|
||||
|
||||
"admin_and_matching_target_project_domain_id": "rule:admin_required and domain_id:%(target.project.domain_id)s",
|
||||
"admin_and_matching_project_domain_id": "rule:admin_required and domain_id:%(project.domain_id)s",
|
||||
"identity:get_project": "rule:cloud_admin or rule:admin_and_matching_target_project_domain_id",
|
||||
"identity:list_projects": "rule:cloud_admin or rule:admin_and_matching_domain_id",
|
||||
"identity:list_user_projects": "rule:owner or rule:admin_and_matching_domain_id",
|
||||
"identity:create_project": "rule:cloud_admin or rule:admin_and_matching_project_domain_id",
|
||||
"identity:update_project": "rule:cloud_admin or rule:admin_and_matching_target_project_domain_id",
|
||||
"identity:delete_project": "rule:cloud_admin or rule:admin_and_matching_target_project_domain_id",
|
||||
|
||||
"admin_and_matching_target_user_domain_id": "rule:admin_required and domain_id:%(target.user.domain_id)s",
|
||||
"admin_and_matching_user_domain_id": "rule:admin_required and domain_id:%(user.domain_id)s",
|
||||
"identity:get_user": "rule:cloud_admin or rule:admin_and_matching_target_user_domain_id",
|
||||
"identity:list_users": "rule:cloud_admin or rule:admin_and_matching_domain_id",
|
||||
"identity:create_user": "rule:cloud_admin or rule:admin_and_matching_user_domain_id",
|
||||
"identity:update_user": "rule:cloud_admin or rule:admin_and_matching_target_user_domain_id",
|
||||
"identity:delete_user": "rule:cloud_admin or rule:admin_and_matching_target_user_domain_id",
|
||||
|
||||
"admin_and_matching_target_group_domain_id": "rule:admin_required and domain_id:%(target.group.domain_id)s",
|
||||
"admin_and_matching_group_domain_id": "rule:admin_required and domain_id:%(group.domain_id)s",
|
||||
"identity:get_group": "rule:cloud_admin or rule:admin_and_matching_target_group_domain_id",
|
||||
"identity:list_groups": "rule:cloud_admin or rule:admin_and_matching_domain_id",
|
||||
"identity:list_groups_for_user": "rule:owner or rule:admin_and_matching_domain_id",
|
||||
"identity:create_group": "rule:cloud_admin or rule:admin_and_matching_group_domain_id",
|
||||
"identity:update_group": "rule:cloud_admin or rule:admin_and_matching_target_group_domain_id",
|
||||
"identity:delete_group": "rule:cloud_admin or rule:admin_and_matching_target_group_domain_id",
|
||||
"identity:list_users_in_group": "rule:cloud_admin or rule:admin_and_matching_target_group_domain_id",
|
||||
"identity:remove_user_from_group": "rule:cloud_admin or rule:admin_and_matching_target_group_domain_id",
|
||||
"identity:check_user_in_group": "rule:cloud_admin or rule:admin_and_matching_target_group_domain_id",
|
||||
"identity:add_user_to_group": "rule:cloud_admin or rule:admin_and_matching_target_group_domain_id",
|
||||
|
||||
"identity:get_credential": "rule:admin_required",
|
||||
"identity:list_credentials": "rule:admin_required or user_id:%(user_id)s",
|
||||
"identity:create_credential": "rule:admin_required",
|
||||
"identity:update_credential": "rule:admin_required",
|
||||
"identity:delete_credential": "rule:admin_required",
|
||||
|
||||
"identity:ec2_get_credential": "rule:admin_or_cloud_admin or (rule:owner and user_id:%(target.credential.user_id)s)",
|
||||
"identity:ec2_list_credentials": "rule:admin_or_cloud_admin or rule:owner",
|
||||
"identity:ec2_create_credential": "rule:admin_or_cloud_admin or rule:owner",
|
||||
"identity:ec2_delete_credential": "rule:admin_or_cloud_admin or (rule:owner and user_id:%(target.credential.user_id)s)",
|
||||
|
||||
"identity:get_role": "rule:admin_or_cloud_admin",
|
||||
"identity:list_roles": "rule:admin_or_cloud_admin",
|
||||
"identity:create_role": "rule:cloud_admin",
|
||||
"identity:update_role": "rule:cloud_admin",
|
||||
"identity:delete_role": "rule:cloud_admin",
|
||||
|
||||
"domain_admin_for_grants": "rule:admin_required and (domain_id:%(domain_id)s or domain_id:%(target.project.domain_id)s)",
|
||||
"project_admin_for_grants": "rule:admin_required and project_id:%(project_id)s",
|
||||
"identity:check_grant": "rule:cloud_admin or rule:domain_admin_for_grants or rule:project_admin_for_grants",
|
||||
"identity:list_grants": "rule:cloud_admin or rule:domain_admin_for_grants or rule:project_admin_for_grants",
|
||||
"identity:create_grant": "rule:cloud_admin or rule:domain_admin_for_grants or rule:project_admin_for_grants",
|
||||
"identity:revoke_grant": "rule:cloud_admin or rule:domain_admin_for_grants or rule:project_admin_for_grants",
|
||||
|
||||
"admin_on_domain_filter" : "rule:admin_required and domain_id:%(scope.domain.id)s",
|
||||
"admin_on_project_filter" : "rule:admin_required and project_id:%(scope.project.id)s",
|
||||
"identity:list_role_assignments": "rule:cloud_admin or rule:admin_on_domain_filter or rule:admin_on_project_filter",
|
||||
|
||||
"identity:get_policy": "rule:cloud_admin",
|
||||
"identity:list_policies": "rule:cloud_admin",
|
||||
"identity:create_policy": "rule:cloud_admin",
|
||||
"identity:update_policy": "rule:cloud_admin",
|
||||
"identity:delete_policy": "rule:cloud_admin",
|
||||
|
||||
"identity:change_password": "rule:owner",
|
||||
"identity:check_token": "rule:admin_or_owner",
|
||||
"identity:validate_token": "rule:service_admin_or_owner",
|
||||
"identity:validate_token_head": "rule:service_or_admin",
|
||||
"identity:revocation_list": "rule:service_or_admin",
|
||||
"identity:revoke_token": "rule:admin_or_owner",
|
||||
|
||||
"identity:create_trust": "user_id:%(trust.trustor_user_id)s",
|
||||
"identity:list_trusts": "",
|
||||
"identity:list_roles_for_trust": "",
|
||||
"identity:get_role_for_trust": "",
|
||||
"identity:delete_trust": "",
|
||||
|
||||
"identity:create_consumer": "rule:admin_required",
|
||||
"identity:get_consumer": "rule:admin_required",
|
||||
"identity:list_consumers": "rule:admin_required",
|
||||
"identity:delete_consumer": "rule:admin_required",
|
||||
"identity:update_consumer": "rule:admin_required",
|
||||
|
||||
"identity:authorize_request_token": "rule:admin_required",
|
||||
"identity:list_access_token_roles": "rule:admin_required",
|
||||
"identity:get_access_token_role": "rule:admin_required",
|
||||
"identity:list_access_tokens": "rule:admin_required",
|
||||
"identity:get_access_token": "rule:admin_required",
|
||||
"identity:delete_access_token": "rule:admin_required",
|
||||
|
||||
"identity:list_projects_for_endpoint": "rule:admin_required",
|
||||
"identity:add_endpoint_to_project": "rule:admin_required",
|
||||
"identity:check_endpoint_in_project": "rule:admin_required",
|
||||
"identity:list_endpoints_for_project": "rule:admin_required",
|
||||
"identity:remove_endpoint_from_project": "rule:admin_required",
|
||||
|
||||
"identity:create_endpoint_group": "rule:admin_required",
|
||||
"identity:list_endpoint_groups": "rule:admin_required",
|
||||
"identity:get_endpoint_group": "rule:admin_required",
|
||||
"identity:update_endpoint_group": "rule:admin_required",
|
||||
"identity:delete_endpoint_group": "rule:admin_required",
|
||||
"identity:list_projects_associated_with_endpoint_group": "rule:admin_required",
|
||||
"identity:list_endpoints_associated_with_endpoint_group": "rule:admin_required",
|
||||
"identity:get_endpoint_group_in_project": "rule:admin_required",
|
||||
"identity:list_endpoint_groups_for_project": "rule:admin_required",
|
||||
"identity:add_endpoint_group_to_project": "rule:admin_required",
|
||||
"identity:remove_endpoint_group_from_project": "rule:admin_required",
|
||||
|
||||
"identity:create_identity_provider": "rule:cloud_admin",
|
||||
"identity:list_identity_providers": "rule:cloud_admin",
|
||||
"identity:get_identity_providers": "rule:cloud_admin",
|
||||
"identity:update_identity_provider": "rule:cloud_admin",
|
||||
"identity:delete_identity_provider": "rule:cloud_admin",
|
||||
|
||||
"identity:create_protocol": "rule:cloud_admin",
|
||||
"identity:update_protocol": "rule:cloud_admin",
|
||||
"identity:get_protocol": "rule:cloud_admin",
|
||||
"identity:list_protocols": "rule:cloud_admin",
|
||||
"identity:delete_protocol": "rule:cloud_admin",
|
||||
|
||||
"identity:create_mapping": "rule:cloud_admin",
|
||||
"identity:get_mapping": "rule:cloud_admin",
|
||||
"identity:list_mappings": "rule:cloud_admin",
|
||||
"identity:delete_mapping": "rule:cloud_admin",
|
||||
"identity:update_mapping": "rule:cloud_admin",
|
||||
|
||||
"identity:create_service_provider": "rule:cloud_admin",
|
||||
"identity:list_service_providers": "rule:cloud_admin",
|
||||
"identity:get_service_provider": "rule:cloud_admin",
|
||||
"identity:update_service_provider": "rule:cloud_admin",
|
||||
"identity:delete_service_provider": "rule:cloud_admin",
|
||||
|
||||
"identity:get_auth_catalog": "",
|
||||
"identity:get_auth_projects": "",
|
||||
"identity:get_auth_domains": "",
|
||||
|
||||
"identity:list_projects_for_groups": "",
|
||||
"identity:list_domains_for_groups": "",
|
||||
|
||||
"identity:list_revoke_events": "",
|
||||
|
||||
"identity:create_policy_association_for_endpoint": "rule:cloud_admin",
|
||||
"identity:check_policy_association_for_endpoint": "rule:cloud_admin",
|
||||
"identity:delete_policy_association_for_endpoint": "rule:cloud_admin",
|
||||
"identity:create_policy_association_for_service": "rule:cloud_admin",
|
||||
"identity:check_policy_association_for_service": "rule:cloud_admin",
|
||||
"identity:delete_policy_association_for_service": "rule:cloud_admin",
|
||||
"identity:create_policy_association_for_region_and_service": "rule:cloud_admin",
|
||||
"identity:check_policy_association_for_region_and_service": "rule:cloud_admin",
|
||||
"identity:delete_policy_association_for_region_and_service": "rule:cloud_admin",
|
||||
"identity:get_policy_for_endpoint": "rule:cloud_admin",
|
||||
"identity:list_endpoints_for_policy": "rule:cloud_admin",
|
||||
|
||||
"identity:create_domain_config": "rule:cloud_admin",
|
||||
"identity:get_domain_config": "rule:cloud_admin",
|
||||
"identity:update_domain_config": "rule:cloud_admin",
|
||||
"identity:delete_domain_config": "rule:cloud_admin"
|
||||
}
|
@ -1,143 +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 django.utils import datetime_safe
|
||||
from keystoneauth1.access import access
|
||||
from keystoneauth1.access import service_catalog
|
||||
from keystoneclient.v2_0 import roles
|
||||
from keystoneclient.v2_0 import tenants
|
||||
from keystoneclient.v2_0 import users
|
||||
|
||||
|
||||
class TestDataContainer(object):
|
||||
"""Arbitrary holder for test data in an object-oriented fashion."""
|
||||
pass
|
||||
|
||||
|
||||
def generate_test_data():
|
||||
'''Builds a set of test_data data as returned by Keystone V2.'''
|
||||
test_data = TestDataContainer()
|
||||
|
||||
keystone_service = {
|
||||
'type': 'identity',
|
||||
'name': 'keystone',
|
||||
'endpoints_links': [],
|
||||
'endpoints': [
|
||||
{
|
||||
'region': 'RegionOne',
|
||||
'adminURL': 'http://admin.localhost:35357/v2.0',
|
||||
'internalURL': 'http://internal.localhost:5000/v2.0',
|
||||
'publicURL': 'http://public.localhost:5000/v2.0'
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
# Users
|
||||
user_dict = {'id': uuid.uuid4().hex,
|
||||
'name': 'gabriel',
|
||||
'email': 'gabriel@example.com',
|
||||
'password': 'swordfish',
|
||||
'token': '',
|
||||
'enabled': True}
|
||||
test_data.user = users.User(None, user_dict, loaded=True)
|
||||
|
||||
# Tenants
|
||||
tenant_dict_1 = {'id': uuid.uuid4().hex,
|
||||
'name': 'tenant_one',
|
||||
'description': '',
|
||||
'enabled': True}
|
||||
tenant_dict_2 = {'id': uuid.uuid4().hex,
|
||||
'name': 'tenant_two',
|
||||
'description': '',
|
||||
'enabled': False}
|
||||
test_data.tenant_one = tenants.Tenant(None, tenant_dict_1, loaded=True)
|
||||
test_data.tenant_two = tenants.Tenant(None, tenant_dict_2, loaded=True)
|
||||
|
||||
nova_service = {
|
||||
'type': 'compute',
|
||||
'name': 'nova',
|
||||
'endpoint_links': [],
|
||||
'endpoints': [
|
||||
{
|
||||
'region': 'RegionOne',
|
||||
'adminURL': ('http://nova-admin.localhost:8774/v2.0/%s'
|
||||
% (tenant_dict_1['id'])),
|
||||
'internalURL': ('http://nova-internal.localhost:8774/v2.0/%s'
|
||||
% (tenant_dict_1['id'])),
|
||||
'publicURL': ('http://nova-public.localhost:8774/v2.0/%s'
|
||||
% (tenant_dict_1['id']))
|
||||
},
|
||||
{
|
||||
'region': 'RegionTwo',
|
||||
'adminURL': ('http://nova2-admin.localhost:8774/v2.0/%s'
|
||||
% (tenant_dict_1['id'])),
|
||||
'internalURL': ('http://nova2-internal.localhost:8774/v2.0/%s'
|
||||
% (tenant_dict_1['id'])),
|
||||
'publicURL': ('http://nova2-public.localhost:8774/v2.0/%s'
|
||||
% (tenant_dict_1['id']))
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
# Roles
|
||||
role_dict = {'id': uuid.uuid4().hex,
|
||||
'name': 'Member'}
|
||||
test_data.role = roles.Role(roles.RoleManager, role_dict)
|
||||
|
||||
# Tokens
|
||||
tomorrow = datetime_safe.datetime.now() + datetime.timedelta(days=1)
|
||||
expiration = datetime_safe.datetime.isoformat(tomorrow)
|
||||
|
||||
scoped_token_dict = {
|
||||
'access': {
|
||||
'token': {
|
||||
'id': uuid.uuid4().hex,
|
||||
'expires': expiration,
|
||||
'tenant': tenant_dict_1,
|
||||
'tenants': [tenant_dict_1, tenant_dict_2]},
|
||||
'user': {
|
||||
'id': user_dict['id'],
|
||||
'name': user_dict['name'],
|
||||
'roles': [role_dict]},
|
||||
'serviceCatalog': [keystone_service, nova_service]
|
||||
}
|
||||
}
|
||||
|
||||
test_data.scoped_access_info = access.create(
|
||||
resp=None,
|
||||
body=scoped_token_dict)
|
||||
|
||||
unscoped_token_dict = {
|
||||
'access': {
|
||||
'token': {
|
||||
'id': uuid.uuid4().hex,
|
||||
'expires': expiration},
|
||||
'user': {
|
||||
'id': user_dict['id'],
|
||||
'name': user_dict['name'],
|
||||
'roles': [role_dict]},
|
||||
'serviceCatalog': [keystone_service]
|
||||
}
|
||||
}
|
||||
test_data.unscoped_access_info = access.create(
|
||||
resp=None,
|
||||
body=unscoped_token_dict)
|
||||
|
||||
# Service Catalog
|
||||
test_data.service_catalog = service_catalog.ServiceCatalogV2(
|
||||
[keystone_service, nova_service])
|
||||
|
||||
return test_data
|
@ -1,361 +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 django.utils import datetime_safe
|
||||
from keystoneauth1.access import access
|
||||
from keystoneauth1.access import service_catalog
|
||||
from keystoneclient.common import cms
|
||||
from keystoneclient.v3 import domains
|
||||
from keystoneclient.v3 import projects
|
||||
from keystoneclient.v3 import roles
|
||||
from keystoneclient.v3 import users
|
||||
import requests
|
||||
|
||||
|
||||
class TestDataContainer(object):
|
||||
"""Arbitrary holder for test data in an object-oriented fashion."""
|
||||
pass
|
||||
|
||||
|
||||
class TestResponse(requests.Response):
|
||||
"""Class used to wrap requests.Response.
|
||||
|
||||
It also provides some convenience to initialize with a dict.
|
||||
"""
|
||||
|
||||
def __init__(self, data):
|
||||
self._text = None
|
||||
super(TestResponse, self).__init__()
|
||||
if isinstance(data, dict):
|
||||
self.status_code = data.get('status_code', 200)
|
||||
self.headers = data.get('headers', None)
|
||||
# Fake the text attribute to streamline Response creation
|
||||
self._text = data.get('text', None)
|
||||
else:
|
||||
self.status_code = data
|
||||
|
||||
def __eq__(self, other):
|
||||
return self.__dict__ == other.__dict__
|
||||
|
||||
@property
|
||||
def text(self):
|
||||
return self._text
|
||||
|
||||
|
||||
def generate_test_data(pki=False, service_providers=False,
|
||||
endpoint='localhost'):
|
||||
'''Builds a set of test_data data as returned by Keystone V2.'''
|
||||
test_data = TestDataContainer()
|
||||
|
||||
keystone_service = {
|
||||
'type': 'identity',
|
||||
'id': uuid.uuid4().hex,
|
||||
'endpoints': [
|
||||
{
|
||||
'url': 'http://admin.%s:35357/v3' % endpoint,
|
||||
'region': 'RegionOne',
|
||||
'interface': 'admin',
|
||||
'id': uuid.uuid4().hex,
|
||||
},
|
||||
{
|
||||
'url': 'http://internal.%s:5000/v3' % endpoint,
|
||||
'region': 'RegionOne',
|
||||
'interface': 'internal',
|
||||
'id': uuid.uuid4().hex
|
||||
},
|
||||
{
|
||||
'url': 'http://public.%s:5000/v3' % endpoint,
|
||||
'region': 'RegionOne',
|
||||
'interface': 'public',
|
||||
'id': uuid.uuid4().hex
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
# Domains
|
||||
domain_dict = {'id': uuid.uuid4().hex,
|
||||
'name': 'domain',
|
||||
'description': '',
|
||||
'enabled': True}
|
||||
test_data.domain = domains.Domain(domains.DomainManager(None),
|
||||
domain_dict, loaded=True)
|
||||
|
||||
# Users
|
||||
user_dict = {'id': uuid.uuid4().hex,
|
||||
'name': 'gabriel',
|
||||
'email': 'gabriel@example.com',
|
||||
'password': 'swordfish',
|
||||
'domain_id': domain_dict['id'],
|
||||
'token': '',
|
||||
'enabled': True}
|
||||
test_data.user = users.User(users.UserManager(None),
|
||||
user_dict, loaded=True)
|
||||
|
||||
# Projects
|
||||
project_dict_1 = {'id': uuid.uuid4().hex,
|
||||
'name': 'tenant_one',
|
||||
'description': '',
|
||||
'domain_id': domain_dict['id'],
|
||||
'enabled': True}
|
||||
project_dict_2 = {'id': uuid.uuid4().hex,
|
||||
'name': 'tenant_two',
|
||||
'description': '',
|
||||
'domain_id': domain_dict['id'],
|
||||
'enabled': False}
|
||||
test_data.project_one = projects.Project(projects.ProjectManager(None),
|
||||
project_dict_1,
|
||||
loaded=True)
|
||||
test_data.project_two = projects.Project(projects.ProjectManager(None),
|
||||
project_dict_2,
|
||||
loaded=True)
|
||||
|
||||
# Roles
|
||||
role_dict = {'id': uuid.uuid4().hex,
|
||||
'name': 'Member'}
|
||||
test_data.role = roles.Role(roles.RoleManager, role_dict)
|
||||
|
||||
nova_service = {
|
||||
'type': 'compute',
|
||||
'id': uuid.uuid4().hex,
|
||||
'endpoints': [
|
||||
{
|
||||
'url': ('http://nova-admin.%s:8774/v2.0/%s'
|
||||
% (endpoint, project_dict_1['id'])),
|
||||
'region': 'RegionOne',
|
||||
'interface': 'admin',
|
||||
'id': uuid.uuid4().hex,
|
||||
},
|
||||
{
|
||||
'url': ('http://nova-internal.%s:8774/v2.0/%s'
|
||||
% (endpoint, project_dict_1['id'])),
|
||||
'region': 'RegionOne',
|
||||
'interface': 'internal',
|
||||
'id': uuid.uuid4().hex
|
||||
},
|
||||
{
|
||||
'url': ('http://nova-public.%s:8774/v2.0/%s'
|
||||
% (endpoint, project_dict_1['id'])),
|
||||
'region': 'RegionOne',
|
||||
'interface': 'public',
|
||||
'id': uuid.uuid4().hex
|
||||
},
|
||||
{
|
||||
'url': ('http://nova2-admin.%s:8774/v2.0/%s'
|
||||
% (endpoint, project_dict_1['id'])),
|
||||
'region': 'RegionTwo',
|
||||
'interface': 'admin',
|
||||
'id': uuid.uuid4().hex,
|
||||
},
|
||||
{
|
||||
'url': ('http://nova2-internal.%s:8774/v2.0/%s'
|
||||
% (endpoint, project_dict_1['id'])),
|
||||
'region': 'RegionTwo',
|
||||
'interface': 'internal',
|
||||
'id': uuid.uuid4().hex
|
||||
},
|
||||
{
|
||||
'url': ('http://nova2-public.%s:8774/v2.0/%s'
|
||||
% (endpoint, project_dict_1['id'])),
|
||||
'region': 'RegionTwo',
|
||||
'interface': 'public',
|
||||
'id': uuid.uuid4().hex
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
# Tokens
|
||||
tomorrow = datetime_safe.datetime.now() + datetime.timedelta(days=1)
|
||||
expiration = datetime_safe.datetime.isoformat(tomorrow)
|
||||
if pki:
|
||||
# We don't need a real PKI token, but just the prefix to make the
|
||||
# keystone client treat it as a PKI token
|
||||
auth_token = cms.PKI_ASN1_PREFIX + uuid.uuid4().hex
|
||||
else:
|
||||
auth_token = uuid.uuid4().hex
|
||||
|
||||
auth_response_headers = {
|
||||
'X-Subject-Token': auth_token
|
||||
}
|
||||
|
||||
auth_response = TestResponse({
|
||||
"headers": auth_response_headers
|
||||
})
|
||||
|
||||
scoped_token_dict = {
|
||||
'token': {
|
||||
'methods': ['password'],
|
||||
'expires_at': expiration,
|
||||
'project': {
|
||||
'id': project_dict_1['id'],
|
||||
'name': project_dict_1['name'],
|
||||
'domain': {
|
||||
'id': domain_dict['id'],
|
||||
'name': domain_dict['name']
|
||||
}
|
||||
},
|
||||
'user': {
|
||||
'id': user_dict['id'],
|
||||
'name': user_dict['name'],
|
||||
'domain': {
|
||||
'id': domain_dict['id'],
|
||||
'name': domain_dict['name']
|
||||
}
|
||||
},
|
||||
'roles': [role_dict],
|
||||
'catalog': [keystone_service, nova_service]
|
||||
}
|
||||
}
|
||||
|
||||
sp_list = None
|
||||
if service_providers:
|
||||
test_data.sp_auth_url = 'http://service_provider_endp:5000/v3'
|
||||
test_data.service_provider_id = 'k2kserviceprovider'
|
||||
# The access info for the identity provider
|
||||
# should return a list of service providers
|
||||
sp_list = [
|
||||
{'auth_url': test_data.sp_auth_url,
|
||||
'id': test_data.service_provider_id,
|
||||
'sp_url': 'https://k2kserviceprovider/sp_url'}
|
||||
]
|
||||
scoped_token_dict['token']['service_providers'] = sp_list
|
||||
|
||||
test_data.scoped_access_info = access.create(
|
||||
resp=auth_response,
|
||||
body=scoped_token_dict
|
||||
)
|
||||
|
||||
domain_token_dict = {
|
||||
'token': {
|
||||
'methods': ['password'],
|
||||
'expires_at': expiration,
|
||||
'domain': {
|
||||
'id': domain_dict['id'],
|
||||
'name': domain_dict['name'],
|
||||
},
|
||||
'user': {
|
||||
'id': user_dict['id'],
|
||||
'name': user_dict['name'],
|
||||
'domain': {
|
||||
'id': domain_dict['id'],
|
||||
'name': domain_dict['name']
|
||||
}
|
||||
},
|
||||
'roles': [role_dict],
|
||||
'catalog': [keystone_service, nova_service]
|
||||
}
|
||||
}
|
||||
test_data.domain_scoped_access_info = access.create(
|
||||
resp=auth_response,
|
||||
body=domain_token_dict
|
||||
)
|
||||
|
||||
unscoped_token_dict = {
|
||||
'token': {
|
||||
'methods': ['password'],
|
||||
'expires_at': expiration,
|
||||
'user': {
|
||||
'id': user_dict['id'],
|
||||
'name': user_dict['name'],
|
||||
'domain': {
|
||||
'id': domain_dict['id'],
|
||||
'name': domain_dict['name']
|
||||
}
|
||||
},
|
||||
'catalog': [keystone_service]
|
||||
}
|
||||
}
|
||||
|
||||
if service_providers:
|
||||
unscoped_token_dict['token']['service_providers'] = sp_list
|
||||
|
||||
test_data.unscoped_access_info = access.create(
|
||||
resp=auth_response,
|
||||
body=unscoped_token_dict
|
||||
)
|
||||
|
||||
# Service Catalog
|
||||
test_data.service_catalog = service_catalog.ServiceCatalogV3(
|
||||
[keystone_service, nova_service])
|
||||
|
||||
# federated user
|
||||
federated_scoped_token_dict = {
|
||||
'token': {
|
||||
'methods': ['password'],
|
||||
'expires_at': expiration,
|
||||
'project': {
|
||||
'id': project_dict_1['id'],
|
||||
'name': project_dict_1['name'],
|
||||
'domain': {
|
||||
'id': domain_dict['id'],
|
||||
'name': domain_dict['name']
|
||||
}
|
||||
},
|
||||
'user': {
|
||||
'id': user_dict['id'],
|
||||
'name': user_dict['name'],
|
||||
'domain': {
|
||||
'id': domain_dict['id'],
|
||||
'name': domain_dict['name']
|
||||
},
|
||||
'OS-FEDERATION': {
|
||||
'identity_provider': 'ACME',
|
||||
'protocol': 'OIDC',
|
||||
'groups': [
|
||||
{'id': uuid.uuid4().hex},
|
||||
{'id': uuid.uuid4().hex}
|
||||
]
|
||||
}
|
||||
},
|
||||
'roles': [role_dict],
|
||||
'catalog': [keystone_service, nova_service]
|
||||
}
|
||||
}
|
||||
|
||||
test_data.federated_scoped_access_info = access.create(
|
||||
resp=auth_response,
|
||||
body=federated_scoped_token_dict
|
||||
)
|
||||
|
||||
federated_unscoped_token_dict = {
|
||||
'token': {
|
||||
'methods': ['password'],
|
||||
'expires_at': expiration,
|
||||
'user': {
|
||||
'id': user_dict['id'],
|
||||
'name': user_dict['name'],
|
||||
'domain': {
|
||||
'id': domain_dict['id'],
|
||||
'name': domain_dict['name']
|
||||
},
|
||||
'OS-FEDERATION': {
|
||||
'identity_provider': 'ACME',
|
||||
'protocol': 'OIDC',
|
||||
'groups': [
|
||||
{'id': uuid.uuid4().hex},
|
||||
{'id': uuid.uuid4().hex}
|
||||
]
|
||||
}
|
||||
},
|
||||
'catalog': [keystone_service]
|
||||
}
|
||||
}
|
||||
|
||||
test_data.federated_unscoped_access_info = access.create(
|
||||
resp=auth_response,
|
||||
body=federated_unscoped_token_dict
|
||||
)
|
||||
|
||||
return test_data
|
@ -1,42 +0,0 @@
|
||||
#!/usr/bin/env 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 django
|
||||
from django.test.runner import DiscoverRunner as test_runner
|
||||
import os
|
||||
import sys
|
||||
|
||||
|
||||
os.environ['DJANGO_SETTINGS_MODULE'] = 'openstack_auth.tests.settings'
|
||||
|
||||
if hasattr(django, 'setup'):
|
||||
django.setup()
|
||||
|
||||
|
||||
def run(*test_args):
|
||||
if not test_args:
|
||||
test_args = ['tests']
|
||||
parent = os.path.join(
|
||||
os.path.dirname(os.path.abspath(__file__)),
|
||||
"..",
|
||||
"..",
|
||||
)
|
||||
sys.path.insert(0, parent)
|
||||
failures = test_runner().run_tests(test_args)
|
||||
sys.exit(failures)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
run(*sys.argv[1:])
|
@ -1,76 +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
|
||||
|
||||
ALLOWED_HOSTS = ['*']
|
||||
|
||||
DATABASES = {'default': {'ENGINE': 'django.db.backends.sqlite3'}}
|
||||
|
||||
INSTALLED_APPS = [
|
||||
'django',
|
||||
'django.contrib.contenttypes',
|
||||
'django.contrib.auth',
|
||||
'django.contrib.sessions',
|
||||
'django.contrib.messages',
|
||||
'openstack_auth',
|
||||
'openstack_auth.tests'
|
||||
]
|
||||
|
||||
MIDDLEWARE_CLASSES = [
|
||||
'django.middleware.common.CommonMiddleware',
|
||||
'django.middleware.csrf.CsrfViewMiddleware',
|
||||
'django.contrib.sessions.middleware.SessionMiddleware',
|
||||
'django.contrib.auth.middleware.AuthenticationMiddleware',
|
||||
'django.contrib.messages.middleware.MessageMiddleware'
|
||||
]
|
||||
|
||||
AUTHENTICATION_BACKENDS = ['openstack_auth.backend.KeystoneBackend']
|
||||
|
||||
OPENSTACK_KEYSTONE_URL = "http://localhost:5000/v3"
|
||||
|
||||
ROOT_URLCONF = 'openstack_auth.tests.urls'
|
||||
|
||||
LOGIN_REDIRECT_URL = '/'
|
||||
|
||||
SECRET_KEY = 'badcafe'
|
||||
|
||||
OPENSTACK_API_VERSIONS = {
|
||||
"identity": 3
|
||||
}
|
||||
|
||||
USE_TZ = True
|
||||
|
||||
OPENSTACK_KEYSTONE_MULTIDOMAIN_SUPPORT = False
|
||||
|
||||
OPENSTACK_KEYSTONE_DEFAULT_DOMAIN = 'domain'
|
||||
|
||||
# NOTE(saschpe): The openstack_auth.user.Token object isn't
|
||||
# JSON-serializable ATM
|
||||
SESSION_SERIALIZER = 'django.contrib.sessions.serializers.PickleSerializer'
|
||||
|
||||
TEST_DIR = os.path.dirname(os.path.abspath(__file__))
|
||||
POLICY_FILES_PATH = os.path.join(TEST_DIR, "conf")
|
||||
POLICY_FILES = {
|
||||
'identity': 'keystone_policy.json',
|
||||
'compute': 'nova_policy.json'
|
||||
}
|
||||
|
||||
TEMPLATES = [
|
||||
{
|
||||
'BACKEND': 'django.template.backends.django.DjangoTemplates',
|
||||
'APP_DIRS': True,
|
||||
},
|
||||
]
|
||||
|
||||
AUTH_USER_MODEL = 'openstack_auth.User'
|
@ -1,11 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Login</title>
|
||||
</head>
|
||||
<body>
|
||||
<form action="." method="POST">{{ csrf_token }}
|
||||
{{ form.as_p }}
|
||||
</form>
|
||||
</body>
|
||||
</html>
|
File diff suppressed because it is too large
Load Diff
@ -1,29 +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 django.conf.urls import include
|
||||
from django.conf.urls import url
|
||||
from django.views import generic
|
||||
|
||||
from openstack_auth import utils
|
||||
from openstack_auth import views
|
||||
|
||||
|
||||
utils.patch_middleware_get_user()
|
||||
|
||||
|
||||
urlpatterns = [
|
||||
url(r"", include('openstack_auth.urls')),
|
||||
url(r"^websso/$", views.websso, name='websso'),
|
||||
url(r"^$", generic.TemplateView.as_view(template_name="auth/blank.html"))
|
||||
]
|
@ -1,36 +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 django.conf.urls import url
|
||||
|
||||
from openstack_auth import utils
|
||||
from openstack_auth import views
|
||||
|
||||
utils.patch_middleware_get_user()
|
||||
|
||||
|
||||
urlpatterns = [
|
||||
url(r"^login/$", views.login, name='login'),
|
||||
url(r"^logout/$", views.logout, name='logout'),
|
||||
url(r'^switch/(?P<tenant_id>[^/]+)/$', views.switch,
|
||||
name='switch_tenants'),
|
||||
url(r'^switch_services_region/(?P<region_name>[^/]+)/$',
|
||||
views.switch_region,
|
||||
name='switch_services_region'),
|
||||
url(r'^switch_keystone_provider/(?P<keystone_provider>[^/]+)/$',
|
||||
views.switch_keystone_provider,
|
||||
name='switch_keystone_provider')
|
||||
]
|
||||
|
||||
if utils.is_websso_enabled():
|
||||
urlpatterns.append(url(r"^websso/$", views.websso, name='websso'))
|
@ -1,454 +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 hashlib
|
||||
import logging
|
||||
|
||||
import django
|
||||
from django.conf import settings
|
||||
from django.contrib.auth import models
|
||||
from django.db import models as db_models
|
||||
from django.utils import deprecation
|
||||
from keystoneauth1 import exceptions as keystone_exceptions
|
||||
from keystoneclient.common import cms as keystone_cms
|
||||
import six
|
||||
|
||||
from openstack_auth import utils
|
||||
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
_TOKEN_HASH_ENABLED = getattr(settings, 'OPENSTACK_TOKEN_HASH_ENABLED', True)
|
||||
|
||||
|
||||
def set_session_from_user(request, user):
|
||||
request.session['token'] = user.token
|
||||
request.session['user_id'] = user.id
|
||||
request.session['region_endpoint'] = user.endpoint
|
||||
request.session['services_region'] = user.services_region
|
||||
# Update the user object cached in the request
|
||||
request._cached_user = user
|
||||
request.user = user
|
||||
|
||||
|
||||
def create_user_from_token(request, token, endpoint, services_region=None):
|
||||
# if the region is provided, use that, otherwise use the preferred region
|
||||
default_service_regions = getattr(settings, 'DEFAULT_SERVICE_REGIONS', {})
|
||||
default_service_region = default_service_regions.get(endpoint)
|
||||
svc_region = services_region or \
|
||||
utils.default_services_region(token.serviceCatalog, request,
|
||||
selected_region=default_service_region)
|
||||
return User(id=token.user['id'],
|
||||
token=token,
|
||||
user=token.user['name'],
|
||||
password_expires_at=token.user['password_expires_at'],
|
||||
user_domain_id=token.user_domain_id,
|
||||
# We need to consider already logged-in users with an old
|
||||
# version of Token without user_domain_name.
|
||||
user_domain_name=getattr(token, 'user_domain_name', None),
|
||||
project_id=token.project['id'],
|
||||
project_name=token.project['name'],
|
||||
domain_id=token.domain['id'],
|
||||
domain_name=token.domain['name'],
|
||||
enabled=True,
|
||||
service_catalog=token.serviceCatalog,
|
||||
roles=token.roles,
|
||||
endpoint=endpoint,
|
||||
services_region=svc_region,
|
||||
is_federated=getattr(token, 'is_federated', False),
|
||||
unscoped_token=getattr(token, 'unscoped_token',
|
||||
request.session.get('unscoped_token')))
|
||||
|
||||
|
||||
class Token(object):
|
||||
"""Encapsulates the AccessInfo object from keystoneclient.
|
||||
|
||||
Token object provides a consistent interface for accessing the keystone
|
||||
token information and service catalog.
|
||||
|
||||
Added for maintaining backward compatibility with horizon that expects
|
||||
Token object in the user object.
|
||||
"""
|
||||
def __init__(self, auth_ref, unscoped_token=None):
|
||||
# User-related attributes
|
||||
user = {'id': auth_ref.user_id, 'name': auth_ref.username}
|
||||
data = getattr(auth_ref, '_data', {})
|
||||
expiration_date = data.get('token', {}).get('user', {})\
|
||||
.get('password_expires_at')
|
||||
user['password_expires_at'] = expiration_date
|
||||
self.user = user
|
||||
self.user_domain_id = auth_ref.user_domain_id
|
||||
self.user_domain_name = auth_ref.user_domain_name
|
||||
|
||||
# Token-related attributes
|
||||
self.id = auth_ref.auth_token
|
||||
self.unscoped_token = unscoped_token
|
||||
if _TOKEN_HASH_ENABLED and self._is_pki_token(self.id):
|
||||
algorithm = getattr(settings, 'OPENSTACK_TOKEN_HASH_ALGORITHM',
|
||||
'md5')
|
||||
hasher = hashlib.new(algorithm)
|
||||
hasher.update(self.id.encode('utf-8'))
|
||||
self.id = hasher.hexdigest()
|
||||
# Only hash unscoped token if needed
|
||||
if self._is_pki_token(self.unscoped_token):
|
||||
hasher = hashlib.new(algorithm)
|
||||
hasher.update(self.unscoped_token.encode('utf-8'))
|
||||
self.unscoped_token = hasher.hexdigest()
|
||||
self.expires = auth_ref.expires
|
||||
|
||||
# Project-related attributes
|
||||
project = {}
|
||||
project['id'] = auth_ref.project_id
|
||||
project['name'] = auth_ref.project_name
|
||||
project['is_admin_project'] = getattr(auth_ref, 'is_admin_project',
|
||||
False)
|
||||
project['domain_id'] = getattr(auth_ref, 'project_domain_id', None)
|
||||
self.project = project
|
||||
self.tenant = self.project
|
||||
|
||||
# Domain-related attributes
|
||||
domain = {}
|
||||
domain['id'] = auth_ref.domain_id
|
||||
domain['name'] = auth_ref.domain_name
|
||||
self.domain = domain
|
||||
|
||||
# Federation-related attributes
|
||||
self.is_federated = auth_ref.is_federated
|
||||
self.roles = [{'name': role} for role in auth_ref.role_names]
|
||||
self.serviceCatalog = auth_ref.service_catalog.catalog
|
||||
|
||||
def _is_pki_token(self, token):
|
||||
"""Determines if this is a pki-based token (pki or pkiz)"""
|
||||
if token is None:
|
||||
return False
|
||||
return (keystone_cms.is_ans1_token(token)
|
||||
or keystone_cms.is_pkiz(token))
|
||||
|
||||
|
||||
class User(models.AbstractBaseUser, models.AnonymousUser):
|
||||
"""A User class with some extra special sauce for Keystone.
|
||||
|
||||
In addition to the standard Django user attributes, this class also has
|
||||
the following:
|
||||
|
||||
.. attribute:: token
|
||||
|
||||
The Keystone token object associated with the current user/tenant.
|
||||
|
||||
The token object is deprecated, user auth_ref instead.
|
||||
|
||||
.. attribute:: tenant_id
|
||||
|
||||
The id of the Keystone tenant for the current user/token.
|
||||
|
||||
The tenant_id keyword argument is deprecated, use project_id instead.
|
||||
|
||||
.. attribute:: tenant_name
|
||||
|
||||
The name of the Keystone tenant for the current user/token.
|
||||
|
||||
The tenant_name keyword argument is deprecated, use project_name
|
||||
instead.
|
||||
|
||||
.. attribute:: project_id
|
||||
|
||||
The id of the Keystone project for the current user/token.
|
||||
|
||||
.. attribute:: project_name
|
||||
|
||||
The name of the Keystone project for the current user/token.
|
||||
|
||||
.. attribute:: service_catalog
|
||||
|
||||
The ``ServiceCatalog`` data returned by Keystone.
|
||||
|
||||
.. attribute:: roles
|
||||
|
||||
A list of dictionaries containing role names and ids as returned
|
||||
by Keystone.
|
||||
|
||||
.. attribute:: services_region
|
||||
|
||||
A list of non-identity service endpoint regions extracted from the
|
||||
service catalog.
|
||||
|
||||
.. attribute:: user_domain_id
|
||||
|
||||
The domain id of the current user.
|
||||
|
||||
.. attribute:: user_domain_name
|
||||
|
||||
The domain name of the current user.
|
||||
|
||||
.. attribute:: domain_id
|
||||
|
||||
The id of the Keystone domain scoped for the current user/token.
|
||||
|
||||
.. attribute:: is_federated
|
||||
|
||||
Whether user is federated Keystone user. (Boolean)
|
||||
|
||||
.. attribute:: unscoped_token
|
||||
|
||||
Unscoped Keystone token.
|
||||
|
||||
.. attribute:: password_expires_at
|
||||
|
||||
Password expiration date. This attribute could be None when using
|
||||
keystone version < 3.0 or if the feature is not enabled in keystone.
|
||||
|
||||
"""
|
||||
|
||||
keystone_user_id = db_models.CharField(primary_key=True, max_length=255)
|
||||
USERNAME_FIELD = 'keystone_user_id'
|
||||
|
||||
def __init__(self, id=None, token=None, user=None, tenant_id=None,
|
||||
service_catalog=None, tenant_name=None, roles=None,
|
||||
authorized_tenants=None, endpoint=None, enabled=False,
|
||||
services_region=None, user_domain_id=None,
|
||||
user_domain_name=None, domain_id=None, domain_name=None,
|
||||
project_id=None, project_name=None, is_federated=False,
|
||||
unscoped_token=None, password=None, password_expires_at=None):
|
||||
self.id = id
|
||||
self.pk = id
|
||||
self.token = token
|
||||
self.keystone_user_id = id
|
||||
self.username = user
|
||||
self.user_domain_id = user_domain_id
|
||||
self.user_domain_name = user_domain_name
|
||||
self.domain_id = domain_id
|
||||
self.domain_name = domain_name
|
||||
self.project_id = project_id or tenant_id
|
||||
self.project_name = project_name or tenant_name
|
||||
self.service_catalog = service_catalog
|
||||
self._services_region = (
|
||||
services_region
|
||||
or utils.default_services_region(service_catalog)
|
||||
)
|
||||
self.roles = roles or []
|
||||
self.endpoint = endpoint
|
||||
self.enabled = enabled
|
||||
self._authorized_tenants = authorized_tenants
|
||||
self.is_federated = is_federated
|
||||
self.password_expires_at = password_expires_at
|
||||
|
||||
# Unscoped token is used for listing user's project that works
|
||||
# for both federated and keystone user.
|
||||
self.unscoped_token = unscoped_token
|
||||
|
||||
# List of variables to be deprecated.
|
||||
self.tenant_id = self.project_id
|
||||
self.tenant_name = self.project_name
|
||||
|
||||
# Required by AbstractBaseUser
|
||||
self.password = None
|
||||
|
||||
def __unicode__(self):
|
||||
return self.username
|
||||
|
||||
def __repr__(self):
|
||||
return "<%s: %s>" % (self.__class__.__name__, self.username)
|
||||
|
||||
def is_token_expired(self, margin=None):
|
||||
"""Determine if the token is expired.
|
||||
|
||||
:returns:
|
||||
``True`` if the token is expired, ``False`` if not, and
|
||||
``None`` if there is no token set.
|
||||
|
||||
:param margin:
|
||||
A security time margin in seconds before real expiration.
|
||||
Will return ``True`` if the token expires in less than ``margin``
|
||||
seconds of time.
|
||||
A default margin can be set by the TOKEN_TIMEOUT_MARGIN in the
|
||||
django settings.
|
||||
|
||||
"""
|
||||
if self.token is None:
|
||||
return None
|
||||
return not utils.is_token_valid(self.token, margin)
|
||||
|
||||
if django.VERSION >= (1, 10):
|
||||
@property
|
||||
def is_authenticated(self):
|
||||
"""Checks for a valid authentication."""
|
||||
if (self.token is not None and utils.is_token_valid(self.token)):
|
||||
return deprecation.CallableTrue
|
||||
else:
|
||||
return deprecation.CallableFalse
|
||||
|
||||
@property
|
||||
def is_anonymous(self):
|
||||
"""Return if the user is not authenticated.
|
||||
|
||||
:returns: ``True`` if not authenticated,``False`` otherwise.
|
||||
"""
|
||||
return deprecation.CallableBool(not self.is_authenticated)
|
||||
else:
|
||||
def is_authenticated(self, margin=None):
|
||||
"""Checks for a valid authentication.
|
||||
|
||||
:param margin:
|
||||
A security time margin in seconds before end of authentication.
|
||||
Will return ``False`` if authentication ends in less than
|
||||
``margin`` seconds of time.
|
||||
A default margin can be set by the TOKEN_TIMEOUT_MARGIN in the
|
||||
django settings.
|
||||
"""
|
||||
return (self.token is not None and
|
||||
utils.is_token_valid(self.token, margin))
|
||||
|
||||
def is_anonymous(self, margin=None):
|
||||
"""Return if the user is not authenticated.
|
||||
|
||||
:returns: ``True`` if not authenticated,``False`` otherwise.
|
||||
|
||||
:param margin:
|
||||
A security time margin in seconds before end of an eventual
|
||||
authentication.
|
||||
Will return ``True`` even if authenticated but that
|
||||
authentication ends in less than ``margin`` seconds of time.
|
||||
A default margin can be set by the TOKEN_TIMEOUT_MARGIN in the
|
||||
django settings.
|
||||
"""
|
||||
return not self.is_authenticated(margin)
|
||||
|
||||
@property
|
||||
def is_active(self):
|
||||
return self.enabled
|
||||
|
||||
@property
|
||||
def is_superuser(self):
|
||||
"""Evaluates whether this user has admin privileges.
|
||||
|
||||
:returns: ``True`` or ``False``.
|
||||
"""
|
||||
admin_roles = utils.get_admin_roles()
|
||||
user_roles = {role['name'].lower() for role in self.roles}
|
||||
return not admin_roles.isdisjoint(user_roles)
|
||||
|
||||
@property
|
||||
def authorized_tenants(self):
|
||||
"""Returns a memoized list of tenants this user may access."""
|
||||
if self.is_authenticated() and self._authorized_tenants is None:
|
||||
endpoint = self.endpoint
|
||||
try:
|
||||
self._authorized_tenants = utils.get_project_list(
|
||||
user_id=self.id,
|
||||
auth_url=endpoint,
|
||||
token=self.unscoped_token,
|
||||
is_federated=self.is_federated)
|
||||
except (keystone_exceptions.ClientException,
|
||||
keystone_exceptions.AuthorizationFailure):
|
||||
LOG.exception('Unable to retrieve project list.')
|
||||
return self._authorized_tenants or []
|
||||
|
||||
@authorized_tenants.setter
|
||||
def authorized_tenants(self, tenant_list):
|
||||
self._authorized_tenants = tenant_list
|
||||
|
||||
@property
|
||||
def services_region(self):
|
||||
return self._services_region
|
||||
|
||||
@services_region.setter
|
||||
def services_region(self, region):
|
||||
self._services_region = region
|
||||
|
||||
@property
|
||||
def available_services_regions(self):
|
||||
"""Returns list of unique region name values in service catalog."""
|
||||
regions = []
|
||||
if self.service_catalog:
|
||||
for service in self.service_catalog:
|
||||
service_type = service.get('type')
|
||||
if service_type is None or service_type == 'identity':
|
||||
continue
|
||||
for endpoint in service.get('endpoints', []):
|
||||
region = utils.get_endpoint_region(endpoint)
|
||||
if region not in regions:
|
||||
regions.append(region)
|
||||
return regions
|
||||
|
||||
def save(*args, **kwargs):
|
||||
# Presume we can't write to Keystone.
|
||||
pass
|
||||
|
||||
def delete(*args, **kwargs):
|
||||
# Presume we can't write to Keystone.
|
||||
pass
|
||||
|
||||
# Check for OR'd permission rules, check that user has one of the
|
||||
# required permission.
|
||||
def has_a_matching_perm(self, perm_list, obj=None):
|
||||
"""Returns True if the user has one of the specified permissions.
|
||||
|
||||
If object is passed, it checks if the user has any of the required
|
||||
perms for this object.
|
||||
"""
|
||||
# If there are no permissions to check, just return true
|
||||
if not perm_list:
|
||||
return True
|
||||
# Check that user has at least one of the required permissions.
|
||||
for perm in perm_list:
|
||||
if self.has_perm(perm, obj):
|
||||
return True
|
||||
return False
|
||||
|
||||
# Override the default has_perms method. Allowing for more
|
||||
# complex combinations of permissions. Will check for logical AND of
|
||||
# all top level permissions. Will use logical OR for all first level
|
||||
# tuples (check that use has one permissions in the tuple)
|
||||
#
|
||||
# Examples:
|
||||
# Checks for all required permissions
|
||||
# ('openstack.roles.admin', 'openstack.roles.L3-support')
|
||||
#
|
||||
# Checks for admin AND (L2 or L3)
|
||||
# ('openstack.roles.admin', ('openstack.roles.L3-support',
|
||||
# 'openstack.roles.L2-support'),)
|
||||
def has_perms(self, perm_list, obj=None):
|
||||
"""Returns True if the user has all of the specified permissions.
|
||||
|
||||
Tuples in the list will possess the required permissions if
|
||||
the user has a permissions matching one of the elements of
|
||||
that tuple
|
||||
"""
|
||||
# If there are no permissions to check, just return true
|
||||
if not perm_list:
|
||||
return True
|
||||
for perm in perm_list:
|
||||
if isinstance(perm, six.string_types):
|
||||
# check that the permission matches
|
||||
if not self.has_perm(perm, obj):
|
||||
return False
|
||||
else:
|
||||
# check that a permission in the tuple matches
|
||||
if not self.has_a_matching_perm(perm, obj):
|
||||
return False
|
||||
return True
|
||||
|
||||
def time_until_expiration(self):
|
||||
"""Returns the number of remaining days until user's password expires.
|
||||
|
||||
Calculates the number days until the user must change their password,
|
||||
once the password expires the user will not able to log in until an
|
||||
admin changes its password.
|
||||
"""
|
||||
if self.password_expires_at is not None:
|
||||
expiration_date = datetime.datetime.strptime(
|
||||
self.password_expires_at, "%Y-%m-%dT%H:%M:%S.%f")
|
||||
return expiration_date - datetime.datetime.now()
|
||||
|
||||
class Meta(object):
|
||||
app_label = 'openstack_auth'
|
@ -1,562 +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 logging
|
||||
import re
|
||||
|
||||
from django.conf import settings
|
||||
from django.contrib import auth
|
||||
from django.contrib.auth import models
|
||||
from django.utils import timezone
|
||||
from keystoneauth1.identity import v2 as v2_auth
|
||||
from keystoneauth1.identity import v3 as v3_auth
|
||||
from keystoneauth1 import session
|
||||
from keystoneauth1 import token_endpoint
|
||||
from keystoneclient.v2_0 import client as client_v2
|
||||
from keystoneclient.v3 import client as client_v3
|
||||
from six.moves.urllib import parse as urlparse
|
||||
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
_TOKEN_TIMEOUT_MARGIN = getattr(settings, 'TOKEN_TIMEOUT_MARGIN', 0)
|
||||
|
||||
"""
|
||||
We need the request object to get the user, so we'll slightly modify the
|
||||
existing django.contrib.auth.get_user method. To do so we update the
|
||||
auth middleware to point to our overridden method.
|
||||
|
||||
Calling the "patch_middleware_get_user" method somewhere like our urls.py
|
||||
file takes care of hooking it in appropriately.
|
||||
"""
|
||||
|
||||
|
||||
def middleware_get_user(request):
|
||||
if not hasattr(request, '_cached_user'):
|
||||
request._cached_user = get_user(request)
|
||||
return request._cached_user
|
||||
|
||||
|
||||
def get_user(request):
|
||||
try:
|
||||
user_id = request.session[auth.SESSION_KEY]
|
||||
backend_path = request.session[auth.BACKEND_SESSION_KEY]
|
||||
backend = auth.load_backend(backend_path)
|
||||
backend.request = request
|
||||
user = backend.get_user(user_id) or models.AnonymousUser()
|
||||
except KeyError:
|
||||
user = models.AnonymousUser()
|
||||
return user
|
||||
|
||||
|
||||
def patch_middleware_get_user():
|
||||
# NOTE(adriant): We can't import middleware until our customer user model
|
||||
# is actually registered, otherwise a call to get_user_model within the
|
||||
# middleware module will fail.
|
||||
from django.contrib.auth import middleware
|
||||
middleware.get_user = middleware_get_user
|
||||
auth.get_user = get_user
|
||||
|
||||
|
||||
""" End Monkey-Patching. """
|
||||
|
||||
|
||||
def is_token_valid(token, margin=None):
|
||||
"""Timezone-aware checking of the auth token's expiration timestamp.
|
||||
|
||||
Returns ``True`` if the token has not yet expired, otherwise ``False``.
|
||||
|
||||
:param token: The openstack_auth.user.Token instance to check
|
||||
|
||||
:param margin:
|
||||
A time margin in seconds to subtract from the real token's validity.
|
||||
An example usage is that the token can be valid once the middleware
|
||||
passed, and invalid (timed-out) during a view rendering and this
|
||||
generates authorization errors during the view rendering.
|
||||
A default margin can be set by the TOKEN_TIMEOUT_MARGIN in the
|
||||
django settings.
|
||||
"""
|
||||
expiration = token.expires
|
||||
# In case we get an unparseable expiration timestamp, return False
|
||||
# so you can't have a "forever" token just by breaking the expires param.
|
||||
if expiration is None:
|
||||
return False
|
||||
if margin is None:
|
||||
margin = getattr(settings, 'TOKEN_TIMEOUT_MARGIN', 0)
|
||||
expiration = expiration - datetime.timedelta(seconds=margin)
|
||||
if settings.USE_TZ and timezone.is_naive(expiration):
|
||||
# Presumes that the Keystone is using UTC.
|
||||
expiration = timezone.make_aware(expiration, timezone.utc)
|
||||
return expiration > timezone.now()
|
||||
|
||||
|
||||
# From django.contrib.auth.views
|
||||
# Added in Django 1.4.3, 1.5b2
|
||||
# Vendored here for compatibility with old Django versions.
|
||||
def is_safe_url(url, host=None):
|
||||
"""Return ``True`` if the url is a safe redirection.
|
||||
|
||||
The safe redirection means that it doesn't point to a different host.
|
||||
Always returns ``False`` on an empty url.
|
||||
"""
|
||||
if not url:
|
||||
return False
|
||||
netloc = urlparse.urlparse(url)[1]
|
||||
return not netloc or netloc == host
|
||||
|
||||
|
||||
# DEPRECATED -- Mitaka
|
||||
# This method definition is included to prevent breaking backward compatibility
|
||||
# The original functionality was problematic and has been removed.
|
||||
def remove_project_cache(token):
|
||||
pass
|
||||
|
||||
|
||||
# Helper for figuring out keystone version
|
||||
# Implementation will change when API version discovery is available
|
||||
def get_keystone_version():
|
||||
return getattr(settings, 'OPENSTACK_API_VERSIONS', {}).get('identity', 2.0)
|
||||
|
||||
|
||||
def get_session():
|
||||
insecure = getattr(settings, 'OPENSTACK_SSL_NO_VERIFY', False)
|
||||
verify = getattr(settings, 'OPENSTACK_SSL_CACERT', True)
|
||||
|
||||
if insecure:
|
||||
verify = False
|
||||
|
||||
return session.Session(verify=verify)
|
||||
|
||||
|
||||
def get_keystone_client():
|
||||
if get_keystone_version() < 3:
|
||||
return client_v2
|
||||
else:
|
||||
return client_v3
|
||||
|
||||
|
||||
def is_token_deletion_disabled():
|
||||
LOG.warning("Deprecated TOKEN_DELETION_DISABLED setting is no longer used")
|
||||
return getattr(settings, 'TOKEN_DELETION_DISABLED', False)
|
||||
|
||||
|
||||
def is_websso_enabled():
|
||||
"""Websso is supported in Keystone version 3."""
|
||||
websso_enabled = getattr(settings, 'WEBSSO_ENABLED', False)
|
||||
keystonev3_plus = (get_keystone_version() >= 3)
|
||||
return websso_enabled and keystonev3_plus
|
||||
|
||||
|
||||
def build_absolute_uri(request, relative_url):
|
||||
"""Ensure absolute_uri are relative to WEBROOT."""
|
||||
webroot = getattr(settings, 'WEBROOT', '')
|
||||
if webroot.endswith("/") and relative_url.startswith("/"):
|
||||
webroot = webroot[:-1]
|
||||
|
||||
return request.build_absolute_uri(webroot + relative_url)
|
||||
|
||||
|
||||
def get_websso_url(request, auth_url, websso_auth):
|
||||
"""Return the keystone endpoint for initiating WebSSO.
|
||||
|
||||
Generate the keystone WebSSO endpoint that will redirect the user
|
||||
to the login page of the federated identity provider.
|
||||
|
||||
Based on the authentication type selected by the user in the login
|
||||
form, it will construct the keystone WebSSO endpoint.
|
||||
|
||||
:param request: Django http request object.
|
||||
:type request: django.http.HttpRequest
|
||||
:param auth_url: Keystone endpoint configured in the horizon setting.
|
||||
The value is derived from:
|
||||
- OPENSTACK_KEYSTONE_URL
|
||||
- AVAILABLE_REGIONS
|
||||
:type auth_url: string
|
||||
:param websso_auth: Authentication type selected by the user from the
|
||||
login form. The value is derived from the horizon
|
||||
setting WEBSSO_CHOICES.
|
||||
:type websso_auth: string
|
||||
|
||||
Example of horizon WebSSO setting::
|
||||
|
||||
WEBSSO_CHOICES = (
|
||||
("credentials", "Keystone Credentials"),
|
||||
("oidc", "OpenID Connect"),
|
||||
("saml2", "Security Assertion Markup Language"),
|
||||
("acme_oidc", "ACME - OpenID Connect"),
|
||||
("acme_saml2", "ACME - SAML2")
|
||||
)
|
||||
|
||||
WEBSSO_IDP_MAPPING = {
|
||||
"acme_oidc": ("acme", "oidc"),
|
||||
"acme_saml2": ("acme", "saml2")
|
||||
}
|
||||
}
|
||||
|
||||
The value of websso_auth will be looked up in the WEBSSO_IDP_MAPPING
|
||||
dictionary, if a match is found it will return a IdP specific WebSSO
|
||||
endpoint using the values found in the mapping.
|
||||
|
||||
The value in WEBSSO_IDP_MAPPING is expected to be a tuple formatted as
|
||||
(<idp_id>, <protocol_id>). Using the values found, a IdP/protocol
|
||||
specific URL will be constructed:
|
||||
|
||||
/auth/OS-FEDERATION/identity_providers/<idp_id>
|
||||
/protocols/<protocol_id>/websso
|
||||
|
||||
If no value is found from the WEBSSO_IDP_MAPPING dictionary, it will
|
||||
treat the value as the global WebSSO protocol <protocol_id> and
|
||||
construct the WebSSO URL by:
|
||||
|
||||
/auth/OS-FEDERATION/websso/<protocol_id>
|
||||
|
||||
:returns: Keystone WebSSO endpoint.
|
||||
:rtype: string
|
||||
|
||||
"""
|
||||
origin = build_absolute_uri(request, '/auth/websso/')
|
||||
idp_mapping = getattr(settings, 'WEBSSO_IDP_MAPPING', {})
|
||||
idp_id, protocol_id = idp_mapping.get(websso_auth,
|
||||
(None, websso_auth))
|
||||
|
||||
if idp_id:
|
||||
# Use the IDP specific WebSSO endpoint
|
||||
url = ('%s/auth/OS-FEDERATION/identity_providers/%s'
|
||||
'/protocols/%s/websso?origin=%s' %
|
||||
(auth_url, idp_id, protocol_id, origin))
|
||||
else:
|
||||
# If no IDP mapping found for the identifier,
|
||||
# perform WebSSO by protocol.
|
||||
url = ('%s/auth/OS-FEDERATION/websso/%s?origin=%s' %
|
||||
(auth_url, protocol_id, origin))
|
||||
|
||||
return url
|
||||
|
||||
|
||||
def has_in_url_path(url, subs):
|
||||
"""Test if any of `subs` strings is present in the `url` path."""
|
||||
scheme, netloc, path, query, fragment = urlparse.urlsplit(url)
|
||||
return any([sub in path for sub in subs])
|
||||
|
||||
|
||||
def url_path_replace(url, old, new, count=None):
|
||||
"""Return a copy of url with replaced path.
|
||||
|
||||
Return a copy of url with all occurrences of old replaced by new in the url
|
||||
path. If the optional argument count is given, only the first count
|
||||
occurrences are replaced.
|
||||
"""
|
||||
args = []
|
||||
scheme, netloc, path, query, fragment = urlparse.urlsplit(url)
|
||||
if count is not None:
|
||||
args.append(count)
|
||||
return urlparse.urlunsplit((
|
||||
scheme, netloc, path.replace(old, new, *args), query, fragment))
|
||||
|
||||
|
||||
def url_path_append(url, suffix):
|
||||
scheme, netloc, path, query, fragment = urlparse.urlsplit(url)
|
||||
path = (path + suffix).replace('//', '/')
|
||||
return urlparse.urlunsplit((scheme, netloc, path, query, fragment))
|
||||
|
||||
|
||||
def _augment_url_with_version(auth_url):
|
||||
"""Optionally augment auth_url path with version suffix.
|
||||
|
||||
Check if path component already contains version suffix and if it does
|
||||
not, append version suffix to the end of path, not erasing the previous
|
||||
path contents, since keystone web endpoint (like /identity) could be
|
||||
there. Keystone version needs to be added to endpoint because as of Kilo,
|
||||
the identity URLs returned by Keystone might no longer contain API
|
||||
versions, leaving the version choice up to the user.
|
||||
"""
|
||||
if has_in_url_path(auth_url, ["/v2.0", "/v3"]):
|
||||
return auth_url
|
||||
|
||||
if get_keystone_version() >= 3:
|
||||
return url_path_append(auth_url, "/v3")
|
||||
else:
|
||||
return url_path_append(auth_url, "/v2.0")
|
||||
|
||||
|
||||
# TODO(tsufiev): remove this legacy version as soon as Horizon switches to
|
||||
# the new fix_auth_url_version_prefix() call
|
||||
def fix_auth_url_version(auth_url):
|
||||
"""Fix up the auth url if an invalid or no version prefix was given.
|
||||
|
||||
People still give a v2 auth_url even when they specify that they want v3
|
||||
authentication. Fix the URL to say v3 in this case and add version if it is
|
||||
missing entirely. This should be smarter and use discovery.
|
||||
"""
|
||||
auth_url = _augment_url_with_version(auth_url)
|
||||
|
||||
if get_keystone_version() >= 3 and has_in_url_path(auth_url, ["/v2.0"]):
|
||||
LOG.warning("The Keystone URL (either in Horizon settings or in "
|
||||
"service catalog) points to a v2.0 Keystone endpoint, "
|
||||
"but v3 is specified as the API version to use by "
|
||||
"Horizon. Using v3 endpoint for authentication.")
|
||||
auth_url = url_path_replace(auth_url, "/v2.0", "/v3", 1)
|
||||
|
||||
return auth_url
|
||||
|
||||
|
||||
def fix_auth_url_version_prefix(auth_url):
|
||||
"""Fix up the auth url if an invalid or no version prefix was given.
|
||||
|
||||
People still give a v2 auth_url even when they specify that they want v3
|
||||
authentication. Fix the URL to say v3 in this case and add version if it is
|
||||
missing entirely. This should be smarter and use discovery.
|
||||
"""
|
||||
auth_url = _augment_url_with_version(auth_url)
|
||||
|
||||
url_fixed = False
|
||||
if get_keystone_version() >= 3 and has_in_url_path(auth_url, ["/v2.0"]):
|
||||
url_fixed = True
|
||||
auth_url = url_path_replace(auth_url, "/v2.0", "/v3", 1)
|
||||
|
||||
return auth_url, url_fixed
|
||||
|
||||
|
||||
def clean_up_auth_url(auth_url):
|
||||
"""Clean up the auth url to extract the exact Keystone URL"""
|
||||
|
||||
# NOTE(mnaser): This drops the query and fragment because we're only
|
||||
# trying to extract the Keystone URL.
|
||||
scheme, netloc, path, query, fragment = urlparse.urlsplit(auth_url)
|
||||
return urlparse.urlunsplit((
|
||||
scheme, netloc, re.sub(r'/auth.*', '', path), '', ''))
|
||||
|
||||
|
||||
def get_token_auth_plugin(auth_url, token, project_id=None, domain_name=None):
|
||||
if get_keystone_version() >= 3:
|
||||
if domain_name:
|
||||
return v3_auth.Token(auth_url=auth_url,
|
||||
token=token,
|
||||
domain_name=domain_name,
|
||||
reauthenticate=False)
|
||||
else:
|
||||
return v3_auth.Token(auth_url=auth_url,
|
||||
token=token,
|
||||
project_id=project_id,
|
||||
reauthenticate=False)
|
||||
else:
|
||||
return v2_auth.Token(auth_url=auth_url,
|
||||
token=token,
|
||||
tenant_id=project_id,
|
||||
reauthenticate=False)
|
||||
|
||||
|
||||
def get_project_list(*args, **kwargs):
|
||||
is_federated = kwargs.get('is_federated', False)
|
||||
sess = kwargs.get('session') or get_session()
|
||||
auth_url, _ = fix_auth_url_version_prefix(kwargs['auth_url'])
|
||||
auth = token_endpoint.Token(auth_url, kwargs['token'])
|
||||
client = get_keystone_client().Client(session=sess, auth=auth)
|
||||
|
||||
if get_keystone_version() < 3:
|
||||
projects = client.tenants.list()
|
||||
elif is_federated:
|
||||
projects = client.federation.projects.list()
|
||||
else:
|
||||
projects = client.projects.list(user=kwargs.get('user_id'))
|
||||
|
||||
projects.sort(key=lambda project: project.name.lower())
|
||||
return projects
|
||||
|
||||
|
||||
def default_services_region(service_catalog, request=None,
|
||||
selected_region=None):
|
||||
"""Returns the first endpoint region for first non-identity service.
|
||||
|
||||
Extracted from the service catalog.
|
||||
"""
|
||||
if service_catalog:
|
||||
available_regions = [get_endpoint_region(endpoint) for service
|
||||
in service_catalog for endpoint
|
||||
in service.get('endpoints', [])
|
||||
if (service.get('type') is not None
|
||||
and service.get('type') != 'identity')]
|
||||
if not available_regions:
|
||||
# this is very likely an incomplete keystone setup
|
||||
LOG.warning('No regions could be found excluding identity.')
|
||||
available_regions = [get_endpoint_region(endpoint) for service
|
||||
in service_catalog for endpoint
|
||||
in service.get('endpoints', [])]
|
||||
|
||||
if not available_regions:
|
||||
# if there are no region setup for any service endpoint,
|
||||
# this is a critical problem and it's not clear how this occurs
|
||||
LOG.error('No regions can be found in the service catalog.')
|
||||
return None
|
||||
|
||||
if request and selected_region is None:
|
||||
selected_region = request.COOKIES.get('services_region',
|
||||
available_regions[0])
|
||||
if selected_region not in available_regions:
|
||||
selected_region = available_regions[0]
|
||||
return selected_region
|
||||
return None
|
||||
|
||||
|
||||
def set_response_cookie(response, cookie_name, cookie_value):
|
||||
"""Common function for setting the cookie in the response.
|
||||
|
||||
Provides a common policy of setting cookies for last used project
|
||||
and region, can be reused in other locations.
|
||||
|
||||
This method will set the cookie to expire in 365 days.
|
||||
"""
|
||||
now = timezone.now()
|
||||
expire_date = now + datetime.timedelta(days=365)
|
||||
response.set_cookie(cookie_name, cookie_value, expires=expire_date)
|
||||
|
||||
|
||||
def get_endpoint_region(endpoint):
|
||||
"""Common function for getting the region from endpoint.
|
||||
|
||||
In Keystone V3, region has been deprecated in favor of
|
||||
region_id.
|
||||
|
||||
This method provides a way to get region that works for both
|
||||
Keystone V2 and V3.
|
||||
"""
|
||||
return endpoint.get('region_id') or endpoint.get('region')
|
||||
|
||||
|
||||
def using_cookie_backed_sessions():
|
||||
engine = getattr(settings, 'SESSION_ENGINE', '')
|
||||
return "signed_cookies" in engine
|
||||
|
||||
|
||||
def get_admin_roles():
|
||||
"""Common function for getting the admin roles from settings
|
||||
|
||||
:return:
|
||||
Set object including all admin roles.
|
||||
If there is no role, this will return empty::
|
||||
|
||||
{
|
||||
"foo", "bar", "admin"
|
||||
}
|
||||
|
||||
"""
|
||||
admin_roles = {role.lower() for role
|
||||
in getattr(settings, 'OPENSTACK_KEYSTONE_ADMIN_ROLES',
|
||||
['admin'])}
|
||||
return admin_roles
|
||||
|
||||
|
||||
def get_role_permission(role):
|
||||
"""Common function for getting the permission froms arg
|
||||
|
||||
This format is 'openstack.roles.xxx' and 'xxx' is a real role name.
|
||||
|
||||
:returns:
|
||||
String like "openstack.roles.admin"
|
||||
If role is None, this will return None.
|
||||
|
||||
"""
|
||||
return "openstack.roles.%s" % role.lower()
|
||||
|
||||
|
||||
def get_admin_permissions():
|
||||
"""Common function for getting the admin permissions from settings
|
||||
|
||||
This format is 'openstack.roles.xxx' and 'xxx' is a real role name.
|
||||
|
||||
:returns:
|
||||
Set object including all admin permission.
|
||||
If there is no permission, this will return empty::
|
||||
|
||||
{
|
||||
"openstack.roles.foo",
|
||||
"openstack.roles.bar",
|
||||
"openstack.roles.admin"
|
||||
}
|
||||
|
||||
"""
|
||||
return {get_role_permission(role) for role in get_admin_roles()}
|
||||
|
||||
|
||||
def get_client_ip(request):
|
||||
"""Return client ip address using SECURE_PROXY_ADDR_HEADER variable.
|
||||
|
||||
If not present or not defined on settings then REMOTE_ADDR is used.
|
||||
|
||||
:param request: Django http request object.
|
||||
:type request: django.http.HttpRequest
|
||||
|
||||
:returns: Possible client ip address
|
||||
:rtype: string
|
||||
"""
|
||||
_SECURE_PROXY_ADDR_HEADER = getattr(
|
||||
settings, 'SECURE_PROXY_ADDR_HEADER', False
|
||||
)
|
||||
if _SECURE_PROXY_ADDR_HEADER:
|
||||
return request.META.get(
|
||||
_SECURE_PROXY_ADDR_HEADER,
|
||||
request.META.get('REMOTE_ADDR')
|
||||
)
|
||||
return request.META.get('REMOTE_ADDR')
|
||||
|
||||
|
||||
def store_initial_k2k_session(auth_url, request, scoped_auth_ref,
|
||||
unscoped_auth_ref):
|
||||
"""Stores session variables if there are k2k service providers
|
||||
|
||||
This stores variables related to Keystone2Keystone federation. This
|
||||
function gets skipped if there are no Keystone service providers.
|
||||
An unscoped token to the identity provider keystone gets stored
|
||||
so that it can be used to do federated login into the service
|
||||
providers when switching keystone providers.
|
||||
The settings file can be configured to set the display name
|
||||
of the local (identity provider) keystone by setting
|
||||
KEYSTONE_PROVIDER_IDP_NAME. The KEYSTONE_PROVIDER_IDP_ID settings
|
||||
variable is used for comparison against the service providers.
|
||||
It should not conflict with any of the service provider ids.
|
||||
|
||||
:param auth_url: base token auth url
|
||||
:param request: Django http request object
|
||||
:param scoped_auth_ref: Scoped Keystone access info object
|
||||
:param unscoped_auth_ref: Unscoped Keystone access info object
|
||||
"""
|
||||
keystone_provider_id = request.session.get('keystone_provider_id', None)
|
||||
if keystone_provider_id:
|
||||
return None
|
||||
|
||||
providers = getattr(scoped_auth_ref, 'service_providers', None)
|
||||
if providers:
|
||||
providers = getattr(providers, '_service_providers', None)
|
||||
|
||||
if providers:
|
||||
keystone_idp_name = getattr(settings, 'KEYSTONE_PROVIDER_IDP_NAME',
|
||||
'Local Keystone')
|
||||
keystone_idp_id = getattr(
|
||||
settings, 'KEYSTONE_PROVIDER_IDP_ID', 'localkeystone')
|
||||
keystone_identity_provider = {'name': keystone_idp_name,
|
||||
'id': keystone_idp_id}
|
||||
# (edtubill) We will use the IDs as the display names
|
||||
# We may want to be able to set display names in the future.
|
||||
keystone_providers = [
|
||||
{'name': provider_id, 'id': provider_id}
|
||||
for provider_id in providers]
|
||||
|
||||
keystone_providers.append(keystone_identity_provider)
|
||||
|
||||
# We treat the Keystone idp ID as None
|
||||
request.session['keystone_provider_id'] = keystone_idp_id
|
||||
request.session['keystone_providers'] = keystone_providers
|
||||
request.session['k2k_base_unscoped_token'] =\
|
||||
unscoped_auth_ref.auth_token
|
||||
request.session['k2k_auth_url'] = auth_url
|
@ -1,327 +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 logging
|
||||
|
||||
from django.conf import settings
|
||||
from django.contrib import auth
|
||||
from django.contrib.auth.decorators import login_required
|
||||
from django.contrib.auth import views as django_auth_views
|
||||
from django.contrib import messages
|
||||
from django import http as django_http
|
||||
from django import shortcuts
|
||||
from django.utils import functional
|
||||
from django.utils import http
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
from django.views.decorators.cache import never_cache
|
||||
from django.views.decorators.csrf import csrf_exempt
|
||||
from django.views.decorators.csrf import csrf_protect
|
||||
from django.views.decorators.debug import sensitive_post_parameters
|
||||
from keystoneauth1 import exceptions as keystone_exceptions
|
||||
import six
|
||||
|
||||
from openstack_auth import exceptions
|
||||
from openstack_auth import forms
|
||||
from openstack_auth import plugin
|
||||
|
||||
# This is historic and is added back in to not break older versions of
|
||||
# Horizon, fix to Horizon to remove this requirement was committed in
|
||||
# Juno
|
||||
from openstack_auth.forms import Login # noqa:F401
|
||||
from openstack_auth import user as auth_user
|
||||
from openstack_auth import utils
|
||||
|
||||
try:
|
||||
is_safe_url = http.is_safe_url
|
||||
except AttributeError:
|
||||
is_safe_url = utils.is_safe_url
|
||||
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
@sensitive_post_parameters()
|
||||
@csrf_protect
|
||||
@never_cache
|
||||
def login(request, template_name=None, extra_context=None, **kwargs):
|
||||
"""Logs a user in using the :class:`~openstack_auth.forms.Login` form."""
|
||||
|
||||
# If the user enabled websso and selects default protocol
|
||||
# from the dropdown, We need to redirect user to the websso url
|
||||
if request.method == 'POST':
|
||||
auth_type = request.POST.get('auth_type', 'credentials')
|
||||
if utils.is_websso_enabled() and auth_type != 'credentials':
|
||||
auth_url = request.POST.get('region')
|
||||
url = utils.get_websso_url(request, auth_url, auth_type)
|
||||
return shortcuts.redirect(url)
|
||||
|
||||
if not request.is_ajax():
|
||||
# If the user is already authenticated, redirect them to the
|
||||
# dashboard straight away, unless the 'next' parameter is set as it
|
||||
# usually indicates requesting access to a page that requires different
|
||||
# permissions.
|
||||
if (request.user.is_authenticated() and
|
||||
auth.REDIRECT_FIELD_NAME not in request.GET and
|
||||
auth.REDIRECT_FIELD_NAME not in request.POST):
|
||||
return shortcuts.redirect(settings.LOGIN_REDIRECT_URL)
|
||||
|
||||
# Get our initial region for the form.
|
||||
initial = {}
|
||||
current_region = request.session.get('region_endpoint', None)
|
||||
requested_region = request.GET.get('region', None)
|
||||
regions = dict(getattr(settings, "AVAILABLE_REGIONS", []))
|
||||
if requested_region in regions and requested_region != current_region:
|
||||
initial.update({'region': requested_region})
|
||||
|
||||
if request.method == "POST":
|
||||
form = functional.curry(forms.Login)
|
||||
else:
|
||||
form = functional.curry(forms.Login, initial=initial)
|
||||
|
||||
if extra_context is None:
|
||||
extra_context = {'redirect_field_name': auth.REDIRECT_FIELD_NAME}
|
||||
|
||||
if not template_name:
|
||||
if request.is_ajax():
|
||||
template_name = 'auth/_login.html'
|
||||
extra_context['hide'] = True
|
||||
else:
|
||||
template_name = 'auth/login.html'
|
||||
|
||||
res = django_auth_views.login(request,
|
||||
template_name=template_name,
|
||||
authentication_form=form,
|
||||
extra_context=extra_context,
|
||||
**kwargs)
|
||||
# Save the region in the cookie, this is used as the default
|
||||
# selected region next time the Login form loads.
|
||||
if request.method == "POST":
|
||||
utils.set_response_cookie(res, 'login_region',
|
||||
request.POST.get('region', ''))
|
||||
utils.set_response_cookie(res, 'login_domain',
|
||||
request.POST.get('domain', ''))
|
||||
|
||||
# Set the session data here because django's session key rotation
|
||||
# will erase it if we set it earlier.
|
||||
if request.user.is_authenticated():
|
||||
auth_user.set_session_from_user(request, request.user)
|
||||
regions = dict(forms.Login.get_region_choices())
|
||||
region = request.user.endpoint
|
||||
login_region = request.POST.get('region')
|
||||
region_name = regions.get(login_region)
|
||||
request.session['region_endpoint'] = region
|
||||
request.session['region_name'] = region_name
|
||||
expiration_time = request.user.time_until_expiration()
|
||||
threshold_days = getattr(
|
||||
settings, 'PASSWORD_EXPIRES_WARNING_THRESHOLD_DAYS', -1)
|
||||
if expiration_time is not None and \
|
||||
expiration_time.days <= threshold_days:
|
||||
expiration_time = str(expiration_time).rsplit(':', 1)[0]
|
||||
msg = (_('Please consider changing your password, it will expire'
|
||||
' in %s minutes') %
|
||||
expiration_time).replace(':', ' Hours and ')
|
||||
messages.warning(request, msg)
|
||||
return res
|
||||
|
||||
|
||||
@sensitive_post_parameters()
|
||||
@csrf_exempt
|
||||
@never_cache
|
||||
def websso(request):
|
||||
"""Logs a user in using a token from Keystone's POST."""
|
||||
referer = request.META.get('HTTP_REFERER', settings.OPENSTACK_KEYSTONE_URL)
|
||||
auth_url = utils.clean_up_auth_url(referer)
|
||||
token = request.POST.get('token')
|
||||
try:
|
||||
request.user = auth.authenticate(request=request, auth_url=auth_url,
|
||||
token=token)
|
||||
except exceptions.KeystoneAuthException as exc:
|
||||
msg = 'Login failed: %s' % six.text_type(exc)
|
||||
res = django_http.HttpResponseRedirect(settings.LOGIN_URL)
|
||||
res.set_cookie('logout_reason', msg, max_age=10)
|
||||
return res
|
||||
|
||||
auth_user.set_session_from_user(request, request.user)
|
||||
auth.login(request, request.user)
|
||||
if request.session.test_cookie_worked():
|
||||
request.session.delete_test_cookie()
|
||||
return django_http.HttpResponseRedirect(settings.LOGIN_REDIRECT_URL)
|
||||
|
||||
|
||||
def logout(request, login_url=None, **kwargs):
|
||||
"""Logs out the user if he is logged in. Then redirects to the log-in page.
|
||||
|
||||
:param login_url:
|
||||
Once logged out, defines the URL where to redirect after login
|
||||
|
||||
:param kwargs:
|
||||
see django.contrib.auth.views.logout_then_login extra parameters.
|
||||
|
||||
"""
|
||||
msg = 'Logging out user "%(username)s".' % \
|
||||
{'username': request.user.username}
|
||||
LOG.info(msg)
|
||||
|
||||
""" Securely logs a user out. """
|
||||
return django_auth_views.logout_then_login(request, login_url=login_url,
|
||||
**kwargs)
|
||||
|
||||
|
||||
def delete_token(endpoint, token_id):
|
||||
"""Delete a token."""
|
||||
LOG.warning("The delete_token method is deprecated and now does nothing")
|
||||
|
||||
|
||||
@login_required
|
||||
def switch(request, tenant_id, redirect_field_name=auth.REDIRECT_FIELD_NAME):
|
||||
"""Switches an authenticated user from one project to another."""
|
||||
LOG.debug('Switching to tenant %s for user "%s".'
|
||||
% (tenant_id, request.user.username))
|
||||
|
||||
endpoint, __ = utils.fix_auth_url_version_prefix(request.user.endpoint)
|
||||
session = utils.get_session()
|
||||
# Keystone can be configured to prevent exchanging a scoped token for
|
||||
# another token. Always use the unscoped token for requesting a
|
||||
# scoped token.
|
||||
unscoped_token = request.user.unscoped_token
|
||||
auth = utils.get_token_auth_plugin(auth_url=endpoint,
|
||||
token=unscoped_token,
|
||||
project_id=tenant_id)
|
||||
|
||||
try:
|
||||
auth_ref = auth.get_access(session)
|
||||
msg = 'Project switch successful for user "%(username)s".' % \
|
||||
{'username': request.user.username}
|
||||
LOG.info(msg)
|
||||
except keystone_exceptions.ClientException:
|
||||
msg = (
|
||||
_('Project switch failed for user "%(username)s".') %
|
||||
{'username': request.user.username})
|
||||
messages.error(request, msg)
|
||||
auth_ref = None
|
||||
LOG.exception('An error occurred while switching sessions.')
|
||||
|
||||
# Ensure the user-originating redirection url is safe.
|
||||
# Taken from django.contrib.auth.views.login()
|
||||
redirect_to = request.GET.get(redirect_field_name, '')
|
||||
if not is_safe_url(url=redirect_to, host=request.get_host()):
|
||||
redirect_to = settings.LOGIN_REDIRECT_URL
|
||||
|
||||
if auth_ref:
|
||||
user = auth_user.create_user_from_token(
|
||||
request,
|
||||
auth_user.Token(auth_ref, unscoped_token=unscoped_token),
|
||||
endpoint)
|
||||
auth_user.set_session_from_user(request, user)
|
||||
message = (
|
||||
_('Switch to project "%(project_name)s" successful.') %
|
||||
{'project_name': request.user.project_name})
|
||||
messages.success(request, message)
|
||||
response = shortcuts.redirect(redirect_to)
|
||||
utils.set_response_cookie(response, 'recent_project',
|
||||
request.user.project_id)
|
||||
return response
|
||||
|
||||
|
||||
@login_required
|
||||
def switch_region(request, region_name,
|
||||
redirect_field_name=auth.REDIRECT_FIELD_NAME):
|
||||
"""Switches the user's region for all services except Identity service.
|
||||
|
||||
The region will be switched if the given region is one of the regions
|
||||
available for the scoped project. Otherwise the region is not switched.
|
||||
"""
|
||||
if region_name in request.user.available_services_regions:
|
||||
request.session['services_region'] = region_name
|
||||
LOG.debug('Switching services region to %s for user "%s".'
|
||||
% (region_name, request.user.username))
|
||||
|
||||
redirect_to = request.GET.get(redirect_field_name, '')
|
||||
if not is_safe_url(url=redirect_to, host=request.get_host()):
|
||||
redirect_to = settings.LOGIN_REDIRECT_URL
|
||||
|
||||
response = shortcuts.redirect(redirect_to)
|
||||
utils.set_response_cookie(response, 'services_region',
|
||||
request.session['services_region'])
|
||||
return response
|
||||
|
||||
|
||||
@login_required
|
||||
def switch_keystone_provider(request, keystone_provider=None,
|
||||
redirect_field_name=auth.REDIRECT_FIELD_NAME):
|
||||
"""Switches the user's keystone provider using K2K Federation
|
||||
|
||||
If keystone_provider is given then we switch the user to
|
||||
the keystone provider using K2K federation. Otherwise if keystone_provider
|
||||
is None then we switch the user back to the Identity Provider Keystone
|
||||
which a non federated token auth will be used.
|
||||
"""
|
||||
base_token = request.session.get('k2k_base_unscoped_token', None)
|
||||
k2k_auth_url = request.session.get('k2k_auth_url', None)
|
||||
keystone_providers = request.session.get('keystone_providers', None)
|
||||
|
||||
if not base_token or not k2k_auth_url:
|
||||
msg = _('K2K Federation not setup for this session')
|
||||
raise exceptions.KeystoneAuthException(msg)
|
||||
|
||||
redirect_to = request.GET.get(redirect_field_name, '')
|
||||
if not is_safe_url(url=redirect_to, host=request.get_host()):
|
||||
redirect_to = settings.LOGIN_REDIRECT_URL
|
||||
|
||||
unscoped_auth_ref = None
|
||||
keystone_idp_id = getattr(
|
||||
settings, 'KEYSTONE_PROVIDER_IDP_ID', 'localkeystone')
|
||||
|
||||
if keystone_provider == keystone_idp_id:
|
||||
current_plugin = plugin.TokenPlugin()
|
||||
unscoped_auth = current_plugin.get_plugin(auth_url=k2k_auth_url,
|
||||
token=base_token)
|
||||
else:
|
||||
# Switch to service provider using K2K federation
|
||||
plugins = [plugin.TokenPlugin()]
|
||||
current_plugin = plugin.K2KAuthPlugin()
|
||||
|
||||
unscoped_auth = current_plugin.get_plugin(
|
||||
auth_url=k2k_auth_url, service_provider=keystone_provider,
|
||||
plugins=plugins, token=base_token)
|
||||
|
||||
try:
|
||||
# Switch to identity provider using token auth
|
||||
unscoped_auth_ref = current_plugin.get_access_info(unscoped_auth)
|
||||
except exceptions.KeystoneAuthException as exc:
|
||||
msg = 'Switching to Keystone Provider %s has failed. %s' \
|
||||
% (keystone_provider, (six.text_type(exc)))
|
||||
messages.error(request, msg)
|
||||
|
||||
if unscoped_auth_ref:
|
||||
try:
|
||||
request.user = auth.authenticate(
|
||||
request=request, auth_url=unscoped_auth.auth_url,
|
||||
token=unscoped_auth_ref.auth_token)
|
||||
except exceptions.KeystoneAuthException as exc:
|
||||
msg = 'Keystone provider switch failed: %s' % six.text_type(exc)
|
||||
res = django_http.HttpResponseRedirect(settings.LOGIN_URL)
|
||||
res.set_cookie('logout_reason', msg, max_age=10)
|
||||
return res
|
||||
auth.login(request, request.user)
|
||||
auth_user.set_session_from_user(request, request.user)
|
||||
request.session['keystone_provider_id'] = keystone_provider
|
||||
request.session['keystone_providers'] = keystone_providers
|
||||
request.session['k2k_base_unscoped_token'] = base_token
|
||||
request.session['k2k_auth_url'] = k2k_auth_url
|
||||
message = (
|
||||
_('Switch to Keystone Provider "%(keystone_provider)s"'
|
||||
'successful.') % {'keystone_provider': keystone_provider})
|
||||
messages.success(request, message)
|
||||
|
||||
response = shortcuts.redirect(redirect_to)
|
||||
return response
|
@ -1,10 +0,0 @@
|
||||
# The order of packages is significant, because pip processes them in the order
|
||||
# of appearance. Changing the order has an impact on the overall integration
|
||||
# process, which may cause wedges in the gate later.
|
||||
pbr!=2.1.0,>=2.0.0 # Apache-2.0
|
||||
Django<2.0,>=1.8 # BSD
|
||||
oslo.config!=4.3.0,!=4.4.0,>=4.0.0 # Apache-2.0
|
||||
oslo.policy>=1.23.0 # Apache-2.0
|
||||
python-keystoneclient>=3.8.0 # Apache-2.0
|
||||
keystoneauth1>=2.21.0 # Apache-2.0
|
||||
six>=1.9.0 # MIT
|
53
setup.cfg
53
setup.cfg
@ -1,53 +0,0 @@
|
||||
[metadata]
|
||||
name = django_openstack_auth
|
||||
summary = Django authentication backend for use with OpenStack Identity
|
||||
description-file =
|
||||
README.rst
|
||||
author = OpenStack
|
||||
author-email = openstack-dev@lists.openstack.org
|
||||
home-page = http://www.openstack.org/
|
||||
classifier =
|
||||
Development Status :: 5 - Production/Stable
|
||||
Environment :: OpenStack
|
||||
Framework :: Django
|
||||
Intended Audience :: Developers
|
||||
Intended Audience :: Information Technology
|
||||
Intended Audience :: System Administrators
|
||||
License :: OSI Approved :: Apache Software License
|
||||
Operating System :: OS Independent
|
||||
Operating System :: POSIX :: Linux
|
||||
Programming Language :: Python
|
||||
Programming Language :: Python :: 2
|
||||
Programming Language :: Python :: 2.7
|
||||
Programming Language :: Python :: 3
|
||||
Programming Language :: Python :: 3.4
|
||||
Programming Language :: Python :: 3.5
|
||||
Topic :: Internet :: WWW/HTTP
|
||||
|
||||
[files]
|
||||
packages =
|
||||
openstack_auth
|
||||
|
||||
[build_sphinx]
|
||||
all-files = 1
|
||||
warning-is-error = 1
|
||||
build-dir = doc/build
|
||||
source-dir = doc/source
|
||||
|
||||
[nosetests]
|
||||
verbosity=2
|
||||
detailed-errors=1
|
||||
|
||||
[extract_messages]
|
||||
keywords = _ gettext ngettext l_ lazy_gettext
|
||||
mapping_file = babel.cfg
|
||||
output_file = openstack_auth/locale/django.pot
|
||||
|
||||
[compile_catalog]
|
||||
directory = openstack_auth/locale
|
||||
domain = django
|
||||
|
||||
[update_catalog]
|
||||
domain = django
|
||||
output_dir = openstack_auth/locale
|
||||
input_file = openstack_auth/locale/django.pot
|
29
setup.py
29
setup.py
@ -1,29 +0,0 @@
|
||||
# Copyright (c) 2013 Hewlett-Packard Development Company, L.P.
|
||||
#
|
||||
# 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 FILE IS MANAGED BY THE GLOBAL REQUIREMENTS REPO - DO NOT EDIT
|
||||
import setuptools
|
||||
|
||||
# In python < 2.7.4, a lazy loading of package `pbr` will break
|
||||
# setuptools if some other modules registered functions in `atexit`.
|
||||
# solution from: http://bugs.python.org/issue15881#msg170215
|
||||
try:
|
||||
import multiprocessing # noqa
|
||||
except ImportError:
|
||||
pass
|
||||
|
||||
setuptools.setup(
|
||||
setup_requires=['pbr>=2.0.0'],
|
||||
pbr=True)
|
@ -1,11 +0,0 @@
|
||||
# The order of packages is significant, because pip processes them in the order
|
||||
# of appearance. Changing the order has an impact on the overall integration
|
||||
# process, which may cause wedges in the gate later.
|
||||
hacking!=0.13.0,<0.14,>=0.12.0 # Apache-2.0
|
||||
Babel!=2.4.0,>=2.3.4 # BSD
|
||||
coverage!=4.4,>=4.0 # Apache-2.0
|
||||
mock>=2.0 # BSD
|
||||
mox3!=0.19.0,>=0.7.0 # Apache-2.0
|
||||
sphinx>=1.6.2 # BSD
|
||||
openstackdocstheme>=1.11.0 # Apache-2.0
|
||||
testscenarios>=0.4 # Apache-2.0/BSD
|
@ -1,30 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# Client constraint file contains this client version pin that is in conflict
|
||||
# with installing the client from source. We should remove the version pin in
|
||||
# the constraints file before applying it for from-source installation.
|
||||
|
||||
CONSTRAINTS_FILE="$1"
|
||||
shift 1
|
||||
|
||||
set -e
|
||||
|
||||
# NOTE(tonyb): Place this in the tox enviroment's log dir so it will get
|
||||
# published to logs.openstack.org for easy debugging.
|
||||
localfile="$VIRTUAL_ENV/log/upper-constraints.txt"
|
||||
|
||||
if [[ "$CONSTRAINTS_FILE" != http* ]]; then
|
||||
CONSTRAINTS_FILE="file://$CONSTRAINTS_FILE"
|
||||
fi
|
||||
# NOTE(tonyb): need to add curl to bindep.txt if the project supports bindep
|
||||
curl "$CONSTRAINTS_FILE" --insecure --progress-bar --output "$localfile"
|
||||
|
||||
pip install -c"$localfile" openstack-requirements
|
||||
|
||||
# This is the main purpose of the script: Allow local installation of
|
||||
# the current repo. It is listed in constraints file and thus any
|
||||
# install will be constrained and we need to unconstrain it.
|
||||
edit-constraints "$localfile" -- "$CLIENT_NAME"
|
||||
|
||||
pip install -c"$localfile" -U "$@"
|
||||
exit $?
|
64
tox.ini
64
tox.ini
@ -1,64 +0,0 @@
|
||||
[tox]
|
||||
minversion = 2.0
|
||||
skipsdist = True
|
||||
envlist = py27,py27dj18,pep8,py35
|
||||
|
||||
[testenv]
|
||||
usedevelop = True
|
||||
install_command = {toxinidir}/tools/tox_install.sh {env:UPPER_CONSTRAINTS_FILE:https://git.openstack.org/cgit/openstack/requirements/plain/upper-constraints.txt} {opts} {packages}
|
||||
setenv = {[env-common]setenv}
|
||||
NOSE_WITH_OPENSTACK=1
|
||||
NOSE_OPENSTACK_COLOR=1
|
||||
NOSE_OPENSTACK_RED=0.05
|
||||
NOSE_OPENSTACK_YELLOW=0.025
|
||||
NOSE_OPENSTACK_SHOW_ELAPSED=1
|
||||
deps = -r{toxinidir}/requirements.txt
|
||||
-r{toxinidir}/test-requirements.txt
|
||||
commands = python openstack_auth/tests/run_tests.py {posargs}
|
||||
|
||||
[env-common]
|
||||
setenv = VIRTUAL_ENV={envdir}
|
||||
BRANCH_NAME=master
|
||||
CLIENT_NAME=django_openstack_auth
|
||||
|
||||
[testenv:cover]
|
||||
setenv = {[env-common]setenv}
|
||||
DJANGO_SETTINGS_MODULE=openstack_auth.tests.settings
|
||||
commands =
|
||||
python -m coverage erase
|
||||
python -m coverage run openstack_auth/tests/run_tests.py {posargs}
|
||||
python -m coverage html --include='openstack_auth/*' --omit='openstack_auth/tests/*' -d 'reports'
|
||||
python -m coverage xml --include='openstack_auth/*' --omit='openstack_auth/tests/*'
|
||||
|
||||
# Django 1.8 is LTS
|
||||
[testenv:py27dj18]
|
||||
commands = pip install django>=1.8,<1.9
|
||||
python openstack_auth/tests/run_tests.py {posargs}
|
||||
|
||||
[testenv:py27dj19]
|
||||
commands = pip install django>=1.9,<1.10
|
||||
python openstack_auth/tests/run_tests.py {posargs}
|
||||
|
||||
[testenv:py27dj110]
|
||||
commands = pip install django>=1.10,<1.11
|
||||
python openstack_auth/tests/run_tests.py {posargs}
|
||||
|
||||
[testenv:py27dj111]
|
||||
commands = pip install django>=1.11,<2.0
|
||||
python openstack_auth/tests/run_tests.py {posargs}
|
||||
|
||||
[testenv:pep8]
|
||||
setenv = {[env-common]setenv}
|
||||
DJANGO_SETTINGS_MODULE=openstack_auth.tests.settings
|
||||
commands = flake8
|
||||
|
||||
[testenv:venv]
|
||||
commands = {posargs}
|
||||
|
||||
[testenv:docs]
|
||||
setenv = {[env-common]setenv}
|
||||
commands = python setup.py build_sphinx
|
||||
|
||||
[flake8]
|
||||
builtins = _
|
||||
exclude = .venv,.git,.tox,dist,doc,*openstack/common*,*lib/python*,*egg,build,panel_template,dash_template,local_settings.py
|
Loading…
x
Reference in New Issue
Block a user